diff --git a/aconfig/accessibility/accessibility_flags.aconfig b/aconfig/accessibility/accessibility_flags.aconfig index 2b843cd79b6..691999005cb 100644 --- a/aconfig/accessibility/accessibility_flags.aconfig +++ b/aconfig/accessibility/accessibility_flags.aconfig @@ -24,6 +24,16 @@ flag { bug: "301198830" } +flag { + name: "hide_magnification_always_on_toggle_when_window_mode_only" + namespace: "accessibility" + description: "Decides whether to hide the magnification always on setting when capabilities is window mode only." + bug: "328787031" + metadata { + purpose: PURPOSE_BUGFIX + } +} + flag { name: "remove_qs_tooltip_in_suw" namespace: "accessibility" diff --git a/res/values/strings.xml b/res/values/strings.xml index 56856b67c0a..49635fe39ea 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4737,6 +4737,8 @@ Keep on while switching apps Magnifier stays on and zooms out when you switch apps + + Unavailable while only magnifying part of the screen Joystick diff --git a/src/com/android/settings/accessibility/MagnificationAlwaysOnPreferenceController.java b/src/com/android/settings/accessibility/MagnificationAlwaysOnPreferenceController.java index f3d857580aa..4a37a416414 100644 --- a/src/com/android/settings/accessibility/MagnificationAlwaysOnPreferenceController.java +++ b/src/com/android/settings/accessibility/MagnificationAlwaysOnPreferenceController.java @@ -20,26 +20,73 @@ import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; import android.provider.Settings; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + import com.android.settings.R; +import com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode; import com.android.settings.core.TogglePreferenceController; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; /** * Controller that accesses and switches the preference status of the magnification always on * feature, where the magnifier will not deactivate on Activity transitions; it will only zoom out * to 100%. */ -public class MagnificationAlwaysOnPreferenceController extends TogglePreferenceController { +public class MagnificationAlwaysOnPreferenceController extends TogglePreferenceController + implements LifecycleObserver, OnResume, OnPause { private static final String TAG = MagnificationAlwaysOnPreferenceController.class.getSimpleName(); static final String PREF_KEY = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED; + private Preference mPreference; + + @VisibleForTesting + final ContentObserver mContentObserver = new ContentObserver( + new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange, @Nullable Uri uri) { + updateState(mPreference); + } + }; + public MagnificationAlwaysOnPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); } + @Override + public void onResume() { + if (Flags.hideMagnificationAlwaysOnToggleWhenWindowModeOnly()) { + MagnificationCapabilities.registerObserver(mContext, mContentObserver); + } + } + + @Override + public void onPause() { + if (Flags.hideMagnificationAlwaysOnToggleWhenWindowModeOnly()) { + MagnificationCapabilities.unregisterObserver(mContext, mContentObserver); + } + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + updateState(mPreference); + } + @Override public int getAvailabilityStatus() { return AVAILABLE; @@ -62,4 +109,33 @@ public class MagnificationAlwaysOnPreferenceController extends TogglePreferenceC public int getSliceHighlightMenuRes() { return R.string.menu_key_accessibility; } + + @Override + public CharSequence getSummary() { + if (!Flags.hideMagnificationAlwaysOnToggleWhenWindowModeOnly()) { + return super.getSummary(); + } + + @StringRes int resId = mPreference.isEnabled() + ? R.string.accessibility_screen_magnification_always_on_summary + : R.string.accessibility_screen_magnification_always_on_unavailable_summary; + return mContext.getString(resId); + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + if (!Flags.hideMagnificationAlwaysOnToggleWhenWindowModeOnly()) { + return; + } + + if (preference == null) { + return; + } + @MagnificationMode int mode = + MagnificationCapabilities.getCapabilities(mContext); + preference.setEnabled( + mode == MagnificationMode.FULLSCREEN || mode == MagnificationMode.ALL); + refreshSummary(preference); + } } diff --git a/src/com/android/settings/accessibility/MagnificationCapabilities.java b/src/com/android/settings/accessibility/MagnificationCapabilities.java index 04a2992f011..eb2ba5356c9 100644 --- a/src/com/android/settings/accessibility/MagnificationCapabilities.java +++ b/src/com/android/settings/accessibility/MagnificationCapabilities.java @@ -18,6 +18,7 @@ package com.android.settings.accessibility; import android.content.ContentResolver; import android.content.Context; +import android.database.ContentObserver; import android.provider.Settings; import androidx.annotation.IntDef; @@ -101,5 +102,28 @@ public final class MagnificationCapabilities { MagnificationMode.FULLSCREEN, contentResolver.getUserId()); } + /** + * Register an observer class that gets callbacks when magnification capabilities changes. + * + * @param context A {@link Context}. + * @param contentObserver The object that receives callbacks when changes occur. + */ + public static void registerObserver(Context context, ContentObserver contentObserver) { + context.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(KEY_CAPABILITY), + /* notifyForDescendants= */ false, + contentObserver); + } + + /** + * Unregisters a magnification capabilities change observer. + * + * @param context A {@link Context}. + * @param contentObserver The previously registered observer that is no longer needed. + */ + public static void unregisterObserver(Context context, ContentObserver contentObserver) { + context.getContentResolver().unregisterContentObserver(contentObserver); + } + private MagnificationCapabilities() {} } diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java index 6ef764edfaf..8e17fa5c207 100644 --- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java @@ -266,6 +266,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends defaultValue ); } + private void addAlwaysOnSetting(PreferenceCategory generalCategory) { if (!isAlwaysOnSettingEnabled()) { return; @@ -282,6 +283,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends var alwaysOnPreferenceController = new MagnificationAlwaysOnPreferenceController( getContext(), MagnificationAlwaysOnPreferenceController.PREF_KEY); + getSettingsLifecycle().addObserver(alwaysOnPreferenceController); alwaysOnPreferenceController.displayPreference(getPreferenceScreen()); addPreferenceController(alwaysOnPreferenceController); } diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationAlwaysOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationAlwaysOnPreferenceControllerTest.java index 417c3d412aa..3e97cec0f4a 100644 --- a/tests/robotests/src/com/android/settings/accessibility/MagnificationAlwaysOnPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationAlwaysOnPreferenceControllerTest.java @@ -17,6 +17,7 @@ package com.android.settings.accessibility; import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; +import static com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode; import static com.google.common.truth.Truth.assertThat; @@ -25,6 +26,9 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import androidx.preference.PreferenceManager; @@ -33,31 +37,42 @@ import androidx.preference.SwitchPreference; import androidx.test.core.app.ApplicationProvider; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowContentResolver; @RunWith(RobolectricTestRunner.class) public class MagnificationAlwaysOnPreferenceControllerTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final String KEY_ALWAYS_ON = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED; - private final Context mContext = ApplicationProvider.getApplicationContext(); - private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext)); - private final MagnificationAlwaysOnPreferenceController mController = - new MagnificationAlwaysOnPreferenceController(mContext, - MagnificationAlwaysOnPreferenceController.PREF_KEY); + private Context mContext; + private ShadowContentResolver mShadowContentResolver; + private SwitchPreference mSwitchPreference; + private MagnificationAlwaysOnPreferenceController mController; @Before public void setUp() { + mContext = ApplicationProvider.getApplicationContext(); + mShadowContentResolver = Shadow.extract(mContext.getContentResolver()); + final PreferenceManager preferenceManager = new PreferenceManager(mContext); final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext); + mSwitchPreference = spy(new SwitchPreference(mContext)); mSwitchPreference.setKey(MagnificationAlwaysOnPreferenceController.PREF_KEY); screen.addPreference(mSwitchPreference); - mController.displayPreference(screen); + mController = new MagnificationAlwaysOnPreferenceController(mContext, + MagnificationAlwaysOnPreferenceController.PREF_KEY); + mController.displayPreference(screen); mController.updateState(mSwitchPreference); + reset(mSwitchPreference); } @@ -80,4 +95,57 @@ public class MagnificationAlwaysOnPreferenceControllerTest { assertThat(mController.isChecked()).isFalse(); assertThat(mSwitchPreference.isChecked()).isFalse(); } + + @Test + @EnableFlags(Flags.FLAG_HIDE_MAGNIFICATION_ALWAYS_ON_TOGGLE_WHEN_WINDOW_MODE_ONLY) + public void onResume_flagOn_verifyRegisterCapabilityObserver() { + mController.onResume(); + assertThat(mShadowContentResolver.getContentObservers( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY))) + .hasSize(1); + } + + @Test + @EnableFlags(Flags.FLAG_HIDE_MAGNIFICATION_ALWAYS_ON_TOGGLE_WHEN_WINDOW_MODE_ONLY) + public void onPause_flagOn_verifyUnregisterCapabilityObserver() { + mController.onResume(); + mController.onPause(); + assertThat(mShadowContentResolver.getContentObservers( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY))) + .isEmpty(); + } + + @Test + @DisableFlags(Flags.FLAG_HIDE_MAGNIFICATION_ALWAYS_ON_TOGGLE_WHEN_WINDOW_MODE_ONLY) + public void updateState_windowModeOnlyAndFlagOff_preferenceIsAvailable() { + MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.WINDOW); + + mController.updateState(mSwitchPreference); + assertThat(mSwitchPreference.isEnabled()).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_HIDE_MAGNIFICATION_ALWAYS_ON_TOGGLE_WHEN_WINDOW_MODE_ONLY) + public void updateState_windowModeOnlyAndFlagOn_preferenceBecomesUnavailable() { + MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.WINDOW); + + mController.updateState(mSwitchPreference); + assertThat(mSwitchPreference.isEnabled()).isFalse(); + } + + @Test + public void updateState_fullscreenModeOnly_preferenceIsAvailable() { + MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.FULLSCREEN); + + mController.updateState(mSwitchPreference); + assertThat(mSwitchPreference.isEnabled()).isTrue(); + } + + @Test + public void updateState_switchMode_preferenceIsAvailable() { + MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.ALL); + + mController.updateState(mSwitchPreference); + assertThat(mSwitchPreference.isEnabled()).isTrue(); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationCapabilitiesTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationCapabilitiesTest.java index b0d03564c75..754b1a7f38e 100644 --- a/tests/robotests/src/com/android/settings/accessibility/MagnificationCapabilitiesTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationCapabilitiesTest.java @@ -18,7 +18,14 @@ package com.android.settings.accessibility; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + import android.content.Context; +import android.database.ContentObserver; import androidx.test.core.app.ApplicationProvider; @@ -42,7 +49,6 @@ public final class MagnificationCapabilitiesTest { final int windowCapabilities = MagnificationCapabilities.getCapabilities(mContext); assertThat(windowCapabilities).isEqualTo( MagnificationCapabilities.MagnificationMode.WINDOW); - } @Test @@ -63,4 +69,35 @@ public final class MagnificationCapabilitiesTest { assertThat(windowCapabilities).isEqualTo( MagnificationCapabilities.MagnificationMode.FULLSCREEN); } + + @Test + public void registerObserver_triggeredWhenCapabilitiesChanged() { + MagnificationCapabilities.setCapabilities(mContext, + MagnificationCapabilities.MagnificationMode.FULLSCREEN); + + ContentObserver contentObserver = + spy(new ContentObserver(/* handler= */ null) {}); + + MagnificationCapabilities.registerObserver(mContext, contentObserver); + MagnificationCapabilities.setCapabilities(mContext, + MagnificationCapabilities.MagnificationMode.WINDOW); + + verify(contentObserver).onChange(/* selfChange= */ anyBoolean(), /* uri= */ any()); + } + + @Test + public void unregisterObserver_neverTriggeredWhenCapabilitiesChanged() { + MagnificationCapabilities.setCapabilities(mContext, + MagnificationCapabilities.MagnificationMode.FULLSCREEN); + + ContentObserver contentObserver = + spy(new ContentObserver(/* handler= */ null) {}); + + MagnificationCapabilities.registerObserver(mContext, contentObserver); + MagnificationCapabilities.unregisterObserver(mContext, contentObserver); + MagnificationCapabilities.setCapabilities(mContext, + MagnificationCapabilities.MagnificationMode.WINDOW); + + verify(contentObserver, never()).onChange(/* selfChange= */ anyBoolean(), /* uri= */ any()); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java index 1d85705a9e7..cc1c72ea10e 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java @@ -816,6 +816,21 @@ public class ToggleScreenMagnificationPreferenceFragmentTest { assertThat(lifecycleObservers).comparingElementsUsing(instanceOf).contains(true); } + @Test + public void onCreateView_addTheAlwaysOnControllerToLifeCycleObserver() { + Correspondence instanceOf = Correspondence.transforming( + observer -> (observer instanceof MagnificationAlwaysOnPreferenceController), + "contains MagnificationAlwaysOnPreferenceController"); + + ToggleScreenMagnificationPreferenceFragment fragment = mFragController.create( + R.id.main_content, /* bundle= */ null).start().resume().get(); + + List lifecycleObservers = ReflectionHelpers.getField( + fragment.getSettingsLifecycle(), "mObservers"); + assertThat(lifecycleObservers).isNotNull(); + assertThat(lifecycleObservers).comparingElementsUsing(instanceOf).contains(true); + } + @Test public void onCreateDialog_setDialogDelegate_invokeDialogDelegate() { ToggleScreenMagnificationPreferenceFragment fragment =