diff options
| author | 2020-04-08 20:16:25 +0800 | |
|---|---|---|
| committer | 2020-04-17 15:34:30 +0800 | |
| commit | 7d9af05e13f3e8979e60f73a48f29f917aea9dcf (patch) | |
| tree | 6f7fdb89a45ce403090b6b56cd1bc1a20a5c0aff | |
| parent | fbad548bf003b8d9c780259b44b93141c05f49ce (diff) | |
Redesign for AccessibilityShortcutChooserActivity (2/n).
Refactor and replace it with new components and functions.
Bug: 147655054
Test: manual test
Change-Id: I12ef647ef0524d2d2cada63c46341c0fddb828da
3 files changed, 350 insertions, 635 deletions
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java index 9338c3c87217..42b827b25dfe 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java @@ -19,62 +19,32 @@ import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTT import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static android.view.accessibility.AccessibilityManager.ShortcutType; -import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME; -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.AccessibilityFragmentType; 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; -import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.LABEL_ID; -import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.SETTINGS_KEY; -import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType; -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.accessibility.dialog.AccessibilityTargetHelper.getInstalledTargets; +import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets; import static com.android.internal.util.Preconditions.checkArgument; -import android.accessibilityservice.AccessibilityServiceInfo; -import android.accessibilityservice.AccessibilityShortcutInfo; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; -import android.app.ActivityManager; import android.app.AlertDialog; -import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; 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; 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; import android.widget.TextView; -import android.widget.Toast; import com.android.internal.R; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Locale; @@ -84,38 +54,11 @@ import java.util.Locale; */ public class AccessibilityShortcutChooserActivity extends Activity { @ShortcutType - private static int sShortcutType; - @UserShortcutType - private int mShortcutUserType; - private final List<AccessibilityButtonTarget> mTargets = new ArrayList<>(); - private AlertDialog mAlertDialog; - private AlertDialog mEnableDialog; - private TargetAdapter mTargetAdapter; - private AccessibilityButtonTarget mCurrentCheckedTarget; - - private static final String[][] WHITE_LISTING_FEATURES = { - { - COLOR_INVERSION_COMPONENT_NAME.flattenToString(), - String.valueOf(R.string.color_inversion_feature_name), - String.valueOf(R.drawable.ic_accessibility_color_inversion), - String.valueOf(AccessibilityFragmentType.TOGGLE), - Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, - }, - { - DALTONIZER_COMPONENT_NAME.flattenToString(), - String.valueOf(R.string.color_correction_feature_name), - String.valueOf(R.drawable.ic_accessibility_color_correction), - String.valueOf(AccessibilityFragmentType.TOGGLE), - Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, - }, - { - MAGNIFICATION_CONTROLLER_NAME, - String.valueOf(R.string.accessibility_magnification_chooser_text), - String.valueOf(R.drawable.ic_accessibility_magnification), - String.valueOf(AccessibilityFragmentType.INVISIBLE_TOGGLE), - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, - }, - }; + private int mShortcutType; + private final List<AccessibilityTarget> mTargets = new ArrayList<>(); + private AlertDialog mMenuDialog; + private AlertDialog mPermissionDialog; + private ShortcutTargetAdapter mTargetAdapter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -126,20 +69,18 @@ public class AccessibilityShortcutChooserActivity extends Activity { requestWindowFeature(Window.FEATURE_NO_TITLE); } - sShortcutType = getIntent().getIntExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE, + mShortcutType = getIntent().getIntExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE, /* unexpectedShortcutType */ -1); - final boolean existInShortcutType = (sShortcutType == ACCESSIBILITY_BUTTON) - || (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY); - checkArgument(existInShortcutType, "Unexpected shortcut type: " + sShortcutType); + final boolean existInShortcutType = (mShortcutType == ACCESSIBILITY_BUTTON) + || (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY); + checkArgument(existInShortcutType, "Unexpected shortcut type: " + mShortcutType); - mShortcutUserType = convertToUserType(sShortcutType); - - mTargets.addAll(getServiceTargets(this, sShortcutType)); + mTargets.addAll(getTargets(this, mShortcutType)); final String selectDialogTitle = getString(R.string.accessibility_select_shortcut_menu_title); - mTargetAdapter = new TargetAdapter(mTargets); - mAlertDialog = new AlertDialog.Builder(this) + mTargetAdapter = new ShortcutTargetAdapter(mTargets); + mMenuDialog = new AlertDialog.Builder(this) .setTitle(selectDialogTitle) .setAdapter(mTargetAdapter, /* listener= */ null) .setPositiveButton( @@ -147,561 +88,55 @@ public class AccessibilityShortcutChooserActivity extends Activity { /* listener= */ null) .setOnDismissListener(dialog -> finish()) .create(); - mAlertDialog.setOnShowListener(dialog -> updateDialogListeners()); - mAlertDialog.show(); + mMenuDialog.setOnShowListener(dialog -> updateDialogListeners()); + mMenuDialog.show(); } @Override protected void onDestroy() { - mAlertDialog.dismiss(); + mMenuDialog.dismiss(); super.onDestroy(); } - private static List<AccessibilityButtonTarget> getServiceTargets(@NonNull Context context, - @ShortcutType int shortcutType) { - final List<AccessibilityButtonTarget> targets = getInstalledServiceTargets(context); - final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); - final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType); - targets.removeIf(target -> !requiredTargets.contains(target.getId())); - - return targets; - } - - private static List<AccessibilityButtonTarget> getInstalledServiceTargets( - @NonNull Context context) { - final List<AccessibilityButtonTarget> targets = new ArrayList<>(); - targets.addAll(getAccessibilityFilteredTargets(context)); - targets.addAll(getWhiteListingServiceTargets(context)); - - return targets; - } - - private static List<AccessibilityButtonTarget> getAccessibilityFilteredTargets( - @NonNull Context context) { - final List<AccessibilityButtonTarget> serviceTargets = - getAccessibilityServiceTargets(context); - final List<AccessibilityButtonTarget> activityTargets = - getAccessibilityActivityTargets(context); - - for (AccessibilityButtonTarget activityTarget : activityTargets) { - serviceTargets.removeIf(serviceTarget -> { - final ComponentName serviceComponentName = - ComponentName.unflattenFromString(serviceTarget.getId()); - final ComponentName activityComponentName = - ComponentName.unflattenFromString(activityTarget.getId()); - final boolean isSamePackageName = activityComponentName.getPackageName().equals( - serviceComponentName.getPackageName()); - final boolean isSameLabel = activityTarget.getLabel().equals( - serviceTarget.getLabel()); - - return isSamePackageName && isSameLabel; - }); - } - - final List<AccessibilityButtonTarget> targets = new ArrayList<>(); - targets.addAll(serviceTargets); - targets.addAll(activityTargets); - - return targets; - } - - private static List<AccessibilityButtonTarget> getAccessibilityServiceTargets( - @NonNull Context context) { - final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); - final List<AccessibilityServiceInfo> installedServices = - ams.getInstalledAccessibilityServiceList(); - if (installedServices == null) { - return Collections.emptyList(); - } - - final List<AccessibilityButtonTarget> 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)); - } - - return targets; - } - - private static List<AccessibilityButtonTarget> getAccessibilityActivityTargets( - @NonNull Context context) { - final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); - final List<AccessibilityShortcutInfo> installedServices = - ams.getInstalledAccessibilityShortcutListAsUser(context, - ActivityManager.getCurrentUser()); - if (installedServices == null) { - return Collections.emptyList(); - } - - final List<AccessibilityButtonTarget> targets = new ArrayList<>(installedServices.size()); - for (AccessibilityShortcutInfo info : installedServices) { - targets.add(new AccessibilityButtonTarget(context, info)); - } - - return targets; - } - - private static List<AccessibilityButtonTarget> getWhiteListingServiceTargets( - @NonNull Context context) { - final List<AccessibilityButtonTarget> targets = new ArrayList<>(); - - for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) { - final AccessibilityButtonTarget target = new AccessibilityButtonTarget( - context, - WHITE_LISTING_FEATURES[i][COMPONENT_ID], - Integer.parseInt(WHITE_LISTING_FEATURES[i][LABEL_ID]), - Integer.parseInt(WHITE_LISTING_FEATURES[i][ICON_ID]), - Integer.parseInt(WHITE_LISTING_FEATURES[i][FRAGMENT_TYPE])); - targets.add(target); - } - - return targets; - } - - private static boolean isWhiteListingServiceEnabled(@NonNull Context context, - AccessibilityButtonTarget target) { - - for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) { - if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(target.getId())) { - return Settings.Secure.getInt(context.getContentResolver(), - WHITE_LISTING_FEATURES[i][SETTINGS_KEY], - /* settingsValueOff */ 0) == /* settingsValueOn */ 1; - } - } - - return false; - } - - private static boolean isWhiteListingService(String componentId) { - for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) { - if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(componentId)) { - return true; - } - } - - return false; - } - - private void setWhiteListingServiceEnabled(String componentId, int settingsValue) { - for (int i = 0; i < WHITE_LISTING_FEATURES.length; i++) { - if (WHITE_LISTING_FEATURES[i][COMPONENT_ID].equals(componentId)) { - Settings.Secure.putInt(getContentResolver(), - WHITE_LISTING_FEATURES[i][SETTINGS_KEY], settingsValue); - return; - } - } - } - - private void setServiceEnabled(String componentId, boolean enabled) { - if (isWhiteListingService(componentId)) { - setWhiteListingServiceEnabled(componentId, - enabled ? /* settingsValueOn */ 1 : /* settingsValueOff */ 0); - } else { - final ComponentName componentName = ComponentName.unflattenFromString(componentId); - setAccessibilityServiceState(this, componentName, enabled); - } - } - - private static class ViewHolder { - View mItemView; - CheckBox mCheckBox; - ImageView mIconView; - TextView mLabelView; - Switch mSwitchItem; - } - - private static class TargetAdapter extends BaseAdapter { - @ShortcutMenuMode - private int mShortcutMenuMode = ShortcutMenuMode.LAUNCH; - private List<AccessibilityButtonTarget> mButtonTargets; - - TargetAdapter(List<AccessibilityButtonTarget> targets) { - this.mButtonTargets = targets; - } - - void setShortcutMenuMode(@ShortcutMenuMode int shortcutMenuMode) { - mShortcutMenuMode = shortcutMenuMode; - } - - @ShortcutMenuMode - int getShortcutMenuMode() { - return mShortcutMenuMode; - } - - @Override - public int getCount() { - return mButtonTargets.size(); - } - - @Override - public Object getItem(int position) { - return mButtonTargets.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final Context context = parent.getContext(); - ViewHolder holder; - if (convertView == null) { - convertView = LayoutInflater.from(context).inflate( - R.layout.accessibility_shortcut_chooser_item, parent, /* attachToRoot= */ - false); - holder = new ViewHolder(); - holder.mItemView = convertView; - holder.mCheckBox = convertView.findViewById( - R.id.accessibility_shortcut_target_checkbox); - holder.mIconView = convertView.findViewById( - R.id.accessibility_shortcut_target_icon); - holder.mLabelView = convertView.findViewById( - R.id.accessibility_shortcut_target_label); - holder.mSwitchItem = convertView.findViewById( - R.id.accessibility_shortcut_target_switch_item); - convertView.setTag(holder); - } else { - holder = (ViewHolder) convertView.getTag(); - } - - final AccessibilityButtonTarget target = mButtonTargets.get(position); - updateActionItem(context, holder, target); - - return convertView; - } - - private void updateActionItem(@NonNull Context context, - @NonNull ViewHolder holder, AccessibilityButtonTarget target) { - - switch (target.getFragmentType()) { - case AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE: - updateVolumeShortcutToggleTargetActionItemVisibility(holder, target); - break; - case AccessibilityFragmentType.INVISIBLE_TOGGLE: - updateInvisibleToggleTargetActionItemVisibility(holder, target); - break; - case AccessibilityFragmentType.TOGGLE: - updateToggleTargetActionItemVisibility(context, holder, target); - break; - case AccessibilityFragmentType.LAUNCH_ACTIVITY: - updateLaunchActivityTargetActionItemVisibility(holder, target); - break; - default: - throw new IllegalStateException("Unexpected fragment type"); - } - } - - private void updateVolumeShortcutToggleTargetActionItemVisibility( - @NonNull ViewHolder holder, AccessibilityButtonTarget target) { - final boolean isLaunchMenuMode = (mShortcutMenuMode == ShortcutMenuMode.LAUNCH); - - holder.mCheckBox.setChecked(!isLaunchMenuMode && target.isChecked()); - holder.mCheckBox.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE); - holder.mIconView.setImageDrawable(target.getDrawable()); - holder.mLabelView.setText(target.getLabel()); - holder.mSwitchItem.setVisibility(View.GONE); - } - - private void updateInvisibleToggleTargetActionItemVisibility(@NonNull ViewHolder holder, - AccessibilityButtonTarget target) { - final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); - - holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked()); - holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); - holder.mIconView.setImageDrawable(target.getDrawable()); - holder.mLabelView.setText(target.getLabel()); - holder.mSwitchItem.setVisibility(View.GONE); - } - - private void updateToggleTargetActionItemVisibility(@NonNull Context context, - @NonNull ViewHolder holder, AccessibilityButtonTarget target) { - final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); - final boolean isServiceEnabled = isWhiteListingService(target.getId()) - ? isWhiteListingServiceEnabled(context, target) - : isAccessibilityServiceEnabled(context, target); - - holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked()); - holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); - holder.mIconView.setImageDrawable(target.getDrawable()); - holder.mLabelView.setText(target.getLabel()); - holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE); - holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled); - } - - private void updateLaunchActivityTargetActionItemVisibility(@NonNull ViewHolder holder, - AccessibilityButtonTarget target) { - final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT); - - holder.mCheckBox.setChecked(isEditMenuMode && target.isChecked()); - holder.mCheckBox.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); - holder.mIconView.setImageDrawable(target.getDrawable()); - holder.mLabelView.setText(target.getLabel()); - holder.mSwitchItem.setVisibility(View.GONE); - } - } - - private static class AccessibilityButtonTarget { - private String mId; - @TargetType - private int mType; - private boolean mChecked; - private CharSequence mLabel; - private Drawable mDrawable; - @AccessibilityFragmentType - private int mFragmentType; - - 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); - } - - 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 = AccessibilityFragmentType.LAUNCH_ACTIVITY; - } - - AccessibilityButtonTarget(Context context, @NonNull String id, int labelResId, - int iconRes, @AccessibilityFragmentType 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; - } - - public Drawable getDrawable() { - return mDrawable; - } - - public int getFragmentType() { - return mFragmentType; - } - } - - private static boolean isAccessibilityServiceEnabled(@NonNull Context context, - AccessibilityButtonTarget target) { - final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); - final List<AccessibilityServiceInfo> enabledServices = - ams.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK); - - for (AccessibilityServiceInfo info : enabledServices) { - final String id = info.getComponentName().flattenToString(); - if (id.equals(target.getId())) { - return true; - } - } - - return false; - } - private void onTargetSelected(AdapterView<?> parent, View view, int position, long id) { - final AccessibilityButtonTarget target = mTargets.get(position); - switch (target.getFragmentType()) { - case AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE: - onVolumeShortcutToggleTargetSelected(target); - break; - case AccessibilityFragmentType.INVISIBLE_TOGGLE: - onInvisibleToggleTargetSelected(target); - break; - case AccessibilityFragmentType.TOGGLE: - onToggleTargetSelected(target); - break; - case AccessibilityFragmentType.LAUNCH_ACTIVITY: - onLaunchActivityTargetSelected(target); - break; - default: - throw new IllegalStateException("Unexpected fragment type"); - } - - mAlertDialog.dismiss(); - } - - private void onVolumeShortcutToggleTargetSelected(AccessibilityButtonTarget target) { - if (sShortcutType == ACCESSIBILITY_BUTTON) { - final AccessibilityManager ams = getSystemService(AccessibilityManager.class); - ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId()); - } else if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { - switchServiceState(target); - } - } - - private void onInvisibleToggleTargetSelected(AccessibilityButtonTarget target) { - final AccessibilityManager ams = getSystemService(AccessibilityManager.class); - if (sShortcutType == ACCESSIBILITY_BUTTON) { - ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId()); - } else if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { - ams.performAccessibilityShortcut(target.getId()); - } - } - - private void onToggleTargetSelected(AccessibilityButtonTarget target) { - switchServiceState(target); - } - - private void onLaunchActivityTargetSelected(AccessibilityButtonTarget target) { - final AccessibilityManager ams = getSystemService(AccessibilityManager.class); - if (sShortcutType == ACCESSIBILITY_BUTTON) { - ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId()); - } else if (sShortcutType == ACCESSIBILITY_SHORTCUT_KEY) { - ams.performAccessibilityShortcut(target.getId()); - } - } - - private void switchServiceState(AccessibilityButtonTarget target) { - final ComponentName componentName = - ComponentName.unflattenFromString(target.getId()); - final String componentId = componentName.flattenToString(); - - if (isWhiteListingService(componentId)) { - setWhiteListingServiceEnabled(componentId, - isWhiteListingServiceEnabled(this, target) - ? /* settingsValueOff */ 0 - : /* settingsValueOn */ 1); - } else { - setAccessibilityServiceState(this, componentName, - /* enabled= */!isAccessibilityServiceEnabled(this, target)); - } + final AccessibilityTarget target = mTargets.get(position); + target.onSelected(); + mMenuDialog.dismiss(); } 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)) + final AccessibilityTarget target = mTargets.get(position); + + if ((target instanceof AccessibilityServiceTarget) && !target.isShortcutEnabled()) { + mPermissionDialog = new AlertDialog.Builder(this) + .setView(createEnableDialogContentView(this, + (AccessibilityServiceTarget) target, + v -> { + mPermissionDialog.dismiss(); + mTargetAdapter.notifyDataSetChanged(); + }, + v -> mPermissionDialog.dismiss())) .create(); - mEnableDialog.show(); + mPermissionDialog.show(); return; } - onTargetChecked(mCurrentCheckedTarget, !mCurrentCheckedTarget.isChecked()); - } - - private void onTargetChecked(AccessibilityButtonTarget target, boolean checked) { - switch (target.getFragmentType()) { - case AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE: - onVolumeShortcutToggleTargetChecked(checked); - break; - case AccessibilityFragmentType.INVISIBLE_TOGGLE: - onInvisibleToggleTargetChecked(checked); - break; - case AccessibilityFragmentType.TOGGLE: - onToggleTargetChecked(checked); - break; - case AccessibilityFragmentType.LAUNCH_ACTIVITY: - onLaunchActivityTargetChecked(checked); - break; - default: - throw new IllegalStateException("Unexpected fragment type"); - } - } - - private void onVolumeShortcutToggleTargetChecked(boolean checked) { - 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); - } else { - throw new IllegalStateException("Unexpected shortcut type"); - } - - mCurrentCheckedTarget.setChecked(checked); - mTargetAdapter.notifyDataSetChanged(); - } - - private void onInvisibleToggleTargetChecked(boolean checked) { - final int shortcutTypes = UserShortcutType.SOFTWARE | HARDWARE; - if (!hasValuesInSettings(this, shortcutTypes, mCurrentCheckedTarget.getId())) { - setServiceEnabled(mCurrentCheckedTarget.getId(), checked); - } - - updateValueToSettings(mCurrentCheckedTarget.getId(), checked); - mCurrentCheckedTarget.setChecked(checked); - mTargetAdapter.notifyDataSetChanged(); - } - - private void onToggleTargetChecked(boolean checked) { - updateValueToSettings(mCurrentCheckedTarget.getId(), checked); - mCurrentCheckedTarget.setChecked(checked); + target.onCheckedChanged(!target.isShortcutEnabled()); mTargetAdapter.notifyDataSetChanged(); } - private void onLaunchActivityTargetChecked(boolean checked) { - updateValueToSettings(mCurrentCheckedTarget.getId(), checked); - mCurrentCheckedTarget.setChecked(checked); - mTargetAdapter.notifyDataSetChanged(); - } - - 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)); + mTargets.addAll(getTargets(this, mShortcutType)); if (mTargets.isEmpty()) { - mAlertDialog.dismiss(); + mMenuDialog.dismiss(); return; } mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH); mTargetAdapter.notifyDataSetChanged(); - mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText( + mMenuDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText( getString(R.string.edit_accessibility_shortcut_menu_button)); updateDialogListeners(); @@ -709,11 +144,11 @@ public class AccessibilityShortcutChooserActivity extends Activity { private void onEditButtonClicked() { mTargets.clear(); - mTargets.addAll(getInstalledServiceTargets(this)); + mTargets.addAll(getInstalledTargets(this, mShortcutType)); mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.EDIT); mTargetAdapter.notifyDataSetChanged(); - mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText( + mMenuDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText( getString(R.string.done_accessibility_shortcut_menu_button)); updateDialogListeners(); @@ -724,38 +159,19 @@ public class AccessibilityShortcutChooserActivity extends Activity { (mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT); final int selectDialogTitleId = R.string.accessibility_select_shortcut_menu_title; final int editDialogTitleId = - (sShortcutType == ACCESSIBILITY_BUTTON) + (mShortcutType == 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( + mMenuDialog.setTitle(getString(isEditMenuMode ? editDialogTitleId : selectDialogTitleId)); + mMenuDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener( isEditMenuMode ? view -> onDoneButtonClicked() : view -> onEditButtonClicked()); - mAlertDialog.getListView().setOnItemClickListener( + mMenuDialog.getListView().setOnItemClickListener( isEditMenuMode ? this::onTargetChecked : this::onTargetSelected); } - private static boolean isTargetShortcutUsed(@NonNull Context context, String id) { - final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); - final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(sShortcutType); - return requiredTargets.contains(id); - } - - private void onPermissionAllowButtonClicked(View view) { - if (mCurrentCheckedTarget.getFragmentType() - != AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE) { - 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, + AccessibilityServiceTarget target, View.OnClickListener allowListener, View.OnClickListener denyListener) { final LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE); @@ -775,21 +191,27 @@ public class AccessibilityShortcutChooserActivity extends Activity { encryptionWarningView.setVisibility(View.GONE); } - final ImageView permissionDialogIcon = content.findViewById( + final ImageView dialogIcon = content.findViewById( R.id.accessibility_permissionDialog_icon); - permissionDialogIcon.setImageDrawable(target.getDrawable()); + dialogIcon.setImageDrawable(target.getIcon()); - final TextView permissionDialogTitle = content.findViewById( + final TextView dialogTitle = content.findViewById( R.id.accessibility_permissionDialog_title); - permissionDialogTitle.setText(context.getString(R.string.accessibility_enable_service_title, + dialogTitle.setText(context.getString(R.string.accessibility_enable_service_title, getServiceName(context, target.getLabel()))); - final Button permissionAllowButton = content.findViewById( + final Button allowButton = content.findViewById( R.id.accessibility_permission_enable_allow_button); - final Button permissionDenyButton = content.findViewById( + final Button denyButton = content.findViewById( R.id.accessibility_permission_enable_deny_button); - permissionAllowButton.setOnClickListener(allowListener); - permissionDenyButton.setOnClickListener(denyListener); + allowButton.setOnClickListener((view) -> { + target.onCheckedChanged(/* isChecked= */ true); + allowListener.onClick(view); + }); + denyButton.setOnClickListener((view) -> { + target.onCheckedChanged(/* isChecked= */ false); + denyListener.onClick(view); + }); return content; } diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java new file mode 100644 index 000000000000..2772e2ce0fd4 --- /dev/null +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2020 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.internal.accessibility.dialog; + +import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; + +import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME; +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.util.AccessibilityUtils.getAccessibilityServiceFragmentType; +import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityShortcutInfo; +import android.annotation.NonNull; +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.os.Build; +import android.provider.Settings; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityManager.ShortcutType; + +import com.android.internal.R; +import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Collection of utilities for accessibility target. + */ +final class AccessibilityTargetHelper { + private AccessibilityTargetHelper() {} + + static List<AccessibilityTarget> getTargets(Context context, + @ShortcutType int shortcutType) { + final List<AccessibilityTarget> targets = getInstalledTargets(context, shortcutType); + final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); + final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType); + targets.removeIf(target -> !requiredTargets.contains(target.getId())); + + return targets; + } + + static List<AccessibilityTarget> getInstalledTargets(Context context, + @ShortcutType int shortcutType) { + final List<AccessibilityTarget> targets = new ArrayList<>(); + targets.addAll(getAccessibilityFilteredTargets(context, shortcutType)); + targets.addAll(getWhiteListingFeatureTargets(context, shortcutType)); + + return targets; + } + + private static List<AccessibilityTarget> getAccessibilityFilteredTargets(Context context, + @ShortcutType int shortcutType) { + final List<AccessibilityTarget> serviceTargets = + getAccessibilityServiceTargets(context, shortcutType); + final List<AccessibilityTarget> activityTargets = + getAccessibilityActivityTargets(context, shortcutType); + + for (AccessibilityTarget activityTarget : activityTargets) { + serviceTargets.removeIf( + serviceTarget -> arePackageNameAndLabelTheSame(serviceTarget, activityTarget)); + } + + final List<AccessibilityTarget> targets = new ArrayList<>(); + targets.addAll(serviceTargets); + targets.addAll(activityTargets); + + return targets; + } + + private static boolean arePackageNameAndLabelTheSame(@NonNull AccessibilityTarget serviceTarget, + @NonNull AccessibilityTarget activityTarget) { + final ComponentName serviceComponentName = + ComponentName.unflattenFromString(serviceTarget.getId()); + final ComponentName activityComponentName = + ComponentName.unflattenFromString(activityTarget.getId()); + final boolean isSamePackageName = activityComponentName.getPackageName().equals( + serviceComponentName.getPackageName()); + final boolean isSameLabel = activityTarget.getLabel().equals( + serviceTarget.getLabel()); + + return isSamePackageName && isSameLabel; + } + + private static List<AccessibilityTarget> getAccessibilityServiceTargets(Context context, + @ShortcutType int shortcutType) { + final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); + final List<AccessibilityServiceInfo> installedServices = + ams.getInstalledAccessibilityServiceList(); + if (installedServices == null) { + return Collections.emptyList(); + } + + final List<AccessibilityTarget> 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.Q) && !hasRequestAccessibilityButtonFlag + && (shortcutType == ACCESSIBILITY_BUTTON)) { + continue; + } + + targets.add(createAccessibilityServiceTarget(context, shortcutType, info)); + } + + return targets; + } + + private static List<AccessibilityTarget> getAccessibilityActivityTargets(Context context, + @ShortcutType int shortcutType) { + final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); + final List<AccessibilityShortcutInfo> installedServices = + ams.getInstalledAccessibilityShortcutListAsUser(context, + ActivityManager.getCurrentUser()); + if (installedServices == null) { + return Collections.emptyList(); + } + + final List<AccessibilityTarget> targets = new ArrayList<>(installedServices.size()); + for (AccessibilityShortcutInfo info : installedServices) { + targets.add(new AccessibilityActivityTarget(context, shortcutType, info)); + } + + return targets; + } + + private static List<AccessibilityTarget> getWhiteListingFeatureTargets(Context context, + @ShortcutType int shortcutType) { + final List<AccessibilityTarget> targets = new ArrayList<>(); + + final InvisibleToggleWhiteListingFeatureTarget magnification = + new InvisibleToggleWhiteListingFeatureTarget(context, + shortcutType, + isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME), + MAGNIFICATION_CONTROLLER_NAME, + context.getString(R.string.accessibility_magnification_chooser_text), + context.getDrawable(R.drawable.ic_accessibility_magnification), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); + + final ToggleWhiteListingFeatureTarget daltonizer = + new ToggleWhiteListingFeatureTarget(context, + shortcutType, + isShortcutContained(context, shortcutType, + DALTONIZER_COMPONENT_NAME.flattenToString()), + DALTONIZER_COMPONENT_NAME.flattenToString(), + context.getString(R.string.color_correction_feature_name), + context.getDrawable(R.drawable.ic_accessibility_color_correction), + Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED); + + final ToggleWhiteListingFeatureTarget colorInversion = + new ToggleWhiteListingFeatureTarget(context, + shortcutType, + isShortcutContained(context, shortcutType, + COLOR_INVERSION_COMPONENT_NAME.flattenToString()), + COLOR_INVERSION_COMPONENT_NAME.flattenToString(), + context.getString(R.string.color_inversion_feature_name), + context.getDrawable(R.drawable.ic_accessibility_color_inversion), + Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); + + targets.add(magnification); + targets.add(daltonizer); + targets.add(colorInversion); + + return targets; + } + + private static AccessibilityTarget createAccessibilityServiceTarget(Context context, + @ShortcutType int shortcutType, @NonNull AccessibilityServiceInfo info) { + switch (getAccessibilityServiceFragmentType(info)) { + case AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE: + return new VolumeShortcutToggleAccessibilityServiceTarget(context, shortcutType, + info); + case AccessibilityFragmentType.INVISIBLE_TOGGLE: + return new InvisibleToggleAccessibilityServiceTarget(context, shortcutType, info); + case AccessibilityFragmentType.TOGGLE: + return new ToggleAccessibilityServiceTarget(context, shortcutType, info); + default: + throw new IllegalStateException("Unexpected fragment type"); + } + } +} diff --git a/core/java/com/android/internal/accessibility/dialog/ShortcutTargetAdapter.java b/core/java/com/android/internal/accessibility/dialog/ShortcutTargetAdapter.java new file mode 100644 index 000000000000..b7605b7fc011 --- /dev/null +++ b/core/java/com/android/internal/accessibility/dialog/ShortcutTargetAdapter.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 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.internal.accessibility.dialog; + +import android.annotation.NonNull; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.R; +import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode; + +import java.util.List; + +/** + * Extension for {@link TargetAdapter} and used for AccessibilityShortcutChooserActivity. + */ +class ShortcutTargetAdapter extends TargetAdapter { + @ShortcutMenuMode + private int mShortcutMenuMode = ShortcutMenuMode.LAUNCH; + private final List<AccessibilityTarget> mTargets; + + ShortcutTargetAdapter(@NonNull List<AccessibilityTarget> targets) { + mTargets = targets; + } + + @Override + public int getCount() { + return mTargets.size(); + } + + @Override + public Object getItem(int position) { + return mTargets.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final Context context = parent.getContext(); + ViewHolder holder; + if (convertView == null) { + convertView = LayoutInflater.from(context).inflate( + R.layout.accessibility_shortcut_chooser_item, parent, /* attachToRoot= */ + false); + holder = new ViewHolder(); + holder.mCheckBoxView = convertView.findViewById( + R.id.accessibility_shortcut_target_checkbox); + holder.mIconView = convertView.findViewById(R.id.accessibility_shortcut_target_icon); + holder.mLabelView = convertView.findViewById( + R.id.accessibility_shortcut_target_label); + holder.mSwitchItem = convertView.findViewById( + R.id.accessibility_shortcut_target_switch_item); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + final AccessibilityTarget target = mTargets.get(position); + target.updateActionItem(holder, mShortcutMenuMode); + + return convertView; + } + + void setShortcutMenuMode(@ShortcutMenuMode int shortcutMenuMode) { + mShortcutMenuMode = shortcutMenuMode; + } + + @ShortcutMenuMode + int getShortcutMenuMode() { + return mShortcutMenuMode; + } +} |