Refactor SettingsContentObserver and add it to follow typing feature.

Refactor SettingsContentObserver and rename it to
AccessibilitySettingsContentObserver. Besides, we register it to observe
follow typing feature preference value.

The reasons behind refactor:
1. We change callback signature due to the consistency for register it
by preference key, not by Uri.
2. We refactor the default preference key to a seperate method. Since
the default value is related to accessibility, we rename it with
accessibility prefix.
3. We can register different callback for difference collections of
preference keys.

Default preference keys: They existed in the previous constructor.
1. Settings.Secure.ACCESSIBILITY_ENABLED
2. Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES

Bug: 194668976
Test: make RunSettingsRoboTests ROBOTEST_FILTER=
           AccessibilitySettingsTest
           AccessibilitySettingsContentObserverTest
           AccessibilityShortcutPreferenceFragmentTest
           MagnificationFollowTypingPreferenceControllerTest
           ToggleFeaturePreferenceFragmentTest
           ToggleScreenMagnificationPreferenceFragmentTest
Change-Id: Iafd27e044ebe2536ae7ae55c1c80af54f7f0f822
This commit is contained in:
Daniel Hsieh
2022-01-17 05:03:41 +00:00
parent 0c229fdb4d
commit 55bc49860b
16 changed files with 489 additions and 168 deletions

View File

@@ -30,7 +30,6 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
@@ -147,7 +146,7 @@ public class AccessibilitySettings extends DashboardFragment {
};
@VisibleForTesting
final SettingsContentObserver mSettingsContentObserver;
final AccessibilitySettingsContentObserver mSettingsContentObserver;
private final Map<String, PreferenceCategory> mCategoryToPrefCategoryMap =
new ArrayMap<>();
@@ -171,12 +170,9 @@ public class AccessibilitySettings extends DashboardFragment {
// Observe changes from accessibility selection menu
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
mSettingsContentObserver = new SettingsContentObserver(mHandler, shortcutFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
onContentChanged();
}
};
mSettingsContentObserver = new AccessibilitySettingsContentObserver(mHandler);
mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys,
key -> onContentChanged());
}
@Override

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class AccessibilitySettingsContentObserver extends ContentObserver {
private static final String TAG = "AccessibilitySettingsContentObserver";
public interface ContentObserverCallback {
void onChange(String key);
}
// Key: Preference key's uri, Value: Preference key
private final Map<Uri, String> mUriToKey = new HashMap<>(2);
// Key: Collection of preference keys, Value: onChange callback for keys
private final Map<List<String>, ContentObserverCallback> mUrisToCallback = new HashMap<>();
AccessibilitySettingsContentObserver(Handler handler) {
super(handler);
// default key to be observed
addDefaultKeysToMap();
}
public void register(ContentResolver contentResolver) {
for (Uri uri : mUriToKey.keySet()) {
contentResolver.registerContentObserver(uri, false, this);
}
}
public void unregister(ContentResolver contentResolver) {
contentResolver.unregisterContentObserver(this);
}
private void addDefaultKeysToMap() {
addKeyToMap(Settings.Secure.ACCESSIBILITY_ENABLED);
addKeyToMap(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
}
private boolean isDefaultKey(String key) {
return Settings.Secure.ACCESSIBILITY_ENABLED.equals(key)
|| Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES.equals(key);
}
private void addKeyToMap(String key) {
mUriToKey.put(Settings.Secure.getUriFor(key), key);
}
/**
* {@link ContentObserverCallback} is added to {@link ContentObserver} to handle the
* onChange event triggered by the key collection of {@code keysToObserve} and the default
* keys.
*
* Note: The following key are default to be observed.
* {@link Settings.Secure.ACCESSIBILITY_ENABLED}
* {@link Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES}
*
* @param keysToObserve A collection of keys which are going to be observed.
* @param observerCallback A callback which is used to handle the onChange event triggered
* by the key collection of {@code keysToObserve}.
*/
public void registerKeysToObserverCallback(List<String> keysToObserve,
ContentObserverCallback observerCallback) {
for (String key: keysToObserve) {
addKeyToMap(key);
}
mUrisToCallback.put(keysToObserve, observerCallback);
}
/**
* {@link ContentObserverCallback} is added to {@link ContentObserver} to handle the
* onChange event triggered by the default keys.
*
* Note: The following key are default to be observed.
* {@link Settings.Secure.ACCESSIBILITY_ENABLED}
* {@link Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES}
*
* @param observerCallback A callback which is used to handle the onChange event triggered
* * by the key collection of {@code keysToObserve}.
*/
public void registerObserverCallback(ContentObserverCallback observerCallback) {
mUrisToCallback.put(Collections.emptyList(), observerCallback);
}
@Override
public final void onChange(boolean selfChange, Uri uri) {
final String key = mUriToKey.get(uri);
if (key == null) {
Log.w(TAG, "AccessibilitySettingsContentObserver can not find the key for "
+ "uri: " + uri);
return;
}
for (List<String> keys : mUrisToCallback.keySet()) {
final boolean isDefaultKey = isDefaultKey(key);
if (isDefaultKey || keys.contains(key)) {
mUrisToCallback.get(keys).onChange(key);
}
}
}
}

View File

@@ -25,7 +25,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.icu.text.CaseMap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
@@ -64,7 +63,7 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
protected ShortcutPreference mShortcutPreference;
private AccessibilityManager.TouchExplorationStateChangeListener
mTouchExplorationStateChangeListener;
private SettingsContentObserver mSettingsContentObserver;
private AccessibilitySettingsContentObserver mSettingsContentObserver;
private CheckBox mSoftwareTypeCheckBox;
private CheckBox mHardwareTypeCheckBox;
@@ -98,13 +97,11 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
final List<String> shortcutFeatureKeys = new ArrayList<>();
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
mSettingsContentObserver = new SettingsContentObserver(new Handler(), shortcutFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateShortcutPreferenceData();
updateShortcutPreference();
}
};
mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> {
updateShortcutPreferenceData();
updateShortcutPreference();
});
}
@Override

