1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.accessibility;
18 
19 import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
20 import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_GENERAL_CATEGORY;
21 import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_SAVED_QS_TOOLTIP_TYPE;
22 
23 import android.app.Activity;
24 import android.app.Dialog;
25 import android.app.settings.SettingsEnums;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.icu.text.CaseMap;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.provider.Settings;
33 import android.text.TextUtils;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.view.accessibility.AccessibilityManager;
38 import android.widget.CheckBox;
39 
40 import androidx.annotation.Nullable;
41 import androidx.annotation.VisibleForTesting;
42 import androidx.preference.PreferenceCategory;
43 import androidx.preference.PreferenceScreen;
44 
45 import com.android.internal.accessibility.common.ShortcutConstants;
46 import com.android.settings.R;
47 import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
48 import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
49 import com.android.settings.dashboard.RestrictedDashboardFragment;
50 import com.android.settings.utils.LocaleUtils;
51 
52 import com.google.android.setupcompat.util.WizardManagerHelper;
53 
54 import java.util.ArrayList;
55 import java.util.List;
56 import java.util.Locale;
57 
58 /**
59  * Base class for accessibility fragments shortcut functions and dialog management.
60  */
61 public abstract class AccessibilityShortcutPreferenceFragment extends RestrictedDashboardFragment
62         implements ShortcutPreference.OnClickCallback {
63     private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
64     protected static final String KEY_SAVED_USER_SHORTCUT_TYPE = "shortcut_type";
65     protected static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
66     protected static final int NOT_SET = -1;
67     // Save user's shortcutType value when savedInstance has value (e.g. device rotated).
68     protected int mSavedCheckBoxValue = NOT_SET;
69 
70     protected ShortcutPreference mShortcutPreference;
71     protected Dialog mDialog;
72     private AccessibilityManager.TouchExplorationStateChangeListener
73             mTouchExplorationStateChangeListener;
74     private AccessibilitySettingsContentObserver mSettingsContentObserver;
75     private CheckBox mSoftwareTypeCheckBox;
76     private CheckBox mHardwareTypeCheckBox;
77     private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
78     private boolean mNeedsQSTooltipReshow = false;
79     private int mNeedsQSTooltipType = QuickSettingsTooltipType.GUIDE_TO_EDIT;
80 
AccessibilityShortcutPreferenceFragment(String restrictionKey)81     public AccessibilityShortcutPreferenceFragment(String restrictionKey) {
82         super(restrictionKey);
83     }
84 
85     /** Returns the accessibility component name. */
getComponentName()86     protected abstract ComponentName getComponentName();
87 
88     /** Returns the accessibility feature name. */
getLabelName()89     protected abstract CharSequence getLabelName();
90 
91     /** Returns the accessibility tile component name. */
getTileComponentName()92     protected abstract ComponentName getTileComponentName();
93 
94     /** Returns the accessibility tile tooltip content. */
getTileTooltipContent(@uickSettingsTooltipType int type)95     protected abstract CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type);
96 
97     @Override
onCreate(Bundle savedInstanceState)98     public void onCreate(Bundle savedInstanceState) {
99         super.onCreate(savedInstanceState);
100 
101         // Restore the user shortcut type and tooltip.
102         if (savedInstanceState != null) {
103             if (savedInstanceState.containsKey(KEY_SAVED_USER_SHORTCUT_TYPE)) {
104                 mSavedCheckBoxValue = savedInstanceState.getInt(KEY_SAVED_USER_SHORTCUT_TYPE,
105                         NOT_SET);
106             }
107             if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
108                 mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
109             }
110             if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_TYPE)) {
111                 mNeedsQSTooltipType = savedInstanceState.getInt(KEY_SAVED_QS_TOOLTIP_TYPE);
112             }
113         }
114 
115         final int resId = getPreferenceScreenResId();
116         if (resId <= 0) {
117             final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
118                     getPrefContext());
119             setPreferenceScreen(preferenceScreen);
120         }
121 
122         if (showGeneralCategory()) {
123             initGeneralCategory();
124         }
125 
126         final List<String> shortcutFeatureKeys = new ArrayList<>();
127         shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
128         shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
129         if (android.view.accessibility.Flags.a11yQsShortcut()) {
130             shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
131         }
132         mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
133         mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> {
134             updateShortcutPreferenceData();
135             updateShortcutPreference();
136         });
137     }
138 
139     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)140     public View onCreateView(LayoutInflater inflater, ViewGroup container,
141             Bundle savedInstanceState) {
142         mShortcutPreference = new ShortcutPreference(getPrefContext(), /* attrs= */ null);
143         mShortcutPreference.setPersistent(false);
144         mShortcutPreference.setKey(getShortcutPreferenceKey());
145         mShortcutPreference.setOnClickCallback(this);
146         mShortcutPreference.setTitle(getShortcutTitle());
147 
148         getPreferenceScreen().addPreference(mShortcutPreference);
149 
150         mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
151             removeDialog(DialogEnums.EDIT_SHORTCUT);
152             mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
153         };
154 
155         return super.onCreateView(inflater, container, savedInstanceState);
156     }
157 
158     @Override
onViewCreated(View view, Bundle savedInstanceState)159     public void onViewCreated(View view, Bundle savedInstanceState) {
160         super.onViewCreated(view, savedInstanceState);
161 
162         // Reshow tooltip when activity recreate, such as rotate device.
163         if (mNeedsQSTooltipReshow) {
164             view.post(() -> {
165                 final Activity activity = getActivity();
166                 if (activity != null && !activity.isFinishing()) {
167                     showQuickSettingsTooltipIfNeeded();
168                 }
169             });
170         }
171     }
172 
173     @Override
onResume()174     public void onResume() {
175         super.onResume();
176 
177         final AccessibilityManager am = getPrefContext().getSystemService(
178                 AccessibilityManager.class);
179         am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
180         mSettingsContentObserver.register(getContentResolver());
181         updateShortcutPreferenceData();
182         updateShortcutPreference();
183 
184         updateEditShortcutDialogIfNeeded();
185     }
186 
187     @Override
onPause()188     public void onPause() {
189         final AccessibilityManager am = getPrefContext().getSystemService(
190                 AccessibilityManager.class);
191         am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
192         mSettingsContentObserver.unregister(getContentResolver());
193         super.onPause();
194     }
195 
196     @Override
onSaveInstanceState(Bundle outState)197     public void onSaveInstanceState(Bundle outState) {
198         final int value = getShortcutTypeCheckBoxValue();
199         if (value != NOT_SET) {
200             outState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE, value);
201         }
202         final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
203         if (mNeedsQSTooltipReshow || isTooltipWindowShowing) {
204             outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
205             outState.putInt(KEY_SAVED_QS_TOOLTIP_TYPE, mNeedsQSTooltipType);
206         }
207         super.onSaveInstanceState(outState);
208     }
209 
210     @Override
onCreateDialog(int dialogId)211     public Dialog onCreateDialog(int dialogId) {
212         switch (dialogId) {
213             case DialogEnums.EDIT_SHORTCUT:
214                 final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
215                         ? AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC_SUW :
216                         AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC;
217                 mDialog = AccessibilityDialogUtils.showEditShortcutDialog(
218                         getPrefContext(), dialogType, getShortcutTitle(),
219                         this::callOnAlertDialogCheckboxClicked);
220                 setupEditShortcutDialog(mDialog);
221                 return mDialog;
222             case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
223                 if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
224                     mDialog = AccessibilityShortcutsTutorial
225                             .createAccessibilityTutorialDialogForSetupWizard(
226                                     getPrefContext(), getUserPreferredShortcutTypes(),
227                                     this::callOnTutorialDialogButtonClicked, getLabelName());
228                 } else {
229                     mDialog = AccessibilityShortcutsTutorial
230                             .createAccessibilityTutorialDialog(
231                                     getPrefContext(), getUserPreferredShortcutTypes(),
232                                     this::callOnTutorialDialogButtonClicked, getLabelName());
233                 }
234                 mDialog.setCanceledOnTouchOutside(false);
235                 return mDialog;
236             default:
237                 throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
238         }
239     }
240 
getShortcutTitle()241     protected CharSequence getShortcutTitle() {
242         return getString(R.string.accessibility_shortcut_title, getLabelName());
243     }
244 
245     @Override
getDialogMetricsCategory(int dialogId)246     public int getDialogMetricsCategory(int dialogId) {
247         switch (dialogId) {
248             case DialogEnums.EDIT_SHORTCUT:
249                 return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_EDIT_SHORTCUT;
250             case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
251                 return SettingsEnums.DIALOG_ACCESSIBILITY_TUTORIAL;
252             default:
253                 return SettingsEnums.ACTION_UNKNOWN;
254         }
255     }
256 
257     @Override
onSettingsClicked(ShortcutPreference preference)258     public void onSettingsClicked(ShortcutPreference preference) {
259         if (Flags.editShortcutsInFullScreen()) {
260             EditShortcutsPreferenceFragment.showEditShortcutScreen(
261                     getContext(),
262                     getMetricsCategory(),
263                     getShortcutTitle(),
264                     getComponentName(),
265                     getIntent()
266             );
267         } else {
268             showDialog(DialogEnums.EDIT_SHORTCUT);
269         }
270     }
271 
272     @Override
onToggleClicked(ShortcutPreference preference)273     public void onToggleClicked(ShortcutPreference preference) {
274         if (getComponentName() == null) {
275             return;
276         }
277 
278         final int shortcutTypes = getUserPreferredShortcutTypes();
279         if (preference.isChecked()) {
280             AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes,
281                     getComponentName());
282             showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);
283         } else {
284             AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes,
285                     getComponentName());
286         }
287         mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
288     }
289 
290     /**
291      * Overrides to return specific shortcut preference key
292      *
293      * @return String The specific shortcut preference key
294      */
getShortcutPreferenceKey()295     protected String getShortcutPreferenceKey() {
296         return KEY_SHORTCUT_PREFERENCE;
297     }
298 
299     @VisibleForTesting
setupEditShortcutDialog(Dialog dialog)300     void setupEditShortcutDialog(Dialog dialog) {
301         final View dialogSoftwareView = dialog.findViewById(R.id.software_shortcut);
302         mSoftwareTypeCheckBox = dialogSoftwareView.findViewById(R.id.checkbox);
303         setDialogTextAreaClickListener(dialogSoftwareView, mSoftwareTypeCheckBox);
304 
305         final View dialogHardwareView = dialog.findViewById(R.id.hardware_shortcut);
306         mHardwareTypeCheckBox = dialogHardwareView.findViewById(R.id.checkbox);
307         setDialogTextAreaClickListener(dialogHardwareView, mHardwareTypeCheckBox);
308 
309         updateEditShortcutDialogCheckBox();
310     }
311 
312     /**
313      * Returns accumulated {@link AccessibilityUtil.UserShortcutType} checkbox value or
314      * {@code NOT_SET} if checkboxes did not exist.
315      */
getShortcutTypeCheckBoxValue()316     protected int getShortcutTypeCheckBoxValue() {
317         if (mSoftwareTypeCheckBox == null || mHardwareTypeCheckBox == null) {
318             return NOT_SET;
319         }
320 
321         int value = AccessibilityUtil.UserShortcutType.EMPTY;
322         if (mSoftwareTypeCheckBox.isChecked()) {
323             value |= AccessibilityUtil.UserShortcutType.SOFTWARE;
324         }
325         if (mHardwareTypeCheckBox.isChecked()) {
326             value |= AccessibilityUtil.UserShortcutType.HARDWARE;
327         }
328         return value;
329     }
330 
331     /**
332      * Returns the shortcut type list which has been checked by user.
333      */
getUserShortcutTypes()334     protected int getUserShortcutTypes() {
335         return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),
336                 getComponentName());
337     };
338 
getSoftwareShortcutTypeSummary(Context context)339     private static CharSequence getSoftwareShortcutTypeSummary(Context context) {
340         int resId;
341         if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
342             resId = R.string.accessibility_shortcut_edit_summary_software;
343         } else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
344             resId = R.string.accessibility_shortcut_edit_summary_software_gesture;
345         } else {
346             resId = R.string.accessibility_shortcut_edit_summary_software;
347         }
348         return context.getText(resId);
349     }
350 
351     /**
352      * This method will be invoked when a button in the tutorial dialog is clicked.
353      *
354      * @param dialog The dialog that received the click
355      * @param which  The button that was clicked
356      */
callOnTutorialDialogButtonClicked(DialogInterface dialog, int which)357     private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
358         dialog.dismiss();
359         showQuickSettingsTooltipIfNeeded();
360     }
361 
362     /**
363      * This method will be invoked when a button in the edit shortcut dialog is clicked.
364      *
365      * @param dialog The dialog that received the click
366      * @param which  The button that was clicked
367      */
callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which)368     protected void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) {
369         if (getComponentName() == null) {
370             return;
371         }
372 
373         final int value = getShortcutTypeCheckBoxValue();
374         saveNonEmptyUserShortcutType(value);
375         AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), value, getComponentName());
376         AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), ~value, getComponentName());
377         final boolean shortcutAssigned = value != AccessibilityUtil.UserShortcutType.EMPTY;
378         mShortcutPreference.setChecked(shortcutAssigned);
379         mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
380 
381         if (mHardwareTypeCheckBox.isChecked()) {
382             AccessibilityUtil.skipVolumeShortcutDialogTimeoutRestriction(getPrefContext());
383         }
384 
385         // Show the quick setting tooltip if the shortcut assigned in the first time
386         if (shortcutAssigned) {
387             showQuickSettingsTooltipIfNeeded();
388         }
389     }
390 
391     @VisibleForTesting
initGeneralCategory()392     void initGeneralCategory() {
393         final PreferenceCategory generalCategory = new PreferenceCategory(getPrefContext());
394         generalCategory.setKey(KEY_GENERAL_CATEGORY);
395         generalCategory.setTitle(getGeneralCategoryDescription(null));
396 
397         getPreferenceScreen().addPreference(generalCategory);
398     }
399 
updateEditShortcutDialogIfNeeded()400     private void updateEditShortcutDialogIfNeeded() {
401         if (mDialog == null || !mDialog.isShowing()) {
402             return;
403         }
404         AccessibilityDialogUtils.updateShortcutInDialog(getContext(), mDialog);
405     }
406 
407     @VisibleForTesting
saveNonEmptyUserShortcutType(int type)408     void saveNonEmptyUserShortcutType(int type) {
409         if (type == AccessibilityUtil.UserShortcutType.EMPTY) {
410             return;
411         }
412 
413         final PreferredShortcut shortcut = new PreferredShortcut(
414                 getComponentName().flattenToString(), type);
415         PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
416     }
417 
418     /**
419      * Overrides to return customized description for general category above shortcut
420      *
421      * @return CharSequence The customized description for general category
422      */
getGeneralCategoryDescription(@ullable CharSequence title)423     protected CharSequence getGeneralCategoryDescription(@Nullable CharSequence title) {
424         if (title == null || title.toString().isEmpty()) {
425             // Return default 'Options' string for category
426             return getContext().getString(R.string.accessibility_screen_option);
427         }
428         return title;
429     }
430 
431     /**
432      * Overrides to determinate if showing additional category description above shortcut
433      *
434      * @return boolean true to show category, false otherwise.
435      */
showGeneralCategory()436     protected boolean showGeneralCategory() {
437         return false;
438     }
439 
setDialogTextAreaClickListener(View dialogView, CheckBox checkBox)440     private void setDialogTextAreaClickListener(View dialogView, CheckBox checkBox) {
441         final View dialogTextArea = dialogView.findViewById(R.id.container);
442         dialogTextArea.setOnClickListener(v -> checkBox.toggle());
443     }
444 
getShortcutTypeSummary(Context context)445     protected CharSequence getShortcutTypeSummary(Context context) {
446         if (!mShortcutPreference.isSettingsEditable()) {
447             return context.getText(R.string.accessibility_shortcut_edit_dialog_title_hardware);
448         }
449 
450         if (!mShortcutPreference.isChecked()) {
451             return context.getText(R.string.accessibility_shortcut_state_off);
452         }
453 
454         final int shortcutTypes = getUserPreferredShortcutTypes();
455 
456         // LINT.IfChange(shortcut_type_ui_order)
457         final List<CharSequence> list = new ArrayList<>();
458         if (android.view.accessibility.Flags.a11yQsShortcut()) {
459             if (hasShortcutType(shortcutTypes, AccessibilityUtil.UserShortcutType.QUICK_SETTINGS)) {
460                 final CharSequence qsTitle = context.getText(
461                         R.string.accessibility_feature_shortcut_setting_summary_quick_settings);
462                 list.add(qsTitle);
463             }
464         }
465         if (hasShortcutType(shortcutTypes, AccessibilityUtil.UserShortcutType.SOFTWARE)) {
466             list.add(getSoftwareShortcutTypeSummary(context));
467         }
468         if (hasShortcutType(shortcutTypes, AccessibilityUtil.UserShortcutType.HARDWARE)) {
469             final CharSequence hardwareTitle = context.getText(
470                     R.string.accessibility_shortcut_hardware_keyword);
471             list.add(hardwareTitle);
472         }
473         // LINT.ThenChange(/res/xml/accessibility_edit_shortcuts.xml:shortcut_type_ui_order)
474 
475         // Show software shortcut if first time to use.
476         if (list.isEmpty()) {
477             list.add(getSoftwareShortcutTypeSummary(context));
478         }
479 
480         return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
481                 null, LocaleUtils.getConcatenatedString(list));
482     }
483 
updateEditShortcutDialogCheckBox()484     private void updateEditShortcutDialogCheckBox() {
485         // If it is during onConfigChanged process then restore the value, or get the saved value
486         // when shortcutPreference is checked.
487         int value = restoreOnConfigChangedValue();
488         if (value == NOT_SET) {
489             final int lastNonEmptyUserShortcutType = getUserPreferredShortcutTypes();
490             value = mShortcutPreference.isChecked() ? lastNonEmptyUserShortcutType
491                     : AccessibilityUtil.UserShortcutType.EMPTY;
492         }
493 
494         mSoftwareTypeCheckBox.setChecked(
495                 hasShortcutType(value, AccessibilityUtil.UserShortcutType.SOFTWARE));
496         mHardwareTypeCheckBox.setChecked(
497                 hasShortcutType(value, AccessibilityUtil.UserShortcutType.HARDWARE));
498     }
499 
restoreOnConfigChangedValue()500     private int restoreOnConfigChangedValue() {
501         final int savedValue = mSavedCheckBoxValue;
502         mSavedCheckBoxValue = NOT_SET;
503         return savedValue;
504     }
505 
hasShortcutType(int value, @AccessibilityUtil.UserShortcutType int type)506     private boolean hasShortcutType(int value, @AccessibilityUtil.UserShortcutType int type) {
507         return (value & type) == type;
508     }
509 
updateShortcutPreferenceData()510     protected void updateShortcutPreferenceData() {
511         if (getComponentName() == null) {
512             return;
513         }
514 
515         final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(
516                 getPrefContext(), getComponentName());
517         if (shortcutTypes != AccessibilityUtil.UserShortcutType.EMPTY) {
518             final PreferredShortcut shortcut = new PreferredShortcut(
519                     getComponentName().flattenToString(), shortcutTypes);
520             PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
521         }
522     }
523 
updateShortcutPreference()524     protected void updateShortcutPreference() {
525         if (getComponentName() == null) {
526             return;
527         }
528 
529         final int shortcutTypes = getUserPreferredShortcutTypes();
530         mShortcutPreference.setChecked(
531                 AccessibilityUtil.hasValuesInSettings(getPrefContext(), shortcutTypes,
532                         getComponentName()));
533         mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
534     }
535 
536     /**
537      * Shows the quick settings tooltip if the quick settings feature is assigned. The tooltip only
538      * shows once.
539      *
540      * @param type The quick settings tooltip type
541      */
showQuickSettingsTooltipIfNeeded(@uickSettingsTooltipType int type)542     protected void showQuickSettingsTooltipIfNeeded(@QuickSettingsTooltipType int type) {
543         mNeedsQSTooltipType = type;
544         showQuickSettingsTooltipIfNeeded();
545     }
546 
showQuickSettingsTooltipIfNeeded()547     private void showQuickSettingsTooltipIfNeeded() {
548         if (android.view.accessibility.Flags.a11yQsShortcut()) {
549             // Don't show Quick Settings tooltip
550             return;
551         }
552         final ComponentName tileComponentName = getTileComponentName();
553         if (tileComponentName == null) {
554             // Returns if no tile service assigned.
555             return;
556         }
557 
558         if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
559                 getContext(), tileComponentName)) {
560             // Returns if quick settings tooltip only show once.
561             return;
562         }
563 
564         final CharSequence content = getTileTooltipContent(mNeedsQSTooltipType);
565         if (TextUtils.isEmpty(content)) {
566             // Returns if no content of tile tooltip assigned.
567             return;
568         }
569 
570         final int imageResId = mNeedsQSTooltipType == QuickSettingsTooltipType.GUIDE_TO_EDIT
571                 ? R.drawable.accessibility_qs_tooltip_illustration
572                 : R.drawable.accessibility_auto_added_qs_tooltip_illustration;
573         mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(getContext());
574         mTooltipWindow.setup(content, imageResId);
575         mTooltipWindow.showAtTopCenter(getView());
576         AccessibilityQuickSettingUtils.optInValueToSharedPreferences(getContext(),
577                 tileComponentName);
578         mNeedsQSTooltipReshow = false;
579     }
580 
581     /**
582      * Returns the user preferred shortcut types or the default shortcut types if not set
583      */
584     @ShortcutConstants.UserShortcutType
getUserPreferredShortcutTypes()585     protected int getUserPreferredShortcutTypes() {
586         return PreferredShortcuts.retrieveUserShortcutType(
587                 getPrefContext(),
588                 getComponentName().flattenToString());
589     }
590 }
591