From 7efe0b9bd1515d1618fa6728b467f4dfa9b82712 Mon Sep 17 00:00:00 2001 From: ryanlwlin Date: Tue, 29 Mar 2022 11:38:23 +0800 Subject: [PATCH] Disable volume-key-accessibility shortcut upon exiting setup wizard The volume-key-accessibility shortcut is often triggered accidentally, which causes much frustration for users who are not familiar with it, and do not know how to revert. To help prevent this from happening, we will set the shortcut targets to empty if setup has been completed without the enabled AccessibilityService associated with the default shortcut. Bug: 226866175 Test: manual test: 1. activate shortcut during setup wizard then finish setup wizard, observe settings value of shortcut targets. It should not be empty. 2. Finish setup wizard then observe esttings value of shortcut targets. It should be empty. 3. Create a new user and run step 2 and 3 separately. Change-Id: Iaf99b850043f7606d6b183f0b4822c36287fa106 --- .../AccessibilityShortcutController.java | 116 ++++++++++++++++-- 1 file changed, 106 insertions(+), 10 deletions(-) diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index 40ca9fb22d098..43be0312245e8 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -16,6 +16,7 @@ package com.android.internal.accessibility; +import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; @@ -107,6 +108,8 @@ public class AccessibilityShortcutController { private final Context mContext; private final Handler mHandler; + private final UserSetupCompleteObserver mUserSetupCompleteObserver; + private AlertDialog mAlertDialog; private boolean mIsShortcutEnabled; private boolean mEnabledOnLockScreen; @@ -114,11 +117,11 @@ public class AccessibilityShortcutController { @Retention(RetentionPolicy.SOURCE) @IntDef({ - DialogStaus.NOT_SHOWN, - DialogStaus.SHOWN, + DialogStatus.NOT_SHOWN, + DialogStatus.SHOWN, }) /** Denotes the user shortcut type. */ - private @interface DialogStaus { + private @interface DialogStatus { int NOT_SHOWN = 0; int SHOWN = 1; } @@ -163,6 +166,7 @@ public class AccessibilityShortcutController { mContext = context; mHandler = handler; mUserId = initialUserId; + mUserSetupCompleteObserver = new UserSetupCompleteObserver(handler, initialUserId); // Keep track of state of shortcut settings final ContentObserver co = new ContentObserver(handler) { @@ -188,6 +192,7 @@ public class AccessibilityShortcutController { public void setCurrentUser(int currentUserId) { mUserId = currentUserId; onSettingsChanged(); + mUserSetupCompleteObserver.onUserSwitched(currentUserId); } /** @@ -206,7 +211,7 @@ public class AccessibilityShortcutController { final ContentResolver cr = mContext.getContentResolver(); // Enable the shortcut from the lockscreen by default if the dialog has been shown final int dialogAlreadyShown = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, DialogStaus.NOT_SHOWN, + cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, DialogStatus.NOT_SHOWN, mUserId); mEnabledOnLockScreen = Settings.Secure.getIntForUser( cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, @@ -222,7 +227,7 @@ public class AccessibilityShortcutController { final ContentResolver cr = mContext.getContentResolver(); final int userId = ActivityManager.getCurrentUser(); final int dialogAlreadyShown = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, DialogStaus.NOT_SHOWN, + cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, DialogStatus.NOT_SHOWN, userId); // Play a notification vibration Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); @@ -234,7 +239,7 @@ public class AccessibilityShortcutController { vibrator.vibrate(vibePattern, -1, VIBRATION_ATTRIBUTES); } - if (dialogAlreadyShown == 0) { + if (dialogAlreadyShown == DialogStatus.NOT_SHOWN) { // The first time, we show a warning rather than toggle the service to give the user a // chance to turn off this feature before stuff gets enabled. mAlertDialog = createShortcutWarningDialog(userId); @@ -250,7 +255,7 @@ public class AccessibilityShortcutController { w.setAttributes(attr); mAlertDialog.show(); Settings.Secure.putIntForUser( - cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, DialogStaus.SHOWN, + cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, DialogStatus.SHOWN, userId); } else { playNotificationTone(); @@ -320,13 +325,13 @@ public class AccessibilityShortcutController { // If canceled, treat as if the dialog has never been shown Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, - DialogStaus.NOT_SHOWN, userId); + DialogStatus.NOT_SHOWN, userId); }) .setOnCancelListener((DialogInterface d) -> { // If canceled, treat as if the dialog has never been shown Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, - DialogStaus.NOT_SHOWN, userId); + DialogStatus.NOT_SHOWN, userId); }) .create(); return alertDialog; @@ -398,7 +403,7 @@ public class AccessibilityShortcutController { AccessibilityManager accessibilityManager = mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext); return accessibilityManager.getEnabledAccessibilityServiceList( - AccessibilityServiceInfo.FEEDBACK_ALL_MASK).contains(serviceInfo); + FEEDBACK_ALL_MASK).contains(serviceInfo); } private boolean hasFeatureLeanback() { @@ -559,6 +564,97 @@ public class AccessibilityShortcutController { } } + private class UserSetupCompleteObserver extends ContentObserver { + + private boolean mIsRegistered = false; + private int mUserId; + + /** + * Creates a content observer. + * + * @param handler The handler to run {@link #onChange} on, or null if none. + * @param userId The current user id. + */ + UserSetupCompleteObserver(Handler handler, int userId) { + super(handler); + mUserId = userId; + if (!isUserSetupComplete()) { + registerObserver(); + } + } + + private boolean isUserSetupComplete() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, 0, mUserId) == 1; + } + + private void registerObserver() { + if (mIsRegistered) { + return; + } + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), + false, this, mUserId); + mIsRegistered = true; + } + + @Override + public void onChange(boolean selfChange) { + if (isUserSetupComplete()) { + unregisterObserver(); + setEmptyShortcutTargetIfNeeded(); + } + } + + private void unregisterObserver() { + if (!mIsRegistered) { + return; + } + mContext.getContentResolver().unregisterContentObserver(this); + mIsRegistered = false; + } + + /** + * Sets empty shortcut target if shortcut targets is not assigned and there is no any + * enabled service matching the default target after the setup wizard completed. + * + */ + private void setEmptyShortcutTargetIfNeeded() { + final ContentResolver contentResolver = mContext.getContentResolver(); + + final String shortcutTargets = Settings.Secure.getStringForUser(contentResolver, + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, mUserId); + if (shortcutTargets != null) { + return; + } + + final String defaultShortcutTarget = mContext.getString( + R.string.config_defaultAccessibilityService); + final List enabledServices = + mFrameworkObjectProvider.getAccessibilityManagerInstance( + mContext).getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK); + for (int i = enabledServices.size() - 1; i >= 0; i--) { + if (TextUtils.equals(defaultShortcutTarget, enabledServices.get(i).getId())) { + return; + } + } + + Settings.Secure.putStringForUser(contentResolver, + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "", mUserId); + } + + void onUserSwitched(int userId) { + if (mUserId == userId) { + return; + } + unregisterObserver(); + mUserId = userId; + if (!isUserSetupComplete()) { + registerObserver(); + } + } + } + /** * Immutable class to hold info about framework features that can be controlled by shortcut */