diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java index 1cb5ffb1a40..ac9cd09be51 100644 --- a/src/com/android/settings/accessibility/AccessibilityUtil.java +++ b/src/com/android/settings/accessibility/AccessibilityUtil.java @@ -16,12 +16,44 @@ package com.android.settings.accessibility; +import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; +import android.os.Build; import android.provider.Settings; +import androidx.annotation.IntDef; + import com.android.settings.R; -public class AccessibilityUtil { +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** Provides utility methods to accessibility settings only. */ +final class AccessibilityUtil { + + private AccessibilityUtil(){} + + /** + * Annotation for different accessibilityService fragment UI type. + * + * {@code LEGACY} for displaying appearance aligned with sdk version Q accessibility service + * page, but only hardware shortcut allowed. + * {@code HEADLESS} for displaying appearance without switch bar. + * {@code INTUITIVE} for displaying appearance with new design. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + AccessibilityServiceFragmentType.LEGACY, + AccessibilityServiceFragmentType.HEADLESS, + AccessibilityServiceFragmentType.INTUITIVE, + }) + + public @interface AccessibilityServiceFragmentType { + int LEGACY = 0; + int HEADLESS = 1; + int INTUITIVE = 2; + } + /** * Return On/Off string according to the setting which specifies the integer value 1 or 0. This * setting is defined in the secure system settings {@link android.provider.Settings.Secure}. @@ -33,4 +65,25 @@ public class AccessibilityUtil { : R.string.accessibility_feature_state_off; return context.getResources().getText(resId); } + + /** + * Gets the corresponding fragment type of a given accessibility service + * + * @param accessibilityServiceInfo The accessibilityService's info + * @return int from {@link AccessibilityServiceFragmentType} + */ + static @AccessibilityServiceFragmentType int getAccessibilityServiceFragmentType( + AccessibilityServiceInfo accessibilityServiceInfo) { + final int targetSdk = accessibilityServiceInfo.getResolveInfo() + .serviceInfo.applicationInfo.targetSdkVersion; + final boolean requestA11yButton = (accessibilityServiceInfo.flags + & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; + + if (targetSdk <= Build.VERSION_CODES.Q) { + return AccessibilityServiceFragmentType.LEGACY; + } + return requestA11yButton + ? AccessibilityServiceFragmentType.HEADLESS + : AccessibilityServiceFragmentType.INTUITIVE; + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java index 70d86d06992..c4d8ead6b0a 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java @@ -18,7 +18,13 @@ package com.android.settings.accessibility; import static com.google.common.truth.Truth.assertThat; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.ComponentName; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Build; import android.provider.Settings; import com.android.settings.R; @@ -28,12 +34,18 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; @RunWith(RobolectricTestRunner.class) -public class AccessibilityUtilTest { +public final class AccessibilityUtilTest { private static final int ON = 1; private static final int OFF = 0; private static final String SECURE_TEST_KEY = "secure_test_key"; + private static final String DUMMY_PACKAGE_NAME = "com.dummy.example"; + private static final String DUMMY_CLASS_NAME = DUMMY_PACKAGE_NAME + ".dummy_a11y_service"; + private static final String DUMMY_COMPONENT_NAME = DUMMY_PACKAGE_NAME + "/" + DUMMY_CLASS_NAME; private Context mContext; @Before @@ -68,4 +80,66 @@ public class AccessibilityUtilTest { assertThat(result) .isEqualTo(mContext.getText(R.string.accessibility_feature_state_off)); } + + @Test + public void getAccessibilityServiceFragmentType_targetSdkQ_legacyType() { + final AccessibilityServiceInfo info = getMockAccessibilityServiceInfo(); + + info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; + info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; + + assertThat(AccessibilityUtil.getAccessibilityServiceFragmentType(info)).isEqualTo( + AccessibilityUtil.AccessibilityServiceFragmentType.LEGACY); + + } + + @Test + public void getAccessibilityServiceFragmentType_targetSdkR_HaveA11yButton_headlessType() { + final AccessibilityServiceInfo info = getMockAccessibilityServiceInfo(); + + info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; + info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; + + assertThat(AccessibilityUtil.getAccessibilityServiceFragmentType(info)).isEqualTo( + AccessibilityUtil.AccessibilityServiceFragmentType.HEADLESS); + + } + + @Test + public void getAccessibilityServiceFragmentType_targetSdkR_NoA11yButton_intuitiveType() { + final AccessibilityServiceInfo info = getMockAccessibilityServiceInfo(); + + info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; + info.flags |= ~AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; + + assertThat(AccessibilityUtil.getAccessibilityServiceFragmentType(info)).isEqualTo( + AccessibilityUtil.AccessibilityServiceFragmentType.INTUITIVE); + + } + + + private AccessibilityServiceInfo getMockAccessibilityServiceInfo() { + final ApplicationInfo applicationInfo = new ApplicationInfo(); + final ServiceInfo serviceInfo = new ServiceInfo(); + applicationInfo.packageName = DUMMY_PACKAGE_NAME; + serviceInfo.packageName = DUMMY_PACKAGE_NAME; + serviceInfo.name = DUMMY_CLASS_NAME; + serviceInfo.applicationInfo = applicationInfo; + + final ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.serviceInfo = serviceInfo; + + try { + final AccessibilityServiceInfo info = new AccessibilityServiceInfo(resolveInfo, + mContext); + final ComponentName componentName = ComponentName.unflattenFromString( + DUMMY_COMPONENT_NAME); + info.setComponentName(componentName); + return info; + } catch (XmlPullParserException | IOException e) { + // Do nothing + } + + return null; + } }