diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index 8e8c7e45039..c08e6690ac4 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -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 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 diff --git a/src/com/android/settings/accessibility/AccessibilitySettingsContentObserver.java b/src/com/android/settings/accessibility/AccessibilitySettingsContentObserver.java new file mode 100644 index 00000000000..d2ca75437b3 --- /dev/null +++ b/src/com/android/settings/accessibility/AccessibilitySettingsContentObserver.java @@ -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 mUriToKey = new HashMap<>(2); + + // Key: Collection of preference keys, Value: onChange callback for keys + private final Map, 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 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 keys : mUrisToCallback.keySet()) { + final boolean isDefaultKey = isDefaultKey(key); + if (isDefaultKey || keys.contains(key)) { + mUrisToCallback.get(keys).onChange(key); + } + } + } +} diff --git a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java index 22f037b1f4b..115f44f0c14 100644 --- a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java +++ b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java @@ -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 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 diff --git a/src/com/android/settings/accessibility/MagnificationFollowTypingPreferenceController.java b/src/com/android/settings/accessibility/MagnificationFollowTypingPreferenceController.java index 8757fdfaa43..a758276afbe 100644 --- a/src/com/android/settings/accessibility/MagnificationFollowTypingPreferenceController.java +++ b/src/com/android/settings/accessibility/MagnificationFollowTypingPreferenceController.java @@ -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); } } diff --git a/src/com/android/settings/accessibility/SettingsContentObserver.java b/src/com/android/settings/accessibility/SettingsContentObserver.java deleted file mode 100644 index de67f6c4e3d..00000000000 --- a/src/com/android/settings/accessibility/SettingsContentObserver.java +++ /dev/null @@ -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 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 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); -} diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index 04b5347346e..0632d3f84bb 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -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) { diff --git a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java index 419514f4ed4..e65c9c5b2bb 100644 --- a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java @@ -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 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 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(); } diff --git a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java index c9449d24109..f0a4f0a151e 100644 --- a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java @@ -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 sControllers = new ArrayList<>(); - private final Handler mHandler = new Handler(); - private SettingsContentObserver mSettingsContentObserver; private static List 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 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 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); diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index 233b7ee2997..cba5d83150a 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -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 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 getFeatureSettingsKeys() { + protected void registerKeysToObserverCallback( + AccessibilitySettingsContentObserver contentObserver) { + final List shortcutFeatureKeys = getShortcutFeatureSettingsKeys(); + + contentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> { + updateShortcutPreferenceData(); + updateShortcutPreference(); + }); + } + + protected List getShortcutFeatureSettingsKeys() { final List shortcutFeatureKeys = new ArrayList<>(); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); diff --git a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java index 81bd45a0541..feb41a19463 100644 --- a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java @@ -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 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 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(); } diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java index d1e9b565805..08679180184 100644 --- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java @@ -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 getFeatureSettingsKeys() { - final List shortcutKeys = super.getFeatureSettingsKeys(); + protected void registerKeysToObserverCallback( + AccessibilitySettingsContentObserver contentObserver) { + super.registerKeysToObserverCallback(contentObserver); + + final List 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 getShortcutFeatureSettingsKeys() { + final List shortcutKeys = super.getShortcutFeatureSettingsKeys(); shortcutKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED); return shortcutKeys; } diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsContentObserverTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsContentObserverTest.java new file mode 100644 index 00000000000..e23d2d798f8 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsContentObserverTest.java @@ -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 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 + } + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java index d1c59f708c9..5843265656e 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java @@ -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)); } diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationFollowTypingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationFollowTypingPreferenceControllerTest.java index ccb66addd62..fd282a06932 100644 --- a/tests/robotests/src/com/android/settings/accessibility/MagnificationFollowTypingPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationFollowTypingPreferenceControllerTest.java @@ -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(); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index c47a7935de5..4584b159b20 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -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; diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java index 10495c5f00a..8500e61a9d8 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java @@ -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);