Expose A11yDisplayProxy#setFocusAppearance

A proxy can prevent an app's accessibility focus from being drawn, to
avoid conflict with its own focus outline (For example, an Exo screen
reader should have its own focus).

Bug: 254545943
Test: manual test with non-default display, atest AccessibilityDisplayProxyTest
Change-Id: Ib794de54490aa53f5d478fdba00dbd9578982a47
This commit is contained in:
sallyyuen
2023-01-09 13:49:58 -08:00
parent 1d6502723c
commit 880c2380d7
7 changed files with 98 additions and 2 deletions

View File

@@ -16545,6 +16545,7 @@ package android.view.accessibility {
method public void interrupt();
method public void onAccessibilityEvent(@NonNull android.view.accessibility.AccessibilityEvent);
method public void onProxyConnected();
method public void setAccessibilityFocusAppearance(int, @ColorInt int);
method public void setInstalledAndEnabledServices(@NonNull java.util.List<android.accessibilityservice.AccessibilityServiceInfo>);
}

View File

@@ -22,6 +22,7 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.accessibilityservice.MagnificationConfig;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -239,6 +240,27 @@ public abstract class AccessibilityDisplayProxy {
return Collections.emptyList();
}
/**
* Sets the strokeWidth and color of the accessibility focus rectangle.
*
* @param strokeWidth The stroke width of the rectangle in pixels.
* Setting this value to zero results in no focus rectangle being drawn.
* @param color The color of the rectangle.
*/
public void setAccessibilityFocusAppearance(int strokeWidth, @ColorInt int color) {
IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
if (connection != null) {
try {
connection.setFocusAppearance(strokeWidth, color);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while setting the strokeWidth and color of the "
+ "accessibility focus rectangle", re);
re.rethrowFromSystemServer();
}
}
}
/**
* An IAccessibilityServiceClient that handles interrupts, accessibility events, and system
* connection.

View File

@@ -314,6 +314,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
void unbindImeLocked(AbstractAccessibilityServiceConnection connection);
void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc);
void setCurrentUserFocusAppearance(int strokeWidth, int color);
}
public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,

View File

@@ -4876,4 +4876,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
transaction.apply();
transaction.close();
}
@Override
public void setCurrentUserFocusAppearance(int strokeWidth, int color) {
synchronized (mLock) {
getCurrentUserStateLocked().setFocusAppearanceLocked(strokeWidth, color);
}
}
}

View File

@@ -43,6 +43,7 @@ import android.view.accessibility.AccessibilityWindowInfo;
import androidx.annotation.Nullable;
import com.android.internal.R;
import com.android.server.wm.WindowManagerInternal;
import java.util.Arrays;
@@ -63,6 +64,11 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
private int mDisplayId;
private List<AccessibilityServiceInfo> mInstalledAndEnabledServices;
/** The stroke width of the focus rectangle in pixels */
private int mFocusStrokeWidth;
/** The color of the focus rectangle */
private int mFocusColor;
ProxyAccessibilityServiceConnection(
Context context,
ComponentName componentName,
@@ -77,6 +83,10 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
/* systemActionPerformer= */ null, awm, /* activityTaskManagerService= */ null);
mDisplayId = displayId;
setDisplayTypes(DISPLAY_TYPE_PROXY);
mFocusStrokeWidth = mContext.getResources().getDimensionPixelSize(
R.dimen.accessibility_focus_highlight_stroke_width);
mFocusColor = mContext.getResources().getColor(
R.color.accessibility_focus_highlight_color);
}
/**
@@ -202,6 +212,48 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
return displayWindows;
}
@Override
public void setFocusAppearance(int strokeWidth, int color) {
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return;
}
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return;
}
if (getFocusStrokeWidthLocked() == strokeWidth && getFocusColorLocked() == color) {
return;
}
mFocusStrokeWidth = strokeWidth;
mFocusColor = color;
// Sets the appearance data in the A11yUserState for now, since the A11yManagers are not
// separated.
// TODO(254545943): Separate proxy and non-proxy states so the focus appearance on the
// phone is not affected by the appearance of a proxy-ed app.
mSystemSupport.setCurrentUserFocusAppearance(mFocusStrokeWidth, mFocusColor);
mSystemSupport.onClientChangeLocked(false);
}
}
/**
* Gets the stroke width of the focus rectangle.
* @return The stroke width.
*/
public int getFocusStrokeWidthLocked() {
return mFocusStrokeWidth;
}
/**
* Gets the color of the focus rectangle.
* @return The color.
*/
public int getFocusColorLocked() {
return mFocusColor;
}
@Override
public void binderDied() {
}

View File

@@ -178,9 +178,15 @@ public class ProxyManager {
if (a11yEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
}
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
if (proxy.mRequestTouchExplorationMode) {
clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
}
}
return clientState;
// TODO(b/254545943): When A11yManager is separated, include support for other properties
// like isTouchExplorationEnabled.
// TODO(b/254545943): When A11yManager is separated, include support for other properties.
}
/**

View File

@@ -28,6 +28,7 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
import android.view.accessibility.AccessibilityEvent;
@@ -35,6 +36,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.android.server.wm.WindowManagerInternal;
@@ -79,7 +81,10 @@ public class ProxyAccessibilityServiceConnectionTest {
@Before
public void setup() {
final Resources resources = getInstrumentation().getContext().getResources();
MockitoAnnotations.initMocks(this);
when(mMockContext.getResources()).thenReturn(resources);
mAccessibilityServiceInfo = new AccessibilityServiceInfo();
mProxyConnection = new ProxyAccessibilityServiceConnection(mMockContext, COMPONENT_NAME,
mAccessibilityServiceInfo, CONNECTION_ID , new Handler(