Revert "Move A11y events throttling away from View(RootImpl)"
This reverts commit e4d31b3c103045d5b2b141a05084dced595cc64f.
Fixes: 71904218
Test: presubmit
Change-Id: Id73bde1a0c11696cf561c84cde027cdca4c6a00f
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index ba39740..8f01685 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -46,14 +47,10 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
-
-import com.android.internal.util.CollectionUtils;
-
import libcore.io.IoUtils;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeoutException;
@@ -583,8 +580,6 @@
// Execute the command *without* the lock being held.
command.run();
- List<AccessibilityEvent> eventsReceived = Collections.emptyList();
-
// Acquire the lock and wait for the event.
try {
// Wait for the event.
@@ -605,14 +600,14 @@
if (filter.accept(event)) {
return event;
}
- eventsReceived = CollectionUtils.add(eventsReceived, event);
+ event.recycle();
}
// Check if timed out and if not wait.
final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
if (remainingTimeMillis <= 0) {
throw new TimeoutException("Expected event not received within: "
- + timeoutMillis + " ms, among " + eventsReceived);
+ + timeoutMillis + " ms.");
}
synchronized (mLock) {
if (mEventQueue.isEmpty()) {
@@ -625,10 +620,6 @@
}
}
} finally {
- for (int i = 0; i < CollectionUtils.size(eventsReceived); i++) {
- AccessibilityEvent event = eventsReceived.get(i);
- event.recycle();
- }
synchronized (mLock) {
mWaitingForEventDelivery = false;
mEventQueue.clear();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3d6a6fe..5150c1f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4442,6 +4442,7 @@
private CheckForLongPress mPendingCheckForLongPress;
private CheckForTap mPendingCheckForTap = null;
private PerformClick mPerformClick;
+ private SendViewScrolledAccessibilityEvent mSendViewScrolledAccessibilityEvent;
private UnsetPressedState mUnsetPressedState;
@@ -7201,7 +7202,7 @@
if (gainFocus) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
} else {
- notifyAccessibilityStateChanged(
+ notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@@ -7271,7 +7272,8 @@
public void setAccessibilityPaneTitle(CharSequence accessibilityPaneTitle) {
if (!TextUtils.equals(accessibilityPaneTitle, mAccessibilityPaneTitle)) {
mAccessibilityPaneTitle = accessibilityPaneTitle;
- notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE);
}
}
@@ -8924,9 +8926,9 @@
final boolean nonEmptyDesc = contentDescription != null && contentDescription.length() > 0;
if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
} else {
- notifyAccessibilityStateChanged(
+ notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
}
}
@@ -8959,7 +8961,8 @@
return;
}
mAccessibilityTraversalBeforeId = beforeId;
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
/**
@@ -9002,7 +9005,8 @@
return;
}
mAccessibilityTraversalAfterId = afterId;
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
/**
@@ -9044,7 +9048,8 @@
&& mID == View.NO_ID) {
mID = generateViewId();
}
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
/**
@@ -10544,7 +10549,8 @@
if (pflags3 != mPrivateFlags3) {
mPrivateFlags3 = pflags3;
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -11374,7 +11380,8 @@
mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
mPrivateFlags2 |= (mode << PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT)
& PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -11431,9 +11438,10 @@
mPrivateFlags2 |= (mode << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT)
& PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK;
if (!maySkipNotify || oldIncludeForAccessibility != includeForAccessibility()) {
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
} else {
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
}
@@ -11609,32 +11617,11 @@
*
* @hide
*/
- public void notifyAccessibilityStateChanged(int changeType) {
- notifyAccessibilityStateChanged(this, changeType);
- }
-
- /**
- * Notifies that the accessibility state of this view changed. The change
- * is *not* local to this view and does represent structural changes such
- * as children and parent. For example, the view size changed. The
- * notification is at at most once every
- * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
- * to avoid unnecessary load to the system. Also once a view has a pending
- * notification this method is a NOP until the notification has been sent.
- *
- * @hide
- */
- public void notifyAccessibilitySubtreeChanged() {
- if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
- mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
- notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
- }
- }
-
- void notifyAccessibilityStateChanged(View source, int changeType) {
+ public void notifyViewAccessibilityStateChangedIfNeeded(int changeType) {
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())) {
@@ -11652,12 +11639,49 @@
}
}
- if (mParent != null) {
+ // If this is a live region, we should send a subtree change event
+ // from this view immediately. Otherwise, we can let it propagate up.
+ if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
+ final AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeTypes(changeType);
+ sendAccessibilityEventUnchecked(event);
+ } else if (mParent != null) {
try {
- mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
+ mParent.notifySubtreeAccessibilityStateChanged(this, this, changeType);
} catch (AbstractMethodError e) {
- Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()
- + " does not fully implement ViewParent", e);
+ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+ " does not fully implement ViewParent", e);
+ }
+ }
+ }
+
+ /**
+ * Notifies that the accessibility state of this view changed. The change
+ * is *not* local to this view and does represent structural changes such
+ * as children and parent. For example, the view size changed. The
+ * notification is at at most once every
+ * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
+ * to avoid unnecessary load to the system. Also once a view has a pending
+ * notification this method is a NOP until the notification has been sent.
+ *
+ * @hide
+ */
+ public void notifySubtreeAccessibilityStateChangedIfNeeded() {
+ if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
+ return;
+ }
+
+ if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
+ mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
+ if (mParent != null) {
+ try {
+ 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);
+ }
}
}
}
@@ -11679,10 +11703,8 @@
/**
* Reset the flag indicating the accessibility state of the subtree rooted
* at this view changed.
- *
- * @hide
*/
- public void resetSubtreeAccessibilityStateChanged() {
+ void resetSubtreeAccessibilityStateChanged() {
mPrivateFlags2 &= ~PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
}
@@ -11843,7 +11865,8 @@
|| getAccessibilitySelectionEnd() != end)
&& (start == end)) {
setAccessibilitySelection(start, end);
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
return true;
}
} break;
@@ -12643,7 +12666,7 @@
}
if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) {
if (isVisible != oldVisible) {
- notifyAccessibilityStateChanged(isVisible
+ notifyViewAccessibilityStateChangedIfNeeded(isVisible
? AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED
: AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
}
@@ -13860,7 +13883,7 @@
((!(mParent instanceof ViewGroup)) || ((ViewGroup) mParent).isShown())) {
dispatchVisibilityAggregated(newVisibility == VISIBLE);
}
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -13906,12 +13929,14 @@
|| (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0
|| (changed & CONTEXT_CLICKABLE) != 0) {
if (oldIncludeForAccessibility != includeForAccessibility()) {
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
} else {
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
} else if ((changed & ENABLED_MASK) != 0) {
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
}
@@ -13945,13 +13970,10 @@
* @param oldt Previous vertical scroll origin.
*/
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
- ViewRootImpl root = getViewRootImpl();
- if (root != null) {
- root.getAccessibilityState()
- .getSendViewScrolledAccessibilityEvent()
- .post(this, /* dx */ l - oldl, /* dy */ t - oldt);
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ postSendViewScrolledAccessibilityEventCallback(l - oldl, t - oldt);
}
mBackgroundSizeChanged = true;
@@ -14347,7 +14369,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -14391,7 +14413,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -14435,7 +14457,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -14472,7 +14494,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -14509,7 +14531,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -14712,7 +14734,7 @@
if (mTransformationInfo.mAlpha != alpha) {
// Report visibility changes, which can affect children, to accessibility
if ((alpha == 0) ^ (mTransformationInfo.mAlpha == 0)) {
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
mTransformationInfo.mAlpha = alpha;
if (onSetAlpha((int) (alpha * 255))) {
@@ -15214,7 +15236,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -15248,7 +15270,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -15418,7 +15440,7 @@
public void invalidateOutline() {
rebuildOutline();
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
invalidateViewProperty(false, false);
}
@@ -15613,7 +15635,7 @@
}
invalidateParentIfNeeded();
}
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -15661,7 +15683,7 @@
}
invalidateParentIfNeeded();
}
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
@@ -16539,6 +16561,18 @@
}
/**
+ * Post a callback to send a {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
+ * This event is sent at most once every
+ * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
+ */
+ private void postSendViewScrolledAccessibilityEventCallback(int dx, int dy) {
+ if (mSendViewScrolledAccessibilityEvent == null) {
+ mSendViewScrolledAccessibilityEvent = new SendViewScrolledAccessibilityEvent();
+ }
+ mSendViewScrolledAccessibilityEvent.post(dx, dy);
+ }
+
+ /**
* Called by a parent to request that a child update its values for mScrollX
* and mScrollY if necessary. This will typically be done if the child is
* animating a scroll using a {@link android.widget.Scroller Scroller}
@@ -17793,13 +17827,7 @@
removeUnsetPressCallback();
removeLongPressCallback();
removePerformClickCallback();
- if (mAttachInfo != null
- && mAttachInfo.mViewRootImpl.mAccessibilityState != null
- && mAttachInfo.mViewRootImpl.mAccessibilityState.isScrollEventSenderInitialized()) {
- mAttachInfo.mViewRootImpl.mAccessibilityState
- .getSendViewScrolledAccessibilityEvent()
- .cancelIfPendingFor(this);
- }
+ cancel(mSendViewScrolledAccessibilityEvent);
stopNestedScroll();
// Anything that started animating right before detach should already
@@ -20427,7 +20455,7 @@
mForegroundInfo.mBoundsChanged = true;
}
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
return changed;
}
@@ -21871,7 +21899,8 @@
if (selected) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
} else {
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
}
@@ -26458,6 +26487,53 @@
}
/**
+ * Resuable callback for sending
+ * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event.
+ */
+ private class SendViewScrolledAccessibilityEvent implements Runnable {
+ public volatile boolean mIsPending;
+ public int mDeltaX;
+ public int mDeltaY;
+
+ public void post(int dx, int dy) {
+ mDeltaX += dx;
+ mDeltaY += dy;
+ if (!mIsPending) {
+ mIsPending = true;
+ postDelayed(this, ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
+ }
+ }
+
+ @Override
+ public void run() {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_SCROLLED);
+ event.setScrollDeltaX(mDeltaX);
+ event.setScrollDeltaY(mDeltaY);
+ sendAccessibilityEventUnchecked(event);
+ }
+ reset();
+ }
+
+ private void reset() {
+ mIsPending = false;
+ mDeltaX = 0;
+ mDeltaY = 0;
+ }
+ }
+
+ /**
+ * Remove the pending callback for sending a
+ * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event.
+ */
+ private void cancel(@Nullable SendViewScrolledAccessibilityEvent callback) {
+ if (callback == null || !callback.mIsPending) return;
+ removeCallbacks(callback);
+ callback.reset();
+ }
+
+ /**
* <p>
* This class represents a delegate that can be registered in a {@link View}
* to enhance accessibility support via composition rather via inheritance.
@@ -27074,7 +27150,7 @@
mTooltipInfo.mTooltipPopup.show(this, x, y, fromTouch, mTooltipInfo.mTooltipText);
mAttachInfo.mTooltipHost = this;
// The available accessibility actions have changed
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(CONTENT_CHANGE_TYPE_UNDEFINED);
return true;
}
@@ -27094,7 +27170,7 @@
mAttachInfo.mTooltipHost = null;
}
// The available accessibility actions have changed
- notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(CONTENT_CHANGE_TYPE_UNDEFINED);
}
private boolean showLongClickTooltip(int x, int y) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 4631261..a8bdb85 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3649,34 +3649,44 @@
return ViewGroup.class.getName();
}
+ @Override
+ 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(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+ } else if (mParent != null) {
+ try {
+ mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
+ } catch (AbstractMethodError e) {
+ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+ " does not fully implement ViewParent", e);
+ }
+ }
+ }
+
/** @hide */
@Override
- public void notifyAccessibilitySubtreeChanged() {
+ public void notifySubtreeAccessibilityStateChangedIfNeeded() {
if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
return;
}
// If something important for a11y is happening in this subtree, make sure it's dispatched
// from a view that is important for a11y so it doesn't get lost.
- if (getImportantForAccessibility() != IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- && !isImportantForAccessibility()
- && getChildCount() > 0) {
+ if ((getImportantForAccessibility() != IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
+ && !isImportantForAccessibility() && (getChildCount() > 0)) {
ViewParent a11yParent = getParentForAccessibility();
if (a11yParent instanceof View) {
- ((View) a11yParent).notifyAccessibilitySubtreeChanged();
+ ((View) a11yParent).notifySubtreeAccessibilityStateChangedIfNeeded();
return;
}
}
- super.notifyAccessibilitySubtreeChanged();
+ super.notifySubtreeAccessibilityStateChangedIfNeeded();
}
@Override
- public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
- notifyAccessibilityStateChanged(source, changeType);
- }
-
- /** @hide */
- @Override
- public void resetSubtreeAccessibilityStateChanged() {
+ void resetSubtreeAccessibilityStateChanged() {
super.resetSubtreeAccessibilityStateChanged();
View[] children = mChildren;
final int childCount = mChildrenCount;
@@ -5088,7 +5098,7 @@
}
if (child.getVisibility() != View.GONE) {
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
if (mTransientIndices != null) {
@@ -5358,7 +5368,7 @@
dispatchViewRemoved(view);
if (view.getVisibility() != View.GONE) {
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
@@ -6077,7 +6087,7 @@
if (invalidate) {
invalidateViewProperty(false, false);
}
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
@Override
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 30f584c..29246fd 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -89,11 +89,9 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
-import android.view.accessibility.AccessibilityViewHierarchyState;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
-import android.view.accessibility.ThrottlingAccessibilityEventSender;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.inputmethod.InputMethodManager;
@@ -115,6 +113,7 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.concurrent.CountDownLatch;
/**
@@ -461,6 +460,10 @@
new AccessibilityInteractionConnectionManager();
final HighContrastTextManager mHighContrastTextManager;
+ SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent;
+
+ HashSet<View> mTempHashSet;
+
private final int mDensity;
private final int mNoncompatDensity;
@@ -475,8 +478,6 @@
private boolean mNeedsRendererSetup;
- protected AccessibilityViewHierarchyState mAccessibilityState;
-
/**
* Consistency verifier for debugging purposes.
*/
@@ -7258,9 +7259,11 @@
* {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
*/
private void postSendWindowContentChangedCallback(View source, int changeType) {
- getAccessibilityState()
- .getSendWindowContentChangedAccessibilityEvent()
- .runOrPost(source, changeType);
+ if (mSendWindowContentChangedAccessibilityEvent == null) {
+ mSendWindowContentChangedAccessibilityEvent =
+ new SendWindowContentChangedAccessibilityEvent();
+ }
+ mSendWindowContentChangedAccessibilityEvent.runOrPost(source, changeType);
}
/**
@@ -7268,20 +7271,11 @@
* {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
*/
private void removeSendWindowContentChangedCallback() {
- if (mAccessibilityState != null
- && mAccessibilityState.isWindowContentChangedEventSenderInitialized()) {
- ThrottlingAccessibilityEventSender.cancelIfPending(
- mAccessibilityState.getSendWindowContentChangedAccessibilityEvent());
+ if (mSendWindowContentChangedAccessibilityEvent != null) {
+ mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
}
}
- AccessibilityViewHierarchyState getAccessibilityState() {
- if (mAccessibilityState == null) {
- mAccessibilityState = new AccessibilityViewHierarchyState();
- }
- return mAccessibilityState;
- }
-
@Override
public boolean showContextMenuForChild(View originalView) {
return false;
@@ -7317,8 +7311,12 @@
return false;
}
- // Send any pending event to prevent reordering
- flushPendingAccessibilityEvents();
+ // Immediately flush pending content changed event (if any) to preserve event order
+ if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+ && mSendWindowContentChangedAccessibilityEvent != null
+ && mSendWindowContentChangedAccessibilityEvent.mSource != null) {
+ mSendWindowContentChangedAccessibilityEvent.removeCallbacksAndRun();
+ }
// Intercept accessibility focus events fired by virtual nodes to keep
// track of accessibility focus position in such nodes.
@@ -7362,19 +7360,6 @@
return true;
}
- /** @hide */
- public void flushPendingAccessibilityEvents() {
- if (mAccessibilityState != null) {
- if (mAccessibilityState.isScrollEventSenderInitialized()) {
- mAccessibilityState.getSendViewScrolledAccessibilityEvent().sendNowIfPending();
- }
- if (mAccessibilityState.isWindowContentChangedEventSenderInitialized()) {
- mAccessibilityState.getSendWindowContentChangedAccessibilityEvent()
- .sendNowIfPending();
- }
- }
- }
-
/**
* Updates the focused virtual view, when necessary, in response to a
* content changed event.
@@ -7509,6 +7494,39 @@
return View.TEXT_ALIGNMENT_RESOLVED_DEFAULT;
}
+ private View getCommonPredecessor(View first, View second) {
+ if (mTempHashSet == null) {
+ mTempHashSet = new HashSet<View>();
+ }
+ HashSet<View> seen = mTempHashSet;
+ seen.clear();
+ View firstCurrent = first;
+ while (firstCurrent != null) {
+ seen.add(firstCurrent);
+ ViewParent firstCurrentParent = firstCurrent.mParent;
+ if (firstCurrentParent instanceof View) {
+ firstCurrent = (View) firstCurrentParent;
+ } else {
+ firstCurrent = null;
+ }
+ }
+ View secondCurrent = second;
+ while (secondCurrent != null) {
+ if (seen.contains(secondCurrent)) {
+ seen.clear();
+ return secondCurrent;
+ }
+ ViewParent secondCurrentParent = secondCurrent.mParent;
+ if (secondCurrentParent instanceof View) {
+ secondCurrent = (View) secondCurrentParent;
+ } else {
+ secondCurrent = null;
+ }
+ }
+ seen.clear();
+ return null;
+ }
+
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
@@ -8119,6 +8137,80 @@
}
}
+ private class SendWindowContentChangedAccessibilityEvent implements Runnable {
+ private int mChangeTypes = 0;
+
+ public View mSource;
+ public long mLastEventTimeMillis;
+
+ @Override
+ public void run() {
+ // Protect against re-entrant code and attempt to do the right thing in the case that
+ // we're multithreaded.
+ View source = mSource;
+ mSource = null;
+ if (source == null) {
+ Log.e(TAG, "Accessibility content change has no source");
+ return;
+ }
+ // The accessibility may be turned off while we were waiting so check again.
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ mLastEventTimeMillis = SystemClock.uptimeMillis();
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeTypes(mChangeTypes);
+ source.sendAccessibilityEventUnchecked(event);
+ } else {
+ mLastEventTimeMillis = 0;
+ }
+ // In any case reset to initial state.
+ source.resetSubtreeAccessibilityStateChanged();
+ mChangeTypes = 0;
+ }
+
+ public void runOrPost(View source, int changeType) {
+ if (mHandler.getLooper() != Looper.myLooper()) {
+ CalledFromWrongThreadException e = new CalledFromWrongThreadException("Only the "
+ + "original thread that created a view hierarchy can touch its views.");
+ // TODO: Throw the exception
+ Log.e(TAG, "Accessibility content change on non-UI thread. Future Android "
+ + "versions will throw an exception.", e);
+ // Attempt to recover. This code does not eliminate the thread safety issue, but
+ // it should force any issues to happen near the above log.
+ mHandler.removeCallbacks(this);
+ if (mSource != null) {
+ // Dispatch whatever was pending. It's still possible that the runnable started
+ // just before we removed the callbacks, and bad things will happen, but at
+ // least they should happen very close to the logged error.
+ run();
+ }
+ }
+ 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();
+ if (timeSinceLastMillis >= minEventIntevalMillis) {
+ removeCallbacksAndRun();
+ } else {
+ mHandler.postDelayed(this, minEventIntevalMillis - timeSinceLastMillis);
+ }
+ }
+
+ public void removeCallbacksAndRun() {
+ mHandler.removeCallbacks(this);
+ run();
+ }
+ }
+
private static class KeyFallbackManager {
// This is used to ensure that key-fallback events are only dispatched once. We attempt
diff --git a/core/java/android/view/accessibility/AccessibilityViewHierarchyState.java b/core/java/android/view/accessibility/AccessibilityViewHierarchyState.java
deleted file mode 100644
index 447fafa..0000000
--- a/core/java/android/view/accessibility/AccessibilityViewHierarchyState.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.accessibility;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * Accessibility-related state of a {@link android.view.ViewRootImpl}
- *
- * @hide
- */
-public class AccessibilityViewHierarchyState {
- private @Nullable SendViewScrolledAccessibilityEvent mSendViewScrolledAccessibilityEvent;
- private @Nullable SendWindowContentChangedAccessibilityEvent
- mSendWindowContentChangedAccessibilityEvent;
-
- /**
- * @return a {@link SendViewScrolledAccessibilityEvent}, creating one if needed
- */
- public @NonNull SendViewScrolledAccessibilityEvent getSendViewScrolledAccessibilityEvent() {
- if (mSendViewScrolledAccessibilityEvent == null) {
- mSendViewScrolledAccessibilityEvent = new SendViewScrolledAccessibilityEvent();
- }
- return mSendViewScrolledAccessibilityEvent;
- }
-
- public boolean isScrollEventSenderInitialized() {
- return mSendViewScrolledAccessibilityEvent != null;
- }
-
- /**
- * @return a {@link SendWindowContentChangedAccessibilityEvent}, creating one if needed
- */
- public @NonNull SendWindowContentChangedAccessibilityEvent
- getSendWindowContentChangedAccessibilityEvent() {
- if (mSendWindowContentChangedAccessibilityEvent == null) {
- mSendWindowContentChangedAccessibilityEvent =
- new SendWindowContentChangedAccessibilityEvent();
- }
- return mSendWindowContentChangedAccessibilityEvent;
- }
-
- public boolean isWindowContentChangedEventSenderInitialized() {
- return mSendWindowContentChangedAccessibilityEvent != null;
- }
-}
diff --git a/core/java/android/view/accessibility/SendViewScrolledAccessibilityEvent.java b/core/java/android/view/accessibility/SendViewScrolledAccessibilityEvent.java
deleted file mode 100644
index 40a1b6a..0000000
--- a/core/java/android/view/accessibility/SendViewScrolledAccessibilityEvent.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.accessibility;
-
-
-import android.annotation.NonNull;
-import android.view.View;
-
-/**
- * Sender for {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event.
- *
- * @hide
- */
-public class SendViewScrolledAccessibilityEvent extends ThrottlingAccessibilityEventSender {
-
- public int mDeltaX;
- public int mDeltaY;
-
- /**
- * Post a scroll event to be sent for the given view
- */
- public void post(View source, int dx, int dy) {
- if (!isPendingFor(source)) sendNowIfPending();
-
- mDeltaX += dx;
- mDeltaY += dy;
-
- if (!isPendingFor(source)) scheduleFor(source);
- }
-
- @Override
- protected void performSendEvent(@NonNull View source) {
- AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
- event.setScrollDeltaX(mDeltaX);
- event.setScrollDeltaY(mDeltaY);
- source.sendAccessibilityEventUnchecked(event);
- }
-
- @Override
- protected void resetState(@NonNull View source) {
- mDeltaX = 0;
- mDeltaY = 0;
- }
-}
diff --git a/core/java/android/view/accessibility/SendWindowContentChangedAccessibilityEvent.java b/core/java/android/view/accessibility/SendWindowContentChangedAccessibilityEvent.java
deleted file mode 100644
index df38fba..0000000
--- a/core/java/android/view/accessibility/SendWindowContentChangedAccessibilityEvent.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.accessibility;
-
-
-import static com.android.internal.util.ObjectUtils.firstNotNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.view.View;
-import android.view.ViewParent;
-
-import java.util.HashSet;
-
-/**
- * @hide
- */
-public class SendWindowContentChangedAccessibilityEvent
- extends ThrottlingAccessibilityEventSender {
-
- private int mChangeTypes = 0;
-
- private HashSet<View> mTempHashSet;
-
- @Override
- protected void performSendEvent(@NonNull View source) {
- AccessibilityEvent event = AccessibilityEvent.obtain();
- event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- event.setContentChangeTypes(mChangeTypes);
- source.sendAccessibilityEventUnchecked(event);
- }
-
- @Override
- protected void resetState(@Nullable View source) {
- if (source != null) {
- source.resetSubtreeAccessibilityStateChanged();
- }
- mChangeTypes = 0;
- }
-
- /**
- * Post the {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event with the given
- * {@link AccessibilityEvent#getContentChangeTypes change type} for the given view
- */
- public void runOrPost(View source, int changeType) {
- if (source.getAccessibilityLiveRegion() != View.ACCESSIBILITY_LIVE_REGION_NONE) {
- sendNowIfPending();
- mChangeTypes = changeType;
- sendNow(source);
- } else {
- mChangeTypes |= changeType;
- scheduleFor(source);
- }
- }
-
- @Override
- protected @Nullable View tryMerge(@NonNull View oldSource, @NonNull View newSource) {
- // If there is no common predecessor, then oldSource points to
- // a removed view, hence in this case always prefer the newSource.
- return firstNotNull(
- getCommonPredecessor(oldSource, newSource),
- newSource);
- }
-
- private View getCommonPredecessor(View first, View second) {
- if (mTempHashSet == null) {
- mTempHashSet = new HashSet<>();
- }
- HashSet<View> seen = mTempHashSet;
- seen.clear();
- View firstCurrent = first;
- while (firstCurrent != null) {
- seen.add(firstCurrent);
- ViewParent firstCurrentParent = firstCurrent.getParent();
- if (firstCurrentParent instanceof View) {
- firstCurrent = (View) firstCurrentParent;
- } else {
- firstCurrent = null;
- }
- }
- View secondCurrent = second;
- while (secondCurrent != null) {
- if (seen.contains(secondCurrent)) {
- seen.clear();
- return secondCurrent;
- }
- ViewParent secondCurrentParent = secondCurrent.getParent();
- if (secondCurrentParent instanceof View) {
- secondCurrent = (View) secondCurrentParent;
- } else {
- secondCurrent = null;
- }
- }
- seen.clear();
- return null;
- }
-}
diff --git a/core/java/android/view/accessibility/ThrottlingAccessibilityEventSender.java b/core/java/android/view/accessibility/ThrottlingAccessibilityEventSender.java
deleted file mode 100644
index 66fa301..0000000
--- a/core/java/android/view/accessibility/ThrottlingAccessibilityEventSender.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.accessibility;
-
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewRootImpl;
-import android.view.ViewRootImpl.CalledFromWrongThreadException;
-
-/**
- * A throttling {@link AccessibilityEvent} sender that relies on its currently associated
- * 'source' view's {@link View#postDelayed delayed execution} to delay and possibly
- * {@link #tryMerge merge} together any events that come in less than
- * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval
- * the configured amount of milliseconds} apart.
- *
- * The suggested usage is to create a singleton extending this class, holding any state specific to
- * the particular event type that the subclass represents, and have an 'entrypoint' method that
- * delegates to {@link #scheduleFor(View)}.
- * For example:
- *
- * {@code
- * public void post(View view, String text, int resId) {
- * mText = text;
- * mId = resId;
- * scheduleFor(view);
- * }
- * }
- *
- * @see #scheduleFor(View)
- * @see #tryMerge(View, View)
- * @see #performSendEvent(View)
- * @hide
- */
-public abstract class ThrottlingAccessibilityEventSender {
-
- private static final boolean DEBUG = false;
- private static final String LOG_TAG = "ThrottlingA11ySender";
-
- View mSource;
- private long mLastSendTimeMillis = Long.MIN_VALUE;
- private boolean mIsPending = false;
-
- private final Runnable mWorker = () -> {
- View source = mSource;
- if (DEBUG) Log.d(LOG_TAG, thisClass() + ".run(mSource = " + source + ")");
-
- if (!checkAndResetIsPending() || source == null) {
- resetStateInternal();
- return;
- }
-
- // Accessibility may be turned off while we were waiting
- if (isAccessibilityEnabled(source)) {
- mLastSendTimeMillis = SystemClock.uptimeMillis();
- performSendEvent(source);
- }
- resetStateInternal();
- };
-
- /**
- * Populate and send an {@link AccessibilityEvent} using the given {@code source} view, as well
- * as any extra data from this instance's state.
- *
- * Send the event via {@link View#sendAccessibilityEventUnchecked(AccessibilityEvent)} or
- * {@link View#sendAccessibilityEvent(int)} on the provided {@code source} view to allow for
- * overrides of those methods on {@link View} subclasses to take effect, and/or make sure that
- * an {@link View#getAccessibilityDelegate() accessibility delegate} is not ignored if any.
- */
- protected abstract void performSendEvent(@NonNull View source);
-
- /**
- * Perform optional cleanup after {@link #performSendEvent}
- *
- * @param source the view this event was associated with
- */
- protected abstract void resetState(@Nullable View source);
-
- /**
- * Attempt to merge the pending events for source views {@code oldSource} and {@code newSource}
- * into one, with source set to the resulting {@link View}
- *
- * A result of {@code null} means merger is not possible, resulting in the currently pending
- * event being flushed before proceeding.
- */
- protected @Nullable View tryMerge(@NonNull View oldSource, @NonNull View newSource) {
- return null;
- }
-
- /**
- * Schedules a {@link #performSendEvent} with the source {@link View} set to given
- * {@code source}
- *
- * If an event is already scheduled a {@link #tryMerge merge} will be attempted.
- * If merging is not possible (as indicated by the null result from {@link #tryMerge}),
- * the currently scheduled event will be {@link #sendNow sent immediately} and the new one
- * will be scheduled afterwards.
- */
- protected final void scheduleFor(@NonNull View source) {
- if (DEBUG) Log.d(LOG_TAG, thisClass() + ".scheduleFor(source = " + source + ")");
-
- Handler uiHandler = source.getHandler();
- if (uiHandler == null || uiHandler.getLooper() != Looper.myLooper()) {
- CalledFromWrongThreadException e = new CalledFromWrongThreadException(
- "Expected to be called from main thread but was called from "
- + Thread.currentThread());
- // TODO: Throw the exception
- Log.e(LOG_TAG, "Accessibility content change on non-UI thread. Future Android "
- + "versions will throw an exception.", e);
- }
-
- if (!isAccessibilityEnabled(source)) return;
-
- if (mIsPending) {
- View merged = tryMerge(mSource, source);
- if (merged != null) {
- setSource(merged);
- return;
- } else {
- sendNow();
- }
- }
-
- setSource(source);
-
- final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastSendTimeMillis;
- final long minEventIntervalMillis =
- ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
- if (timeSinceLastMillis >= minEventIntervalMillis) {
- sendNow();
- } else {
- mSource.postDelayed(mWorker, minEventIntervalMillis - timeSinceLastMillis);
- }
- }
-
- static boolean isAccessibilityEnabled(@NonNull View contextProvider) {
- return AccessibilityManager.getInstance(contextProvider.getContext()).isEnabled();
- }
-
- protected final void sendNow(View source) {
- setSource(source);
- sendNow();
- }
-
- private void sendNow() {
- mSource.removeCallbacks(mWorker);
- mWorker.run();
- }
-
- /**
- * Flush the event if one is pending
- */
- public void sendNowIfPending() {
- if (mIsPending) sendNow();
- }
-
- /**
- * Cancel the event if one is pending and is for the given view
- */
- public final void cancelIfPendingFor(@NonNull View source) {
- if (isPendingFor(source)) cancelIfPending(this);
- }
-
- /**
- * @return whether an event is currently pending for the given source view
- */
- protected final boolean isPendingFor(@Nullable View source) {
- return mIsPending && mSource == source;
- }
-
- /**
- * Cancel the event if one is not null and pending
- */
- public static void cancelIfPending(@Nullable ThrottlingAccessibilityEventSender sender) {
- if (sender == null || !sender.checkAndResetIsPending()) return;
- sender.mSource.removeCallbacks(sender.mWorker);
- sender.resetStateInternal();
- }
-
- void resetStateInternal() {
- if (DEBUG) Log.d(LOG_TAG, thisClass() + ".resetStateInternal()");
-
- resetState(mSource);
- setSource(null);
- }
-
- boolean checkAndResetIsPending() {
- if (mIsPending) {
- mIsPending = false;
- return true;
- } else {
- return false;
- }
- }
-
- private void setSource(@Nullable View source) {
- if (DEBUG) Log.d(LOG_TAG, thisClass() + ".setSource(" + source + ")");
-
- if (source == null && mIsPending) {
- Log.e(LOG_TAG, "mSource nullified while callback still pending: " + this);
- return;
- }
-
- if (source != null && !mIsPending) {
- // At most one can be pending at any given time
- View oldSource = mSource;
- if (oldSource != null) {
- ViewRootImpl viewRootImpl = oldSource.getViewRootImpl();
- if (viewRootImpl != null) {
- viewRootImpl.flushPendingAccessibilityEvents();
- }
- }
- mIsPending = true;
- }
- mSource = source;
- }
-
- String thisClass() {
- return getClass().getSimpleName();
- }
-
- @Override
- public String toString() {
- return thisClass() + "(" + mSource + ")";
- }
-
-}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 594d240..a13de75 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -6864,7 +6864,7 @@
// detached and we do not allow detached views to fire accessibility
// events. So we are announcing that the subtree changed giving a chance
// to clients holding on to a view in this subtree to refresh it.
- notifyAccessibilityStateChanged(
+ notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
// Don't scrap views that have transient state.
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 08374cb..6c19256 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -1093,7 +1093,7 @@
checkSelectionChanged();
}
- notifyAccessibilitySubtreeChanged();
+ notifySubtreeAccessibilityStateChangedIfNeeded();
}
/**
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index af01a3e..92bfd56 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -132,7 +132,7 @@
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
- notifyAccessibilityStateChanged(
+ notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index e57f153..0762b15 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -158,7 +158,7 @@
mCheckedFromResource = false;
mChecked = checked;
refreshDrawableState();
- notifyAccessibilityStateChanged(
+ notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
// Avoid infinite recursions if setChecked() is called from a listener
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 7d3fcf4..5710db3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2397,7 +2397,7 @@
setText(mText);
if (hasPasswordTransformationMethod()) {
- notifyAccessibilityStateChanged(
+ notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@@ -5152,7 +5152,8 @@
public void setAccessibilityHeading(boolean isHeading) {
if (isHeading != mIsAccessibilityHeading) {
mIsAccessibilityHeading = isHeading;
- notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -5659,7 +5660,7 @@
sendOnTextChanged(text, 0, oldlen, textLength);
onTextChanged(text, 0, oldlen, textLength);
- notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+ notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
if (needEditableForNotification) {
sendAfterTextChanged((Editable) text);
@@ -6393,7 +6394,7 @@
public void setError(CharSequence error, Drawable icon) {
createEditorIfNeeded();
mEditor.setError(error, icon);
- notifyAccessibilityStateChanged(
+ notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
diff --git a/core/java/com/android/internal/util/ObjectUtils.java b/core/java/com/android/internal/util/ObjectUtils.java
index 379602a..59e5a64 100644
--- a/core/java/com/android/internal/util/ObjectUtils.java
+++ b/core/java/com/android/internal/util/ObjectUtils.java
@@ -29,9 +29,6 @@
return a != null ? a : Preconditions.checkNotNull(b);
}
- /**
- * Compares two {@link Nullable} objects with {@code null} values considered the smallest
- */
public static <T extends Comparable> int compare(@Nullable T a, @Nullable T b) {
if (a != null) {
return (b != null) ? a.compareTo(b) : 1;
@@ -39,13 +36,4 @@
return (b != null) ? -1 : 0;
}
}
-
- /**
- * @return {@code null} if the given instance is not of the given calss, or the given
- * instance otherwise
- */
- @Nullable
- public static <S, T extends S> T castOrNull(@Nullable S instance, @NonNull Class<T> c) {
- return c.isInstance(instance) ? (T) instance : null;
- }
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 6f2246a..7635a72 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -22,7 +22,9 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
@@ -502,7 +504,7 @@
}
private void onCollapsedChanged(boolean isCollapsed) {
- notifyAccessibilityStateChanged(
+ notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
if (mScrollIndicatorDrawable != null) {