1 /*
2  * Copyright (C) 2017 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 android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
20 
21 import android.accessibilityservice.AccessibilityServiceInfo;
22 import android.app.settings.SettingsEnums;
23 import android.content.ComponentName;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.res.Resources;
27 import android.os.Bundle;
28 import android.provider.SearchIndexableResource;
29 import android.provider.Settings;
30 import android.text.TextUtils;
31 import android.view.accessibility.AccessibilityManager;
32 
33 import androidx.annotation.VisibleForTesting;
34 import androidx.preference.Preference;
35 
36 import com.android.settings.R;
37 import com.android.settings.dashboard.DashboardFragment;
38 import com.android.settings.search.BaseSearchIndexProvider;
39 import com.android.settings.search.Indexable;
40 import com.android.settings.search.actionbar.SearchMenuController;
41 import com.android.settings.support.actionbar.HelpResourceProvider;
42 import com.android.settingslib.search.SearchIndexable;
43 
44 import java.util.Arrays;
45 import java.util.List;
46 
47 @SearchIndexable
48 public final class MagnificationPreferenceFragment extends DashboardFragment {
49     @VisibleForTesting
50     static final int ON = 1;
51     @VisibleForTesting
52     static final int OFF = 0;
53 
54     private static final String TAG = "MagnificationPreferenceFragment";
55 
56     // Settings App preference keys
57     private static final String PREFERENCE_TITLE_KEY = "magnification_preference_screen_title";
58 
59     // Pseudo ComponentName used to represent navbar magnification in Settings.Secure.
60     private static final String MAGNIFICATION_COMPONENT_ID =
61             "com.android.server.accessibility.MagnificationController";
62 
63     private boolean mLaunchedFromSuw = false;
64 
65     @Override
getMetricsCategory()66     public int getMetricsCategory() {
67         return SettingsEnums.ACCESSIBILITY_SCREEN_MAGNIFICATION_SETTINGS;
68     }
69 
70     @Override
getLogTag()71     protected String getLogTag() {
72         return TAG;
73     }
74 
75     @Override
getHelpResource()76     public int getHelpResource() {
77         return R.string.help_url_magnification;
78     }
79 
80     @Override
getPreferenceScreenResId()81     protected int getPreferenceScreenResId() {
82         return R.xml.accessibility_magnification_settings;
83     }
84 
85     @Override
onAttach(Context context)86     public void onAttach(Context context) {
87         super.onAttach(context);
88         final Bundle args = getArguments();
89         if ((args != null) && args.containsKey(AccessibilitySettings.EXTRA_LAUNCHED_FROM_SUW)) {
90             mLaunchedFromSuw = args.getBoolean(AccessibilitySettings.EXTRA_LAUNCHED_FROM_SUW);
91         }
92         use(MagnificationGesturesPreferenceController.class)
93                 .setIsFromSUW(mLaunchedFromSuw);
94         use(MagnificationNavbarPreferenceController.class)
95                 .setIsFromSUW(mLaunchedFromSuw);
96     }
97 
98     @Override
onPreferenceTreeClick(Preference preference)99     public boolean onPreferenceTreeClick(Preference preference) {
100         if (mLaunchedFromSuw) {
101             // If invoked from SUW, redirect to fragment instrumented for Vision Settings metrics
102             preference.setFragment(
103                     ToggleScreenMagnificationPreferenceFragmentForSetupWizard.class.getName());
104             Bundle args = preference.getExtras();
105             // Copy from AccessibilitySettingsForSetupWizardActivity, hide search and help menu
106             args.putInt(HelpResourceProvider.HELP_URI_RESOURCE_KEY, 0);
107             args.putBoolean(SearchMenuController.NEED_SEARCH_ICON_IN_ACTION_BAR, false);
108         }
109         return super.onPreferenceTreeClick(preference);
110     }
111 
getConfigurationWarningStringForSecureSettingsKey(String key, Context context)112     static CharSequence getConfigurationWarningStringForSecureSettingsKey(String key,
113             Context context) {
114         if (!Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED.equals(key)) {
115             return null;
116         }
117         if (Settings.Secure.getInt(context.getContentResolver(),
118                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 0) {
119             return null;
120         }
121         final AccessibilityManager am = (AccessibilityManager) context.getSystemService(
122                 Context.ACCESSIBILITY_SERVICE);
123         final String assignedId = Settings.Secure.getString(context.getContentResolver(),
124                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
125         if (!TextUtils.isEmpty(assignedId) && !MAGNIFICATION_COMPONENT_ID.equals(assignedId)) {
126             final ComponentName assignedComponentName = ComponentName.unflattenFromString(
127                     assignedId);
128             final List<AccessibilityServiceInfo> activeServices =
129                     am.getEnabledAccessibilityServiceList(
130                             AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
131             final int serviceCount = activeServices.size();
132             for (int i = 0; i < serviceCount; i++) {
133                 final AccessibilityServiceInfo info = activeServices.get(i);
134                 if (info.getComponentName().equals(assignedComponentName)) {
135                     final CharSequence assignedServiceName = info.getResolveInfo().loadLabel(
136                             context.getPackageManager());
137                     final int messageId = isGestureNavigateEnabled(context)
138                             ? R.string.accessibility_screen_magnification_gesture_navigation_warning
139                             : R.string.accessibility_screen_magnification_navbar_configuration_warning;
140                     return context.getString(messageId, assignedServiceName);
141                 }
142             }
143         }
144         return null;
145     }
146 
isChecked(ContentResolver contentResolver, String settingsKey)147     static boolean isChecked(ContentResolver contentResolver, String settingsKey) {
148         return Settings.Secure.getInt(contentResolver, settingsKey, OFF) == ON;
149     }
150 
setChecked(ContentResolver contentResolver, String settingsKey, boolean isChecked)151     static boolean setChecked(ContentResolver contentResolver, String settingsKey,
152             boolean isChecked) {
153         return Settings.Secure.putInt(contentResolver, settingsKey, isChecked ? ON : OFF);
154     }
155 
156     /**
157      * @return {@code true} if this fragment should be shown, {@code false} otherwise. This
158      * fragment is shown in the case that more than one magnification mode is available.
159      */
isApplicable(Resources res)160     static boolean isApplicable(Resources res) {
161         return res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
162     }
163 
isGestureNavigateEnabled(Context context)164     private static boolean isGestureNavigateEnabled(Context context) {
165         return context.getResources().getInteger(
166                 com.android.internal.R.integer.config_navBarInteractionMode)
167                 == NAV_BAR_MODE_GESTURAL;
168     }
169 
170     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
171             new BaseSearchIndexProvider() {
172                 @Override
173                 public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
174                         boolean enabled) {
175                     final SearchIndexableResource sir = new SearchIndexableResource(context);
176                     sir.xmlResId = R.xml.accessibility_magnification_settings;
177                     return Arrays.asList(sir);
178                 }
179 
180                 @Override
181                 protected boolean isPageSearchEnabled(Context context) {
182                     return isApplicable(context.getResources());
183                 }
184             };
185 }