Merge "fix(magnification): make always on toggle unavailable when capabilities is window only" into main

This commit is contained in:
Daniel Norman
2024-03-29 19:09:35 +00:00
committed by Android (Google) Code Review
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

@@ -4764,6 +4764,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 =