View File

@@ -35,6 +35,8 @@ import com.android.settings.core.TogglePreferenceController;
public class MagnificationFollowTypingPreferenceController extends TogglePreferenceController
implements LifecycleObserver {
private static final String TAG =
MagnificationFollowTypingPreferenceController.class.getSimpleName();
static final String PREF_KEY = "magnification_follow_typing";
private SwitchPreference mFollowTypingPreference;
@@ -75,6 +77,14 @@ public class MagnificationFollowTypingPreferenceController extends TogglePrefere
// TODO(b/186731461): Remove it when this controller is used in DashBoardFragment only.
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void onResume() {
updateState();
}
/**
* Updates the state of preference components which has been displayed by
* {@link MagnificationFollowTypingPreferenceController#displayPreference}.
*/
void updateState() {
updateState(mFollowTypingPreference);
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
import java.util.ArrayList;
import java.util.List;
abstract class SettingsContentObserver extends ContentObserver {
private final List<String> mKeysToObserve = new ArrayList<>(2);
public SettingsContentObserver(Handler handler) {
super(handler);
mKeysToObserve.add(Settings.Secure.ACCESSIBILITY_ENABLED);
mKeysToObserve.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
}
public SettingsContentObserver(Handler handler, List<String> keysToObserve) {
this(handler);
mKeysToObserve.addAll(keysToObserve);
}
public void register(ContentResolver contentResolver) {
for (int i = 0; i < mKeysToObserve.size(); i++) {
contentResolver.registerContentObserver(
Settings.Secure.getUriFor(mKeysToObserve.get(i)), false, this);
}
}
public void unregister(ContentResolver contentResolver) {
contentResolver.unregisterContentObserver(this);
}
@Override
public abstract void onChange(boolean selfChange, Uri uri);
}

View File

@@ -35,7 +35,6 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
@@ -65,15 +64,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
private static final String EMPTY_STRING = "";
private final SettingsContentObserver mSettingsContentObserver =
new SettingsContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateSwitchBarToggleSwitch();
}
};
private Dialog mDialog;
private Dialog mWarningDialog;
private BroadcastReceiver mPackageRemovedReceiver;
private boolean mDisabledStateLogged = false;
private long mStartTimeMillsForLogging = 0;
@@ -108,6 +99,13 @@ public class ToggleAccessibilityServicePreferenceFragment extends
}
}
@Override
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
super.registerKeysToObserverCallback(contentObserver);
contentObserver.registerObserverCallback(key -> updateSwitchBarToggleSwitch());
}
@Override
public void onStart() {
super.onStart();
@@ -123,7 +121,11 @@ public class ToggleAccessibilityServicePreferenceFragment extends
public void onResume() {
super.onResume();
updateSwitchBarToggleSwitch();
mSettingsContentObserver.register(getContentResolver());
}
@Override
public void onPause() {
super.onPause();
}
@Override
@@ -172,7 +174,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
if (info == null) {
return null;
}
mDialog = AccessibilityServiceWarning
mWarningDialog = AccessibilityServiceWarning
.createCapabilitiesDialog(getPrefContext(), info,
this::onDialogButtonFromEnableToggleClicked,
this::onDialogButtonFromUninstallClicked);
@@ -183,7 +185,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
if (info == null) {
return null;
}
mDialog = AccessibilityServiceWarning
mWarningDialog = AccessibilityServiceWarning
.createCapabilitiesDialog(getPrefContext(), info,
this::onDialogButtonFromShortcutToggleClicked,
this::onDialogButtonFromUninstallClicked);
@@ -194,7 +196,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
if (info == null) {
return null;
}
mDialog = AccessibilityServiceWarning
mWarningDialog = AccessibilityServiceWarning
.createCapabilitiesDialog(getPrefContext(), info,
this::onDialogButtonFromShortcutClicked,
this::onDialogButtonFromUninstallClicked);
@@ -205,16 +207,16 @@ public class ToggleAccessibilityServicePreferenceFragment extends
if (info == null) {
return null;
}
mDialog = AccessibilityServiceWarning
mWarningDialog = AccessibilityServiceWarning
.createDisableDialog(getPrefContext(), info,
this::onDialogButtonFromDisableToggleClicked);
break;
}
default: {
mDialog = super.onCreateDialog(dialogId);
mWarningDialog = super.onCreateDialog(dialogId);
}
}
return mDialog;
return mWarningDialog;
}
@Override
@@ -402,7 +404,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
}
private void onDialogButtonFromUninstallClicked() {
mDialog.dismiss();
mWarningDialog.dismiss();
final Intent uninstallIntent = createUninstallPackageActivityIntent();
if (uninstallIntent == null) {
return;
@@ -436,12 +438,12 @@ public class ToggleAccessibilityServicePreferenceFragment extends
mIsDialogShown.set(false);
showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);
}
mDialog.dismiss();
mWarningDialog.dismiss();
}
private void onDenyButtonFromEnableToggleClicked() {
handleConfirmServiceEnabled(/* confirmed= */ false);
mDialog.dismiss();
mWarningDialog.dismiss();
}
void onDialogButtonFromShortcutToggleClicked(View view) {
@@ -465,7 +467,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
mIsDialogShown.set(false);
showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);
mDialog.dismiss();
mWarningDialog.dismiss();
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
@@ -473,7 +475,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
private void onDenyButtonFromShortcutToggleClicked() {
mShortcutPreference.setChecked(false);
mDialog.dismiss();
mWarningDialog.dismiss();
}
void onDialogButtonFromShortcutClicked(View view) {
@@ -491,11 +493,11 @@ public class ToggleAccessibilityServicePreferenceFragment extends
mIsDialogShown.set(false);
showPopupDialog(DialogEnums.EDIT_SHORTCUT);
mDialog.dismiss();
mWarningDialog.dismiss();
}
private void onDenyButtonFromShortcutClicked() {
mDialog.dismiss();
mWarningDialog.dismiss();
}
private boolean onPreferenceClick(boolean isChecked) {

View File

@@ -25,7 +25,6 @@ import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
@@ -41,8 +40,6 @@ import java.util.List;
public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePreferenceFragment {
private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED;
private final Handler mHandler = new Handler();
private SettingsContentObserver mSettingsContentObserver;
@Override
public int getMetricsCategory() {
@@ -86,20 +83,22 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
.authority(getPrefContext().getPackageName())
.appendPath(String.valueOf(R.raw.accessibility_color_inversion_banner))
.build();
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(ENABLED);
mSettingsContentObserver = new SettingsContentObserver(mHandler, enableServiceFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateSwitchBarToggleSwitch();
}
};
final View view = super.onCreateView(inflater, container, savedInstanceState);
updateFooterPreference();
return view;
}
@Override
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
super.registerKeysToObserverCallback(contentObserver);
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(ENABLED);
contentObserver.registerKeysToObserverCallback(enableServiceFeatureKeys,
key -> updateSwitchBarToggleSwitch());
}
private void updateFooterPreference() {
final String title = getPrefContext().getString(
R.string.accessibility_color_inversion_about_title);
@@ -114,12 +113,10 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
public void onResume() {
super.onResume();
updateSwitchBarToggleSwitch();
mSettingsContentObserver.register(getContentResolver());
}
@Override
public void onPause() {
mSettingsContentObserver.unregister(getContentResolver());
super.onPause();
}

View File

@@ -24,9 +24,7 @@ import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
@@ -53,8 +51,6 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
private static final String KEY_PREVIEW = "daltonizer_preview";
private static final String KEY_CATEGORY_MODE = "daltonizer_mode_category";
private static final List<AbstractPreferenceController> sControllers = new ArrayList<>();
private final Handler mHandler = new Handler();
private SettingsContentObserver mSettingsContentObserver;
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Lifecycle lifecycle) {
@@ -84,20 +80,22 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
mComponentName = DALTONIZER_COMPONENT_NAME;
mPackageName = getText(R.string.accessibility_display_daltonizer_preference_title);
mHtmlDescription = getText(R.string.accessibility_display_daltonizer_preference_subtitle);
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(ENABLED);
mSettingsContentObserver = new SettingsContentObserver(mHandler, enableServiceFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateSwitchBarToggleSwitch();
}
};
final View view = super.onCreateView(inflater, container, savedInstanceState);
updateFooterPreference();
return view;
}
@Override
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
super.registerKeysToObserverCallback(contentObserver);
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(ENABLED);
contentObserver.registerKeysToObserverCallback(enableServiceFeatureKeys,
key -> updateSwitchBarToggleSwitch());
}
private void updateFooterPreference() {
final String title = getPrefContext()
.getString(R.string.accessibility_daltonizer_about_title);
@@ -123,8 +121,6 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
public void onResume() {
super.onResume();
updateSwitchBarToggleSwitch();
mSettingsContentObserver.register(getContentResolver());
for (AbstractPreferenceController controller :
buildPreferenceControllers(getPrefContext(), getSettingsLifecycle())) {
((DaltonizerRadioButtonPreferenceController) controller).setOnChangeListener(this);
@@ -135,7 +131,6 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
@Override
public void onPause() {
mSettingsContentObserver.unregister(getContentResolver());
for (AbstractPreferenceController controller :
buildPreferenceControllers(getPrefContext(), getSettingsLifecycle())) {
((DaltonizerRadioButtonPreferenceController) controller).setOnChangeListener(null);

View File

@@ -99,7 +99,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
protected static final String KEY_ANIMATED_IMAGE = "animated_image";
private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
private SettingsContentObserver mSettingsContentObserver;
private AccessibilitySettingsContentObserver mSettingsContentObserver;
private CheckBox mSoftwareTypeCheckBox;
private CheckBox mHardwareTypeCheckBox;
@@ -143,17 +143,21 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
setPreferenceScreen(preferenceScreen);
}
final List<String> shortcutFeatureKeys = getFeatureSettingsKeys();
mSettingsContentObserver = new SettingsContentObserver(new Handler(), shortcutFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateShortcutPreferenceData();
updateShortcutPreference();
}
};
mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
registerKeysToObserverCallback(mSettingsContentObserver);
}
protected List<String> getFeatureSettingsKeys() {
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
final List<String> shortcutFeatureKeys = getShortcutFeatureSettingsKeys();
contentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> {
updateShortcutPreferenceData();
updateShortcutPreference();
});
}
protected List<String> getShortcutFeatureSettingsKeys() {
final List<String> shortcutFeatureKeys = new ArrayList<>();
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);

View File

@@ -22,7 +22,6 @@ import android.content.Context;
import android.hardware.display.ColorDisplayManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
@@ -50,8 +49,6 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
private static final String KEY_INTENSITY = "rbc_intensity";
private static final String KEY_PERSIST = "rbc_persist";
private final Handler mHandler = new Handler();
private SettingsContentObserver mSettingsContentObserver;
private ReduceBrightColorsIntensityPreferenceController mRbcIntensityPreferenceController;
private ReduceBrightColorsPersistencePreferenceController mRbcPersistencePreferenceController;
private ColorDisplayManager mColorDisplayManager;
@@ -67,20 +64,12 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
mComponentName = AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
mPackageName = getText(R.string.reduce_bright_colors_preference_title);
mHtmlDescription = getText(R.string.reduce_bright_colors_preference_subtitle);
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(REDUCE_BRIGHT_COLORS_ACTIVATED_KEY);
mRbcIntensityPreferenceController =
new ReduceBrightColorsIntensityPreferenceController(getContext(), KEY_INTENSITY);
mRbcPersistencePreferenceController =
new ReduceBrightColorsPersistencePreferenceController(getContext(), KEY_PERSIST);
mRbcIntensityPreferenceController.displayPreference(getPreferenceScreen());
mRbcPersistencePreferenceController.displayPreference(getPreferenceScreen());
mSettingsContentObserver = new SettingsContentObserver(mHandler, enableServiceFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateSwitchBarToggleSwitch();
}
};
mColorDisplayManager = getContext().getSystemService(ColorDisplayManager.class);
final View view = super.onCreateView(inflater, container, savedInstanceState);
// Parent sets the title when creating the view, so set it after calling super
@@ -90,6 +79,17 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
return view;
}
@Override
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
super.registerKeysToObserverCallback(contentObserver);
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(REDUCE_BRIGHT_COLORS_ACTIVATED_KEY);
contentObserver.registerKeysToObserverCallback(enableServiceFeatureKeys,
key -> updateSwitchBarToggleSwitch());
}
private void updateGeneralCategoryOrder() {
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
final SeekBarPreference intensity = findPreference(KEY_INTENSITY);
@@ -117,12 +117,10 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
public void onResume() {
super.onResume();
updateSwitchBarToggleSwitch();
mSettingsContentObserver.register(getContentResolver());
}
@Override
public void onPause() {
mSettingsContentObserver.unregister(getContentResolver());
super.onPause();
}

View File

@@ -75,6 +75,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends
new TextUtils.SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
private DialogCreatable mDialogDelegate;
private MagnificationFollowTypingPreferenceController mFollowTypingPreferenceController;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -187,11 +188,10 @@ public class ToggleScreenMagnificationPreferenceFragment extends
MagnificationFollowTypingPreferenceController.PREF_KEY);
generalCategory.addPreference(followingTypingSwitchPreference);
final MagnificationFollowTypingPreferenceController followTypingPreferenceController =
new MagnificationFollowTypingPreferenceController(getContext(),
MagnificationFollowTypingPreferenceController.PREF_KEY);
getSettingsLifecycle().addObserver(followTypingPreferenceController);
followTypingPreferenceController.displayPreference(getPreferenceScreen());
mFollowTypingPreferenceController = new MagnificationFollowTypingPreferenceController(
getContext(), MagnificationFollowTypingPreferenceController.PREF_KEY);
getSettingsLifecycle().addObserver(mFollowTypingPreferenceController);
mFollowTypingPreferenceController.displayPreference(getPreferenceScreen());
}
@Override
@@ -293,8 +293,23 @@ public class ToggleScreenMagnificationPreferenceFragment extends
}
@Override
protected List<String> getFeatureSettingsKeys() {
final List<String> shortcutKeys = super.getFeatureSettingsKeys();
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
super.registerKeysToObserverCallback(contentObserver);
final List<String> followingTypingKeys = new ArrayList<>();
followingTypingKeys.add(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
contentObserver.registerKeysToObserverCallback(followingTypingKeys,
key -> updateFollowTypingState());
}
private void updateFollowTypingState() {
mFollowTypingPreferenceController.updateState();
}
@Override
protected List<String> getShortcutFeatureSettingsKeys() {
final List<String> shortcutKeys = super.getShortcutFeatureSettingsKeys();
shortcutKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
return shortcutKeys;
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.provider.Settings;
import androidx.test.core.app.ApplicationProvider;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowContentResolver;
import java.util.Collection;
import java.util.List;
/** Test for {@link AccessibilitySettingsContentObserver}. */
@RunWith(RobolectricTestRunner.class)
public class AccessibilitySettingsContentObserverTest {
private static final String SPECIFIC_KEY_A_1 = "SPECIFIC_KEY_A_1";
private static final String SPECIFIC_KEY_A_2 = "SPECIFIC_KEY_A_2";
private static final String SPECIFIC_KEY_B_1 = "SPECIFIC_KEY_B_1";
private final Context mContext = ApplicationProvider.getApplicationContext();
private final AccessibilitySettingsContentObserver mObserver =
new AccessibilitySettingsContentObserver(new Handler());
private final AccessibilitySettingsContentObserver.ContentObserverCallback mObserverCallbackA =
spy(new TestableContentObserverCallback());
private final AccessibilitySettingsContentObserver.ContentObserverCallback mObserverCallbackB =
spy(new TestableContentObserverCallback());
private final ContentResolver mContentResolver = mContext.getContentResolver();
@Test
public void register_shouldRegisterContentObserverForDefaultKeys() {
mObserver.register(mContentResolver);
ShadowContentResolver shadowContentResolver = Shadow.extract(mContentResolver);
assertObserverToUri(shadowContentResolver,
Settings.Secure.ACCESSIBILITY_ENABLED, mObserver);
assertObserverToUri(shadowContentResolver,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, mObserver);
}
@Test
public void unregister_shouldUnregisterContentObserver() {
mObserver.register(mContentResolver);
mObserver.unregister(mContentResolver);
ShadowContentResolver shadowContentResolver = Shadow.extract(mContentResolver);
assertNotObserverToUri(shadowContentResolver, Settings.Secure.ACCESSIBILITY_ENABLED);
assertNotObserverToUri(shadowContentResolver,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
}
@Test
public void register_addSpecificKeys_shouldRegisterContentObserverForSpecificAndDefaultKeys() {
mObserver.registerKeysToObserverCallback(List.of(SPECIFIC_KEY_A_1), mObserverCallbackA);
mObserver.register(mContentResolver);
ShadowContentResolver shadowContentResolver = Shadow.extract(mContentResolver);
assertObserverToUri(shadowContentResolver,
Settings.Secure.ACCESSIBILITY_ENABLED, mObserver);
assertObserverToUri(shadowContentResolver,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, mObserver);
assertObserverToUri(shadowContentResolver, SPECIFIC_KEY_A_1, mObserver);
}
@Test
public void onChange_shouldTriggerCallbackOnDefaultKey() {
mObserver.registerObserverCallback(mObserverCallbackA);
mObserver.register(mContentResolver);
mObserver.onChange(/* selfChange= */ false,
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED));
verify(mObserverCallbackA).onChange(Settings.Secure.ACCESSIBILITY_ENABLED);
}
@Test
public void onChange_registerSpecificKeys_shouldTriggerSpecificCallback() {
mObserver.registerKeysToObserverCallback(
List.of(SPECIFIC_KEY_A_1, SPECIFIC_KEY_A_2), mObserverCallbackA);
mObserver.register(mContentResolver);
mObserver.onChange(/* selfChange= */ false,
Settings.Secure.getUriFor(SPECIFIC_KEY_A_2));
verify(mObserverCallbackA).onChange(SPECIFIC_KEY_A_2);
}
@Test
public void onChange_registerSpecificKeys_withoutTriggerOtherCallback() {
mObserver.registerKeysToObserverCallback(
List.of(SPECIFIC_KEY_A_1, SPECIFIC_KEY_A_2), mObserverCallbackA);
mObserver.registerKeysToObserverCallback(
List.of(SPECIFIC_KEY_B_1), mObserverCallbackB);
mObserver.register(mContentResolver);
mObserver.onChange(/* selfChange= */ false,
Settings.Secure.getUriFor(SPECIFIC_KEY_B_1));
verify(mObserverCallbackA, never()).onChange(SPECIFIC_KEY_A_1);
verify(mObserverCallbackA, never()).onChange(SPECIFIC_KEY_A_2);
verify(mObserverCallbackA, never()).onChange(SPECIFIC_KEY_B_1);
verify(mObserverCallbackB).onChange(SPECIFIC_KEY_B_1);
}
@After
public void tearDown() {
mObserver.unregister(mContentResolver);
}
private void assertNotObserverToUri(ShadowContentResolver resolver, String key) {
assertThat(resolver.getContentObservers(Settings.Secure.getUriFor(key))).isEmpty();
}
private void assertObserverToUri(ShadowContentResolver resolver,
String key, ContentObserver observer) {
Collection<ContentObserver> observers =
resolver.getContentObservers(Settings.Secure.getUriFor(key));
assertThat(observers).contains(observer);
}
private static class TestableContentObserverCallback implements
AccessibilitySettingsContentObserver.ContentObserverCallback {
@Override
public void onChange(String key) {
// do nothing
}
}
}

View File

@@ -281,10 +281,10 @@ public class AccessibilitySettingsTest {
verify(mContentResolver).registerContentObserver(
eq(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS)),
anyBoolean(),
any(SettingsContentObserver.class));
any(AccessibilitySettingsContentObserver.class));
verify(mContentResolver).registerContentObserver(eq(Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)), anyBoolean(),
any(SettingsContentObserver.class));
any(AccessibilitySettingsContentObserver.class));
verify(mActivity, atLeast(1)).registerReceiver(any(PackageMonitor.class), captor.capture(),
isNull(), any());
intentFilter = captor.getAllValues().get(/* first time */ 0);
@@ -301,7 +301,8 @@ public class AccessibilitySettingsTest {
mFragment.onDestroy();
verify(mContentResolver).unregisterContentObserver(any(SettingsContentObserver.class));
verify(mContentResolver).unregisterContentObserver(
any(AccessibilitySettingsContentObserver.class));
verify(mActivity).unregisterReceiver(any(PackageMonitor.class));
}

