summaryrefslogtreecommitdiff
path: root/core/java/android/view/SurfaceView.java
diff options
context:
space:
mode:
authorJackal Guo <jackalguo@google.com>2020-02-03 15:05:43 +0800
committerJackal Guo <jackalguo@google.com>2020-02-13 13:45:17 +0800
commitac234d660857e9ba659e4f600cd94cf2efbebd67 (patch)
tree4c3f7758ed68f99d3fc78cc49dd88719db59fec6 /core/java/android/view/SurfaceView.java
parent15bee94b774df47fb3eff3401f9b6f738c9e32af (diff)
Support accessibility on embedded hierarchies (3/n)
Link embedded hierarchy for accessibility when #setChildSurfacePackage is invoked. Bug: 137593247 Test: a11y CTS & unit tests Change-Id: I6e9cfb387c863314b0affc5b48fb3c4c62cdf950
Diffstat (limited to 'core/java/android/view/SurfaceView.java')
-rw-r--r--core/java/android/view/SurfaceView.java143
1 files changed, 127 insertions, 16 deletions
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d5ed36b57c02..47ffd3e2714c 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -28,6 +28,7 @@ import android.content.res.CompatibilityInfo.Translator;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
@@ -38,12 +39,14 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceControlViewHost;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
import com.android.internal.view.SurfaceCallbackHelper;
@@ -203,8 +206,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
private int mParentSurfaceGenerationId;
- // The token of embedded windowless view hierarchy.
- private IBinder mEmbeddedViewHierarchy;
+ private RemoteAccessibilityEmbeddedConnection mRemoteAccessibilityEmbeddedConnection;
+
+ private final Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
+ private final Matrix mTmpMatrix = new Matrix();
+ private final float[] mMatrixValues = new float[9];
+
SurfaceControlViewHost.SurfacePackage mSurfacePackage;
public SurfaceView(Context context) {
@@ -923,6 +930,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
mTmpTransaction.apply();
+ updateScreenMatrixForEmbeddedHierarchy();
if (sizeChanged || creating) {
redrawNeeded = true;
@@ -1510,6 +1518,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
@Override
public void surfaceDestroyed() {
setWindowStopped(true);
+ setRemoteAccessibilityEmbeddedConnection(null, null);
}
/**
@@ -1568,31 +1577,133 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private void reparentSurfacePackage(SurfaceControl.Transaction t,
SurfaceControlViewHost.SurfacePackage p) {
- // TODO: Link accessibility IDs here.
+ initEmbeddedHierarchyForAccessibility(p);
final SurfaceControl sc = p.getSurfaceControl();
t.reparent(sc, mSurfaceControl).show(sc);
}
- /**
- * Add the token of embedded view hierarchy. Set {@code null} to clear the embedded view
- * hierarchy.
- *
- * @param token IBinder token.
- * @hide
- */
- public void setEmbeddedViewHierarchy(IBinder token) {
- mEmbeddedViewHierarchy = token;
- }
-
/** @hide */
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
- if (mEmbeddedViewHierarchy == null) {
+ final RemoteAccessibilityEmbeddedConnection wrapper =
+ getRemoteAccessibilityEmbeddedConnection();
+ if (wrapper == null) {
return;
}
// Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this
// leashed child would return the root node in the embedded hierarchy
- info.addChild(mEmbeddedViewHierarchy);
+ info.addChild(wrapper.getLeashToken());
+ }
+
+ private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) {
+ final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection();
+ final RemoteAccessibilityEmbeddedConnection wrapper =
+ getRemoteAccessibilityEmbeddedConnection();
+
+ // Do nothing if package is embedding the same view hierarchy.
+ if (wrapper != null && wrapper.getConnection().equals(connection)) {
+ return;
+ }
+
+ // If this SurfaceView embeds a different view hierarchy, unlink the previous one first.
+ setRemoteAccessibilityEmbeddedConnection(null, null);
+
+ try {
+ final IBinder leashToken = connection.associateEmbeddedHierarchy(
+ getViewRootImpl().mLeashToken, getAccessibilityViewId());
+ setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while associateEmbeddedHierarchy " + e);
+ }
+ updateScreenMatrixForEmbeddedHierarchy();
+ }
+
+ private void setRemoteAccessibilityEmbeddedConnection(
+ IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
+ try {
+ if (mRemoteAccessibilityEmbeddedConnection != null) {
+ mRemoteAccessibilityEmbeddedConnection.getConnection()
+ .disassociateEmbeddedHierarchy();
+ mRemoteAccessibilityEmbeddedConnection.unlinkToDeath();
+ mRemoteAccessibilityEmbeddedConnection = null;
+ }
+ if (connection != null && leashToken != null) {
+ mRemoteAccessibilityEmbeddedConnection =
+ new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
+ mRemoteAccessibilityEmbeddedConnection.linkToDeath();
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
+ }
+ }
+
+ private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
+ return mRemoteAccessibilityEmbeddedConnection;
+ }
+
+ private void updateScreenMatrixForEmbeddedHierarchy() {
+ mTmpMatrix.reset();
+ mTmpMatrix.setTranslate(mScreenRect.left, mScreenRect.top);
+ mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
+ mScreenRect.height() / (float) mSurfaceHeight);
+
+ // If the screen matrix is identity or doesn't change, do nothing.
+ if (mTmpMatrix.isIdentity() || mTmpMatrix.equals(mScreenMatrixForEmbeddedHierarchy)) {
+ return;
+ }
+
+ try {
+ final RemoteAccessibilityEmbeddedConnection wrapper =
+ getRemoteAccessibilityEmbeddedConnection();
+ if (wrapper == null) {
+ return;
+ }
+ mTmpMatrix.getValues(mMatrixValues);
+ wrapper.getConnection().setScreenMatrix(mMatrixValues);
+ mScreenMatrixForEmbeddedHierarchy.set(mTmpMatrix);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while setScreenMatrix " + e);
+ }
+ }
+
+ /**
+ * Wrapper of accessibility embedded connection for embedded view hierarchy.
+ */
+ private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
+ private final IAccessibilityEmbeddedConnection mConnection;
+ private final IBinder mLeashToken;
+
+ RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
+ IBinder leashToken) {
+ mConnection = connection;
+ mLeashToken = leashToken;
+ }
+
+ IAccessibilityEmbeddedConnection getConnection() {
+ return mConnection;
+ }
+
+ IBinder getLeashToken() {
+ return mLeashToken;
+ }
+
+ void linkToDeath() throws RemoteException {
+ mConnection.asBinder().linkToDeath(this, 0);
+ }
+
+ void unlinkToDeath() {
+ mConnection.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ unlinkToDeath();
+ runOnUiThread(() -> {
+ if (mRemoteAccessibilityEmbeddedConnection == this) {
+ mRemoteAccessibilityEmbeddedConnection = null;
+ }
+ });
+ }
}
}