OSDN Git Service

Use default encryption password if an accessibility service is enabled.
[android-x86/packages-apps-Settings.git] / src / com / android / settings / accessibility / ToggleAccessibilityServicePreferenceFragment.java
1 /*
2  * Copyright (C) 2013 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 android.accessibilityservice.AccessibilityServiceInfo;
20 import android.app.AlertDialog;
21 import android.app.Dialog;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.content.Intent;
26 import android.content.pm.ResolveInfo;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.provider.Settings;
31 import android.text.TextUtils;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.accessibility.AccessibilityManager;
35 import android.widget.ImageView;
36 import android.widget.LinearLayout;
37 import android.widget.TextView;
38
39 import com.android.internal.widget.LockPatternUtils;
40 import com.android.settings.R;
41 import com.android.settings.widget.ToggleSwitch;
42 import com.android.settings.widget.ToggleSwitch.OnBeforeCheckedChangeListener;
43
44 import java.util.Collections;
45 import java.util.HashSet;
46 import java.util.List;
47 import java.util.Set;
48
49 public class ToggleAccessibilityServicePreferenceFragment
50         extends ToggleFeaturePreferenceFragment implements DialogInterface.OnClickListener {
51
52     private static final int DIALOG_ID_ENABLE_WARNING = 1;
53     private static final int DIALOG_ID_DISABLE_WARNING = 2;
54
55     private final SettingsContentObserver mSettingsContentObserver =
56             new SettingsContentObserver(new Handler()) {
57             @Override
58                 public void onChange(boolean selfChange, Uri uri) {
59                     String settingValue = Settings.Secure.getString(getContentResolver(),
60                             Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
61                     final boolean enabled = settingValue.contains(mComponentName.flattenToString());
62                     mSwitchBar.setCheckedInternal(enabled);
63                 }
64             };
65
66     private ComponentName mComponentName;
67
68     private int mShownDialogId;
69
70     @Override
71     public void onResume() {
72         mSettingsContentObserver.register(getContentResolver());
73         super.onResume();
74     }
75
76     @Override
77     public void onPause() {
78         mSettingsContentObserver.unregister(getContentResolver());
79         super.onPause();
80     }
81
82     @Override
83     public void onPreferenceToggled(String preferenceKey, boolean enabled) {
84         // Parse the enabled services.
85         Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(
86                 getActivity());
87
88         if (enabledServices == (Set<?>) Collections.emptySet()) {
89             enabledServices = new HashSet<ComponentName>();
90         }
91
92         // Determine enabled services and accessibility state.
93         ComponentName toggledService = ComponentName.unflattenFromString(preferenceKey);
94         boolean accessibilityEnabled = false;
95         if (enabled) {
96             enabledServices.add(toggledService);
97             // Enabling at least one service enables accessibility.
98             accessibilityEnabled = true;
99         } else {
100             enabledServices.remove(toggledService);
101             // Check how many enabled and installed services are present.
102             Set<ComponentName> installedServices = AccessibilitySettings.sInstalledServices;
103             for (ComponentName enabledService : enabledServices) {
104                 if (installedServices.contains(enabledService)) {
105                     // Disabling the last service disables accessibility.
106                     accessibilityEnabled = true;
107                     break;
108                 }
109             }
110         }
111
112         // Update the enabled services setting.
113         StringBuilder enabledServicesBuilder = new StringBuilder();
114         // Keep the enabled services even if they are not installed since we
115         // have no way to know whether the application restore process has
116         // completed. In general the system should be responsible for the
117         // clean up not settings.
118         for (ComponentName enabledService : enabledServices) {
119             enabledServicesBuilder.append(enabledService.flattenToString());
120             enabledServicesBuilder.append(
121                     AccessibilitySettings.ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
122         }
123         final int enabledServicesBuilderLength = enabledServicesBuilder.length();
124         if (enabledServicesBuilderLength > 0) {
125             enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
126         }
127         Settings.Secure.putString(getContentResolver(),
128                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
129                 enabledServicesBuilder.toString());
130
131         // Update accessibility enabled.
132         Settings.Secure.putInt(getContentResolver(),
133                 Settings.Secure.ACCESSIBILITY_ENABLED, accessibilityEnabled ? 1 : 0);
134     }
135
136     // IMPORTANT: Refresh the info since there are dynamically changing
137     // capabilities. For
138     // example, before JellyBean MR2 the user was granting the explore by touch
139     // one.
140     private AccessibilityServiceInfo getAccessibilityServiceInfo() {
141         List<AccessibilityServiceInfo> serviceInfos = AccessibilityManager.getInstance(
142                 getActivity()).getInstalledAccessibilityServiceList();
143         final int serviceInfoCount = serviceInfos.size();
144         for (int i = 0; i < serviceInfoCount; i++) {
145             AccessibilityServiceInfo serviceInfo = serviceInfos.get(i);
146             ResolveInfo resolveInfo = serviceInfo.getResolveInfo();
147             if (mComponentName.getPackageName().equals(resolveInfo.serviceInfo.packageName)
148                     && mComponentName.getClassName().equals(resolveInfo.serviceInfo.name)) {
149                 return serviceInfo;
150             }
151         }
152         return null;
153     }
154
155     @Override
156     public Dialog onCreateDialog(int dialogId) {
157         switch (dialogId) {
158             case DIALOG_ID_ENABLE_WARNING: {
159                 mShownDialogId = DIALOG_ID_ENABLE_WARNING;
160                 AccessibilityServiceInfo info = getAccessibilityServiceInfo();
161                 if (info == null) {
162                     return null;
163                 }
164                 AlertDialog ad = new AlertDialog.Builder(getActivity())
165                         .setTitle(getString(R.string.enable_service_title,
166                                 info.getResolveInfo().loadLabel(getPackageManager())))
167                         .setView(createEnableDialogContentView(info))
168                         .setCancelable(true)
169                         .setPositiveButton(android.R.string.ok, this)
170                         .setNegativeButton(android.R.string.cancel, this)
171                         .create();
172                 ad.create();
173                 ad.getButton(AlertDialog.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
174                 return ad;
175             }
176             case DIALOG_ID_DISABLE_WARNING: {
177                 mShownDialogId = DIALOG_ID_DISABLE_WARNING;
178                 AccessibilityServiceInfo info = getAccessibilityServiceInfo();
179                 if (info == null) {
180                     return null;
181                 }
182                 return new AlertDialog.Builder(getActivity())
183                         .setTitle(getString(R.string.disable_service_title,
184                                 info.getResolveInfo().loadLabel(getPackageManager())))
185                         .setMessage(getString(R.string.disable_service_message,
186                                 info.getResolveInfo().loadLabel(getPackageManager())))
187                         .setCancelable(true)
188                         .setPositiveButton(android.R.string.ok, this)
189                         .setNegativeButton(android.R.string.cancel, this)
190                         .create();
191             }
192             default: {
193                 throw new IllegalArgumentException();
194             }
195         }
196     }
197
198     private View createEnableDialogContentView(AccessibilityServiceInfo info) {
199         LayoutInflater inflater = (LayoutInflater) getSystemService(
200                 Context.LAYOUT_INFLATER_SERVICE);
201
202         View content = inflater.inflate(R.layout.enable_accessibility_service_dialog_content,
203                 null);
204
205         TextView encryptionWarningView = (TextView) content.findViewById(
206                 R.id.encryption_warning);
207         if (LockPatternUtils.isDeviceEncrypted()) {
208             String text = getString(R.string.enable_service_encryption_warning,
209                     info.getResolveInfo().loadLabel(getPackageManager()));
210             encryptionWarningView.setText(text);
211             encryptionWarningView.setVisibility(View.VISIBLE);
212         } else {
213             encryptionWarningView.setVisibility(View.GONE);
214         }
215
216         TextView capabilitiesHeaderView = (TextView) content.findViewById(
217                 R.id.capabilities_header);
218         capabilitiesHeaderView.setText(getString(R.string.capabilities_list_title,
219                 info.getResolveInfo().loadLabel(getPackageManager())));
220
221         LinearLayout capabilitiesView = (LinearLayout) content.findViewById(R.id.capabilities);
222
223         // This capability is implicit for all services.
224         View capabilityView = inflater.inflate(
225                 com.android.internal.R.layout.app_permission_item_old, null);
226
227         ImageView imageView = (ImageView) capabilityView.findViewById(
228                 com.android.internal.R.id.perm_icon);
229         imageView.setImageDrawable(getResources().getDrawable(
230                 com.android.internal.R.drawable.ic_text_dot));
231
232         TextView labelView = (TextView) capabilityView.findViewById(
233                 com.android.internal.R.id.permission_group);
234         labelView.setText(getString(R.string.capability_title_receiveAccessibilityEvents));
235
236         TextView descriptionView = (TextView) capabilityView.findViewById(
237                 com.android.internal.R.id.permission_list);
238         descriptionView.setText(getString(R.string.capability_desc_receiveAccessibilityEvents));
239
240         List<AccessibilityServiceInfo.CapabilityInfo> capabilities =
241                 info.getCapabilityInfos();
242
243         capabilitiesView.addView(capabilityView);
244
245         // Service specific capabilities.
246         final int capabilityCount = capabilities.size();
247         for (int i = 0; i < capabilityCount; i++) {
248             AccessibilityServiceInfo.CapabilityInfo capability = capabilities.get(i);
249
250             capabilityView = inflater.inflate(
251                     com.android.internal.R.layout.app_permission_item_old, null);
252
253             imageView = (ImageView) capabilityView.findViewById(
254                     com.android.internal.R.id.perm_icon);
255             imageView.setImageDrawable(getResources().getDrawable(
256                     com.android.internal.R.drawable.ic_text_dot));
257
258             labelView = (TextView) capabilityView.findViewById(
259                     com.android.internal.R.id.permission_group);
260             labelView.setText(getString(capability.titleResId));
261
262             descriptionView = (TextView) capabilityView.findViewById(
263                     com.android.internal.R.id.permission_list);
264             descriptionView.setText(getString(capability.descResId));
265
266             capabilitiesView.addView(capabilityView);
267         }
268
269         return content;
270     }
271
272     @Override
273     public void onClick(DialogInterface dialog, int which) {
274         final boolean checked;
275         switch (which) {
276             case DialogInterface.BUTTON_POSITIVE:
277                 checked = (mShownDialogId == DIALOG_ID_ENABLE_WARNING);
278                 mSwitchBar.setCheckedInternal(checked);
279                 getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, checked);
280                 onPreferenceToggled(mPreferenceKey, checked);
281                 break;
282             case DialogInterface.BUTTON_NEGATIVE:
283                 checked = (mShownDialogId == DIALOG_ID_DISABLE_WARNING);
284                 mSwitchBar.setCheckedInternal(checked);
285                 getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, checked);
286                 onPreferenceToggled(mPreferenceKey, checked);
287                 break;
288             default:
289                 throw new IllegalArgumentException();
290         }
291     }
292
293     @Override
294     protected void onInstallSwitchBarToggleSwitch() {
295         super.onInstallSwitchBarToggleSwitch();
296         mToggleSwitch.setOnBeforeCheckedChangeListener(new OnBeforeCheckedChangeListener() {
297                 @Override
298             public boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked) {
299                 if (checked) {
300                     mSwitchBar.setCheckedInternal(false);
301                     getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, false);
302                     showDialog(DIALOG_ID_ENABLE_WARNING);
303                 } else {
304                     mSwitchBar.setCheckedInternal(true);
305                     getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, true);
306                     showDialog(DIALOG_ID_DISABLE_WARNING);
307                 }
308                 return true;
309             }
310         });
311     }
312
313     @Override
314     protected void onProcessArguments(Bundle arguments) {
315         super.onProcessArguments(arguments);
316         // Settings title and intent.
317         String settingsTitle = arguments.getString(AccessibilitySettings.EXTRA_SETTINGS_TITLE);
318         String settingsComponentName = arguments.getString(
319                 AccessibilitySettings.EXTRA_SETTINGS_COMPONENT_NAME);
320         if (!TextUtils.isEmpty(settingsTitle) && !TextUtils.isEmpty(settingsComponentName)) {
321             Intent settingsIntent = new Intent(Intent.ACTION_MAIN).setComponent(
322                     ComponentName.unflattenFromString(settingsComponentName.toString()));
323             if (!getPackageManager().queryIntentActivities(settingsIntent, 0).isEmpty()) {
324                 mSettingsTitle = settingsTitle;
325                 mSettingsIntent = settingsIntent;
326                 setHasOptionsMenu(true);
327             }
328         }
329
330         mComponentName = arguments.getParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME);
331     }
332 }