diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java index 9666fe9354d..00f280440a9 100644 --- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java +++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java @@ -114,6 +114,16 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature mComponentName); } + @Override + ComponentName getTileComponentName() { + return null; + } + + @Override + CharSequence getTileName() { + return null; + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // Do not call super. We don't want to see the "Help & feedback" option on this page so as diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index 0632d3f84bb..a1c98cdc0c4 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -241,6 +241,16 @@ public class ToggleAccessibilityServicePreferenceFragment extends mComponentName); } + @Override + ComponentName getTileComponentName() { + return null; + } + + @Override + CharSequence getTileName() { + return null; + } + @Override protected void updateSwitchBarToggleSwitch() { final boolean checked = isAccessibilityServiceEnabled(); diff --git a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java index e65c9c5b2bb..8766c020981 100644 --- a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java @@ -22,6 +22,7 @@ import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import android.app.settings.SettingsEnums; +import android.content.ComponentName; import android.content.ContentResolver; import android.net.Uri; import android.os.Bundle; @@ -131,6 +132,16 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere mComponentName); } + @Override + ComponentName getTileComponentName() { + return null; + } + + @Override + CharSequence getTileName() { + return null; + } + @Override protected void updateSwitchBarToggleSwitch() { final boolean checked = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON; diff --git a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java index f0a4f0a151e..5845aef9ba9 100644 --- a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java @@ -22,6 +22,7 @@ import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import android.app.settings.SettingsEnums; +import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.os.Bundle; @@ -181,6 +182,16 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe mComponentName); } + @Override + ComponentName getTileComponentName() { + return null; + } + + @Override + CharSequence getTileName() { + return null; + } + @Override protected void updateSwitchBarToggleSwitch() { final boolean checked = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON; diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index cba5d83150a..0cc1f1c1662 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -96,6 +96,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference protected static final String KEY_HTML_DESCRIPTION_PREFERENCE = "html_description"; private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference"; protected static final String KEY_SAVED_USER_SHORTCUT_TYPE = "shortcut_type"; + protected static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow"; protected static final String KEY_ANIMATED_IMAGE = "animated_image"; private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener; @@ -104,6 +105,9 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference private CheckBox mSoftwareTypeCheckBox; private CheckBox mHardwareTypeCheckBox; + private AccessibilityQuickSettingsTooltipWindow mTooltipWindow; + private boolean mNeedsQSTooltipReshow = false; + public static final int NOT_SET = -1; // Save user's shortcutType value when savedInstance has value (e.g. device rotated). protected int mSavedCheckBoxValue = NOT_SET; @@ -129,10 +133,15 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // Restore the user shortcut type. - if (savedInstanceState != null && savedInstanceState.containsKey( - KEY_SAVED_USER_SHORTCUT_TYPE)) { - mSavedCheckBoxValue = savedInstanceState.getInt(KEY_SAVED_USER_SHORTCUT_TYPE, NOT_SET); + // Restore the user shortcut type and tooltip. + if (savedInstanceState != null) { + if (savedInstanceState.containsKey(KEY_SAVED_USER_SHORTCUT_TYPE)) { + mSavedCheckBoxValue = savedInstanceState.getInt(KEY_SAVED_USER_SHORTCUT_TYPE, + NOT_SET); + } + if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) { + mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW); + } } setupDefaultShortcutIfNecessary(getPrefContext()); @@ -198,6 +207,11 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference switchBar.hide(); updatePreferenceOrder(); + + // Reshow tooltips when activity recreate, such as rotate device. + if (mNeedsQSTooltipReshow) { + getView().post(this::showQuickSettingsTooltipIfNeeded); + } } @Override @@ -231,6 +245,9 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference if (value != NOT_SET) { outState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE, value); } + if (mTooltipWindow != null) { + outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, mTooltipWindow.isShowing()); + } super.onSaveInstanceState(outState); } @@ -296,6 +313,12 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference */ abstract int getUserShortcutTypes(); + /** Returns the accessibility tile component name. */ + abstract ComponentName getTileComponentName(); + + /** Returns the accessibility tile feature name. */ + abstract CharSequence getTileName(); + protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) { final CharSequence title = getString(R.string.accessibility_service_primary_switch_title, mPackageName); @@ -307,7 +330,11 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference shortcutPreference.setTitle(title); } - protected abstract void onPreferenceToggled(String preferenceKey, boolean enabled); + protected void onPreferenceToggled(String preferenceKey, boolean enabled) { + if (enabled) { + showQuickSettingsTooltipIfNeeded(); + } + } protected void onInstallSwitchPreferenceToggleSwitch() { // Implement this to set a checked listener. @@ -770,4 +797,37 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference mComponentName.flattenToString(), type); PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut); } + + /** + * Shows the quick settings tooltip if the quick settings service is assigned. The tooltip only + * shows once. + */ + protected void showQuickSettingsTooltipIfNeeded() { + final ComponentName tileComponentName = getTileComponentName(); + if (tileComponentName == null) { + // Returns if no tile service assigned. + return; + } + + if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences( + getContext(), tileComponentName)) { + // Returns if quick settings tooltip only show once. + return; + } + + final CharSequence tileName = getTileName(); + if (TextUtils.isEmpty(tileName)) { + // Returns if no title of tile service assigned. + return; + } + + final String title = + getString(R.string.accessibility_service_quick_settings_tooltips_content, tileName); + mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(getContext()); + mTooltipWindow.setup(title); + mTooltipWindow.showAtTopCenter(getView()); + AccessibilityQuickSettingUtils.optInValueToSharedPreferences(getContext(), + tileComponentName); + mNeedsQSTooltipReshow = false; + } } diff --git a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java index feb41a19463..d3446dc763d 100644 --- a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java @@ -17,6 +17,7 @@ package com.android.settings.accessibility; import android.app.settings.SettingsEnums; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.hardware.display.ColorDisplayManager; @@ -169,6 +170,16 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre mComponentName); } + @Override + ComponentName getTileComponentName() { + return null; + } + + @Override + CharSequence getTileName() { + return null; + } + @Override protected void updateSwitchBarToggleSwitch() { final boolean checked = mColorDisplayManager.isReduceBrightColorsActivated(); diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java index 5f76d5a0a85..62f42820401 100644 --- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java @@ -23,6 +23,7 @@ import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import android.app.Dialog; import android.app.settings.SettingsEnums; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; @@ -398,6 +399,16 @@ public class ToggleScreenMagnificationPreferenceFragment extends return getUserShortcutTypeFromSettings(getPrefContext()); } + @Override + ComponentName getTileComponentName() { + return null; + } + + @Override + CharSequence getTileName() { + return null; + } + @Override protected void onPreferenceToggled(String preferenceKey, boolean enabled) { if (enabled && TextUtils.equals( diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index 4584b159b20..5ea4edc8a8d 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -16,6 +16,7 @@ package com.android.settings.accessibility; +import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_SAVED_QS_TOOLTIP_RESHOW; import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_SAVED_USER_SHORTCUT_TYPE; import static com.google.common.truth.Truth.assertThat; @@ -37,6 +38,7 @@ import android.provider.Settings; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.PopupWindow; import androidx.annotation.XmlRes; import androidx.appcompat.app.AlertDialog; @@ -58,6 +60,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowApplication; import org.robolectric.shadows.androidx.fragment.FragmentController; /** Tests for {@link ToggleFeaturePreferenceFragment} */ @@ -68,6 +72,12 @@ public class ToggleFeaturePreferenceFragmentTest { private static final String PLACEHOLDER_CLASS_NAME = PLACEHOLDER_PACKAGE_NAME + ".placeholder"; private static final ComponentName PLACEHOLDER_COMPONENT_NAME = new ComponentName( PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CLASS_NAME); + private static final String PLACEHOLDER_TILE_CLASS_NAME = + PLACEHOLDER_PACKAGE_NAME + "tile.placeholder"; + private static final ComponentName PLACEHOLDER_TILE_COMPONENT_NAME = new ComponentName( + PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME); + private static final String PLACEHOLDER_TILE_NAME = + PLACEHOLDER_PACKAGE_NAME + "tile.placeholder"; private static final String PLACEHOLDER_DIALOG_TITLE = "title"; private static final String DEFAULT_SUMMARY = "default summary"; private static final String DEFAULT_DESCRIPTION = "default description"; @@ -211,7 +221,7 @@ public class ToggleFeaturePreferenceFragmentTest { @Test @Config(shadows = ShadowFragment.class) - public void restoreValueFromSavedInstanceState_assignToVariable() { + public void restoreValueFromSavedInstanceState_assignShortcutTypeToVariable() { mContext.setTheme(R.style.Theme_AppCompat); final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog( mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE, @@ -234,6 +244,55 @@ public class ToggleFeaturePreferenceFragmentTest { assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE); } + @Test + @Config(shadows = ShadowFragment.class) + public void onPreferenceToggledOnDisabledService_notShowTooltipView() { + mContext.setTheme(R.style.Theme_AppCompat); + + mFragment.onPreferenceToggled(mFragment.KEY_USE_SERVICE_PREFERENCE, /* enabled= */ false); + + assertThat(getLatestPopupWindow()).isNull(); + } + + @Test + @Config(shadows = ShadowFragment.class) + public void onPreferenceToggledOnEnabledService_showTooltipView() { + mContext.setTheme(R.style.Theme_AppCompat); + + mFragment.onPreferenceToggled(mFragment.KEY_USE_SERVICE_PREFERENCE, /* enabled= */ true); + + assertThat(getLatestPopupWindow().isShowing()).isTrue(); + } + + @Test + @Config(shadows = ShadowFragment.class) + public void onPreferenceToggledOnEnabledService_tooltipViewShown_notShowTooltipView() { + mContext.setTheme(R.style.Theme_AppCompat); + mFragment.onPreferenceToggled(mFragment.KEY_USE_SERVICE_PREFERENCE, /* enabled= */ true); + getLatestPopupWindow().dismiss(); + + mFragment.onPreferenceToggled(mFragment.KEY_USE_SERVICE_PREFERENCE, /* enabled= */ true); + + assertThat(getLatestPopupWindow().isShowing()).isFalse(); + } + + @Test + @Config(shadows = ShadowFragment.class) + public void restoreValueFromSavedInstanceState_showTooltipView() { + mContext.setTheme(R.style.Theme_AppCompat); + mFragment.onPreferenceToggled(mFragment.KEY_USE_SERVICE_PREFERENCE, /* enabled= */ true); + assertThat(getLatestPopupWindow().isShowing()).isTrue(); + + final Bundle savedInstanceState = new Bundle(); + savedInstanceState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true); + mFragment.onCreate(savedInstanceState); + mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY); + mFragment.onViewCreated(mFragment.getView(), savedInstanceState); + mFragment.onAttach(mContext); + + assertThat(getLatestPopupWindow().isShowing()).isTrue(); + } + @Test public void createFooterPreference_shouldSetAsExpectedValue() { mFragment.createFooterPreference(mFragment.getPreferenceScreen(), @@ -258,13 +317,15 @@ public class ToggleFeaturePreferenceFragmentTest { private void callEmptyOnClicked(DialogInterface dialog, int which) {} + private static PopupWindow getLatestPopupWindow() { + final ShadowApplication shadowApplication = + Shadow.extract(ApplicationProvider.getApplicationContext()); + return shadowApplication.getLatestPopupWindow(); + } + public static class TestToggleFeaturePreferenceFragment extends ToggleFeaturePreferenceFragment { - @Override - protected void onPreferenceToggled(String preferenceKey, boolean enabled) { - } - @Override public int getMetricsCategory() { return 0; @@ -275,6 +336,16 @@ public class ToggleFeaturePreferenceFragmentTest { return 0; } + @Override + ComponentName getTileComponentName() { + return PLACEHOLDER_TILE_COMPONENT_NAME; + } + + @Override + CharSequence getTileName() { + return PLACEHOLDER_TILE_NAME; + } + @Override public int getPreferenceScreenResId() { return R.xml.placeholder_prefs; @@ -306,5 +377,9 @@ public class ToggleFeaturePreferenceFragmentTest { // UI related function, do nothing in tests } + @Override + public View getView() { + return mock(View.class); + } } }