From 1e20bc41476a19f5ef4cf7e816ef50078aba0972 Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Thu, 5 Mar 2020 20:18:16 +0800 Subject: [PATCH 1/3] Update the edit shortcut menu (2/n). Add checkbox into list item and change the beavior. Bug: 148989057 Test: manual test Change-Id: I7782898582c2f9759d4ef466d2864f124d8c83be --- .../common/ShortcutConstants.java | 15 ++ .../accessibility/util/ShortcutUtils.java | 27 ++ .../AccessibilityButtonChooserActivity.java | 244 +++++++++++------- .../accessibility_button_chooser_item.xml | 40 ++- core/res/res/values/strings.xml | 4 +- core/res/res/values/symbols.xml | 5 +- 6 files changed, 215 insertions(+), 120 deletions(-) diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java index 1daa5052a5657..72c30f9740607 100644 --- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java +++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java @@ -79,6 +79,21 @@ public final class ShortcutConstants { int BOUNCE = 3; } + /** + * Annotation for different shortcut target. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + TargetType.ACCESSIBILITY_SERVICE, + TargetType.ACCESSIBILITY_ACTIVITY, + TargetType.WHITE_LISTING, + }) + public @interface TargetType { + int ACCESSIBILITY_SERVICE = 0; + int ACCESSIBILITY_ACTIVITY = 1; + int WHITE_LISTING = 2; + } + /** * Annotation for different shortcut menu mode. * diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java index 7df712fbcf7bf..717e78078b1ca 100644 --- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java +++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java @@ -38,6 +38,33 @@ public final class ShortcutUtils { private static final TextUtils.SimpleStringSplitter sStringColonSplitter = new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR); + /** + * Opts in component name into colon-separated {@link UserShortcutType} + * key's string in Settings. + * + * @param context The current context. + * @param shortcutType The preferred shortcut type user selected. + * @param componentId The component id that need to be opted out from Settings. + */ + public static void optInValueToSettings(Context context, @UserShortcutType int shortcutType, + String componentId) { + final StringJoiner joiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR)); + final String targetKey = convertToKey(shortcutType); + final String targetString = Settings.Secure.getString(context.getContentResolver(), + targetKey); + + if (hasValueInSettings(context, shortcutType, componentId)) { + return; + } + + if (!TextUtils.isEmpty(targetString)) { + joiner.add(targetString); + } + joiner.add(componentId); + + Settings.Secure.putString(context.getContentResolver(), targetKey, joiner.toString()); + } + /** * Opts out component name into colon-separated {@code shortcutType} key's string in Settings. * diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java index 852deb208ea61..8672175005b4c 100644 --- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java @@ -26,6 +26,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.Access import static com.android.internal.accessibility.common.ShortcutConstants.DISABLED_ALPHA; import static com.android.internal.accessibility.common.ShortcutConstants.ENABLED_ALPHA; import static com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode; +import static com.android.internal.accessibility.common.ShortcutConstants.TargetType; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.COMPONENT_ID; import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.FRAGMENT_TYPE; @@ -36,6 +37,7 @@ import static com.android.internal.accessibility.util.AccessibilityUtils.getAcce import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState; import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType; import static com.android.internal.accessibility.util.ShortcutUtils.hasValuesInSettings; +import static com.android.internal.accessibility.util.ShortcutUtils.optInValueToSettings; import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings; import static com.android.internal.util.Preconditions.checkArgument; @@ -62,7 +64,7 @@ import android.view.Window; import android.view.accessibility.AccessibilityManager; import android.widget.AdapterView; import android.widget.BaseAdapter; -import android.widget.FrameLayout; +import android.widget.CheckBox; import android.widget.ImageView; import android.widget.Switch; import android.widget.TextView; @@ -78,12 +80,13 @@ import java.util.List; */ public class AccessibilityButtonChooserActivity extends Activity { @ShortcutType - private int mShortcutType; + private static int sShortcutType; @UserShortcutType private int mShortcutUserType; private final List mTargets = new ArrayList<>(); private AlertDialog mAlertDialog; private TargetAdapter mTargetAdapter; + private AccessibilityButtonTarget mCurrentCheckedTarget; private static final String[][] WHITE_LISTING_FEATURES = { { @@ -118,19 +121,19 @@ public class AccessibilityButtonChooserActivity extends Activity { requestWindowFeature(Window.FEATURE_NO_TITLE); } - mShortcutType = getIntent().getIntExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE, + sShortcutType = getIntent().getIntExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE, /* unexpectedShortcutType */ -1); - final boolean existInShortcutType = (mShortcutType == ACCESSIBILITY_BUTTON) - || (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY); - checkArgument(existInShortcutType, "Unexpected shortcut type: " + mShortcutType); + final boolean existInShortcutType = (sShortcutType == ACCESSIBILITY_BUTTON) + || (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY); + checkArgument(existInShortcutType, "Unexpected shortcut type: " + sShortcutType); - mShortcutUserType = convertToUserType(mShortcutType); + mShortcutUserType = convertToUserType(sShortcutType); - mTargets.addAll(getServiceTargets(this, mShortcutType)); + mTargets.addAll(getServiceTargets(this, sShortcutType)); final String selectDialogTitle = getString(R.string.accessibility_select_shortcut_menu_title); - mTargetAdapter = new TargetAdapter(mTargets, mShortcutType); + mTargetAdapter = new TargetAdapter(mTargets, sShortcutType); mAlertDialog = new AlertDialog.Builder(this) .setTitle(selectDialogTitle) .setAdapter(mTargetAdapter, /* listener= */ null) @@ -151,15 +154,21 @@ public class AccessibilityButtonChooserActivity extends Activity { private static List getServiceTargets(@NonNull Context context, @ShortcutType int shortcutType) { + final List targets = getInstalledServiceTargets(context); + final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); + final List requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType); + targets.removeIf(target -> !requiredTargets.contains(target.getId())); + + return targets; + } + + private static List getInstalledServiceTargets( + @NonNull Context context) { final List targets = new ArrayList<>(); targets.addAll(getAccessibilityServiceTargets(context)); targets.addAll(getAccessibilityActivityTargets(context)); targets.addAll(getWhiteListingServiceTargets(context)); - final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); - final List requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType); - targets.removeIf(target -> !requiredTargets.contains(target.getId())); - return targets; } @@ -249,21 +258,21 @@ public class AccessibilityButtonChooserActivity extends Activity { } } - private void disableService(String componentId) { + private void setServiceEnabled(String componentId, boolean enabled) { if (isWhiteListingService(componentId)) { - setWhiteListingServiceEnabled(componentId, /* settingsValueOff */ 0); + setWhiteListingServiceEnabled(componentId, + enabled ? /* settingsValueOn */ 1 : /* settingsValueOff */ 0); } else { final ComponentName componentName = ComponentName.unflattenFromString(componentId); - setAccessibilityServiceState(this, componentName, /* enabled= */ false); + setAccessibilityServiceState(this, componentName, enabled); } } private static class ViewHolder { View mItemView; + CheckBox mCheckBox; ImageView mIconView; TextView mLabelView; - FrameLayout mItemContainer; - ImageView mActionViewItem; Switch mSwitchItem; } @@ -314,13 +323,11 @@ public class AccessibilityButtonChooserActivity extends Activity { false); holder = new ViewHolder(); holder.mItemView = convertView; + holder.mCheckBox = convertView.findViewById( + R.id.accessibility_button_target_checkbox); holder.mIconView = convertView.findViewById(R.id.accessibility_button_target_icon); holder.mLabelView = convertView.findViewById( R.id.accessibility_button_target_label); - holder.mItemContainer = convertView.findViewById( - R.id.accessibility_button_target_item_container); - holder.mActionViewItem = convertView.findViewById( - R.id.accessibility_button_target_view_item); holder.mSwitchItem = convertView.findViewById( R.id.accessibility_button_target_switch_item); convertView.setTag(holder); @@ -329,9 +336,6 @@ public class AccessibilityButtonChooserActivity extends Activity { } final AccessibilityButtonTarget target = mButtonTargets.get(position); - holder.mIconView.setImageDrawable(target.getDrawable()); - holder.mLabelView.setText(target.getLabel()); - updateActionItem(context, holder, target); return convertView; @@ -342,24 +346,24 @@ public class AccessibilityButtonChooserActivity extends Activity { switch (target.getFragmentType()) { case AccessibilityServiceFragmentType.LEGACY: - updateLegacyActionItemVisibility(context, holder); + updateLegacyActionItemVisibility(holder, target); break; case AccessibilityServiceFragmentType.INVISIBLE: - updateInvisibleActionItemVisibility(context, holder); + updateInvisibleActionItemVisibility(holder, target); break; case AccessibilityServiceFragmentType.INTUITIVE: updateIntuitiveActionItemVisibility(context, holder, target); break; case AccessibilityServiceFragmentType.BOUNCE: - updateBounceActionItemVisibility(context, holder); + updateBounceActionItemVisibility(holder, target); break; default: throw new IllegalStateException("Unexpected fragment type"); } } - private void updateLegacyActionItemVisibility(@NonNull Context context, - @NonNull ViewHolder holder) { + private void updateLegacyActionItemVisibility(@NonNull ViewHolder holder, + AccessibilityButtonTarget target) { final boolean isLaunchMenuMode = (mShortcutMenuMode == ShortcutMenuMode.LAUNCH); final boolean isHardwareButtonTriggered = (mShortcutButtonType == ACCESSIBILITY_SHORTCUT_KEY); @@ -367,31 +371,42 @@ public class AccessibilityButtonChooserActivity extends Activity { final ColorMatrix grayScaleMatrix = new ColorMatrix(); grayScaleMatrix.setSaturation(/* grayScale */0); + holder.mCheckBox.setChecked(!isLaunchMenuMode && target.isChecked()); + holder.mCheckBox.setEnabled(enabledState); + holder.mCheckBox.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE); + + holder.mIconView.setImageDrawable(target.getDrawable()); holder.mIconView.setColorFilter(enabledState ? null : new ColorMatrixColorFilter(grayScaleMatrix)); holder.mIconView.setAlpha(enabledState ? ENABLED_ALPHA : DISABLED_ALPHA); + + holder.mLabelView.setText(target.getLabel()); holder.mLabelView.setEnabled(enabledState); - holder.mActionViewItem.setEnabled(enabledState); - holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item)); - holder.mActionViewItem.setVisibility(View.VISIBLE); + holder.mSwitchItem.setVisibility(View.GONE); - holder.mItemContainer.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE); + holder.mItemView.setEnabled(enabledState); holder.mItemView.setClickable(!enabledState); } - private void updateInvisibleActionItemVisibility(@NonNull Context context, - @NonNull ViewHolder holder) { + private void updateInvisibleActionItemVisibility(@NonNull ViewHolder holder, + AccessibilityButtonTarget target) { + final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); + + holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked()); + holder.mCheckBox.setEnabled(true); + holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); + holder.mIconView.setColorFilter(null); holder.mIconView.setAlpha(ENABLED_ALPHA); + holder.mIconView.setImageDrawable(target.getDrawable()); + + holder.mLabelView.setText(target.getLabel()); holder.mLabelView.setEnabled(true); - holder.mActionViewItem.setEnabled(true); - holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item)); - holder.mActionViewItem.setVisibility(View.VISIBLE); + holder.mSwitchItem.setVisibility(View.GONE); - holder.mItemContainer.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT) - ? View.VISIBLE : View.GONE); + holder.mItemView.setEnabled(true); holder.mItemView.setClickable(false); } @@ -403,30 +418,41 @@ public class AccessibilityButtonChooserActivity extends Activity { ? isWhiteListingServiceEnabled(context, target) : isAccessibilityServiceEnabled(context, target); + holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked()); + holder.mCheckBox.setEnabled(true); + holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); + holder.mIconView.setColorFilter(null); holder.mIconView.setAlpha(ENABLED_ALPHA); + holder.mIconView.setImageDrawable(target.getDrawable()); + + holder.mLabelView.setText(target.getLabel()); holder.mLabelView.setEnabled(true); - holder.mActionViewItem.setEnabled(true); - holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item)); - holder.mActionViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); + holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE); holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled); - holder.mItemContainer.setVisibility(View.VISIBLE); + holder.mItemView.setEnabled(true); holder.mItemView.setClickable(false); } - private void updateBounceActionItemVisibility(@NonNull Context context, - @NonNull ViewHolder holder) { + private void updateBounceActionItemVisibility(@NonNull ViewHolder holder, + AccessibilityButtonTarget target) { + final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); + + holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked()); + holder.mCheckBox.setEnabled(true); + holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); + holder.mIconView.setColorFilter(null); holder.mIconView.setAlpha(ENABLED_ALPHA); + holder.mIconView.setImageDrawable(target.getDrawable()); + + holder.mLabelView.setText(target.getLabel()); holder.mLabelView.setEnabled(true); - holder.mActionViewItem.setEnabled(true); - holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item)); - holder.mActionViewItem.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT) - ? View.VISIBLE : View.GONE); + holder.mSwitchItem.setVisibility(View.GONE); - holder.mItemContainer.setVisibility(View.VISIBLE); + holder.mItemView.setEnabled(true); holder.mItemView.setClickable(false); } @@ -434,6 +460,9 @@ public class AccessibilityButtonChooserActivity extends Activity { private static class AccessibilityButtonTarget { private String mId; + @TargetType + private int mType; + private boolean mChecked; private CharSequence mLabel; private Drawable mDrawable; @AccessibilityServiceFragmentType @@ -442,6 +471,8 @@ public class AccessibilityButtonChooserActivity extends Activity { AccessibilityButtonTarget(@NonNull Context context, @NonNull AccessibilityServiceInfo serviceInfo) { this.mId = serviceInfo.getComponentName().flattenToString(); + this.mType = TargetType.ACCESSIBILITY_SERVICE; + this.mChecked = isTargetShortcutUsed(context, mId); this.mLabel = serviceInfo.getResolveInfo().loadLabel(context.getPackageManager()); this.mDrawable = serviceInfo.getResolveInfo().loadIcon(context.getPackageManager()); this.mFragmentType = getAccessibilityServiceFragmentType(serviceInfo); @@ -450,6 +481,8 @@ public class AccessibilityButtonChooserActivity extends Activity { AccessibilityButtonTarget(@NonNull Context context, @NonNull AccessibilityShortcutInfo shortcutInfo) { this.mId = shortcutInfo.getComponentName().flattenToString(); + this.mType = TargetType.ACCESSIBILITY_ACTIVITY; + this.mChecked = isTargetShortcutUsed(context, mId); this.mLabel = shortcutInfo.getActivityInfo().loadLabel(context.getPackageManager()); this.mDrawable = shortcutInfo.getActivityInfo().loadIcon(context.getPackageManager()); this.mFragmentType = AccessibilityServiceFragmentType.BOUNCE; @@ -458,15 +491,29 @@ public class AccessibilityButtonChooserActivity extends Activity { AccessibilityButtonTarget(Context context, @NonNull String id, int labelResId, int iconRes, @AccessibilityServiceFragmentType int fragmentType) { this.mId = id; + this.mType = TargetType.WHITE_LISTING; + this.mChecked = isTargetShortcutUsed(context, mId); this.mLabel = context.getText(labelResId); this.mDrawable = context.getDrawable(iconRes); this.mFragmentType = fragmentType; } + public void setChecked(boolean checked) { + mChecked = checked; + } + public String getId() { return mId; } + public int getType() { + return mType; + } + + public boolean isChecked() { + return mChecked; + } + public CharSequence getLabel() { return mLabel; } @@ -519,19 +566,19 @@ public class AccessibilityButtonChooserActivity extends Activity { } private void onLegacyTargetSelected(AccessibilityButtonTarget target) { - if (mShortcutType == ACCESSIBILITY_BUTTON) { + if (sShortcutType == ACCESSIBILITY_BUTTON) { final AccessibilityManager ams = getSystemService(AccessibilityManager.class); ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId()); - } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { + } else if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { switchServiceState(target); } } private void onInvisibleTargetSelected(AccessibilityButtonTarget target) { final AccessibilityManager ams = getSystemService(AccessibilityManager.class); - if (mShortcutType == ACCESSIBILITY_BUTTON) { + if (sShortcutType == ACCESSIBILITY_BUTTON) { ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId()); - } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { + } else if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { ams.performAccessibilityShortcut(target.getId()); } } @@ -542,9 +589,9 @@ public class AccessibilityButtonChooserActivity extends Activity { private void onBounceTargetSelected(AccessibilityButtonTarget target) { final AccessibilityManager ams = getSystemService(AccessibilityManager.class); - if (mShortcutType == ACCESSIBILITY_BUTTON) { + if (sShortcutType == ACCESSIBILITY_BUTTON) { ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId()); - } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { + } else if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { ams.performAccessibilityShortcut(target.getId()); } } @@ -565,66 +612,72 @@ public class AccessibilityButtonChooserActivity extends Activity { } } - private void onTargetDeleted(AdapterView parent, View view, int position, long id) { - final AccessibilityButtonTarget target = mTargets.get(position); - final String componentId = target.getId(); + private void onTargetChecked(AdapterView parent, View view, int position, long id) { + mCurrentCheckedTarget = mTargets.get(position); + onTargetChecked(mCurrentCheckedTarget, !mCurrentCheckedTarget.isChecked()); + } + private void onTargetChecked(AccessibilityButtonTarget target, boolean checked) { switch (target.getFragmentType()) { case AccessibilityServiceFragmentType.LEGACY: - onLegacyTargetDeleted(position, componentId); + onLegacyTargetChecked(checked); break; case AccessibilityServiceFragmentType.INVISIBLE: - onInvisibleTargetDeleted(position, componentId); + onInvisibleTargetChecked(checked); break; case AccessibilityServiceFragmentType.INTUITIVE: - onIntuitiveTargetDeleted(position, componentId); + onIntuitiveTargetChecked(checked); break; case AccessibilityServiceFragmentType.BOUNCE: - onBounceTargetDeleted(position, componentId); + onBounceTargetChecked(checked); break; default: throw new IllegalStateException("Unexpected fragment type"); } - - if (mTargets.isEmpty()) { - mAlertDialog.dismiss(); - } } - private void onLegacyTargetDeleted(int position, String componentId) { - if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { - optOutValueFromSettings(this, mShortcutUserType, componentId); - - mTargets.remove(position); + private void onLegacyTargetChecked(boolean checked) { + if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { + updateValueToSettings(mCurrentCheckedTarget.getId(), checked); + mCurrentCheckedTarget.setChecked(checked); mTargetAdapter.notifyDataSetChanged(); } } - private void onInvisibleTargetDeleted(int position, String componentId) { - optOutValueFromSettings(this, mShortcutUserType, componentId); - + private void onInvisibleTargetChecked(boolean checked) { final int shortcutTypes = UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE; - if (!hasValuesInSettings(this, shortcutTypes, componentId)) { - disableService(componentId); + if (!hasValuesInSettings(this, shortcutTypes, mCurrentCheckedTarget.getId())) { + setServiceEnabled(mCurrentCheckedTarget.getId(), checked); } - mTargets.remove(position); + updateValueToSettings(mCurrentCheckedTarget.getId(), checked); + mCurrentCheckedTarget.setChecked(checked); mTargetAdapter.notifyDataSetChanged(); } - private void onIntuitiveTargetDeleted(int position, String componentId) { - optOutValueFromSettings(this, mShortcutUserType, componentId); - mTargets.remove(position); + private void onIntuitiveTargetChecked(boolean checked) { + updateValueToSettings(mCurrentCheckedTarget.getId(), checked); + mCurrentCheckedTarget.setChecked(checked); mTargetAdapter.notifyDataSetChanged(); } - private void onBounceTargetDeleted(int position, String componentId) { - optOutValueFromSettings(this, mShortcutUserType, componentId); - mTargets.remove(position); + private void onBounceTargetChecked(boolean checked) { + updateValueToSettings(mCurrentCheckedTarget.getId(), checked); + mCurrentCheckedTarget.setChecked(checked); mTargetAdapter.notifyDataSetChanged(); } - private void onCancelButtonClicked() { + private void updateValueToSettings(String componentId, boolean checked) { + if (checked) { + optInValueToSettings(this, mShortcutUserType, componentId); + } else { + optOutValueFromSettings(this, mShortcutUserType, componentId); + } + } + + private void onDoneButtonClicked() { + mTargets.clear(); + mTargets.addAll(getServiceTargets(this, sShortcutType)); mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH); mTargetAdapter.notifyDataSetChanged(); @@ -635,11 +688,13 @@ public class AccessibilityButtonChooserActivity extends Activity { } private void onEditButtonClicked() { + mTargets.clear(); + mTargets.addAll(getInstalledServiceTargets(this)); mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.EDIT); mTargetAdapter.notifyDataSetChanged(); mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText( - getString(R.string.cancel_accessibility_shortcut_menu_button)); + getString(R.string.done_accessibility_shortcut_menu_button)); updateDialogListeners(); } @@ -649,15 +704,20 @@ public class AccessibilityButtonChooserActivity extends Activity { (mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT); final int selectDialogTitleId = R.string.accessibility_select_shortcut_menu_title; final int editDialogTitleId = - (mShortcutType == ACCESSIBILITY_BUTTON) + (sShortcutType == ACCESSIBILITY_BUTTON) ? R.string.accessibility_edit_shortcut_menu_button_title : R.string.accessibility_edit_shortcut_menu_volume_title; mAlertDialog.setTitle(getString(isEditMenuMode ? editDialogTitleId : selectDialogTitleId)); - mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener( - isEditMenuMode ? view -> onCancelButtonClicked() : view -> onEditButtonClicked()); + isEditMenuMode ? view -> onDoneButtonClicked() : view -> onEditButtonClicked()); mAlertDialog.getListView().setOnItemClickListener( - isEditMenuMode ? this::onTargetDeleted : this::onTargetSelected); + isEditMenuMode ? this::onTargetChecked : this::onTargetSelected); + } + + private static boolean isTargetShortcutUsed(@NonNull Context context, String id) { + final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); + final List requiredTargets = ams.getAccessibilityShortcutTargets(sShortcutType); + return requiredTargets.contains(id); } } diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml index c01766b2c7482..b7dd892fd161d 100644 --- a/core/res/res/layout/accessibility_button_chooser_item.xml +++ b/core/res/res/layout/accessibility_button_chooser_item.xml @@ -21,10 +21,17 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" - android:paddingStart="16dp" - android:paddingEnd="16dp" - android:paddingTop="12dp" - android:paddingBottom="12dp"> + android:padding="16dp"> + + - - - - - - + android:background="@null" + android:clickable="false" + android:focusable="false"/> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index addcd81ae5c89..a623cc834361e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4422,8 +4422,8 @@ any service item in the menu list. [CHAR LIMIT=100] --> Edit shortcuts - - Cancel + + Done Turn off Shortcut diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0971aadfe4ab8..5f0db15ec8586 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3243,14 +3243,13 @@ + - - - + From b6d9e4168e0cec024dfe0e12901580a944e9bd10 Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Fri, 6 Mar 2020 15:45:23 +0800 Subject: [PATCH 2/3] Update the edit shortcut menu (3/n). 1. Enable to uncheck legacy service target item in edit menu mode. 2. Add toast to warng the user when uncheck service. Bug: 148989057 Bug: 150365947 Test: manual test Change-Id: Ieb35d3593ed25025dbc37507d0c54e6ba266d2ed --- .../common/ShortcutConstants.java | 2 - .../AccessibilityButtonChooserActivity.java | 95 +++++++------------ core/res/res/values/strings.xml | 5 + core/res/res/values/symbols.xml | 1 + 4 files changed, 40 insertions(+), 63 deletions(-) diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java index 72c30f9740607..79b34c05bf872 100644 --- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java +++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java @@ -28,8 +28,6 @@ public final class ShortcutConstants { private ShortcutConstants() {} public static final char SERVICES_SEPARATOR = ':'; - public static final float DISABLED_ALPHA = 0.5f; - public static final float ENABLED_ALPHA = 1.0f; /** * Annotation for different user shortcut type UI type. diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java index 8672175005b4c..958a341ee56db 100644 --- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java @@ -23,11 +23,10 @@ import static com.android.internal.accessibility.AccessibilityShortcutController import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityServiceFragmentType; -import static com.android.internal.accessibility.common.ShortcutConstants.DISABLED_ALPHA; -import static com.android.internal.accessibility.common.ShortcutConstants.ENABLED_ALPHA; import static com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode; import static com.android.internal.accessibility.common.ShortcutConstants.TargetType; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.COMPONENT_ID; import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.FRAGMENT_TYPE; import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.ICON_ID; @@ -52,9 +51,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.res.TypedArray; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.view.LayoutInflater; @@ -68,6 +66,7 @@ import android.widget.CheckBox; import android.widget.ImageView; import android.widget.Switch; import android.widget.TextView; +import android.widget.Toast; import com.android.internal.R; @@ -133,7 +132,7 @@ public class AccessibilityButtonChooserActivity extends Activity { final String selectDialogTitle = getString(R.string.accessibility_select_shortcut_menu_title); - mTargetAdapter = new TargetAdapter(mTargets, sShortcutType); + mTargetAdapter = new TargetAdapter(mTargets); mAlertDialog = new AlertDialog.Builder(this) .setTitle(selectDialogTitle) .setAdapter(mTargetAdapter, /* listener= */ null) @@ -183,6 +182,14 @@ public class AccessibilityButtonChooserActivity extends Activity { final List targets = new ArrayList<>(installedServices.size()); for (AccessibilityServiceInfo info : installedServices) { + final int targetSdk = + info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion; + final boolean hasRequestAccessibilityButtonFlag = + (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; + if ((targetSdk < Build.VERSION_CODES.R) && !hasRequestAccessibilityButtonFlag + && (sShortcutType == ACCESSIBILITY_BUTTON)) { + continue; + } targets.add(new AccessibilityButtonTarget(context, info)); } @@ -279,14 +286,10 @@ public class AccessibilityButtonChooserActivity extends Activity { private static class TargetAdapter extends BaseAdapter { @ShortcutMenuMode private int mShortcutMenuMode = ShortcutMenuMode.LAUNCH; - @ShortcutType - private int mShortcutButtonType; private List mButtonTargets; - TargetAdapter(List targets, - @ShortcutType int shortcutButtonType) { + TargetAdapter(List targets) { this.mButtonTargets = targets; - this.mShortcutButtonType = shortcutButtonType; } void setShortcutMenuMode(@ShortcutMenuMode int shortcutMenuMode) { @@ -365,29 +368,12 @@ public class AccessibilityButtonChooserActivity extends Activity { private void updateLegacyActionItemVisibility(@NonNull ViewHolder holder, AccessibilityButtonTarget target) { final boolean isLaunchMenuMode = (mShortcutMenuMode == ShortcutMenuMode.LAUNCH); - final boolean isHardwareButtonTriggered = - (mShortcutButtonType == ACCESSIBILITY_SHORTCUT_KEY); - final boolean enabledState = (isLaunchMenuMode || isHardwareButtonTriggered); - final ColorMatrix grayScaleMatrix = new ColorMatrix(); - grayScaleMatrix.setSaturation(/* grayScale */0); holder.mCheckBox.setChecked(!isLaunchMenuMode && target.isChecked()); - holder.mCheckBox.setEnabled(enabledState); holder.mCheckBox.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE); - holder.mIconView.setImageDrawable(target.getDrawable()); - holder.mIconView.setColorFilter(enabledState - ? null : new ColorMatrixColorFilter(grayScaleMatrix)); - holder.mIconView.setAlpha(enabledState - ? ENABLED_ALPHA : DISABLED_ALPHA); - holder.mLabelView.setText(target.getLabel()); - holder.mLabelView.setEnabled(enabledState); - holder.mSwitchItem.setVisibility(View.GONE); - - holder.mItemView.setEnabled(enabledState); - holder.mItemView.setClickable(!enabledState); } private void updateInvisibleActionItemVisibility(@NonNull ViewHolder holder, @@ -395,20 +381,10 @@ public class AccessibilityButtonChooserActivity extends Activity { final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked()); - holder.mCheckBox.setEnabled(true); holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); - - holder.mIconView.setColorFilter(null); - holder.mIconView.setAlpha(ENABLED_ALPHA); holder.mIconView.setImageDrawable(target.getDrawable()); - holder.mLabelView.setText(target.getLabel()); - holder.mLabelView.setEnabled(true); - holder.mSwitchItem.setVisibility(View.GONE); - - holder.mItemView.setEnabled(true); - holder.mItemView.setClickable(false); } private void updateIntuitiveActionItemVisibility(@NonNull Context context, @@ -419,21 +395,11 @@ public class AccessibilityButtonChooserActivity extends Activity { : isAccessibilityServiceEnabled(context, target); holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked()); - holder.mCheckBox.setEnabled(true); holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); - - holder.mIconView.setColorFilter(null); - holder.mIconView.setAlpha(ENABLED_ALPHA); holder.mIconView.setImageDrawable(target.getDrawable()); - holder.mLabelView.setText(target.getLabel()); - holder.mLabelView.setEnabled(true); - holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE); holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled); - - holder.mItemView.setEnabled(true); - holder.mItemView.setClickable(false); } private void updateBounceActionItemVisibility(@NonNull ViewHolder holder, @@ -441,20 +407,10 @@ public class AccessibilityButtonChooserActivity extends Activity { final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked()); - holder.mCheckBox.setEnabled(true); holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); - - holder.mIconView.setColorFilter(null); - holder.mIconView.setAlpha(ENABLED_ALPHA); holder.mIconView.setImageDrawable(target.getDrawable()); - holder.mLabelView.setText(target.getLabel()); - holder.mLabelView.setEnabled(true); - holder.mSwitchItem.setVisibility(View.GONE); - - holder.mItemView.setEnabled(true); - holder.mItemView.setClickable(false); } } @@ -637,15 +593,27 @@ public class AccessibilityButtonChooserActivity extends Activity { } private void onLegacyTargetChecked(boolean checked) { - if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { + if (sShortcutType == ACCESSIBILITY_BUTTON) { + setServiceEnabled(mCurrentCheckedTarget.getId(), checked); + if (!checked) { + optOutValueFromSettings(this, HARDWARE, mCurrentCheckedTarget.getId()); + final String warningText = + getString(R.string.accessibility_uncheck_legacy_item_warning, + mCurrentCheckedTarget.getLabel()); + Toast.makeText(this, warningText, Toast.LENGTH_SHORT).show(); + } + } else if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { updateValueToSettings(mCurrentCheckedTarget.getId(), checked); - mCurrentCheckedTarget.setChecked(checked); - mTargetAdapter.notifyDataSetChanged(); + } else { + throw new IllegalStateException("Unexpected shortcut type"); } + + mCurrentCheckedTarget.setChecked(checked); + mTargetAdapter.notifyDataSetChanged(); } private void onInvisibleTargetChecked(boolean checked) { - final int shortcutTypes = UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE; + final int shortcutTypes = UserShortcutType.SOFTWARE | HARDWARE; if (!hasValuesInSettings(this, shortcutTypes, mCurrentCheckedTarget.getId())) { setServiceEnabled(mCurrentCheckedTarget.getId(), checked); } @@ -678,6 +646,11 @@ public class AccessibilityButtonChooserActivity extends Activity { private void onDoneButtonClicked() { mTargets.clear(); mTargets.addAll(getServiceTargets(this, sShortcutType)); + if (mTargets.isEmpty()) { + mAlertDialog.dismiss(); + return; + } + mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH); mTargetAdapter.notifyDataSetChanged(); diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index a623cc834361e..18ec0f2aa55fd 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4418,6 +4418,11 @@ volume key shortcut + + + %s has been turned off + Edit shortcuts diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 5f0db15ec8586..ac9bd6f90ce0f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3240,6 +3240,7 @@ + From baf4f05387eb8d1a5a766a6776a90361032cb7fb Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Fri, 6 Mar 2020 23:33:27 +0800 Subject: [PATCH 3/3] Update the edit shortcut menu (4/n). 1.Popup the permission dialog to warn user when trigger service target. 2.Update strings Bug: 148989057 Test: manual test Change-Id: If2d72bb5a98ba5a4c284a5d9a32991ea38fbadd7 --- .../AccessibilityButtonChooserActivity.java | 75 ++++++++ core/res/res/drawable/ic_pan_tool.xml | 26 +++ .../{ic_delete_item.xml => ic_visibility.xml} | 4 +- ...lity_enable_service_encryption_warning.xml | 175 ++++++++++++++++++ core/res/res/values/strings.xml | 46 ++++- core/res/res/values/symbols.xml | 11 +- 6 files changed, 330 insertions(+), 7 deletions(-) create mode 100644 core/res/res/drawable/ic_pan_tool.xml rename core/res/res/drawable/{ic_delete_item.xml => ic_visibility.xml} (77%) create mode 100644 core/res/res/layout/accessibility_enable_service_encryption_warning.xml diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java index 958a341ee56db..c40864131a2e6 100644 --- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java @@ -54,7 +54,9 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.os.storage.StorageManager; import android.provider.Settings; +import android.text.BidiFormatter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -62,6 +64,7 @@ import android.view.Window; import android.view.accessibility.AccessibilityManager; import android.widget.AdapterView; import android.widget.BaseAdapter; +import android.widget.Button; import android.widget.CheckBox; import android.widget.ImageView; import android.widget.Switch; @@ -73,6 +76,7 @@ import com.android.internal.R; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; /** * Activity used to display and persist a service or feature target for the Accessibility button. @@ -84,6 +88,7 @@ public class AccessibilityButtonChooserActivity extends Activity { private int mShortcutUserType; private final List mTargets = new ArrayList<>(); private AlertDialog mAlertDialog; + private AlertDialog mEnableDialog; private TargetAdapter mTargetAdapter; private AccessibilityButtonTarget mCurrentCheckedTarget; @@ -570,6 +575,18 @@ public class AccessibilityButtonChooserActivity extends Activity { private void onTargetChecked(AdapterView parent, View view, int position, long id) { mCurrentCheckedTarget = mTargets.get(position); + + if ((mCurrentCheckedTarget.getType() == TargetType.ACCESSIBILITY_SERVICE) + && !mCurrentCheckedTarget.isChecked()) { + mEnableDialog = new AlertDialog.Builder(this) + .setView(createEnableDialogContentView(this, mCurrentCheckedTarget, + this::onPermissionAllowButtonClicked, + this::onPermissionDenyButtonClicked)) + .create(); + mEnableDialog.show(); + return; + } + onTargetChecked(mCurrentCheckedTarget, !mCurrentCheckedTarget.isChecked()); } @@ -693,4 +710,62 @@ public class AccessibilityButtonChooserActivity extends Activity { final List requiredTargets = ams.getAccessibilityShortcutTargets(sShortcutType); return requiredTargets.contains(id); } + + private void onPermissionAllowButtonClicked(View view) { + if (mCurrentCheckedTarget.getFragmentType() != AccessibilityServiceFragmentType.LEGACY) { + updateValueToSettings(mCurrentCheckedTarget.getId(), /* checked= */ true); + } + onTargetChecked(mCurrentCheckedTarget, /* checked= */ true); + mEnableDialog.dismiss(); + } + + private void onPermissionDenyButtonClicked(View view) { + mEnableDialog.dismiss(); + } + + private static View createEnableDialogContentView(Context context, + AccessibilityButtonTarget target, View.OnClickListener allowListener, + View.OnClickListener denyListener) { + final LayoutInflater inflater = (LayoutInflater) context.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + + final View content = inflater.inflate( + R.layout.accessibility_enable_service_encryption_warning, /* root= */ null); + + final TextView encryptionWarningView = (TextView) content.findViewById( + R.id.accessibility_encryption_warning); + if (StorageManager.isNonDefaultBlockEncrypted()) { + final String text = context.getString( + R.string.accessibility_enable_service_encryption_warning, + getServiceName(context, target.getLabel())); + encryptionWarningView.setText(text); + encryptionWarningView.setVisibility(View.VISIBLE); + } else { + encryptionWarningView.setVisibility(View.GONE); + } + + final ImageView permissionDialogIcon = content.findViewById( + R.id.accessibility_permissionDialog_icon); + permissionDialogIcon.setImageDrawable(target.getDrawable()); + + final TextView permissionDialogTitle = content.findViewById( + R.id.accessibility_permissionDialog_title); + permissionDialogTitle.setText(context.getString(R.string.accessibility_enable_service_title, + getServiceName(context, target.getLabel()))); + + final Button permissionAllowButton = content.findViewById( + R.id.accessibility_permission_enable_allow_button); + final Button permissionDenyButton = content.findViewById( + R.id.accessibility_permission_enable_deny_button); + permissionAllowButton.setOnClickListener(allowListener); + permissionDenyButton.setOnClickListener(denyListener); + + return content; + } + + // Gets the service name and bidi wrap it to protect from bidi side effects. + private static CharSequence getServiceName(Context context, CharSequence label) { + final Locale locale = context.getResources().getConfiguration().getLocales().get(0); + return BidiFormatter.getInstance(locale).unicodeWrap(label); + } } diff --git a/core/res/res/drawable/ic_pan_tool.xml b/core/res/res/drawable/ic_pan_tool.xml new file mode 100644 index 0000000000000..c1a8549b21410 --- /dev/null +++ b/core/res/res/drawable/ic_pan_tool.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/core/res/res/drawable/ic_delete_item.xml b/core/res/res/drawable/ic_visibility.xml similarity index 77% rename from core/res/res/drawable/ic_delete_item.xml rename to core/res/res/drawable/ic_visibility.xml index 8a398a44635ec..1956241569997 100644 --- a/core/res/res/drawable/ic_delete_item.xml +++ b/core/res/res/drawable/ic_visibility.xml @@ -12,7 +12,7 @@ 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. - --> +--> diff --git a/core/res/res/layout/accessibility_enable_service_encryption_warning.xml b/core/res/res/layout/accessibility_enable_service_encryption_warning.xml new file mode 100644 index 0000000000000..4000516605927 --- /dev/null +++ b/core/res/res/layout/accessibility_enable_service_encryption_warning.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +