diff --git a/res/values/strings.xml b/res/values/strings.xml index 6bd1d29c615..4ac217f2f3d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3986,12 +3986,6 @@ To zoom, quickly tap the screen 3 times with one finger.\n\n\nTo zoom temporarily, quickly tap the screen 3 times and hold down your finger on the third tap.\n\n\nYou can\'t zoom in on the keyboard and navigation bar. Accessibility shortcut - - On - - Off - - When this feature is turned on, you can quickly activate accessibility features in two steps:\n\nStep 1: Press and hold the power button until you hear a sound or feel a vibration.\n\nStep 2: Touch and hold two fingers until you hear audio confirmation.\n\nIf the device has multiple users, using this shortcut on the lock screen temporarily enables accessibility until the device is unlocked. High contrast text diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml index 8bb54c6251a..fb9e171d76d 100644 --- a/res/xml/accessibility_settings.xml +++ b/res/xml/accessibility_settings.xml @@ -81,9 +81,8 @@ android:summary="@string/accessibility_toggle_master_mono_summary" android:persistent="false"/> - { + // Filter obscured touches by consuming them. + if ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) { + if (event.getAction() == MotionEvent.ACTION_UP) { + Toast.makeText(v.getContext(), R.string.touch_filtered_warning, + Toast.LENGTH_SHORT).show(); + } + return true; + } + return false; + }; + + ad.create(); + ad.getButton(AlertDialog.BUTTON_POSITIVE).setOnTouchListener(filterTouchListener); + return ad; + } + + /** + * Return whether the device is encrypted with legacy full disk encryption. Newer devices + * should be using File Based Encryption. + * + * @return true if device is encrypted + */ + private static boolean isFullDiskEncrypted() { + return StorageManager.isNonDefaultBlockEncrypted(); + } + + private static View createEnableDialogContentView(Activity parentActivity, + AccessibilityServiceInfo info) { + LayoutInflater inflater = (LayoutInflater) parentActivity.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + + View content = inflater.inflate(R.layout.enable_accessibility_service_dialog_content, + null); + + TextView encryptionWarningView = (TextView) content.findViewById( + R.id.encryption_warning); + if (isFullDiskEncrypted()) { + String text = parentActivity.getString(R.string.enable_service_encryption_warning, + info.getResolveInfo().loadLabel(parentActivity.getPackageManager())); + encryptionWarningView.setText(text); + encryptionWarningView.setVisibility(View.VISIBLE); + } else { + encryptionWarningView.setVisibility(View.GONE); + } + + TextView capabilitiesHeaderView = (TextView) content.findViewById( + R.id.capabilities_header); + capabilitiesHeaderView.setText(parentActivity.getString(R.string.capabilities_list_title, + info.getResolveInfo().loadLabel(parentActivity.getPackageManager()))); + + LinearLayout capabilitiesView = (LinearLayout) content.findViewById(R.id.capabilities); + + // This capability is implicit for all services. + View capabilityView = inflater.inflate( + com.android.internal.R.layout.app_permission_item_old, null); + + ImageView imageView = (ImageView) capabilityView.findViewById( + com.android.internal.R.id.perm_icon); + imageView.setImageDrawable(parentActivity.getDrawable( + com.android.internal.R.drawable.ic_text_dot)); + + TextView labelView = (TextView) capabilityView.findViewById( + com.android.internal.R.id.permission_group); + labelView.setText(parentActivity.getString( + R.string.capability_title_receiveAccessibilityEvents)); + + TextView descriptionView = (TextView) capabilityView.findViewById( + com.android.internal.R.id.permission_list); + descriptionView.setText( + parentActivity.getString(R.string.capability_desc_receiveAccessibilityEvents)); + + List capabilities = + info.getCapabilityInfos(); + + capabilitiesView.addView(capabilityView); + + // Service-specific capabilities. + final int capabilityCount = capabilities.size(); + for (int i = 0; i < capabilityCount; i++) { + AccessibilityServiceInfo.CapabilityInfo capability = capabilities.get(i); + + capabilityView = inflater.inflate( + com.android.internal.R.layout.app_permission_item_old, null); + + imageView = (ImageView) capabilityView.findViewById( + com.android.internal.R.id.perm_icon); + imageView.setImageDrawable(parentActivity.getDrawable( + com.android.internal.R.drawable.ic_text_dot)); + + labelView = (TextView) capabilityView.findViewById( + com.android.internal.R.id.permission_group); + labelView.setText(parentActivity.getString(capability.titleResId)); + + descriptionView = (TextView) capabilityView.findViewById( + com.android.internal.R.id.permission_list); + descriptionView.setText(parentActivity.getString(capability.descResId)); + + capabilitiesView.addView(capabilityView); + } + + return content; + } +} diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index af5b5cdfe13..dc57bddc6ac 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -17,9 +17,11 @@ package com.android.settings.accessibility; import android.accessibilityservice.AccessibilityServiceInfo; +import android.app.Dialog; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.content.res.Resources; @@ -87,8 +89,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements "toggle_master_mono"; private static final String SELECT_LONG_PRESS_TIMEOUT_PREFERENCE = "select_long_press_timeout_preference"; - private static final String ENABLE_ACCESSIBILITY_GESTURE_PREFERENCE_SCREEN = - "enable_global_gesture_preference_screen"; + private static final String ACCESSIBILITY_SHORTCUT_PREFERENCE = + "accessibility_shortcut_preference"; private static final String CAPTIONING_PREFERENCE_SCREEN = "captioning_preference_screen"; private static final String DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN = @@ -115,6 +117,9 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements // presentation. private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000; + // ID for dialog that confirms shortcut capabilities + private static final int DIALOG_ID_ADD_SHORTCUT_WARNING = 1; + // Auxiliary members. static final Set sInstalledServices = new HashSet<>(); @@ -188,7 +193,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements private Preference mDisplayMagnificationPreferenceScreen; private Preference mFontSizePreferenceScreen; private Preference mAutoclickPreferenceScreen; - private Preference mGlobalGesturePreferenceScreen; + private ListPreference mAccessibilityShortcutPreference; private Preference mDisplayDaltonizerPreferenceScreen; private SwitchPreference mToggleInversionPreference; @@ -247,6 +252,9 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements } else if (mToggleInversionPreference == preference) { handleToggleInversionPreferenceChange((Boolean) newValue); return true; + } else if (mAccessibilityShortcutPreference == preference) { + handleAccessibilityShortcutPreferenceChange((String) newValue); + return true; } return false; } @@ -263,6 +271,58 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, (checked ? 1 : 0)); } + private void handleAccessibilityShortcutPreferenceChange(String serviceComponentName) { + // When assigning a service to the shortcut the user must explicitly agree to the same + // capabilities that are present if the service were being enabled. + // No need if clearing the setting or the service is already enabled. + if (TextUtils.isEmpty(serviceComponentName) + || AccessibilityUtils.getEnabledServicesFromSettings(getActivity()) + .contains(ComponentName.unflattenFromString(serviceComponentName))) { + Settings.Secure.putString(getContentResolver(), + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, serviceComponentName); + updateAccessibilityShortcut(); + return; + } + if (!serviceComponentName.equals(mAccessibilityShortcutPreference.getValue())) { + showDialog(DIALOG_ID_ADD_SHORTCUT_WARNING); + } + } + + @Override + public Dialog onCreateDialog(int dialogId) { + switch (dialogId) { + case DIALOG_ID_ADD_SHORTCUT_WARNING: { + DialogInterface.OnClickListener listener = + (DialogInterface dialogInterface, int buttonId) -> { + if (buttonId == DialogInterface.BUTTON_POSITIVE) { + Settings.Secure.putString(getContentResolver(), + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + mAccessibilityShortcutPreference.getValue()); + } + updateAccessibilityShortcut(); + }; + AccessibilityServiceInfo info = AccessibilityManager.getInstance(getActivity()) + .getInstalledServiceInfoWithComponentName( + ComponentName.unflattenFromString( + mAccessibilityShortcutPreference.getValue())); + if (info == null) { + return null; + } + return AccessibilityServiceWarning + .createCapabilitiesDialog(getActivity(), info, listener); + } + default: { + throw new IllegalArgumentException(); + } + } + } + + @Override + public int getDialogMetricsCategory(int dialogId) { + // The only dialog is the one that confirms the properties for the accessibility shortcut + return MetricsEvent.ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE; + } + @Override public boolean onPreferenceTreeClick(Preference preference) { if (mToggleHighTextContrastPreference == preference) { @@ -283,9 +343,6 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements } else if (mToggleMasterMonoPreference == preference) { handleToggleMasterMonoPreferenceClick(); return true; - } else if (mGlobalGesturePreferenceScreen == preference) { - handleToggleEnableAccessibilityGesturePreferenceClick(); - return true; } else if (mDisplayMagnificationPreferenceScreen == preference) { handleDisplayMagnificationPreferenceScreenClick(); return true; @@ -329,17 +386,6 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements mToggleMasterMonoPreference.isChecked() ? 1 : 0, UserHandle.USER_CURRENT); } - private void handleToggleEnableAccessibilityGesturePreferenceClick() { - Bundle extras = mGlobalGesturePreferenceScreen.getExtras(); - extras.putString(EXTRA_TITLE, getString( - R.string.accessibility_global_gesture_preference_title)); - extras.putString(EXTRA_SUMMARY, getString( - R.string.accessibility_global_gesture_preference_description)); - extras.putBoolean(EXTRA_CHECKED, Settings.Global.getInt(getContentResolver(), - Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1); - super.onPreferenceTreeClick(mGlobalGesturePreferenceScreen); - } - private void handleDisplayMagnificationPreferenceScreenClick() { Bundle extras = mDisplayMagnificationPreferenceScreen.getExtras(); extras.putString(EXTRA_TITLE, getString( @@ -422,18 +468,10 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements // Display color adjustments. mDisplayDaltonizerPreferenceScreen = findPreference(DISPLAY_DALTONIZER_PREFERENCE_SCREEN); - // Global gesture. - mGlobalGesturePreferenceScreen = findPreference( - ENABLE_ACCESSIBILITY_GESTURE_PREFERENCE_SCREEN); - final int longPressOnPowerBehavior = getActivity().getResources().getInteger( - com.android.internal.R.integer.config_longPressOnPowerBehavior); - final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1; - if (!KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER) - || longPressOnPowerBehavior != LONG_PRESS_POWER_GLOBAL_ACTIONS) { - // Remove accessibility shortcut if power key is not present - // nor long press power does not show global actions menu. - mSystemsCategory.removePreference(mGlobalGesturePreferenceScreen); - } + // Accessibility shortcut + mAccessibilityShortcutPreference = + (ListPreference) findPreference(ACCESSIBILITY_SHORTCUT_PREFERENCE); + mAccessibilityShortcutPreference.setOnPreferenceChangeListener(this); } private void updateAllPreferences() { @@ -598,16 +636,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements updateAutoclickSummary(mAutoclickPreferenceScreen); - // Global gesture - final boolean globalGestureEnabled = Settings.Global.getInt(getContentResolver(), - Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1; - if (globalGestureEnabled) { - mGlobalGesturePreferenceScreen.setSummary( - R.string.accessibility_global_gesture_preference_summary_on); - } else { - mGlobalGesturePreferenceScreen.setSummary( - R.string.accessibility_global_gesture_preference_summary_off); - } + updateAccessibilityShortcut(); } private void updateFeatureSummary(String prefKey, Preference pref) { @@ -656,6 +685,37 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements mToggleMasterMonoPreference.setChecked(masterMono); } + private void updateAccessibilityShortcut() { + String currentShortcutNameString = + AccessibilityUtils.getShortcutTargetServiceComponentNameString(getActivity(), + UserHandle.myUserId()); + final PackageManager pm = getPackageManager(); + final AccessibilityManager accessibilityManager = getActivity() + .getSystemService(AccessibilityManager.class); + final List installedServices = + accessibilityManager.getInstalledAccessibilityServiceList(); + final int numInstalledServices = installedServices.size(); + + CharSequence[] entries = new CharSequence[numInstalledServices + 1]; + CharSequence[] entryValues = new CharSequence[numInstalledServices + 1]; + int currentSettingIndex = numInstalledServices; + for (int i = 0; i < numInstalledServices; i++) { + AccessibilityServiceInfo installedService = installedServices.get(i); + entries[i] = installedService.getResolveInfo().loadLabel(pm); + entryValues[i] = installedService.getComponentName().flattenToShortString(); + if (installedService.getId().equals(currentShortcutNameString)) { + currentSettingIndex = i; + } + } + entries[numInstalledServices] = + getString(com.android.internal.R.string.disable_accessibility_shortcut); + entryValues[numInstalledServices] = ""; + mAccessibilityShortcutPreference.setEntryValues(entryValues); + mAccessibilityShortcutPreference.setEntries(entries); + mAccessibilityShortcutPreference.setSummary(entries[currentSettingIndex]); + mAccessibilityShortcutPreference.setValueIndex(currentSettingIndex); + } + public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider() { @Override @@ -663,8 +723,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements List indexables = new ArrayList(); PackageManager packageManager = context.getPackageManager(); - AccessibilityManager accessibilityManager = (AccessibilityManager) - context.getSystemService(Context.ACCESSIBILITY_SERVICE); + AccessibilityManager accessibilityManager = + context.getSystemService(AccessibilityManager.class); String screenTitle = context.getResources().getString( R.string.accessibility_services_title); diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index 3bd450b5c29..9c01a5f21aa 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -146,39 +146,13 @@ public class ToggleAccessibilityServicePreferenceFragment switch (dialogId) { case DIALOG_ID_ENABLE_WARNING: { mShownDialogId = DIALOG_ID_ENABLE_WARNING; - final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); if (info == null) { return null; } - final AlertDialog ad = new AlertDialog.Builder(getActivity()) - .setTitle(getString(R.string.enable_service_title, - info.getResolveInfo().loadLabel(getPackageManager()))) - .setView(createEnableDialogContentView(info)) - .setCancelable(true) - .setPositiveButton(android.R.string.ok, this) - .setNegativeButton(android.R.string.cancel, this) - .create(); - - final View.OnTouchListener filterTouchListener = new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - // Filter obscured touches by consuming them. - if ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) { - if (event.getAction() == MotionEvent.ACTION_UP) { - Toast.makeText(v.getContext(), R.string.touch_filtered_warning, - Toast.LENGTH_SHORT).show(); - } - return true; - } - return false; - } - }; - - ad.create(); - ad.getButton(AlertDialog.BUTTON_POSITIVE).setOnTouchListener(filterTouchListener); - return ad; + return AccessibilityServiceWarning + .createCapabilitiesDialog(getActivity(), info, this); } case DIALOG_ID_DISABLE_WARNING: { mShownDialogId = DIALOG_ID_DISABLE_WARNING; @@ -227,80 +201,6 @@ public class ToggleAccessibilityServicePreferenceFragment return StorageManager.isNonDefaultBlockEncrypted(); } - private View createEnableDialogContentView(AccessibilityServiceInfo info) { - LayoutInflater inflater = (LayoutInflater) getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - - View content = inflater.inflate(R.layout.enable_accessibility_service_dialog_content, - null); - - TextView encryptionWarningView = (TextView) content.findViewById( - R.id.encryption_warning); - if (isFullDiskEncrypted()) { - String text = getString(R.string.enable_service_encryption_warning, - info.getResolveInfo().loadLabel(getPackageManager())); - encryptionWarningView.setText(text); - encryptionWarningView.setVisibility(View.VISIBLE); - } else { - encryptionWarningView.setVisibility(View.GONE); - } - - TextView capabilitiesHeaderView = (TextView) content.findViewById( - R.id.capabilities_header); - capabilitiesHeaderView.setText(getString(R.string.capabilities_list_title, - info.getResolveInfo().loadLabel(getPackageManager()))); - - LinearLayout capabilitiesView = (LinearLayout) content.findViewById(R.id.capabilities); - - // This capability is implicit for all services. - View capabilityView = inflater.inflate( - com.android.internal.R.layout.app_permission_item_old, null); - - ImageView imageView = (ImageView) capabilityView.findViewById( - com.android.internal.R.id.perm_icon); - imageView.setImageDrawable(getActivity().getDrawable( - com.android.internal.R.drawable.ic_text_dot)); - - TextView labelView = (TextView) capabilityView.findViewById( - com.android.internal.R.id.permission_group); - labelView.setText(getString(R.string.capability_title_receiveAccessibilityEvents)); - - TextView descriptionView = (TextView) capabilityView.findViewById( - com.android.internal.R.id.permission_list); - descriptionView.setText(getString(R.string.capability_desc_receiveAccessibilityEvents)); - - List capabilities = - info.getCapabilityInfos(); - - capabilitiesView.addView(capabilityView); - - // Service specific capabilities. - final int capabilityCount = capabilities.size(); - for (int i = 0; i < capabilityCount; i++) { - AccessibilityServiceInfo.CapabilityInfo capability = capabilities.get(i); - - capabilityView = inflater.inflate( - com.android.internal.R.layout.app_permission_item_old, null); - - imageView = (ImageView) capabilityView.findViewById( - com.android.internal.R.id.perm_icon); - imageView.setImageDrawable(getActivity().getDrawable( - com.android.internal.R.drawable.ic_text_dot)); - - labelView = (TextView) capabilityView.findViewById( - com.android.internal.R.id.permission_group); - labelView.setText(getString(capability.titleResId)); - - descriptionView = (TextView) capabilityView.findViewById( - com.android.internal.R.id.permission_list); - descriptionView.setText(getString(capability.descResId)); - - capabilitiesView.addView(capabilityView); - } - - return content; - } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION) { diff --git a/src/com/android/settings/accessibility/ToggleGlobalGesturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleGlobalGesturePreferenceFragment.java deleted file mode 100644 index 5d95d7e0ed2..00000000000 --- a/src/com/android/settings/accessibility/ToggleGlobalGesturePreferenceFragment.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.accessibility; - -import android.provider.Settings; - -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settings.widget.ToggleSwitch; -import com.android.settings.widget.ToggleSwitch.OnBeforeCheckedChangeListener; - -public class ToggleGlobalGesturePreferenceFragment - extends ToggleFeaturePreferenceFragment { - @Override - protected void onPreferenceToggled(String preferenceKey, boolean enabled) { - Settings.Global.putInt(getContentResolver(), - Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, enabled ? 1 : 0); - } - - @Override - protected void onInstallSwitchBarToggleSwitch() { - super.onInstallSwitchBarToggleSwitch(); - mToggleSwitch.setOnBeforeCheckedChangeListener(new OnBeforeCheckedChangeListener() { - @Override - public boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked) { - mSwitchBar.setCheckedInternal(checked); - getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, checked); - onPreferenceToggled(mPreferenceKey, checked); - return false; - } - }); - } - - @Override - public int getMetricsCategory() { - return MetricsEvent.ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE; - } -}