Merge changes from topic "edit_menu_v2" into rvc-dev

* changes:
  Update the edit shortcut menu (4/n).
  Update the edit shortcut menu (3/n).
  Update the edit shortcut menu (2/n).
This commit is contained in:
PETER LIANG
2020-03-09 07:57:01 +00:00
committed by Android (Google) Code Review
9 changed files with 573 additions and 178 deletions

View File

@@ -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.
@@ -79,6 +77,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.
*

View File

@@ -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.
*

View File

@@ -23,10 +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;
@@ -36,6 +36,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;
@@ -50,11 +51,12 @@ 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.os.storage.StorageManager;
import android.provider.Settings;
import android.text.BidiFormatter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -62,28 +64,33 @@ import android.view.Window;
import android.view.accessibility.AccessibilityManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
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;
/**
* Activity used to display and persist a service or feature target for the Accessibility button.
*/
public class AccessibilityButtonChooserActivity extends Activity {
@ShortcutType
private int mShortcutType;
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 = {
{
@@ -118,19 +125,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);
mAlertDialog = new AlertDialog.Builder(this)
.setTitle(selectDialogTitle)
.setAdapter(mTargetAdapter, /* listener= */ null)
@@ -151,15 +158,21 @@ public class AccessibilityButtonChooserActivity extends Activity {
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(getAccessibilityServiceTargets(context));
targets.addAll(getAccessibilityActivityTargets(context));
targets.addAll(getWhiteListingServiceTargets(context));
final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType);
targets.removeIf(target -> !requiredTargets.contains(target.getId()));
return targets;
}
@@ -174,6 +187,14 @@ public class AccessibilityButtonChooserActivity extends Activity {
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));
}
@@ -249,35 +270,31 @@ 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;
}
private static class TargetAdapter extends BaseAdapter {
@ShortcutMenuMode
private int mShortcutMenuMode = ShortcutMenuMode.LAUNCH;
@ShortcutType
private int mShortcutButtonType;
private List<AccessibilityButtonTarget> mButtonTargets;
TargetAdapter(List<AccessibilityButtonTarget> targets,
@ShortcutType int shortcutButtonType) {
TargetAdapter(List<AccessibilityButtonTarget> targets) {
this.mButtonTargets = targets;
this.mShortcutButtonType = shortcutButtonType;
}
void setShortcutMenuMode(@ShortcutMenuMode int shortcutMenuMode) {
@@ -314,13 +331,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 +344,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,58 +354,42 @@ 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);
final boolean enabledState = (isLaunchMenuMode || isHardwareButtonTriggered);
final ColorMatrix grayScaleMatrix = new ColorMatrix();
grayScaleMatrix.setSaturation(/* grayScale */0);
holder.mIconView.setColorFilter(enabledState
? null : new ColorMatrixColorFilter(grayScaleMatrix));
holder.mIconView.setAlpha(enabledState
? ENABLED_ALPHA : DISABLED_ALPHA);
holder.mLabelView.setEnabled(enabledState);
holder.mActionViewItem.setEnabled(enabledState);
holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
holder.mActionViewItem.setVisibility(View.VISIBLE);
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);
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) {
holder.mIconView.setColorFilter(null);
holder.mIconView.setAlpha(ENABLED_ALPHA);
holder.mLabelView.setEnabled(true);
holder.mActionViewItem.setEnabled(true);
holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
holder.mActionViewItem.setVisibility(View.VISIBLE);
private void updateInvisibleActionItemVisibility(@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);
holder.mItemContainer.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
? View.VISIBLE : View.GONE);
holder.mItemView.setEnabled(true);
holder.mItemView.setClickable(false);
}
private void updateIntuitiveActionItemVisibility(@NonNull Context context,
@@ -403,37 +399,31 @@ public class AccessibilityButtonChooserActivity extends Activity {
? isWhiteListingServiceEnabled(context, target)
: isAccessibilityServiceEnabled(context, target);
holder.mIconView.setColorFilter(null);
holder.mIconView.setAlpha(ENABLED_ALPHA);
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.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);
holder.mItemContainer.setVisibility(View.VISIBLE);
holder.mItemView.setEnabled(true);
holder.mItemView.setClickable(false);
}
private void updateBounceActionItemVisibility(@NonNull Context context,
@NonNull ViewHolder holder) {
holder.mIconView.setColorFilter(null);
holder.mIconView.setAlpha(ENABLED_ALPHA);
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);
private void updateBounceActionItemVisibility(@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);
holder.mItemContainer.setVisibility(View.VISIBLE);
holder.mItemView.setEnabled(true);
holder.mItemView.setClickable(false);
}
}
private static class AccessibilityButtonTarget {
private String mId;
@TargetType
private int mType;
private boolean mChecked;
private CharSequence mLabel;
private Drawable mDrawable;
@AccessibilityServiceFragmentType
@@ -442,6 +432,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 +442,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 +452,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 +527,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 +550,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 +573,101 @@ 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);
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());
}
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");
}
}
private void onLegacyTargetChecked(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 onInvisibleTargetChecked(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 onIntuitiveTargetChecked(boolean checked) {
updateValueToSettings(mCurrentCheckedTarget.getId(), checked);
mCurrentCheckedTarget.setChecked(checked);
mTargetAdapter.notifyDataSetChanged();
}
private void onBounceTargetChecked(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));
if (mTargets.isEmpty()) {
mAlertDialog.dismiss();
}
}
private void onLegacyTargetDeleted(int position, String componentId) {
if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
optOutValueFromSettings(this, mShortcutUserType, componentId);
mTargets.remove(position);
mTargetAdapter.notifyDataSetChanged();
}
}
private void onInvisibleTargetDeleted(int position, String componentId) {
optOutValueFromSettings(this, mShortcutUserType, componentId);
final int shortcutTypes = UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE;
if (!hasValuesInSettings(this, shortcutTypes, componentId)) {
disableService(componentId);
return;
}
mTargets.remove(position);
mTargetAdapter.notifyDataSetChanged();
}
private void onIntuitiveTargetDeleted(int position, String componentId) {
optOutValueFromSettings(this, mShortcutUserType, componentId);
mTargets.remove(position);
mTargetAdapter.notifyDataSetChanged();
}
private void onBounceTargetDeleted(int position, String componentId) {
optOutValueFromSettings(this, mShortcutUserType, componentId);
mTargets.remove(position);
mTargetAdapter.notifyDataSetChanged();
}
private void onCancelButtonClicked() {
mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
mTargetAdapter.notifyDataSetChanged();
@@ -635,11 +678,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 +694,78 @@ 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<String> 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);
}
}

