• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
20 import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
21 import static com.android.settings.accessibility.AccessibilityUtil.getShortcutSummaryList;
22 import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_GENERAL_CATEGORY;
23 
24 import android.annotation.SuppressLint;
25 import android.app.Dialog;
26 import android.app.settings.SettingsEnums;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.DialogInterface;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.provider.Settings;
33 import android.view.LayoutInflater;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.view.accessibility.AccessibilityManager;
37 
38 import androidx.annotation.Nullable;
39 import androidx.annotation.VisibleForTesting;
40 import androidx.preference.PreferenceCategory;
41 import androidx.preference.PreferenceScreen;
42 
43 import com.android.internal.accessibility.common.ShortcutConstants;
44 import com.android.internal.accessibility.util.ShortcutUtils;
45 import com.android.settings.R;
46 import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
47 import com.android.settings.dashboard.RestrictedDashboardFragment;
48 
49 import com.google.android.setupcompat.util.WizardManagerHelper;
50 
51 import java.util.ArrayList;
52 import java.util.List;
53 import java.util.Set;
54 
55 /**
56  * Base class for accessibility fragments shortcut functions and dialog management.
57  */
58 public abstract class AccessibilityShortcutPreferenceFragment extends RestrictedDashboardFragment
59         implements ShortcutPreference.OnClickCallback {
60     private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
61 
62     protected ShortcutPreference mShortcutPreference;
63     protected Dialog mDialog;
64     private AccessibilityManager.TouchExplorationStateChangeListener
65             mTouchExplorationStateChangeListener;
66     private AccessibilitySettingsContentObserver mSettingsContentObserver;
67 
AccessibilityShortcutPreferenceFragment(String restrictionKey)68     public AccessibilityShortcutPreferenceFragment(String restrictionKey) {
69         super(restrictionKey);
70     }
71 
72     /** Returns the accessibility component name. */
getComponentName()73     protected abstract ComponentName getComponentName();
74 
75     /** Returns the accessibility feature name. */
getLabelName()76     protected abstract CharSequence getLabelName();
77 
78     @Override
onCreate(Bundle savedInstanceState)79     public void onCreate(Bundle savedInstanceState) {
80         super.onCreate(savedInstanceState);
81 
82         final int resId = getPreferenceScreenResId();
83         if (resId <= 0) {
84             final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
85                     getPrefContext());
86             setPreferenceScreen(preferenceScreen);
87         }
88 
89         if (showGeneralCategory()) {
90             initGeneralCategory();
91         }
92 
93         final List<String> shortcutFeatureKeys = new ArrayList<>();
94         shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
95         shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
96         shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
97         mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
98         mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> {
99             updateShortcutPreferenceData();
100             updateShortcutPreference();
101         });
102     }
103 
104     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)105     public View onCreateView(LayoutInflater inflater, ViewGroup container,
106             Bundle savedInstanceState) {
107         mShortcutPreference =
108                 getPreferenceScreen().findPreference(getShortcutPreferenceKey());
109         if (mShortcutPreference == null) {
110             mShortcutPreference = new ShortcutPreference(getPrefContext(), /* attrs= */ null);
111             mShortcutPreference.setPersistent(false);
112             mShortcutPreference.setKey(getShortcutPreferenceKey());
113             getPreferenceScreen().addPreference(mShortcutPreference);
114         }
115 
116         mShortcutPreference.setOnClickCallback(this);
117         mShortcutPreference.setTitle(getShortcutTitle());
118         mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
119             mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
120         };
121 
122         return super.onCreateView(inflater, container, savedInstanceState);
123     }
124 
125     @Override
onResume()126     public void onResume() {
127         super.onResume();
128 
129         final AccessibilityManager am = getPrefContext().getSystemService(
130                 AccessibilityManager.class);
131         am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
132         mSettingsContentObserver.register(getContentResolver());
133         updateShortcutPreferenceData();
134         updateShortcutPreference();
135     }
136 
137     @Override
onPause()138     public void onPause() {
139         final AccessibilityManager am = getPrefContext().getSystemService(
140                 AccessibilityManager.class);
141         am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
142         mSettingsContentObserver.unregister(getContentResolver());
143         super.onPause();
144     }
145 
146     @Override
onCreateDialog(int dialogId)147     public Dialog onCreateDialog(int dialogId) {
148         switch (dialogId) {
149             case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
150                 if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
151                     mDialog = AccessibilityShortcutsTutorial
152                             .createAccessibilityTutorialDialogForSetupWizard(
153                                     getPrefContext(), getUserPreferredShortcutTypes(),
154                                     this::callOnTutorialDialogButtonClicked, getLabelName());
155                 } else {
156                     mDialog = AccessibilityShortcutsTutorial
157                             .createAccessibilityTutorialDialog(
158                                     getPrefContext(), getUserPreferredShortcutTypes(),
159                                     this::callOnTutorialDialogButtonClicked, getLabelName());
160                 }
161                 mDialog.setCanceledOnTouchOutside(false);
162                 return mDialog;
163             default:
164                 throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
165         }
166     }
167 
getShortcutTitle()168     protected CharSequence getShortcutTitle() {
169         return getString(R.string.accessibility_shortcut_title, getLabelName());
170     }
171 
172     @Override
getDialogMetricsCategory(int dialogId)173     public int getDialogMetricsCategory(int dialogId) {
174         switch (dialogId) {
175             case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
176                 return SettingsEnums.DIALOG_ACCESSIBILITY_TUTORIAL;
177             default:
178                 return SettingsEnums.ACTION_UNKNOWN;
179         }
180     }
181 
182     @Override
onSettingsClicked(ShortcutPreference preference)183     public void onSettingsClicked(ShortcutPreference preference) {
184         EditShortcutsPreferenceFragment.showEditShortcutScreen(
185                 getContext(),
186                 getMetricsCategory(),
187                 getShortcutTitle(),
188                 getComponentName(),
189                 getIntent()
190         );
191     }
192 
193     @SuppressLint("MissingPermission")
194     @Override
onToggleClicked(ShortcutPreference preference)195     public void onToggleClicked(ShortcutPreference preference) {
196         if (getComponentName() == null) {
197             return;
198         }
199 
200         final int shortcutTypes = getUserPreferredShortcutTypes();
201         final boolean isChecked = preference.isChecked();
202         getPrefContext().getSystemService(AccessibilityManager.class).enableShortcutsForTargets(
203                 isChecked, shortcutTypes,
204                 Set.of(getComponentName().flattenToString()), getPrefContext().getUserId());
205         if (isChecked) {
206             showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);
207         }
208         mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
209     }
210 
211     /**
212      * Overrides to return specific shortcut preference key
213      *
214      * @return String The specific shortcut preference key
215      */
getShortcutPreferenceKey()216     protected String getShortcutPreferenceKey() {
217         return KEY_SHORTCUT_PREFERENCE;
218     }
219 
220     /**
221      * Returns the shortcut type list which has been checked by user.
222      */
getUserShortcutTypes()223     protected int getUserShortcutTypes() {
224         return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),
225                 getComponentName());
226     };
227 
getSoftwareShortcutTypeSummary(Context context)228     private static CharSequence getSoftwareShortcutTypeSummary(Context context) {
229         int resId;
230         if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
231             resId = R.string.accessibility_shortcut_edit_summary_software;
232         } else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
233             resId = R.string.accessibility_shortcut_edit_summary_software_gesture;
234         } else {
235             resId = R.string.accessibility_shortcut_edit_summary_software;
236         }
237         return context.getText(resId);
238     }
239 
240     /**
241      * This method will be invoked when a button in the tutorial dialog is clicked.
242      *
243      * @param dialog The dialog that received the click
244      * @param which  The button that was clicked
245      */
callOnTutorialDialogButtonClicked(DialogInterface dialog, int which)246     private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
247         dialog.dismiss();
248     }
249 
250     @VisibleForTesting
initGeneralCategory()251     void initGeneralCategory() {
252         final PreferenceCategory generalCategory = new PreferenceCategory(getPrefContext());
253         generalCategory.setKey(KEY_GENERAL_CATEGORY);
254         generalCategory.setTitle(getGeneralCategoryDescription(null));
255 
256         getPreferenceScreen().addPreference(generalCategory);
257     }
258 
259     /**
260      * Overrides to return customized description for general category above shortcut
261      *
262      * @return CharSequence The customized description for general category
263      */
getGeneralCategoryDescription(@ullable CharSequence title)264     protected CharSequence getGeneralCategoryDescription(@Nullable CharSequence title) {
265         if (title == null || title.toString().isEmpty()) {
266             // Return default 'Options' string for category
267             return getContext().getString(R.string.accessibility_screen_option);
268         }
269         return title;
270     }
271 
272     /**
273      * Overrides to determinate if showing additional category description above shortcut
274      *
275      * @return boolean true to show category, false otherwise.
276      */
showGeneralCategory()277     protected boolean showGeneralCategory() {
278         return false;
279     }
280 
getShortcutTypeSummary(Context context)281     protected CharSequence getShortcutTypeSummary(Context context) {
282         if (!mShortcutPreference.isSettingsEditable()) {
283             return context.getText(R.string.accessibility_shortcut_edit_dialog_title_hardware);
284         }
285 
286         if (!mShortcutPreference.isChecked()) {
287             return context.getText(R.string.accessibility_shortcut_state_off);
288         }
289 
290         final int shortcutTypes = getUserPreferredShortcutTypes();
291         return getShortcutSummaryList(context, shortcutTypes);
292     }
293 
updateShortcutPreferenceData()294     protected void updateShortcutPreferenceData() {
295         if (getComponentName() == null) {
296             return;
297         }
298 
299         final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(
300                 getPrefContext(), getComponentName());
301         if (shortcutTypes != DEFAULT) {
302             final PreferredShortcut shortcut = new PreferredShortcut(
303                     getComponentName().flattenToString(), shortcutTypes);
304             PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
305         }
306     }
307 
updateShortcutPreference()308     protected void updateShortcutPreference() {
309         if (getComponentName() == null) {
310             return;
311         }
312 
313         final int shortcutTypes = getUserPreferredShortcutTypes();
314         mShortcutPreference.setChecked(
315                 ShortcutUtils.isShortcutContained(
316                         getPrefContext(), shortcutTypes, getComponentName().flattenToString()));
317         mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
318     }
319 
320     /**
321      * Returns the user preferred shortcut types or the default shortcut types if not set
322      */
323     @ShortcutConstants.UserShortcutType
getUserPreferredShortcutTypes()324     protected int getUserPreferredShortcutTypes() {
325         return PreferredShortcuts.retrieveUserShortcutType(
326                 getPrefContext(),
327                 getComponentName().flattenToString());
328     }
329 }
330