fix(magnification): make always on toggle unavailable when capabilities is window only

As b/328787031, we make the MagnificationAlwaysOnPreferenceController observe the magnification capabilities then update the preference enabled state. Therefore, when changing the capabilities to window mode only, the preference will become unavailable.

We use the bug-fix flag to verify the fix with rollout process.

Bug: 328787031
Flag: ACONFIG com.android.settings.accessibility.hide_magnification_always_on_toggle_when_window_mode_only DEVELOPMENT
Test: manually flip the flag
      atest MagnificationCapabilitiesTest
      atest ToggleScreenMagnificationPreferenceFragmentTest
      atest MagnificationAlwaysOnPreferenceControllerTest
Change-Id: I1a25f80131d84ecdd927030e40a18ebb32b7862f
This commit is contained in:
Roy Chou
2024-03-25 09:37:42 +00:00
parent d47736635b
commit 1c898252cb
8 changed files with 242 additions and 8 deletions

View File

@@ -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"

View File

@@ -4737,6 +4737,8 @@
<string name="accessibility_screen_magnification_always_on_title">Keep on while switching apps</string>
<!-- Summary for accessibility magnifier preference where the magnifier never turns off while switching apps. [CHAR LIMIT=none] -->
<string name="accessibility_screen_magnification_always_on_summary">Magnifier stays on and zooms out when you switch apps</string>
<!-- Summary for accessibility magnifier preference where the always on toggle is unavailable while partial-only capability. [CHAR LIMIT=none] -->
<string name="accessibility_screen_magnification_always_on_unavailable_summary">Unavailable while only magnifying part of the screen</string>
<!-- Title for accessibility on-screen joystick controller preference that controls magnification position. [CHAR LIMIT=35] -->
<string name="accessibility_screen_magnification_joystick_title">Joystick</string>
<!-- Summary for accessibility on-screen joystick controller preference that controls magnification position. [CHAR LIMIT=none] -->

View File

@@ -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);
}
}

View File

@@ -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() {}
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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<LifecycleObserver> lifecycleObservers = ReflectionHelpers.getField(
fragment.getSettingsLifecycle(), "mObservers");
assertThat(lifecycleObservers).isNotNull();
assertThat(lifecycleObservers).comparingElementsUsing(instanceOf).contains(true);
}
@Test
public void onCreateDialog_setDialogDelegate_invokeDialogDelegate() {
ToggleScreenMagnificationPreferenceFragment fragment =