View File

@@ -0,0 +1,26 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:pathData="M23,5.5V20c0,2.2 -1.8,4 -4,4h-7.3c-1.08,0 -2.1,-0.43 -2.85,-1.19L1,14.83c0,0 1.26,-1.23 1.3,-1.25c0.22,-0.19 0.49,-0.29 0.79,-0.29c0.22,0 0.42,0.06 0.6,0.16C3.73,13.46 8,15.91 8,15.91V4c0,-0.83 0.67,-1.5 1.5,-1.5S11,3.17 11,4v7h1V1.5C12,0.67 12.67,0 13.5,0S15,0.67 15,1.5V11h1V2.5C16,1.67 16.67,1 17.5,1S19,1.67 19,2.5V11h1V5.5C20,4.67 20.67,4 21.5,4S23,4.67 23,5.5z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@@ -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.
-->
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
@@ -21,6 +21,6 @@
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@@ -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">
<CheckBox
android:id="@+id/accessibility_button_target_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:background="@null"
android:clickable="false"
android:focusable="false"
android:visibility="gone"/>
<ImageView
android:id="@+id/accessibility_button_target_icon"
@@ -36,31 +43,18 @@
android:id="@+id/accessibility_button_target_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:textSize="20sp"
android:textColor="?attr/textColorPrimary"
android:fontFamily="sans-serif-medium"/>
<FrameLayout
android:id="@+id/accessibility_button_target_item_container"
<Switch
android:id="@+id/accessibility_button_target_switch_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="56dp">
<ImageView
android:id="@+id/accessibility_button_target_view_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<Switch android:id="@+id/accessibility_button_target_switch_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@null"
android:clickable="false"
android:focusable="false"/>
</FrameLayout>
android:background="@null"
android:clickable="false"
android:focusable="false"/>
</LinearLayout>

