summaryrefslogtreecommitdiff
path: root/core/java/android/view/ViewRootImpl.java
diff options
context:
space:
mode:
authorEugene Susla <eugenesusla@google.com>2018-01-23 21:12:11 +0000
committerEugene Susla <eugenesusla@google.com>2018-01-26 11:32:25 -0800
commit72c510f1c4d5cff42a3925a784027e258ac0bcdc (patch)
tree3b2d6d9da641f38f6c8afe7fea3fec1789b89055 /core/java/android/view/ViewRootImpl.java
parentf4c5faf932a6a3e5a3120f5a3953af11872e6bf9 (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.java158
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