Merge "Support accessibility on embedded hierarchies (2/n)"

This commit is contained in:
Jackal Guo
2020-02-11 02:20:50 +00:00
committed by Android (Google) Code Review
13 changed files with 547 additions and 25 deletions

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2019 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;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Matrix;
import android.os.IBinder;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
import java.lang.ref.WeakReference;
/**
* This class is an interface this ViewRootImpl provides to the host view to the latter
* can interact with the view hierarchy in SurfaceControlViewHost.
*
* @hide
*/
final class AccessibilityEmbeddedConnection extends IAccessibilityEmbeddedConnection.Stub {
private final WeakReference<ViewRootImpl> mViewRootImpl;
AccessibilityEmbeddedConnection(ViewRootImpl viewRootImpl) {
mViewRootImpl = new WeakReference<>(viewRootImpl);
}
@Override
public @Nullable IBinder associateEmbeddedHierarchy(@NonNull IBinder host, int hostViewId) {
final ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null) {
final AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(
viewRootImpl.mContext);
viewRootImpl.mAttachInfo.mLeashedParentToken = host;
viewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId = hostViewId;
if (accessibilityManager.isEnabled()) {
accessibilityManager.associateEmbeddedHierarchy(host, viewRootImpl.mLeashToken);
}
return viewRootImpl.mLeashToken;
}
return null;
}
@Override
public void disassociateEmbeddedHierarchy() {
final ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null) {
final AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(
viewRootImpl.mContext);
viewRootImpl.mAttachInfo.mLeashedParentToken = null;
viewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId = View.NO_ID;
viewRootImpl.mAttachInfo.mLocationInParentDisplay.set(0, 0);
if (accessibilityManager.isEnabled()) {
accessibilityManager.disassociateEmbeddedHierarchy(viewRootImpl.mLeashToken);
}
}
}
@Override
public void setScreenMatrix(float[] matrixValues) {
final ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null) {
// TODO(b/148821260): Implement the rest of matrix values.
viewRootImpl.mAttachInfo.mLocationInParentDisplay.set(
(int) matrixValues[Matrix.MTRANS_X], (int) matrixValues[Matrix.MTRANS_Y]);
}
}
}

View File