View File

@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textDirection="locale"
android:scrollbarStyle="outsideOverlay">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/accessibility_permissionDialog_icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:scaleType="fitCenter"/>
<TextView
android:id="@+id/accessibility_permissionDialog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20sp"
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="google-sans-medium"/>
<TextView
android:id="@+id/accessibility_encryption_warning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dip"
android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<TextView
android:id="@+id/accessibility_permissionDialog_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="32dp"
android:text="@string/accessibility_service_warning_description"
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="sans-serif"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/accessibility_controlScreen_icon"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginRight="12dp"
android:src="@android:drawable/ic_visibility"
android:scaleType="fitCenter"/>
<TextView
android:id="@+id/accessibility_controlScreen_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/accessibility_service_screen_control_title"
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="sans-serif"/>
</LinearLayout>
<TextView
android:id="@+id/accessibility_controlScreen_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:paddingStart="30dp"
android:text="@string/accessibility_service_screen_control_description"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:fontFamily="sans-serif"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/accessibility_performAction_icon"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginEnd="12dp"
android:src="@android:drawable/ic_pan_tool"
android:scaleType="fitCenter"/>
<TextView
android:id="@+id/accessibility_performAction_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/accessibility_service_action_perform_title"
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="sans-serif"/>
</LinearLayout>
<TextView
android:id="@+id/accessibility_performAction_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:paddingStart="30dp"
android:text="@string/accessibility_service_action_perform_description"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:fontFamily="sans-serif" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/chooser_row_divider"/>
<Button
android:id="@+id/accessibility_permission_enable_allow_button"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?android:attr/selectableItemBackground"
android:text="@string/accessibility_dialog_button_allow"
style="?attr/buttonBarPositiveButtonStyle"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/chooser_row_divider"/>
<Button
android:id="@+id/accessibility_permission_enable_deny_button"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?android:attr/selectableItemBackground"
android:text="@string/accessibility_dialog_button_deny"
style="?attr/buttonBarPositiveButtonStyle"/>
</LinearLayout>
</ScrollView>

View File

@@ -4403,27 +4403,72 @@
accessibility feature.
</string>
<!-- Title for a warning about security implications of enabling an accessibility
service. [CHAR LIMIT=NONE] -->
<string name="accessibility_enable_service_title">Allow
<xliff:g id="service" example="TalkBack">%1$s</xliff:g> to have full control of your
device?</string>
<!-- Warning that the device data will not be encrypted with password or PIN if
enabling an accessibility service and there is a secure lock setup. [CHAR LIMIT=NONE] -->
<string name="accessibility_enable_service_encryption_warning">If you turn on
<xliff:g id="service" example="TalkBack">%1$s</xliff:g>, your device wont use your screen
lock to enhance data encryption.
</string>
<!-- Warning description that explains that it's appropriate for accessibility
services to have full control to help users with accessibility needs. [CHAR LIMIT=NONE] -->
<string name="accessibility_service_warning_description">Full control is appropriate for apps
that help you with accessibility needs, but not for most apps.
</string>
<!-- Title for the screen control in accessibility dialog. [CHAR LIMIT=NONE] -->
<string name="accessibility_service_screen_control_title">View and control screen</string>
<!-- Description for the screen control in accessibility dialog. [CHAR LIMIT=NONE] -->
<string name="accessibility_service_screen_control_description">It can read all content on the
screen and display content over other apps.
</string>
<!-- Title for the action perform in accessibility dialog. [CHAR LIMIT=NONE] -->
<string name="accessibility_service_action_perform_title">View and perform actions</string>
<!-- Description for the action perform in accessibility dialog. [CHAR LIMIT=NONE] -->
<string name="accessibility_service_action_perform_description">It can track your interactions
with an app or a hardware sensor, and interact with apps on your behalf.
</string>
<!-- String for the allow button in accessibility permission dialog. [CHAR LIMIT=10] -->
<string name="accessibility_dialog_button_allow">Allow</string>
<!-- String for the deny button in accessibility permission dialog. [CHAR LIMIT=10] -->
<string name="accessibility_dialog_button_deny">Deny</string>
<!-- Title for accessibility select shortcut menu dialog. [CHAR LIMIT=100] -->
<string name="accessibility_select_shortcut_menu_title">Tap the accessibility app you want to use</string>
<string name="accessibility_select_shortcut_menu_title">Tap a feature to start using it:</string>
<!-- Title for accessibility edit shortcut selection menu dialog, and dialog is triggered
from accessibility button. [CHAR LIMIT=100] -->
<string name="accessibility_edit_shortcut_menu_button_title">Choose apps you want to use with
<string name="accessibility_edit_shortcut_menu_button_title">Choose apps to use with the
accessibility button
</string>
<!-- Title for accessibility edit shortcut selection menu dialog, and dialog is triggered
from volume key shortcut. [CHAR LIMIT=100] -->
<string name="accessibility_edit_shortcut_menu_volume_title">Choose apps you want to use with
<string name="accessibility_edit_shortcut_menu_volume_title">Choose apps to use with the
volume key shortcut
</string>
<!-- Text for showing the warning to user when uncheck the legacy app item in the accessibility
shortcut menu, user can aware the service to be disabled. [CHAR LIMIT=100] -->
<string name="accessibility_uncheck_legacy_item_warning">
<xliff:g id="service_name" example="TalkBack">%s</xliff:g> has been turned off</string>
<!-- Text in button that edit the accessibility shortcut menu, user can delete
any service item in the menu list. [CHAR LIMIT=100] -->
<string name="edit_accessibility_shortcut_menu_button">Edit shortcuts</string>
<!-- Text in button that cancel the accessibility shortcut menu changed status. [CHAR LIMIT=100] -->
<string name="cancel_accessibility_shortcut_menu_button">Cancel</string>
<!-- Text in button that complete the accessibility shortcut menu changed status. [CHAR LIMIT=100] -->
<string name="done_accessibility_shortcut_menu_button">Done</string>
<!-- Text in button that turns off the accessibility shortcut -->
<string name="disable_accessibility_shortcut">Turn off Shortcut</string>

