diff options
| author | Rhed Jao <rhedjao@google.com> | 2019-01-30 08:01:13 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-01-30 08:01:13 +0000 |
| commit | 098876f547fe9d9f2c7cd6143115b357717b55d1 (patch) | |
| tree | 6f6414bbc531d4b4f8ed614eb5e24c3a6d987555 /core/java | |
| parent | 20f1a5d3ab5744fe8bbd75259e8a8e410d385659 (diff) | |
| parent | 23813d90a51710a8c5e45f0ca36b47057a4b855b (diff) | |
Merge "Improve clear a11y forcus for A11yManagerService"
Diffstat (limited to 'core/java')
3 files changed, 89 insertions, 4 deletions
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index eb41e078e59f..59e562fe8071 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -41,12 +41,14 @@ import android.view.View.AttachInfo; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityRequestPreparer; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import java.util.ArrayList; @@ -64,8 +66,11 @@ import java.util.function.Predicate; * called from the interaction connection ViewAncestor gives the system to * talk to it and a corresponding *UiThread method that is executed on the * UI thread. + * + * @hide */ -final class AccessibilityInteractionController { +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public final class AccessibilityInteractionController { private static final String LOG_TAG = "AccessibilityInteractionController"; @@ -85,7 +90,7 @@ final class AccessibilityInteractionController { private final Object mLock = new Object(); - private final Handler mHandler; + private final PrivateHandler mHandler; private final ViewRootImpl mViewRootImpl; @@ -131,11 +136,19 @@ final class AccessibilityInteractionController { // thread in this process, set the message as a static reference so // after this call completes the same thread but in the interrogating // client can handle the message to generate the result. - if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) { + if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId + && mHandler.hasAccessibilityCallback(message)) { AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { - mHandler.sendMessage(message); + // For messages without callback of interrogating client, just handle the + // message immediately if this is UI thread. + if (!mHandler.hasAccessibilityCallback(message) + && Thread.currentThread().getId() == mMyLooperThreadId) { + mHandler.handleMessage(message); + } else { + mHandler.sendMessage(message); + } } } } @@ -731,6 +744,52 @@ final class AccessibilityInteractionController { } } + /** + * Finds the accessibility focused node in the root, and clears the accessibility focus. + */ + public void clearAccessibilityFocusClientThread() { + final Message message = mHandler.obtainMessage(); + message.what = PrivateHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS; + + // Don't care about pid and tid because there's no interrogating client for this message. + scheduleMessage(message, 0, 0, CONSIDER_REQUEST_PREPARERS); + } + + private void clearAccessibilityFocusUiThread() { + if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { + return; + } + try { + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = + AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; + final View root = mViewRootImpl.mView; + if (root != null && isShown(root)) { + final View host = mViewRootImpl.mAccessibilityFocusedHost; + // If there is no accessibility focus host or it is not a descendant + // of the root from which to start the search, then the search failed. + if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) { + return; + } + final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider(); + final AccessibilityNodeInfo focusNode = + mViewRootImpl.mAccessibilityFocusedVirtualView; + if (provider != null && focusNode != null) { + final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId( + focusNode.getSourceNodeId()); + provider.performAction(virtualNodeId, + AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(), + null); + } else { + host.performAccessibilityAction( + AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(), + null); + } + } + } finally { + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; + } + } + private View findViewByAccessibilityId(int accessibilityId) { View root = mViewRootImpl.mView; if (root == null) { @@ -1294,6 +1353,12 @@ final class AccessibilityInteractionController { private static final int MSG_APP_PREPARATION_FINISHED = 8; private static final int MSG_APP_PREPARATION_TIMEOUT = 9; + // Uses FIRST_NO_ACCESSIBILITY_CALLBACK_MSG for messages that don't need to call back + // results to interrogating client. + private static final int FIRST_NO_ACCESSIBILITY_CALLBACK_MSG = 100; + private static final int MSG_CLEAR_ACCESSIBILITY_FOCUS = + FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 1; + public PrivateHandler(Looper looper) { super(looper); } @@ -1320,6 +1385,8 @@ final class AccessibilityInteractionController { return "MSG_APP_PREPARATION_FINISHED"; case MSG_APP_PREPARATION_TIMEOUT: return "MSG_APP_PREPARATION_TIMEOUT"; + case MSG_CLEAR_ACCESSIBILITY_FOCUS: + return "MSG_CLEAR_ACCESSIBILITY_FOCUS"; default: throw new IllegalArgumentException("Unknown message type: " + type); } @@ -1356,10 +1423,17 @@ final class AccessibilityInteractionController { case MSG_APP_PREPARATION_TIMEOUT: { requestPreparerTimeoutUiThread(); } break; + case MSG_CLEAR_ACCESSIBILITY_FOCUS: { + clearAccessibilityFocusUiThread(); + } break; default: throw new IllegalArgumentException("Unknown message type: " + type); } } + + boolean hasAccessibilityCallback(Message message) { + return message.what < FIRST_NO_ACCESSIBILITY_CALLBACK_MSG ? true : false; + } } private final class AddNodeInfosForViewId implements Predicate<View> { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f47eb10efd29..9213f32326b4 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -8718,6 +8718,15 @@ public final class ViewRootImpl implements ViewParent, } } } + + @Override + public void clearAccessibilityFocus() { + ViewRootImpl viewRootImpl = mViewRootImpl.get(); + if (viewRootImpl != null && viewRootImpl.mView != null) { + viewRootImpl.getAccessibilityInteractionController() + .clearAccessibilityFocusClientThread(); + } + } } private class SendWindowContentChangedAccessibilityEvent implements Runnable { diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl index 4c0fdfd5afdf..947ff056627e 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -55,4 +55,6 @@ oneway interface IAccessibilityInteractionConnection { void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid); + + void clearAccessibilityFocus(); } |
