diff --git a/api/current.txt b/api/current.txt index af8251a9b9fe4..c9c9e01e8af04 100644 --- a/api/current.txt +++ b/api/current.txt @@ -48040,6 +48040,8 @@ package android.view.accessibility { method public void setPackageName(java.lang.CharSequence); method public void writeToParcel(android.os.Parcel, int); field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4 + field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10 + field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20 field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8 field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1 field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2 diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0bf690457e1e9..bd3be1b0445ec 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3226,6 +3226,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static final int PFLAG3_SCREEN_READER_FOCUSABLE = 0x10000000; + /** + * The last aggregated visibility. Used to detect when it truly changes. + */ + private static final int PFLAG3_AGGREGATED_VISIBLE = 0x20000000; + /* End of masks for mPrivateFlags3 */ /** @@ -7366,7 +7371,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) { - if (!isShown()) { + // Panes disappearing are relevant even if though the view is no longer visible. + boolean isWindowStateChanged = + (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + boolean isWindowDisappearedEvent = isWindowStateChanged && ((event.getContentChangeTypes() + & AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED) != 0); + if (!isShown() && !isWindowDisappearedEvent) { return; } onInitializeAccessibilityEvent(event); @@ -7474,6 +7484,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) { + if ((event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) + && !TextUtils.isEmpty(getAccessibilityPaneTitle())) { + event.getText().add(getAccessibilityPaneTitle()); + } } /** @@ -11599,6 +11613,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) { return; } + // Changes to views with a pane title count as window state changes, as the pane title + // marks them as significant parts of the UI. + if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) { + final AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + event.setContentChangeTypes(changeType); + onPopulateAccessibilityEvent(event); + if (mParent != null) { + try { + mParent.requestSendAccessibilityEvent(this, event); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + } + } + } + if (mParent != null) { try { mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType); @@ -12532,6 +12563,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @CallSuper public void onVisibilityAggregated(boolean isVisible) { + // Update our internal visibility tracking so we can detect changes + boolean oldVisible = (mPrivateFlags3 & PFLAG3_AGGREGATED_VISIBLE) != 0; + mPrivateFlags3 = isVisible ? (mPrivateFlags3 | PFLAG3_AGGREGATED_VISIBLE) + : (mPrivateFlags3 & ~PFLAG3_AGGREGATED_VISIBLE); if (isVisible && mAttachInfo != null) { initialAwakenScrollBars(); } @@ -12572,6 +12607,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } } + if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) { + if (isVisible != oldVisible) { + notifyAccessibilityStateChanged(isVisible + ? AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED + : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED); + } + } } /** diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index aa61926dcedca..e0f74a7d6dd6e 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -192,9 +192,11 @@ import java.util.List; * TRANSITION TYPES *
*- * Window state changed - represents the event of opening a - * {@link android.widget.PopupWindow}, {@link android.view.Menu}, - * {@link android.app.Dialog}, etc. + * Window state changed - represents the event of a change to a section of + * the user interface that is visually distinct. Should be sent from either the + * root view of a window or from a view that is marked as a pane + * {@link android.view.View#setAccessibilityPaneTitle(CharSequence)}. Not that changes + * to true windows are represented by {@link #TYPE_WINDOWS_CHANGED}. * Type: {@link #TYPE_WINDOW_STATE_CHANGED} * Properties: *
@@ -436,8 +438,10 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010; /** - * Represents the event of opening a {@link android.widget.PopupWindow}, - * {@link android.view.Menu}, {@link android.app.Dialog}, etc. + * Represents the event of a change to a visually distinct section of the user interface. + * These events should only be dispatched from {@link android.view.View}s that have + * accessibility pane titles, and replaces {@link #TYPE_WINDOW_CONTENT_CHANGED} for those + * sources. Details about the change are available from {@link #getContentChangeTypes()}. */ public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020; @@ -565,11 +569,29 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004; /** - * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: + * Change type for {@link #TYPE_WINDOW_STATE_CHANGED} event: * The node's pane title changed. */ public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 0x00000008; + /** + * Change type for {@link #TYPE_WINDOW_STATE_CHANGED} event: + * The node has a pane title, and either just appeared or just was assigned a title when it + * had none before. + */ + public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 0x00000010; + + /** + * Change type for {@link #TYPE_WINDOW_STATE_CHANGED} event: + * Can mean one of two slightly different things. The primary meaning is that the node has + * a pane title, and was removed from the node hierarchy. It will also be sent if the pane + * title is set to {@code null} after it contained a title. + * No source will be returned if the node is no longer on the screen. To make the change more + * clear for the user, the first entry in {@link #getText()} will return the value that would + * have been returned by {@code getSource().getPaneTitle()}. + */ + public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 0x00000020; + /** * Change type for {@link #TYPE_WINDOWS_CHANGED} event: * The window was added.