View File

@@ -94,4 +94,15 @@ public class MagnificationFollowTypingPreferenceControllerTest {
assertThat(mController.isChecked()).isFalse();
assertThat(mSwitchPreference.isChecked()).isFalse();
}
@Test
public void updateState_disableFollowTyping_shouldReturnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(), KEY_FOLLOW_TYPING, OFF);
mController.updateState();
verify(mSwitchPreference).setChecked(false);
assertThat(mController.isChecked()).isFalse();
assertThat(mSwitchPreference.isChecked()).isFalse();
}
}

View File

@@ -20,6 +20,8 @@ import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment
import static com.google.common.truth.Truth.assertThat;
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.spy;
@@ -27,6 +29,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
@@ -75,12 +78,16 @@ public class ToggleFeaturePreferenceFragmentTest {
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
private TestToggleFeaturePreferenceFragment mFragment;
private PreferenceScreen mScreen;
private Context mContext = ApplicationProvider.getApplicationContext();
private final Context mContext = ApplicationProvider.getApplicationContext();
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceManager mPreferenceManager;
@Mock
private FragmentActivity mActivity;
@Mock
private ContentResolver mContentResolver;
@Before
public void setUpTestFragment() {
MockitoAnnotations.initMocks(this);
@@ -89,9 +96,11 @@ public class ToggleFeaturePreferenceFragmentTest {
when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
when(mFragment.getContext()).thenReturn(mContext);
mScreen = spy(new PreferenceScreen(mContext, null));
when(mScreen.getPreferenceManager()).thenReturn(mPreferenceManager);
doReturn(mScreen).when(mFragment).getPreferenceScreen();
when(mFragment.getActivity()).thenReturn(mActivity);
when(mActivity.getContentResolver()).thenReturn(mContentResolver);
final PreferenceScreen screen = spy(new PreferenceScreen(mContext, null));
when(screen.getPreferenceManager()).thenReturn(mPreferenceManager);
doReturn(screen).when(mFragment).getPreferenceScreen();
}
@Test
@@ -103,6 +112,25 @@ public class ToggleFeaturePreferenceFragmentTest {
verify(mFragment).addPreferencesFromResource(R.xml.placeholder_prefs);
}
@Test
@Config(shadows = {ShadowFragment.class})
public void onResume_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));
}
@Test
public void updateShortcutPreferenceData_assignDefaultValueToVariable() {
mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;

View File

@@ -25,14 +25,15 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
@@ -63,8 +64,8 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@@ -96,17 +97,22 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
private Context mContext;
private Resources mResources;
@Mock
private FragmentActivity mActivity;
@Mock
private ContentResolver mContentResolver;
@Before
public void setUpTestFragment() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class);
mFragment = spy(new TestToggleScreenMagnificationPreferenceFragment(mContext));
mResources = spy(mContext.getResources());
when(mContext.getResources()).thenReturn(mResources);
when(mFragment.getContext().getResources()).thenReturn(mResources);
doReturn(activity).when(mFragment).getActivity();
when(mFragment.getActivity()).thenReturn(mActivity);
when(mActivity.getContentResolver()).thenReturn(mContentResolver);
}
@Ignore("Ignore it since a NPE is happened in ShadowWindowManagerGlobal. (Ref. b/214161063)")
@@ -142,6 +148,30 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
assertThat(switchPreference.isChecked()).isFalse();
}
@Test
@Config(shadows = {ShadowFragment.class})
public void onResume_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).registerContentObserver(
eq(Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED)),
eq(false),
any(AccessibilitySettingsContentObserver.class));
}
@Test
public void hasValueInSettings_putValue_hasValue() {
setMagnificationTripleTapEnabled(/* enabled= */ true);