Support OHM quick settings tooltips when feature on

Bug: 210356011
Test: make RunSettingsRoboTests ROBOTEST_FILTER=AccessibilityShortcutPreferenceFragmentTest
Change-Id: I3b35d9bb347b7e516db94a4b801a4484f6f38ca8
diff --git a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
index 115f44f..2b9729e 100644
--- a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
@@ -28,6 +28,7 @@
 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 @@
         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 @@
     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 @@
     /** 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();
@@ -124,6 +139,16 @@
     }
 
     @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();
         final AccessibilityManager am = getPrefContext().getSystemService(
@@ -149,6 +174,9 @@
         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 @@
                         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 1310789..3825896 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.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 @@
 
     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 @@
         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
@@ -116,6 +125,16 @@
     }
 
     @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 48d344a..06a046b 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.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.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 @@
 
     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 @@
         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 @@
         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 @@
         mFragment.setupEditShortcutDialog(dialog);
 
         final int checkboxValue = mFragment.getShortcutTypeCheckBoxValue();
-        assertThat(checkboxValue).isEqualTo(AccessibilityUtil.UserShortcutType.EMPTY);
+        assertThat(checkboxValue).isEqualTo(UserShortcutType.EMPTY);
     }
 
     @Test
@@ -152,8 +160,7 @@
         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 @@
         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 @@
         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 @@
         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 @@
         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;
@@ -234,6 +272,16 @@
         }
 
         @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 @@
         protected String getLogTag() {
             return null;
         }
+
+        @Override
+        public View getView() {
+            return mock(View.class);
+        }
     };
 }