diff --git a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java index 115f44f0c14..2b9729e7c67 100644 --- a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java +++ b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java @@ -28,6 +28,7 @@ import android.icu.text.CaseMap; import android.os.Bundle; import android.os.Handler; import android.provider.Settings; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -56,6 +57,7 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF implements ShortcutPreference.OnClickCallback { 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 int NOT_SET = -1; // Save user's shortcutType value when savedInstance has value (e.g. device rotated). protected int mSavedCheckBoxValue = NOT_SET; @@ -66,6 +68,8 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF private AccessibilitySettingsContentObserver mSettingsContentObserver; private CheckBox mSoftwareTypeCheckBox; private CheckBox mHardwareTypeCheckBox; + private AccessibilityQuickSettingsTooltipWindow mTooltipWindow; + private boolean mNeedsQSTooltipReshow = false; /** Returns the accessibility component name. */ protected abstract ComponentName getComponentName(); @@ -73,14 +77,25 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF /** Returns the accessibility feature name. */ protected abstract CharSequence getLabelName(); + /** Returns the accessibility tile component name. */ + protected abstract ComponentName getTileComponentName(); + + /** Returns the accessibility tile feature name. */ + protected abstract CharSequence getTileName(); + @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); + } } final int resId = getPreferenceScreenResId(); @@ -123,6 +138,16 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF return super.onCreateView(inflater, container, savedInstanceState); } + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + // Reshow tooltips when activity recreate, such as rotate device. + if (mNeedsQSTooltipReshow) { + getView().post(this::showQuickSettingsTooltipIfNeeded); + } + } + @Override public void onResume() { super.onResume(); @@ -149,6 +174,9 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF 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); } @@ -423,4 +451,33 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF getComponentName())); mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); } + + 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/gestures/OneHandedSettings.java b/src/com/android/settings/gestures/OneHandedSettings.java index 1310789bc68..38258968738 100644 --- a/src/com/android/settings/gestures/OneHandedSettings.java +++ b/src/com/android/settings/gestures/OneHandedSettings.java @@ -29,6 +29,7 @@ import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragmen import com.android.settings.accessibility.ShortcutPreference; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settingslib.widget.IllustrationPreference; +import com.android.settingslib.widget.MainSwitchPreference; /** * Fragment for One-handed mode settings @@ -40,6 +41,8 @@ public class OneHandedSettings extends AccessibilityShortcutPreferenceFragment { private static final String ONE_HANDED_SHORTCUT_KEY = "one_handed_shortcuts_preference"; private static final String ONE_HANDED_ILLUSTRATION_KEY = "one_handed_header"; + protected static final String ONE_HANDED_MAIN_SWITCH_KEY = + "gesture_one_handed_mode_enabled_main_switch"; private String mFeatureName; private OneHandedSettingsUtils mUtils; @@ -48,16 +51,22 @@ public class OneHandedSettings extends AccessibilityShortcutPreferenceFragment { OneHandedSettingsUtils.setUserId(UserHandle.myUserId()); super.updatePreferenceStates(); - final IllustrationPreference preference = - (IllustrationPreference) getPreferenceScreen().findPreference( - ONE_HANDED_ILLUSTRATION_KEY); - if (preference != null) { - final boolean isSwipeDownNotification = - OneHandedSettingsUtils.isSwipeDownNotificationEnabled(getContext()); - preference.setLottieAnimationResId( - isSwipeDownNotification ? R.raw.lottie_swipe_for_notifications - : R.raw.lottie_one_hand_mode); - } + final IllustrationPreference illustrationPreference = + getPreferenceScreen().findPreference(ONE_HANDED_ILLUSTRATION_KEY); + final boolean isSwipeDownNotification = + OneHandedSettingsUtils.isSwipeDownNotificationEnabled(getContext()); + illustrationPreference.setLottieAnimationResId( + isSwipeDownNotification ? R.raw.lottie_swipe_for_notifications + : R.raw.lottie_one_hand_mode); + + final MainSwitchPreference mainSwitchPreference = + getPreferenceScreen().findPreference(ONE_HANDED_MAIN_SWITCH_KEY); + mainSwitchPreference.addOnSwitchChangeListener((switchView, isChecked) -> { + switchView.setChecked(isChecked); + if (isChecked) { + showQuickSettingsTooltipIfNeeded(); + } + }); } @Override @@ -115,6 +124,16 @@ public class OneHandedSettings extends AccessibilityShortcutPreferenceFragment { return mFeatureName; } + @Override + protected ComponentName getTileComponentName() { + return AccessibilityShortcutController.ONE_HANDED_TILE_COMPONENT_NAME; + } + + @Override + protected CharSequence getTileName() { + return mFeatureName; + } + @Override protected int getPreferenceScreenResId() { return R.xml.one_handed_settings; diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java index 48d344a65ea..06a046b9aed 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java @@ -16,11 +16,14 @@ package com.android.settings.accessibility; +import static com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment.KEY_SAVED_QS_TOOLTIP_RESHOW; import static com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment.KEY_SAVED_USER_SHORTCUT_TYPE; +import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; import static com.google.common.truth.Truth.assertThat; 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; @@ -30,6 +33,10 @@ import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.provider.Settings; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.PopupWindow; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -48,6 +55,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; /** Tests for {@link AccessibilityShortcutPreferenceFragment} */ @RunWith(RobolectricTestRunner.class) @@ -55,8 +64,12 @@ public class AccessibilityShortcutPreferenceFragmentTest { private static final String PLACEHOLDER_PACKAGE_NAME = "com.placeholder.example"; private static final String PLACEHOLDER_CLASS_NAME = PLACEHOLDER_PACKAGE_NAME + ".placeholder"; + private static final String PLACEHOLDER_TILE_CLASS_NAME = + PLACEHOLDER_PACKAGE_NAME + "tile.placeholder"; private static final ComponentName PLACEHOLDER_COMPONENT_NAME = new ComponentName( PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CLASS_NAME); + private static final ComponentName PLACEHOLDER_TILE_COMPONENT_NAME = new ComponentName( + PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME); private static final String PLACEHOLDER_DIALOG_TITLE = "title"; private static final String SOFTWARE_SHORTCUT_KEY = @@ -89,10 +102,9 @@ public class AccessibilityShortcutPreferenceFragmentTest { mFragment.updateShortcutPreferenceData(); final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext, - mFragment.getComponentName().flattenToString(), - AccessibilityUtil.UserShortcutType.SOFTWARE); + mFragment.getComponentName().flattenToString(), UserShortcutType.SOFTWARE); // Compare to default UserShortcutType - assertThat(expectedType).isEqualTo(AccessibilityUtil.UserShortcutType.SOFTWARE); + assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE); } @Test @@ -103,25 +115,21 @@ public class AccessibilityShortcutPreferenceFragmentTest { mFragment.updateShortcutPreferenceData(); final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext, - mFragment.getComponentName().flattenToString(), - AccessibilityUtil.UserShortcutType.SOFTWARE); - assertThat(expectedType).isEqualTo(AccessibilityUtil.UserShortcutType.SOFTWARE - | AccessibilityUtil.UserShortcutType.HARDWARE); + mFragment.getComponentName().flattenToString(), UserShortcutType.SOFTWARE); + assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE); } @Test public void updateShortcutPreferenceData_hasValueInSharedPreference_assignToVariable() { final PreferredShortcut hardwareShortcut = new PreferredShortcut( - PLACEHOLDER_COMPONENT_NAME.flattenToString(), - AccessibilityUtil.UserShortcutType.HARDWARE); + PLACEHOLDER_COMPONENT_NAME.flattenToString(), UserShortcutType.HARDWARE); putUserShortcutTypeIntoSharedPreference(mContext, hardwareShortcut); mFragment.updateShortcutPreferenceData(); final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext, - mFragment.getComponentName().flattenToString(), - AccessibilityUtil.UserShortcutType.SOFTWARE); - assertThat(expectedType).isEqualTo(AccessibilityUtil.UserShortcutType.HARDWARE); + mFragment.getComponentName().flattenToString(), UserShortcutType.SOFTWARE); + assertThat(expectedType).isEqualTo(UserShortcutType.HARDWARE); } @Test @@ -139,7 +147,7 @@ public class AccessibilityShortcutPreferenceFragmentTest { mFragment.setupEditShortcutDialog(dialog); final int checkboxValue = mFragment.getShortcutTypeCheckBoxValue(); - assertThat(checkboxValue).isEqualTo(AccessibilityUtil.UserShortcutType.EMPTY); + assertThat(checkboxValue).isEqualTo(UserShortcutType.EMPTY); } @Test @@ -152,8 +160,7 @@ public class AccessibilityShortcutPreferenceFragmentTest { final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null); final PreferredShortcut hardwareShortcut = new PreferredShortcut( - PLACEHOLDER_COMPONENT_NAME.flattenToString(), - AccessibilityUtil.UserShortcutType.HARDWARE); + PLACEHOLDER_COMPONENT_NAME.flattenToString(), UserShortcutType.HARDWARE); mFragment.mShortcutPreference = shortcutPreference; PreferredShortcuts.saveUserShortcutType(mContext, hardwareShortcut); @@ -161,12 +168,12 @@ public class AccessibilityShortcutPreferenceFragmentTest { mFragment.setupEditShortcutDialog(dialog); final int checkboxValue = mFragment.getShortcutTypeCheckBoxValue(); - assertThat(checkboxValue).isEqualTo(AccessibilityUtil.UserShortcutType.HARDWARE); + assertThat(checkboxValue).isEqualTo(UserShortcutType.HARDWARE); } @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, AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC, @@ -178,8 +185,7 @@ public class AccessibilityShortcutPreferenceFragmentTest { mFragment.mShortcutPreference = shortcutPreference; savedInstanceState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE, - AccessibilityUtil.UserShortcutType.SOFTWARE - | AccessibilityUtil.UserShortcutType.HARDWARE); + UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE); mFragment.onAttach(mContext); mFragment.onCreate(savedInstanceState); mFragment.setupEditShortcutDialog(dialog); @@ -187,11 +193,25 @@ public class AccessibilityShortcutPreferenceFragmentTest { mFragment.saveNonEmptyUserShortcutType(value); final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext, - mFragment.getComponentName().flattenToString(), - AccessibilityUtil.UserShortcutType.SOFTWARE); - assertThat(expectedType).isEqualTo( - AccessibilityUtil.UserShortcutType.SOFTWARE - | AccessibilityUtil.UserShortcutType.HARDWARE); + mFragment.getComponentName().flattenToString(), UserShortcutType.SOFTWARE); + assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE); + } + + @Test + @Config(shadows = ShadowFragment.class) + public void restoreValueFromSavedInstanceState_showTooltipView() { + mContext.setTheme(R.style.Theme_AppCompat); + mFragment.showQuickSettingsTooltipIfNeeded(); + assertThat(getLatestPopupWindow().isShowing()).isTrue(); + + final Bundle savedInstanceState = new Bundle(); + savedInstanceState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true); + mFragment.onAttach(mContext); + mFragment.onCreate(savedInstanceState); + mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY); + mFragment.onViewCreated(mFragment.getView(), savedInstanceState); + + assertThat(getLatestPopupWindow().isShowing()).isTrue(); } @Test @@ -221,8 +241,26 @@ public class AccessibilityShortcutPreferenceFragmentTest { PreferredShortcuts.saveUserShortcutType(context, shortcut); } + private static PopupWindow getLatestPopupWindow() { + final ShadowApplication shadowApplication = + Shadow.extract(ApplicationProvider.getApplicationContext()); + return shadowApplication.getLatestPopupWindow(); + } + public static class TestAccessibilityShortcutPreferenceFragment extends AccessibilityShortcutPreferenceFragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return mock(View.class); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + // do nothing + } + @Override protected ComponentName getComponentName() { return PLACEHOLDER_COMPONENT_NAME; @@ -233,6 +271,16 @@ public class AccessibilityShortcutPreferenceFragmentTest { return PLACEHOLDER_PACKAGE_NAME; } + @Override + protected ComponentName getTileComponentName() { + return PLACEHOLDER_TILE_COMPONENT_NAME; + } + + @Override + protected CharSequence getTileName() { + return PLACEHOLDER_PACKAGE_NAME; + } + @Override public int getUserShortcutTypes() { return 0; @@ -263,5 +311,10 @@ public class AccessibilityShortcutPreferenceFragmentTest { protected String getLogTag() { return null; } + + @Override + public View getView() { + return mock(View.class); + } }; }