diff options
| author | Eugene Susla <eugenesusla@google.com> | 2018-01-23 21:12:11 +0000 |
|---|---|---|
| committer | Eugene Susla <eugenesusla@google.com> | 2018-01-26 11:32:25 -0800 |
| commit | 72c510f1c4d5cff42a3925a784027e258ac0bcdc (patch) | |
| tree | 3b2d6d9da641f38f6c8afe7fea3fec1789b89055 /core/java/android/view/ViewRootImpl.java | |
| parent | f4c5faf932a6a3e5a3120f5a3953af11872e6bf9 (diff) | |
Revert "Move A11y events throttling away from View(RootImpl)"
This reverts commit e4d31b3c103045d5b2b141a05084dced595cc64f.
Fixes: 71904218
Test: presubmit
Change-Id: Id73bde1a0c11696cf561c84cde027cdca4c6a00f
Diffstat (limited to 'core/java/android/view/ViewRootImpl.java')
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 158 |
1 files changed, 125 insertions, 33 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 30f584c570ca..29246fd34748 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -89,11 +89,9 @@ import android.view.accessibility.AccessibilityManager.HighTextContrastChangeLis 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.OutputStream; 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 @@ public final class ViewRootImpl implements ViewParent, new AccessibilityInteractionConnectionManager(); final HighContrastTextManager mHighContrastTextManager; + SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent; + + HashSet<View> mTempHashSet; + private final int mDensity; private final int mNoncompatDensity; @@ -475,8 +478,6 @@ public final class ViewRootImpl implements ViewParent, private boolean mNeedsRendererSetup; - protected AccessibilityViewHierarchyState mAccessibilityState; - /** * Consistency verifier for debugging purposes. */ @@ -7258,9 +7259,11 @@ public final class ViewRootImpl implements ViewParent, * {@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,18 +7271,9 @@ public final class ViewRootImpl implements ViewParent, * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. */ private void removeSendWindowContentChangedCallback() { - if (mAccessibilityState != null - && mAccessibilityState.isWindowContentChangedEventSenderInitialized()) { - ThrottlingAccessibilityEventSender.cancelIfPending( - mAccessibilityState.getSendWindowContentChangedAccessibilityEvent()); - } - } - - AccessibilityViewHierarchyState getAccessibilityState() { - if (mAccessibilityState == null) { - mAccessibilityState = new AccessibilityViewHierarchyState(); + if (mSendWindowContentChangedAccessibilityEvent != null) { + mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent); } - return mAccessibilityState; } @Override @@ -7317,8 +7311,12 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, } } + 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 |
