diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index fe89bf203b4..35fe6e4e600 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -175,6 +175,9 @@ public class AccessibilitySettings extends DashboardFragment implements // Observe changes from accessibility selection menu shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); + if (android.view.accessibility.Flags.a11yQsShortcut()) { + shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS); + } shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_STICKY_KEYS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SLOW_KEYS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS); diff --git a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java index 8af284db15f..41c5d750b69 100644 --- a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java +++ b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java @@ -125,6 +125,9 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted final List shortcutFeatureKeys = new ArrayList<>(); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); + if (android.view.accessibility.Flags.a11yQsShortcut()) { + shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS); + } mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler()); mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> { updateShortcutPreferenceData(); diff --git a/src/com/android/settings/accessibility/ColorAndMotionFragment.java b/src/com/android/settings/accessibility/ColorAndMotionFragment.java index 28c533532d7..4ea22260c79 100644 --- a/src/com/android/settings/accessibility/ColorAndMotionFragment.java +++ b/src/com/android/settings/accessibility/ColorAndMotionFragment.java @@ -74,6 +74,9 @@ public class ColorAndMotionFragment extends DashboardFragment { mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED); mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); + if (android.view.accessibility.Flags.a11yQsShortcut()) { + mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS); + } if (Flags.forceInvertColor()) { mShortcutFeatureKeys.add(ToggleForceInvertPreferenceController.SETTINGS_KEY); } diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index 6d5f5362e25..ed47007fbcf 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -179,6 +179,9 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment final List shortcutFeatureKeys = new ArrayList<>(); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); + if (android.view.accessibility.Flags.a11yQsShortcut()) { + shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS); + } return shortcutFeatureKeys; } diff --git a/src/com/android/settings/gestures/OneHandedSettingsUtils.java b/src/com/android/settings/gestures/OneHandedSettingsUtils.java index 04898dc0219..fe7db4f7c3e 100644 --- a/src/com/android/settings/gestures/OneHandedSettingsUtils.java +++ b/src/com/android/settings/gestures/OneHandedSettingsUtils.java @@ -50,6 +50,8 @@ public class OneHandedSettingsUtils { Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); static final Uri HARDWARE_SHORTCUT_ENABLED_URI = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); + static final Uri QS_SHORTCUT_ENABLED_URI = + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_QS_TARGETS); public enum OneHandedTimeout { NEVER(0), SHORT(4), MEDIUM(8), LONG(12); @@ -254,6 +256,16 @@ public class OneHandedSettingsUtils { if (!TextUtils.isEmpty(targetsHW) && targetsHW.contains(ONE_HANDED_MODE_TARGET_NAME)) { return true; } + + if (android.view.accessibility.Flags.a11yQsShortcut()) { + // Checks QS_SHORTCUT_KEY + final String targetsQs = Settings.Secure.getStringForUser(context.getContentResolver(), + Settings.Secure.ACCESSIBILITY_QS_TARGETS, sCurrentUserId); + if (!TextUtils.isEmpty(targetsQs) && targetsQs.contains(ONE_HANDED_MODE_TARGET_NAME)) { + return true; + } + } + return false; } @@ -301,6 +313,9 @@ public class OneHandedSettingsUtils { resolver.registerContentObserver(SHOW_NOTIFICATION_ENABLED_URI, true, this); resolver.registerContentObserver(SOFTWARE_SHORTCUT_ENABLED_URI, true, this); resolver.registerContentObserver(HARDWARE_SHORTCUT_ENABLED_URI, true, this); + if (android.view.accessibility.Flags.a11yQsShortcut()) { + resolver.registerContentObserver(QS_SHORTCUT_ENABLED_URI, true, this); + } } @Override diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java index 05e56ca74a4..624a39ab330 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java @@ -38,8 +38,12 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.database.ContentObserver; import android.os.Build; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.Flags; import androidx.fragment.app.Fragment; import androidx.test.core.app.ApplicationProvider; @@ -59,6 +63,8 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.search.SearchIndexableRaw; import com.android.settingslib.testutils.shadow.ShadowColorDisplayManager; +import com.google.common.truth.BooleanSubject; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -103,6 +109,7 @@ public class AccessibilitySettingsTest { @Rule public final MockitoRule mocks = MockitoJUnit.rule(); + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private final Context mContext = ApplicationProvider.getApplicationContext(); @Spy private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo( @@ -316,30 +323,39 @@ public class AccessibilitySettingsTest { } @Test - public void onCreate_haveRegisterToSpecificUrisAndActions() { + @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) + public void onCreate_flagDisabled_haveRegisterToSpecificUrisAndActions() { setupFragment(); - ShadowContentResolver shadowContentResolver = shadowOf(mContext.getContentResolver()); - Collection a11yButtonTargetsObservers = - shadowContentResolver.getContentObservers( - Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS)); - Collection a11yShortcutTargetServiceObservers = - shadowContentResolver.getContentObservers(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, + AccessibilitySettingsContentObserver.class).isTrue(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + AccessibilitySettingsContentObserver.class).isTrue(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS, + AccessibilitySettingsContentObserver.class).isFalse(); + List broadcastReceivers = + shadowOf((Application) ApplicationProvider.getApplicationContext()) + .getRegisteredReceivers() + .stream().map(wrapper -> wrapper.broadcastReceiver).toList(); + assertThat(broadcastReceivers.stream().anyMatch( + broadcastReceiver -> broadcastReceiver instanceof PackageMonitor)).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) + public void onCreate_flagEnabled_haveRegisterToSpecificUrisAndActions() { + setupFragment(); + + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, + AccessibilitySettingsContentObserver.class).isTrue(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + AccessibilitySettingsContentObserver.class).isTrue(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS, + AccessibilitySettingsContentObserver.class).isTrue(); List broadcastReceivers = shadowOf((Application) ApplicationProvider.getApplicationContext()) .getRegisteredReceivers() .stream().map(wrapper -> wrapper.broadcastReceiver).toList(); - assertThat( - a11yButtonTargetsObservers.stream() - .anyMatch(contentObserver -> - contentObserver instanceof AccessibilitySettingsContentObserver)) - .isTrue(); - assertThat( - a11yShortcutTargetServiceObservers.stream() - .anyMatch(contentObserver -> - contentObserver instanceof AccessibilitySettingsContentObserver)) - .isTrue(); assertThat(broadcastReceivers.stream().anyMatch( broadcastReceiver -> broadcastReceiver instanceof PackageMonitor)).isTrue(); } @@ -350,27 +366,16 @@ public class AccessibilitySettingsTest { mActivityController.pause().stop().destroy(); - ShadowContentResolver shadowContentResolver = shadowOf(mContext.getContentResolver()); - Collection a11yButtonTargetsObservers = - shadowContentResolver.getContentObservers( - Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS)); - Collection a11yShortcutTargetServiceObservers = - shadowContentResolver.getContentObservers(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, + AccessibilitySettingsContentObserver.class).isFalse(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + AccessibilitySettingsContentObserver.class).isFalse(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS, + AccessibilitySettingsContentObserver.class).isFalse(); List broadcastReceivers = shadowOf((Application) ApplicationProvider.getApplicationContext()) .getRegisteredReceivers() .stream().map(wrapper -> wrapper.broadcastReceiver).toList(); - assertThat( - a11yButtonTargetsObservers.stream() - .anyMatch(contentObserver -> - contentObserver instanceof AccessibilitySettingsContentObserver)) - .isFalse(); - assertThat( - a11yShortcutTargetServiceObservers.stream() - .anyMatch(contentObserver -> - contentObserver instanceof AccessibilitySettingsContentObserver)) - .isFalse(); assertThat(broadcastReceivers.stream().anyMatch( broadcastReceiver -> broadcastReceiver instanceof PackageMonitor)).isFalse(); } @@ -491,4 +496,14 @@ public class AccessibilitySettingsTest { Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, enabled ? componentName.flattenToString() : ""); } + + private BooleanSubject assertUriObserversContainsClazz( + String settingUri, Class clazz) { + ShadowContentResolver shadowContentResolver = shadowOf(mContext.getContentResolver()); + Collection observers = + shadowContentResolver.getContentObservers( + Settings.Secure.getUriFor(settingUri)); + + return assertThat(observers.stream().anyMatch(clazz::isInstance)); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index e963bd01f68..6fb1c3fb08a 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -146,8 +147,9 @@ public class ToggleFeaturePreferenceFragmentTest { } @Test + @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) @Config(shadows = {ShadowFragment.class}) - public void onResume_haveRegisterToSpecificUris() { + public void onResume_flagEnabled_haveRegisterToSpecificUris() { mFragment.onAttach(mContext); mFragment.onCreate(Bundle.EMPTY); @@ -162,6 +164,36 @@ public class ToggleFeaturePreferenceFragmentTest { Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)), eq(false), any(AccessibilitySettingsContentObserver.class)); + verify(mContentResolver).registerContentObserver( + eq(Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_QS_TARGETS)), + eq(false), + any(AccessibilitySettingsContentObserver.class)); + } + + @Test + @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) + @Config(shadows = {ShadowFragment.class}) + public void onResume_flagDisabled_haveRegisterToSpecificUris() { + mFragment.onAttach(mContext); + mFragment.onCreate(Bundle.EMPTY); + + mFragment.onResume(); + + verify(mContentResolver).registerContentObserver( + eq(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS)), + eq(false), + any(AccessibilitySettingsContentObserver.class)); + verify(mContentResolver).registerContentObserver( + eq(Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)), + eq(false), + any(AccessibilitySettingsContentObserver.class)); + verify(mContentResolver, never()).registerContentObserver( + eq(Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_QS_TARGETS)), + eq(false), + any(AccessibilitySettingsContentObserver.class)); } @Test diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java index 3d24fbb7237..1d85705a9e7 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java @@ -292,7 +292,39 @@ public class ToggleScreenMagnificationPreferenceFragmentTest { } @Test - public void onResume_haveRegisterToSpecificUris() { + @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) + public void onResume_flagEnabled_haveRegisterToSpecificUris() { + ShadowContentResolver shadowContentResolver = Shadows.shadowOf( + mContext.getContentResolver()); + Uri[] observedUri = new Uri[]{ + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS), + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE), + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_QS_TARGETS), + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED), + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED) + }; + for (Uri uri : observedUri) { + // verify no observer registered before launching the fragment + assertThat(shadowContentResolver.getContentObservers(uri)).isEmpty(); + } + + mFragController.create(R.id.main_content, /* bundle= */ null).start().resume(); + + for (Uri uri : observedUri) { + Collection observers = shadowContentResolver.getContentObservers(uri); + assertThat(observers.size()).isEqualTo(1); + assertThat(observers.stream().findFirst().get()).isInstanceOf( + AccessibilitySettingsContentObserver.class); + } + } + + @Test + @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) + public void onResume_flagDisabled_haveRegisterToSpecificUris() { ShadowContentResolver shadowContentResolver = Shadows.shadowOf( mContext.getContentResolver()); Uri[] observedUri = new Uri[]{ @@ -317,6 +349,9 @@ public class ToggleScreenMagnificationPreferenceFragmentTest { assertThat(observers.stream().findFirst().get()).isInstanceOf( AccessibilitySettingsContentObserver.class); } + assertThat(shadowContentResolver.getContentObservers( + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_QS_TARGETS))).hasSize(0); } @Test diff --git a/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsUtilsTest.java b/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsUtilsTest.java index 9559043570c..ee5f72ed2d4 100644 --- a/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsUtilsTest.java +++ b/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsUtilsTest.java @@ -16,13 +16,20 @@ package com.android.settings.gestures; +import static com.android.settings.gestures.OneHandedSettingsUtils.ONE_HANDED_MODE_TARGET_NAME; + import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; +import android.view.accessibility.Flags; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -30,7 +37,8 @@ import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class OneHandedSettingsUtilsTest { - + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final int OFF = 0; private static final int ON = 1; @@ -120,4 +128,66 @@ public class OneHandedSettingsUtilsTest { OneHandedSettingsUtils.OneHandedTimeout.LONG.getValue(), mCurrentUserId)) .isEqualTo(12); } + + @Test + public void getShortcutEnabled_a11yButtonVolumeKeysShortcutEnabled_returnTrue() { + setupShortcuts( + /* enableFab= */ true, /* enableVolumeKeys= */ true, /* enableQs=*/ false); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isTrue(); + } + + @Test + public void getShortcutEnabled_a11yButtonShortcutEnabled_returnTrue() { + setupShortcuts( + /* enableFab= */ true, /* enableVolumeKeys= */ false, /* enableQs=*/ false); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isTrue(); + } + + @Test + public void getShortcutEnabled_volumeKeysShortcutEnabled_returnTrue() { + setupShortcuts( + /* enableFab= */ false, /* enableVolumeKeys= */ true, /* enableQs=*/ false); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isTrue(); + } + + @Test + public void getShortcutEnabled_noShortcutsEnabled_returnFalse() { + setupShortcuts( + /* enableFab= */ false, /* enableVolumeKeys= */ false, /* enableQs=*/ false); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isFalse(); + } + + @Test + @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) + public void getShortcutEnabled_qsShortcutEnabled_returnTrue() { + setupShortcuts( + /* enableFab= */ false, /* enableVolumeKeys= */ false, /* enableQs=*/ true); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isTrue(); + } + + @Test + @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) + public void getShortcutEnabled_flagDisabled_qsShortcutEnabled_returnFalse() { + setupShortcuts( + /* enableFab= */ false, /* enableVolumeKeys= */ false, /* enableQs=*/ true); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isFalse(); + } + + private void setupShortcuts(boolean enableFab, boolean enableVolumeKeys, boolean enableQs) { + setupShortcut(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, enableFab); + setupShortcut(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, enableVolumeKeys); + setupShortcut(Settings.Secure.ACCESSIBILITY_QS_TARGETS, enableQs); + } + + private void setupShortcut(String shortcutSettingKey, boolean enabled) { + final String targetName = enabled ? ONE_HANDED_MODE_TARGET_NAME : ""; + Settings.Secure.putStringForUser( + mContext.getContentResolver(), shortcutSettingKey, targetName, mCurrentUserId); + } }