View File

@@ -3240,24 +3240,31 @@
<java-symbol type="string" name="accessibility_select_shortcut_menu_title" />
<java-symbol type="string" name="accessibility_edit_shortcut_menu_button_title" />
<java-symbol type="string" name="accessibility_edit_shortcut_menu_volume_title" />
<java-symbol type="string" name="accessibility_uncheck_legacy_item_warning" />
<java-symbol type="layout" name="accessibility_enable_service_encryption_warning" />
<java-symbol type="id" name="accessibility_permissionDialog_icon" />
<java-symbol type="id" name="accessibility_permissionDialog_title" />
<java-symbol type="id" name="accessibility_encryption_warning" />
<java-symbol type="id" name="accessibility_permission_enable_allow_button" />
<java-symbol type="id" name="accessibility_permission_enable_deny_button" />
<java-symbol type="string" name="accessibility_enable_service_title" />
<java-symbol type="string" name="accessibility_enable_service_encryption_warning" />
<!-- Accessibility Button -->
<java-symbol type="layout" name="accessibility_button_chooser_item" />
<java-symbol type="id" name="accessibility_button_target_checkbox" />
<java-symbol type="id" name="accessibility_button_target_icon" />
<java-symbol type="id" name="accessibility_button_target_label" />
<java-symbol type="id" name="accessibility_button_target_item_container" />
<java-symbol type="id" name="accessibility_button_target_view_item" />
<java-symbol type="id" name="accessibility_button_target_switch_item" />
<java-symbol type="string" name="accessibility_magnification_chooser_text" />
<java-symbol type="string" name="edit_accessibility_shortcut_menu_button" />
<java-symbol type="string" name="cancel_accessibility_shortcut_menu_button" />
<java-symbol type="string" name="done_accessibility_shortcut_menu_button" />
<java-symbol type="drawable" name="ic_accessibility_color_inversion" />
<java-symbol type="drawable" name="ic_accessibility_color_correction" />
<java-symbol type="drawable" name="ic_accessibility_magnification" />
<java-symbol type="drawable" name="ic_delete_item" />
<!-- com.android.internal.widget.RecyclerView -->
<java-symbol type="id" name="item_touch_helper_previous_elevation"/>
<java-symbol type="dimen" name="item_touch_helper_max_drag_scroll_per_frame"/>