@@ -855,6 +855,36 @@ public final class AccessibilityInteractionController {
return mViewRootImpl.mAttachInfo.mLocationInParentDisplay.equals(0, 0);
}
private void associateLeashedParentIfNeeded(List<AccessibilityNodeInfo> infos) {
if (infos == null || shouldBypassAssociateLeashedParent()) {
return;
}
final int infoCount = infos.size();
for (int i = 0; i < infoCount; i++) {
final AccessibilityNodeInfo info = infos.get(i);
associateLeashedParentIfNeeded(info);
}
}
private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) {
if (info == null || shouldBypassAssociateLeashedParent()) {
return;
}
// The node id of root node in embedded maybe not be ROOT_NODE_ID so we compare the id
// with root view.
if (mViewRootImpl.mView.getAccessibilityViewId()
!= AccessibilityNodeInfo.getAccessibilityViewId(info.getSourceNodeId())) {
return;
}
info.setLeashedParent(mViewRootImpl.mAttachInfo.mLeashedParentToken,
mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId);
}
private boolean shouldBypassAssociateLeashedParent() {
return (mViewRootImpl.mAttachInfo.mLeashedParentToken == null
&& mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId == View.NO_ID);
}
private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
MagnificationSpec spec) {
if (info == null) {
@@ -914,6 +944,7 @@ public final class AccessibilityInteractionController {
MagnificationSpec spec, Region interactiveRegion) {
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
associateLeashedParentIfNeeded(infos);
adjustBoundsInScreenIfNeeded(infos);
// To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
// then impact the visibility result, we need to adjust visibility before apply scale.
@@ -935,6 +966,7 @@ public final class AccessibilityInteractionController {
MagnificationSpec spec, Region interactiveRegion) {
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
associateLeashedParentIfNeeded(info);
adjustBoundsInScreenIfNeeded(info);
// To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
// then impact the visibility result, we need to adjust visibility before apply scale.

View File

@@ -28988,6 +28988,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
OnContentApplyWindowInsetsListener mContentOnApplyWindowInsetsListener;
/**
* The leash token of this view's parent when it's in an embedded hierarchy that is
* re-parented to another window.
*/
IBinder mLeashedParentToken;
/**
* The accessibility view id of this view's parent when it's in an embedded
* hierarchy that is re-parented to another window.
*/
int mLeashedParentAccessibilityViewId;
/**
* Creates a new set of attachment information with the specified
* events handler and thread.

View File

@@ -135,6 +135,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -355,6 +356,8 @@ public final class ViewRootImpl implements ViewParent,
final W mWindow;
final IBinder mLeashToken;
final int mTargetSdkVersion;
int mSeq;
@@ -652,6 +655,8 @@ public final class ViewRootImpl implements ViewParent,
private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
private IAccessibilityEmbeddedConnection mEmbeddedConnection;
static final class SystemUiVisibilityInfo {
int seq;
int globalVisibility;
@@ -685,6 +690,7 @@ public final class ViewRootImpl implements ViewParent,
mVisRect = new Rect();
mWinFrame = new Rect();
mWindow = new W(this);
mLeashToken = new Binder();
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mViewVisibility = View.GONE;
mTransparentRegion = new Region();
@@ -9157,6 +9163,10 @@ public final class ViewRootImpl implements ViewParent,
focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
}
if (mAttachInfo.mLeashedParentToken != null) {
mAccessibilityManager.associateEmbeddedHierarchy(
mAttachInfo.mLeashedParentToken, mLeashToken);
}
} else {
ensureNoConnection();
mHandler.obtainMessage(MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST).sendToTarget();
@@ -9169,6 +9179,7 @@ public final class ViewRootImpl implements ViewParent,
if (!registered) {
mAttachInfo.mAccessibilityWindowId =
mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
mLeashToken,
mContext.getPackageName(),
new AccessibilityInteractionConnection(ViewRootImpl.this));
}
@@ -9355,6 +9366,17 @@ public final class ViewRootImpl implements ViewParent,
}
}
/**
* Gets an accessibility embedded connection interface for this ViewRootImpl.
* @hide
*/
public IAccessibilityEmbeddedConnection getEmbeddedConnection() {
if (mEmbeddedConnection == null) {
mEmbeddedConnection = new AccessibilityEmbeddedConnection(ViewRootImpl.this);
}
return mEmbeddedConnection;
}
private class SendWindowContentChangedAccessibilityEvent implements Runnable {
private int mChangeTypes = 0;

View File

@@ -1085,6 +1085,50 @@ public final class AccessibilityManager {
}
}
/**
* Associate the connection between the host View and the embedded SurfaceControlViewHost.
*
* @hide
*/
public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
if (service == null) {
return;
}
}
try {
service.associateEmbeddedHierarchy(host, embedded);
} catch (RemoteException e) {
return;
}
}
/**
* Disassociate the connection between the host View and the embedded SurfaceControlViewHost.
* The given token could be either from host side or embedded side.
*
* @hide
*/
public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
if (token == null) {
return;
}
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
if (service == null) {
return;
}
}
try {
service.disassociateEmbeddedHierarchy(token);
} catch (RemoteException e) {
return;
}
}
/**
* Sets the current state and notifies listeners, if necessary.
*
@@ -1147,11 +1191,12 @@ public final class AccessibilityManager {
/**
* Adds an accessibility interaction connection interface for a given window.
* @param windowToken The window token to which a connection is added.
* @param leashToken The leash token to which a connection is added.
* @param connection The connection.
*
* @hide
*/
public int addAccessibilityInteractionConnection(IWindow windowToken,
public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
String packageName, IAccessibilityInteractionConnection connection) {
final IAccessibilityManager service;
final int userId;
@@ -1163,8 +1208,8 @@ public final class AccessibilityManager {
userId = mUserId;
}
try {
return service.addAccessibilityInteractionConnection(windowToken, connection,
packageName, userId);
return service.addAccessibilityInteractionConnection(windowToken, leashToken,
connection, packageName, userId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2019 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;
/**
* Interface used by host View to talk to the view root of the embedded SurfaceControlViewHost
* that actually implements the functionality.
*
* @hide
*/
interface IAccessibilityEmbeddedConnection {
IBinder associateEmbeddedHierarchy(IBinder hostToken, int sourceId);
void disassociateEmbeddedHierarchy();
oneway void setScreenMatrix(in float[] matrixValues);
}

View File

@@ -47,7 +47,7 @@ interface IAccessibilityManager {
@UnsupportedAppUsage
List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId);
int addAccessibilityInteractionConnection(IWindow windowToken,
int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
in IAccessibilityInteractionConnection connection,
String packageName, int userId);
@@ -88,4 +88,8 @@ interface IAccessibilityManager {
oneway void registerSystemAction(in RemoteAction action, int actionId);
oneway void unregisterSystemAction(int actionId);
oneway void setWindowMagnificationConnection(in IWindowMagnificationConnection connection);
void associateEmbeddedHierarchy(IBinder host, IBinder embedded);
void disassociateEmbeddedHierarchy(IBinder token);
}