summaryrefslogtreecommitdiffstats
path: root/core/java/android/view
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2013-09-13 01:45:10 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-09-13 01:45:10 +0000
commit2a65a2839e9b3e59a72348ee088f86cda7df604a (patch)
treefd29d3025917df88f1854a79bd40bdd38506846e /core/java/android/view
parentb72ff5aa4b8be587bcea82761aa08cd8c14b8b48 (diff)
parent77e9a28e2faa36f127231b842476d47f9823a83a (diff)
downloadframeworks_base-2a65a2839e9b3e59a72348ee088f86cda7df604a.zip
frameworks_base-2a65a2839e9b3e59a72348ee088f86cda7df604a.tar.gz
frameworks_base-2a65a2839e9b3e59a72348ee088f86cda7df604a.tar.bz2
Merge "Add live region politeness to View, AccessibilityNodeInfo" into klp-dev
Diffstat (limited to 'core/java/android/view')
-rw-r--r--core/java/android/view/View.java180
-rw-r--r--core/java/android/view/ViewGroup.java11
-rw-r--r--core/java/android/view/ViewParent.java21
-rw-r--r--core/java/android/view/ViewRootImpl.java17
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java67
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java114
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfoCache.java8
7 files changed, 278 insertions, 140 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8f8f9c6..a5db6ee 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2134,6 +2134,50 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
<< PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
/**
+ * Shift for the bits in {@link #mPrivateFlags2} related to the
+ * "accessibilityLiveRegion" attribute.
+ */
+ static final int PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT = 22;
+
+ /**
+ * Live region mode specifying that accessibility services should not
+ * automatically announce changes to this view. This is the default live
+ * region mode for most views.
+ * <p>
+ * Use with {@link #setAccessibilityLiveRegion(int)}.
+ */
+ public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0x00000000;
+
+ /**
+ * Live region mode specifying that accessibility services should announce
+ * changes to this view.
+ * <p>
+ * Use with {@link #setAccessibilityLiveRegion(int)}.
+ */
+ public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 0x00000001;
+
+ /**
+ * Live region mode specifying that accessibility services should interrupt
+ * ongoing speech to immediately announce changes to this view.
+ * <p>
+ * Use with {@link #setAccessibilityLiveRegion(int)}.
+ */
+ public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 0x00000002;
+
+ /**
+ * The default whether the view is important for accessibility.
+ */
+ static final int ACCESSIBILITY_LIVE_REGION_DEFAULT = ACCESSIBILITY_LIVE_REGION_NONE;
+
+ /**
+ * Mask for obtaining the bits which specify a view's accessibility live
+ * region mode.
+ */
+ static final int PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK = (ACCESSIBILITY_LIVE_REGION_NONE
+ | ACCESSIBILITY_LIVE_REGION_POLITE | ACCESSIBILITY_LIVE_REGION_ASSERTIVE)
+ << PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT;
+
+ /**
* Flag indicating whether a view has accessibility focus.
*/
static final int PFLAG2_ACCESSIBILITY_FOCUSED = 0x04000000;
@@ -3763,6 +3807,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setImportantForAccessibility(a.getInt(attr,
IMPORTANT_FOR_ACCESSIBILITY_DEFAULT));
break;
+ case R.styleable.View_accessibilityLiveRegion:
+ setAccessibilityLiveRegion(a.getInt(attr, ACCESSIBILITY_LIVE_REGION_DEFAULT));
+ break;
}
}
@@ -4710,7 +4757,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (gainFocus) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
} else {
- notifyViewAccessibilityStateChangedIfNeeded();
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
InputMethodManager imm = InputMethodManager.peekInstance();
@@ -5431,7 +5479,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
notifySubtreeAccessibilityStateChangedIfNeeded();
} else {
- notifyViewAccessibilityStateChangedIfNeeded();
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
}
}
@@ -6954,6 +7003,58 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Sets the live region mode for this view. This indicates to accessibility
+ * services whether they should automatically notify the user about changes
+ * to the view's content description or text, or to the content descriptions
+ * or text of the view's children (where applicable).
+ * <p>
+ * For example, in a login screen with a TextView that displays an "incorrect
+ * password" notification, that view should be marked as a live region with
+ * mode {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
+ * <p>
+ * To disable change notifications for this view, use
+ * {@link #ACCESSIBILITY_LIVE_REGION_NONE}. This is the default live region
+ * mode for most views.
+ * <p>
+ * To indicate that the user should be notified of changes, use
+ * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
+ * <p>
+ * If the view's changes should interrupt ongoing speech and notify the user
+ * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}.
+ *
+ * @param mode The live region mode for this view, one of:
+ * <ul>
+ * <li>{@link #ACCESSIBILITY_LIVE_REGION_NONE}
+ * <li>{@link #ACCESSIBILITY_LIVE_REGION_POLITE}
+ * <li>{@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}
+ * </ul>
+ * @attr ref android.R.styleable#View_accessibilityLiveRegion
+ */
+ public void setAccessibilityLiveRegion(int mode) {
+ if (mode != getAccessibilityLiveRegion()) {
+ mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
+ mPrivateFlags2 |= (mode << PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT)
+ & PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
+ }
+
+ /**
+ * Gets the live region mode for this View.
+ *
+ * @return The live region mode for the view.
+ *
+ * @attr ref android.R.styleable#View_accessibilityLiveRegion
+ *
+ * @see #setAccessibilityLiveRegion(int)
+ */
+ public int getAccessibilityLiveRegion() {
+ return (mPrivateFlags2 & PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK)
+ >> PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT;
+ }
+
+ /**
* Sets how to determine whether this view is important for accessibility
* which is if it fires accessibility events and if it is reported to
* accessibility services that query the screen.
@@ -6975,7 +7076,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (oldIncludeForAccessibility != includeForAccessibility()) {
notifySubtreeAccessibilityStateChangedIfNeeded();
} else {
- notifyViewAccessibilityStateChangedIfNeeded();
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
}
@@ -6997,7 +7099,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return false;
case IMPORTANT_FOR_ACCESSIBILITY_AUTO:
return isActionableForAccessibility() || hasListenersForAccessibility()
- || getAccessibilityNodeProvider() != null;
+ || getAccessibilityNodeProvider() != null
+ || getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE;
default:
throw new IllegalArgumentException("Unknow important for accessibility mode: "
+ mode);
@@ -7094,7 +7197,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @hide
*/
- public void notifyViewAccessibilityStateChangedIfNeeded() {
+ public void notifyViewAccessibilityStateChangedIfNeeded(int changeType) {
if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
return;
}
@@ -7102,7 +7205,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mSendViewStateChangedAccessibilityEvent =
new SendViewStateChangedAccessibilityEvent();
}
- mSendViewStateChangedAccessibilityEvent.runOrPost();
+ mSendViewStateChangedAccessibilityEvent.runOrPost(changeType);
}
/**
@@ -7124,7 +7227,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
if (mParent != null) {
try {
- mParent.childAccessibilityStateChanged(this);
+ mParent.notifySubtreeAccessibilityStateChanged(
+ this, this, AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
} catch (AbstractMethodError e) {
Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
" does not fully implement ViewParent", e);
@@ -7251,7 +7355,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|| getAccessibilitySelectionEnd() != end)
&& (start == end)) {
setAccessibilitySelection(start, end);
- notifyViewAccessibilityStateChangedIfNeeded();
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
return true;
}
} break;
@@ -8825,11 +8930,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (oldIncludeForAccessibility != includeForAccessibility()) {
notifySubtreeAccessibilityStateChangedIfNeeded();
} else {
- notifyViewAccessibilityStateChangedIfNeeded();
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
- }
- if ((changed & ENABLED_MASK) != 0) {
- notifyViewAccessibilityStateChangedIfNeeded();
+ } else if ((changed & ENABLED_MASK) != 0) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
}
@@ -15430,7 +15536,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(true);
refreshDrawableState();
dispatchSetSelected(selected);
- notifyViewAccessibilityStateChangedIfNeeded();
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -19172,21 +19279,44 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
private class SendViewStateChangedAccessibilityEvent implements Runnable {
+ private int mChangeTypes = 0;
private boolean mPosted;
+ private boolean mPostedWithDelay;
private long mLastEventTimeMillis;
+ @Override
public void run() {
mPosted = false;
+ mPostedWithDelay = false;
mLastEventTimeMillis = SystemClock.uptimeMillis();
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- AccessibilityEvent event = AccessibilityEvent.obtain();
+ final AccessibilityEvent event = AccessibilityEvent.obtain();
event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- event.setContentChangeType(AccessibilityEvent.CONTENT_CHANGE_TYPE_NODE);
+ event.setContentChangeTypes(mChangeTypes);
sendAccessibilityEventUnchecked(event);
}
+ mChangeTypes = 0;
}
- public void runOrPost() {
+ public void runOrPost(int changeType) {
+ mChangeTypes |= changeType;
+
+ // If this is a live region or the child of a live region, collect
+ // all events from this frame and send them on the next frame.
+ if (inLiveRegion()) {
+ // If we're already posted with a delay, remove that.
+ if (mPostedWithDelay) {
+ removeCallbacks(this);
+ mPostedWithDelay = false;
+ }
+ // Only post if we're not already posted.
+ if (!mPosted) {
+ post(this);
+ mPosted = true;
+ }
+ return;
+ }
+
if (mPosted) {
return;
}
@@ -19199,10 +19329,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} else {
postDelayed(this, minEventIntevalMillis - timeSinceLastMillis);
mPosted = true;
+ mPostedWithDelay = true;
}
}
}
+ private boolean inLiveRegion() {
+ if (getAccessibilityLiveRegion() != View.ACCESSIBILITY_LIVE_REGION_NONE) {
+ return true;
+ }
+
+ ViewParent parent = getParent();
+ while (parent instanceof View) {
+ if (((View) parent).getAccessibilityLiveRegion()
+ != View.ACCESSIBILITY_LIVE_REGION_NONE) {
+ return true;
+ }
+ parent = parent.getParent();
+ }
+
+ return false;
+ }
+
/**
* Dump all private flags in readable format, useful for documentation and
* sanity checking.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index faeee3f..9414237 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -38,6 +38,7 @@ import android.util.Log;
import android.util.Pools.SynchronizedPool;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -2528,10 +2529,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
@Override
- public void childAccessibilityStateChanged(View root) {
- if (mParent != null) {
+ public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
+ // If this is a live region, we should send a subtree change event
+ // from this view. Otherwise, we can let it propagate up.
+ if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
+ notifyViewAccessibilityStateChangedIfNeeded(changeType);
+ } else if (mParent != null) {
try {
- mParent.childAccessibilityStateChanged(root);
+ mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
} catch (AbstractMethodError e) {
Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
" does not fully implement ViewParent", e);
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 35113db..0137693 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -309,12 +309,21 @@ public interface ViewParent {
public ViewParent getParentForAccessibility();
/**
- * A child notifies its parent that the accessibility state of a subtree rooted
- * at a given node changed. That is the structure of the subtree is different.
- *
- * @param root The root of the changed subtree.
- */
- public void childAccessibilityStateChanged(View root);
+ * Notifies a view parent that the accessibility state of one of its
+ * descendants has changed and that the structure of the subtree is
+ * different.
+ * @param child The direct child whose subtree has changed.
+ * @param source The descendant view that changed.
+ * @param changeType A bit mask of the types of changes that occurred. One
+ * or more of:
+ * <ul>
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_TEXT}
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
+ * </ul>
+ */
+ public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType);
/**
* Tells if this view parent can resolve the layout direction.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 50d5d45..38f28ae 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5807,12 +5807,12 @@ public final class ViewRootImpl implements ViewParent,
* This event is send at most once every
* {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
*/
- private void postSendWindowContentChangedCallback(View source) {
+ private void postSendWindowContentChangedCallback(View source, int changeType) {
if (mSendWindowContentChangedAccessibilityEvent == null) {
mSendWindowContentChangedAccessibilityEvent =
new SendWindowContentChangedAccessibilityEvent();
}
- mSendWindowContentChangedAccessibilityEvent.runOrPost(source);
+ mSendWindowContentChangedAccessibilityEvent.runOrPost(source, changeType);
}
/**
@@ -5884,8 +5884,8 @@ public final class ViewRootImpl implements ViewParent,
}
@Override
- public void childAccessibilityStateChanged(View child) {
- postSendWindowContentChangedCallback(child);
+ public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
+ postSendWindowContentChangedCallback(source, changeType);
}
@Override
@@ -6538,6 +6538,8 @@ public final class ViewRootImpl implements ViewParent,
}
private class SendWindowContentChangedAccessibilityEvent implements Runnable {
+ private int mChangeTypes = 0;
+
public View mSource;
public long mLastEventTimeMillis;
@@ -6548,7 +6550,7 @@ public final class ViewRootImpl implements ViewParent,
mLastEventTimeMillis = SystemClock.uptimeMillis();
AccessibilityEvent event = AccessibilityEvent.obtain();
event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- event.setContentChangeType(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+ event.setContentChangeTypes(mChangeTypes);
mSource.sendAccessibilityEventUnchecked(event);
} else {
mLastEventTimeMillis = 0;
@@ -6556,17 +6558,20 @@ public final class ViewRootImpl implements ViewParent,
// In any case reset to initial state.
mSource.resetSubtreeAccessibilityStateChanged();
mSource = null;
+ mChangeTypes = 0;
}
- public void runOrPost(View source) {
+ public void runOrPost(View source, int changeType) {
if (mSource != null) {
// If there is no common predecessor, then mSource points to
// a removed view, hence in this case always prefer the source.
View predecessor = getCommonPredecessor(mSource, source);
mSource = (predecessor != null) ? predecessor : source;
+ mChangeTypes |= changeType;
return;
}
mSource = source;
+ mChangeTypes = changeType;
final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis;
final long minEventIntevalMillis =
ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 82c8163..7e2bffa 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -326,7 +326,7 @@ import java.util.List;
* <em>Properties:</em></br>
* <ul>
* <li>{@link #getEventType()} - The type of the event.</li>
- * <li>{@link #getContentChangeType()} - The type of content change.</li>
+ * <li>{@link #getContentChangeTypes()} - The type of content changes.</li>
* <li>{@link #getSource()} - The source info (for registered clients).</li>
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
@@ -663,15 +663,27 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
/**
* Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
- * The subtree rooted at the source node changed.
+ * The type of change is not defined.
*/
- public static final int CONTENT_CHANGE_TYPE_SUBTREE = 0;
+ public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0x00000000;
/**
* Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
- * Only the source node changed.
+ * A node in the subtree rooted at the source node was added or removed.
*/
- public static final int CONTENT_CHANGE_TYPE_NODE = 1;
+ public static final int CONTENT_CHANGE_TYPE_SUBTREE = 0x00000001;
+
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * The node's text changed.
+ */
+ public static final int CONTENT_CHANGE_TYPE_TEXT = 0x00000002;
+
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * The node's content description changed.
+ */
+ public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
/**
* Mask for {@link AccessibilityEvent} all types.
@@ -708,7 +720,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
private long mEventTime;
int mMovementGranularity;
int mAction;
- int mContentChangeType;
+ int mContentChangeTypes;
private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>();
@@ -728,7 +740,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
mEventType = event.mEventType;
mMovementGranularity = event.mMovementGranularity;
mAction = event.mAction;
- mContentChangeType = event.mContentChangeType;
+ mContentChangeTypes = event.mContentChangeTypes;
mEventTime = event.mEventTime;
mPackageName = event.mPackageName;
}
@@ -792,30 +804,33 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
}
/**
- * Gets the type of node tree change signaled by an
- * {@link #TYPE_WINDOW_CONTENT_CHANGED} event.
+ * Gets the bit mask of change types signaled by an
+ * {@link #TYPE_WINDOW_CONTENT_CHANGED} event. A single event may represent
+ * multiple change types.
*
- * @see #CONTENT_CHANGE_TYPE_NODE
- * @see #CONTENT_CHANGE_TYPE_SUBTREE
- *
- * @return The change type.
+ * @return The bit mask of change types. One or more of:
+ * <ul>
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_TEXT}
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
+ * </ul>
*/
- public int getContentChangeType() {
- return mContentChangeType;
+ public int getContentChangeTypes() {
+ return mContentChangeTypes;
}
/**
- * Sets the type of node tree change signaled by an
+ * Sets the bit mask of node tree changes signaled by an
* {@link #TYPE_WINDOW_CONTENT_CHANGED} event.
*
- * @see #CONTENT_CHANGE_TYPE_NODE
- * @see #CONTENT_CHANGE_TYPE_SUBTREE
- *
- * @param changeType The change type.
+ * @param changeTypes The bit mask of change types.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ * @see #getContentChangeTypes()
*/
- public void setContentChangeType(int changeType) {
+ public void setContentChangeTypes(int changeTypes) {
enforceNotSealed();
- mContentChangeType = changeType;
+ mContentChangeTypes = changeTypes;
}
/**
@@ -985,7 +1000,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
mEventType = 0;
mMovementGranularity = 0;
mAction = 0;
- mContentChangeType = 0;
+ mContentChangeTypes = 0;
mPackageName = null;
mEventTime = 0;
while (!mRecords.isEmpty()) {
@@ -1004,7 +1019,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
mEventType = parcel.readInt();
mMovementGranularity = parcel.readInt();
mAction = parcel.readInt();
- mContentChangeType = parcel.readInt();
+ mContentChangeTypes = parcel.readInt();
mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
mEventTime = parcel.readLong();
mConnectionId = parcel.readInt();
@@ -1057,7 +1072,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
parcel.writeInt(mEventType);
parcel.writeInt(mMovementGranularity);
parcel.writeInt(mAction);
- parcel.writeInt(mContentChangeType);
+ parcel.writeInt(mContentChangeTypes);
TextUtils.writeToParcel(mPackageName, parcel, 0);
parcel.writeLong(mEventTime);
parcel.writeInt(mConnectionId);
@@ -1119,7 +1134,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
builder.append(super.toString());
if (DEBUG) {
builder.append("\n");
- builder.append("; ContentChangeType: ").append(mContentChangeType);
+ builder.append("; ContentChangeTypes: ").append(mContentChangeTypes);
builder.append("; sourceWindowId: ").append(mSourceWindowId);
builder.append("; mSourceNodeId: ").append(mSourceNodeId);
for (int i = 0; i < mRecords.size(); i++) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index c61516b..9fc37cf 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -418,19 +418,13 @@ public class AccessibilityNodeInfo implements Parcelable {
private static final int BOOLEAN_PROPERTY_EDITABLE = 0x00001000;
- private static final int BOOLEAN_PROPERTY_LIVE_REGION = 0x00002000;
+ private static final int BOOLEAN_PROPERTY_OPENS_POPUP = 0x00002000;
- private static final int BOOLEAN_PROPERTY_OPENS_POPUP = 0x00004000;
+ private static final int BOOLEAN_PROPERTY_DISMISSABLE = 0x00004000;
- private static final int BOOLEAN_PROPERTY_EXPANDABLE = 0x00008000;
+ private static final int BOOLEAN_PROPERTY_MULTI_LINE = 0x00008000;
- private static final int BOOLEAN_PROPERTY_EXPANDED = 0x00010000;
-
- private static final int BOOLEAN_PROPERTY_DISMISSABLE = 0x00020000;
-
- private static final int BOOLEAN_PROPERTY_MULTI_LINE = 0x00040000;
-
- private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00080000;
+ private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00010000;
/**
* Bits that provide the id of a virtual descendant of a view.
@@ -517,6 +511,7 @@ public class AccessibilityNodeInfo implements Parcelable {
private int mTextSelectionStart = UNDEFINED;
private int mTextSelectionEnd = UNDEFINED;
private int mInputType = InputType.TYPE_NULL;
+ private int mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
private Bundle mExtras;
@@ -1471,39 +1466,42 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
- * Gets if the node is a live region.
+ * Gets the node's live region mode.
* <p>
- * A live region is a node that contains information that is important
- * for the user and when it changes the user has to be notified. For
- * example, if the user plays a video and the application shows a
- * progress indicator with the percentage of buffering, then the progress
- * indicator should be marked as a live region.
- * </p>
+ * A live region is a node that contains information that is important for
+ * the user and when it changes the user should be notified. For example,
+ * in a login screen with a TextView that displays an "incorrect password"
+ * notification, that view should be marked as a live region with mode
+ * {@link View#ACCESSIBILITY_LIVE_REGION_POLITE}.
* <p>
- * It is the responsibility of the accessibility
- * service to monitor this region and notify the user if it changes.
- * </p>
+ * It is the responsibility of the accessibility service to monitor
+ * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events indicating
+ * changes to live region nodes and their children.
*
- * @return If the node is a live region.
+ * @return The live region mode, or
+ * {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a
+ * live region.
+ * @see android.view.View#getAccessibilityLiveRegion()
*/
- public boolean isLiveRegion() {
- return getBooleanProperty(BOOLEAN_PROPERTY_LIVE_REGION);
+ public int getLiveRegion() {
+ return mLiveRegion;
}
/**
- * Sets if the node is a live region for whose changes the user
- * should be notified. It is the responsibility of the accessibility
- * service to monitor this region and notify the user if it changes.
+ * Sets the node's live region mode.
* <p>
- * <strong>Note:</strong> Cannot be called from an
- * {@link android.accessibilityservice.AccessibilityService}.
- * This class is made immutable before being delivered to an AccessibilityService.
- * </p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is
+ * made immutable before being delivered to an AccessibilityService.
*
- * @param liveRegion If the node is a live region.
+ * @param mode The live region mode, or
+ * {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a
+ * live region.
+ * @see android.view.View#setAccessibilityLiveRegion(int)
*/
- public void setLiveRegion(boolean liveRegion) {
- setBooleanProperty(BOOLEAN_PROPERTY_LIVE_REGION, liveRegion);
+ public void setLiveRegion(int mode) {
+ enforceNotSealed();
+ mLiveRegion = mode;
}
/**
@@ -1554,52 +1552,6 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
- * Gets if the node can be expanded.
- *
- * @return If the node can be expanded.
- */
- public boolean isExpandable() {
- return getBooleanProperty(BOOLEAN_PROPERTY_EXPANDABLE);
- }
-
- /**
- * Sets if the node can be expanded.
- * <p>
- * <strong>Note:</strong> Cannot be called from an
- * {@link android.accessibilityservice.AccessibilityService}.
- * This class is made immutable before being delivered to an AccessibilityService.
- * </p>
- *
- * @param expandable If the node can be expanded.
- */
- public void setExpandable(boolean expandable) {
- setBooleanProperty(BOOLEAN_PROPERTY_EXPANDABLE, expandable);
- }
-
- /**
- * Gets if the node is expanded.
- *
- * @return If the node is expanded.
- */
- public boolean isExpanded() {
- return getBooleanProperty(BOOLEAN_PROPERTY_EXPANDED);
- }
-
- /**
- * Sets if the node is expanded.
- * <p>
- * <strong>Note:</strong> Cannot be called from an
- * {@link android.accessibilityservice.AccessibilityService}.
- * This class is made immutable before being delivered to an AccessibilityService.
- * </p>
- *
- * @param expanded If the node is expanded.
- */
- public void setExpanded(boolean expanded) {
- setBooleanProperty(BOOLEAN_PROPERTY_EXPANDED, expanded);
- }
-
- /**
* Gets if the node can be dismissed.
*
* @return If the node can be dismissed.
@@ -2203,6 +2155,7 @@ public class AccessibilityNodeInfo implements Parcelable {
parcel.writeInt(mTextSelectionStart);
parcel.writeInt(mTextSelectionEnd);
parcel.writeInt(mInputType);
+ parcel.writeInt(mLiveRegion);
if (mExtras != null) {
parcel.writeInt(1);
@@ -2276,6 +2229,7 @@ public class AccessibilityNodeInfo implements Parcelable {
mTextSelectionStart = other.mTextSelectionStart;
mTextSelectionEnd = other.mTextSelectionEnd;
mInputType = other.mInputType;
+ mLiveRegion = other.mLiveRegion;
if (other.mExtras != null && !other.mExtras.isEmpty()) {
getExtras().putAll(other.mExtras);
}
@@ -2334,6 +2288,7 @@ public class AccessibilityNodeInfo implements Parcelable {
mTextSelectionEnd = parcel.readInt();
mInputType = parcel.readInt();
+ mLiveRegion = parcel.readInt();
if (parcel.readInt() == 1) {
getExtras().putAll(parcel.readBundle());
@@ -2389,6 +2344,7 @@ public class AccessibilityNodeInfo implements Parcelable {
mTextSelectionStart = UNDEFINED;
mTextSelectionEnd = UNDEFINED;
mInputType = InputType.TYPE_NULL;
+ mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
if (mExtras != null) {
mExtras.clear();
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
index 02d4c8d..6bef78e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
@@ -91,11 +91,11 @@ public class AccessibilityNodeInfoCache {
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
synchronized (mLock) {
final long sourceId = event.getSourceNodeId();
- if (event.getContentChangeType()
- == AccessibilityEvent.CONTENT_CHANGE_TYPE_NODE) {
- refreshCachedNode(sourceId);
- } else {
+ if ((event.getContentChangeTypes()
+ & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
clearSubTreeLocked(sourceId);
+ } else {
+ refreshCachedNode(sourceId);
}
}
} break;