Merge changes from topic "redesign shortcut chooser" into rvc-dev
* changes: Redesign for AccessibilityShortcutChooserActivity (3/n). Redesign for AccessibilityShortcutChooserActivity (2/n). Redesign for AccessibilityShortcutChooserActivity (1/n).
This commit is contained in:
@@ -78,21 +78,6 @@ public final class ShortcutConstants {
|
||||
int LAUNCH_ACTIVITY = 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.
|
||||
*
|
||||
@@ -108,30 +93,4 @@ public final class ShortcutConstants {
|
||||
int LAUNCH = 0;
|
||||
int EDIT = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotation for align the element index of white listing feature
|
||||
* {@code WHITE_LISTING_FEATURES}.
|
||||
*
|
||||
* {@code COMPONENT_ID} is to get the service component name.
|
||||
* {@code LABEL_ID} is to get the service label text.
|
||||
* {@code ICON_ID} is to get the service icon.
|
||||
* {@code FRAGMENT_TYPE} is to get the service fragment type.
|
||||
* {@code SETTINGS_KEY} is to get the service settings key.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
WhiteListingFeatureElementIndex.COMPONENT_ID,
|
||||
WhiteListingFeatureElementIndex.LABEL_ID,
|
||||
WhiteListingFeatureElementIndex.ICON_ID,
|
||||
WhiteListingFeatureElementIndex.FRAGMENT_TYPE,
|
||||
WhiteListingFeatureElementIndex.SETTINGS_KEY,
|
||||
})
|
||||
public @interface WhiteListingFeatureElementIndex {
|
||||
int COMPONENT_ID = 0;
|
||||
int LABEL_ID = 1;
|
||||
int ICON_ID = 2;
|
||||
int FRAGMENT_TYPE = 3;
|
||||
int SETTINGS_KEY = 4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 com.android.internal.accessibility.util.ShortcutUtils.convertToKey;
|
||||
import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType;
|
||||
import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained;
|
||||
|
||||
import android.accessibilityservice.AccessibilityShortcutInfo;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||
|
||||
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
||||
|
||||
/**
|
||||
* Base class for creating accessibility activity target.
|
||||
*/
|
||||
class AccessibilityActivityTarget extends AccessibilityTarget {
|
||||
|
||||
AccessibilityActivityTarget(Context context, @ShortcutType int shortcutType,
|
||||
@NonNull AccessibilityShortcutInfo shortcutInfo) {
|
||||
super(context,
|
||||
shortcutType,
|
||||
AccessibilityFragmentType.LAUNCH_ACTIVITY,
|
||||
isShortcutContained(context, shortcutType,
|
||||
shortcutInfo.getComponentName().flattenToString()),
|
||||
shortcutInfo.getComponentName().flattenToString(),
|
||||
shortcutInfo.getActivityInfo().loadLabel(context.getPackageManager()),
|
||||
shortcutInfo.getActivityInfo().loadIcon(context.getPackageManager()),
|
||||
convertToKey(convertToUserType(shortcutType)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 com.android.internal.accessibility.util.ShortcutUtils.convertToKey;
|
||||
import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType;
|
||||
import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||
|
||||
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
||||
|
||||
/**
|
||||
* Base class for creating accessibility service target with various fragment types related to
|
||||
* legacy type, invisible type and intuitive type.
|
||||
*/
|
||||
class AccessibilityServiceTarget extends AccessibilityTarget {
|
||||
|
||||
AccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
|
||||
@AccessibilityFragmentType int fragmentType,
|
||||
@NonNull AccessibilityServiceInfo serviceInfo) {
|
||||
super(context,
|
||||
shortcutType,
|
||||
fragmentType,
|
||||
isShortcutContained(context, shortcutType,
|
||||
serviceInfo.getComponentName().flattenToString()),
|
||||
serviceInfo.getComponentName().flattenToString(),
|
||||
serviceInfo.getResolveInfo().loadLabel(context.getPackageManager()),
|
||||
serviceInfo.getResolveInfo().loadIcon(context.getPackageManager()),
|
||||
convertToKey(convertToUserType(shortcutType)));
|
||||
}
|
||||
}
|
||||
@@ -19,64 +19,27 @@ 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.createEnableDialogContentView;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Activity used to display various targets related to accessibility service, accessibility
|
||||
@@ -84,38 +47,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 +62,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 +81,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);
|
||||
final AccessibilityTarget target = mTargets.get(position);
|
||||
|
||||
if ((mCurrentCheckedTarget.getType() == TargetType.ACCESSIBILITY_SERVICE)
|
||||
&& !mCurrentCheckedTarget.isChecked()) {
|
||||
mEnableDialog = new AlertDialog.Builder(this)
|
||||
.setView(createEnableDialogContentView(this, mCurrentCheckedTarget,
|
||||
this::onPermissionAllowButtonClicked,
|
||||
this::onPermissionDenyButtonClicked))
|
||||
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);
|
||||
target.onCheckedChanged(!target.isShortcutEnabled());
|
||||
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);
|
||||
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 +137,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,79 +152,14 @@ 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,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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 android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
|
||||
|
||||
import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType;
|
||||
import static com.android.internal.accessibility.util.ShortcutUtils.optInValueToSettings;
|
||||
import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||
|
||||
import com.android.internal.accessibility.common.ShortcutConstants;
|
||||
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
||||
import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
|
||||
|
||||
/**
|
||||
* Abstract base class for creating various target related to accessibility service,
|
||||
* accessibility activity, and white listing feature.
|
||||
*/
|
||||
abstract class AccessibilityTarget implements TargetOperations, OnTargetSelectedListener,
|
||||
OnTargetCheckedChangeListener {
|
||||
private Context mContext;
|
||||
@ShortcutType
|
||||
private int mShortcutType;
|
||||
@AccessibilityFragmentType
|
||||
private int mFragmentType;
|
||||
private boolean mShortcutEnabled;
|
||||
private String mId;
|
||||
private CharSequence mLabel;
|
||||
private Drawable mIcon;
|
||||
private String mKey;
|
||||
|
||||
AccessibilityTarget(Context context, @ShortcutType int shortcutType,
|
||||
@AccessibilityFragmentType int fragmentType, boolean isShortcutSwitched, String id,
|
||||
CharSequence label, Drawable icon, String key) {
|
||||
mContext = context;
|
||||
mShortcutType = shortcutType;
|
||||
mFragmentType = fragmentType;
|
||||
mShortcutEnabled = isShortcutSwitched;
|
||||
mId = id;
|
||||
mLabel = label;
|
||||
mIcon = icon;
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateActionItem(@NonNull ViewHolder holder,
|
||||
@ShortcutConstants.ShortcutMenuMode int shortcutMenuMode) {
|
||||
final boolean isEditMenuMode =
|
||||
shortcutMenuMode == ShortcutConstants.ShortcutMenuMode.EDIT;
|
||||
|
||||
holder.mCheckBoxView.setChecked(isEditMenuMode && isShortcutEnabled());
|
||||
holder.mCheckBoxView.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
|
||||
holder.mIconView.setImageDrawable(getIcon());
|
||||
holder.mLabelView.setText(getLabel());
|
||||
holder.mSwitchItem.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelected() {
|
||||
final AccessibilityManager am =
|
||||
getContext().getSystemService(AccessibilityManager.class);
|
||||
switch (getShortcutType()) {
|
||||
case ACCESSIBILITY_BUTTON:
|
||||
am.notifyAccessibilityButtonClicked(getContext().getDisplayId(), getId());
|
||||
return;
|
||||
case ACCESSIBILITY_SHORTCUT_KEY:
|
||||
am.performAccessibilityShortcut(getId());
|
||||
return;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected shortcut type");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(boolean isChecked) {
|
||||
setShortcutEnabled(isChecked);
|
||||
if (isChecked) {
|
||||
optInValueToSettings(getContext(), convertToUserType(getShortcutType()), getId());
|
||||
} else {
|
||||
optOutValueFromSettings(getContext(), convertToUserType(getShortcutType()), getId());
|
||||
}
|
||||
}
|
||||
|
||||
public void setShortcutEnabled(boolean enabled) {
|
||||
mShortcutEnabled = enabled;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public @ShortcutType int getShortcutType() {
|
||||
return mShortcutType;
|
||||
}
|
||||
|
||||
public @AccessibilityFragmentType int getFragmentType() {
|
||||
return mFragmentType;
|
||||
}
|
||||
|
||||
public boolean isShortcutEnabled() {
|
||||
return mShortcutEnabled;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public CharSequence getLabel() {
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public Drawable getIcon() {
|
||||
return mIcon;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return mKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* 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.os.storage.StorageManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.BidiFormatter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
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;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
}
|
||||
|
||||
static View createEnableDialogContentView(Context context,
|
||||
AccessibilityServiceTarget 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 dialogIcon = content.findViewById(
|
||||
R.id.accessibility_permissionDialog_icon);
|
||||
dialogIcon.setImageDrawable(target.getIcon());
|
||||
|
||||
final TextView dialogTitle = content.findViewById(
|
||||
R.id.accessibility_permissionDialog_title);
|
||||
dialogTitle.setText(context.getString(R.string.accessibility_enable_service_title,
|
||||
getServiceName(context, target.getLabel())));
|
||||
|
||||
final Button allowButton = content.findViewById(
|
||||
R.id.accessibility_permission_enable_allow_button);
|
||||
final Button denyButton = content.findViewById(
|
||||
R.id.accessibility_permission_enable_deny_button);
|
||||
allowButton.setOnClickListener((view) -> {
|
||||
target.onCheckedChanged(/* isChecked= */ true);
|
||||
allowListener.onClick(view);
|
||||
});
|
||||
denyButton.setOnClickListener((view) -> {
|
||||
target.onCheckedChanged(/* isChecked= */ false);
|
||||
denyListener.onClick(view);
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
|
||||
|
||||
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
|
||||
import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState;
|
||||
import static com.android.internal.accessibility.util.ShortcutUtils.isComponentIdExistingInSettings;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||
|
||||
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
||||
|
||||
/**
|
||||
* Extension for {@link AccessibilityServiceTarget} with
|
||||
* {@link AccessibilityFragmentType#INVISIBLE_TOGGLE} type.
|
||||
*/
|
||||
class InvisibleToggleAccessibilityServiceTarget extends AccessibilityServiceTarget {
|
||||
|
||||
InvisibleToggleAccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
|
||||
@NonNull AccessibilityServiceInfo serviceInfo) {
|
||||
super(context,
|
||||
shortcutType,
|
||||
AccessibilityFragmentType.INVISIBLE_TOGGLE,
|
||||
serviceInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(boolean isChecked) {
|
||||
final ComponentName componentName = ComponentName.unflattenFromString(getId());
|
||||
|
||||
if (!isComponentIdExistingInOtherShortcut()) {
|
||||
setAccessibilityServiceState(getContext(), componentName, isChecked);
|
||||
}
|
||||
|
||||
super.onCheckedChanged(isChecked);
|
||||
}
|
||||
|
||||
private boolean isComponentIdExistingInOtherShortcut() {
|
||||
switch (getShortcutType()) {
|
||||
case ACCESSIBILITY_BUTTON:
|
||||
return isComponentIdExistingInSettings(getContext(), UserShortcutType.HARDWARE,
|
||||
getId());
|
||||
case ACCESSIBILITY_SHORTCUT_KEY:
|
||||
return isComponentIdExistingInSettings(getContext(), UserShortcutType.SOFTWARE,
|
||||
getId());
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected shortcut type");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||
|
||||
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
||||
|
||||
/**
|
||||
* Extension for {@link AccessibilityTarget} with {@link AccessibilityFragmentType#INVISIBLE_TOGGLE}
|
||||
* type.
|
||||
*/
|
||||
class InvisibleToggleWhiteListingFeatureTarget extends AccessibilityTarget {
|
||||
|
||||
InvisibleToggleWhiteListingFeatureTarget(Context context, @ShortcutType int shortcutType,
|
||||
boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) {
|
||||
super(context, shortcutType, AccessibilityFragmentType.INVISIBLE_TOGGLE,
|
||||
isShortcutSwitched, id, label, icon, key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when the checked state
|
||||
* of a accessibility target changed.
|
||||
*/
|
||||
interface OnTargetCheckedChangeListener {
|
||||
/**
|
||||
* Called when the checked state of a accessibility target has changed.
|
||||
*
|
||||
* @param isChecked The new checked state of accessibility target.
|
||||
*/
|
||||
void onCheckedChanged(boolean isChecked);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when a accessibility target is selected.
|
||||
*/
|
||||
interface OnTargetSelectedListener {
|
||||
/**
|
||||
* Called when a accessibility target has been selected.
|
||||
*/
|
||||
void onSelected();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.widget.BaseAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* Abstract base class for creating target adapter for chooser activity.
|
||||
*/
|
||||
abstract class TargetAdapter extends BaseAdapter {
|
||||
static class ViewHolder{
|
||||
CheckBox mCheckBoxView;
|
||||
ImageView mIconView;
|
||||
TextView mLabelView;
|
||||
Switch mSwitchItem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
|
||||
import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
|
||||
|
||||
/**
|
||||
* Interface definition for operations with a accessibility target that was invoked.
|
||||
*/
|
||||
interface TargetOperations {
|
||||
/**
|
||||
* Called when a accessibility target has been invoked and notified to update latest status.
|
||||
*/
|
||||
void updateActionItem(@NonNull ViewHolder holder,
|
||||
@ShortcutMenuMode int shortcutMenuMode);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 com.android.internal.accessibility.util.AccessibilityUtils.isAccessibilityServiceEnabled;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||
|
||||
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
||||
import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
|
||||
import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
|
||||
|
||||
/**
|
||||
* Extension for {@link AccessibilityServiceTarget} with {@link AccessibilityFragmentType#TOGGLE}
|
||||
* type.
|
||||
*/
|
||||
class ToggleAccessibilityServiceTarget extends AccessibilityServiceTarget {
|
||||
|
||||
ToggleAccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
|
||||
@NonNull AccessibilityServiceInfo serviceInfo) {
|
||||
super(context,
|
||||
shortcutType,
|
||||
AccessibilityFragmentType.TOGGLE,
|
||||
serviceInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateActionItem(@NonNull ViewHolder holder,
|
||||
@ShortcutMenuMode int shortcutMenuMode) {
|
||||
super.updateActionItem(holder, shortcutMenuMode);
|
||||
|
||||
final boolean isEditMenuMode =
|
||||
shortcutMenuMode == ShortcutMenuMode.EDIT;
|
||||
holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
|
||||
holder.mSwitchItem.setChecked(isAccessibilityServiceEnabled(getContext(), getId()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.graphics.drawable.Drawable;
|
||||
import android.provider.Settings;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||
|
||||
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
||||
import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
|
||||
import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
|
||||
|
||||
/**
|
||||
* Extension for {@link AccessibilityTarget} with {@link AccessibilityFragmentType#TOGGLE}
|
||||
* type.
|
||||
*/
|
||||
class ToggleWhiteListingFeatureTarget extends AccessibilityTarget {
|
||||
|
||||
ToggleWhiteListingFeatureTarget(Context context, @ShortcutType int shortcutType,
|
||||
boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) {
|
||||
super(context, shortcutType, AccessibilityFragmentType.TOGGLE,
|
||||
isShortcutSwitched, id, label, icon, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateActionItem(@NonNull ViewHolder holder,
|
||||
@ShortcutMenuMode int shortcutMenuMode) {
|
||||
super.updateActionItem(holder, shortcutMenuMode);
|
||||
|
||||
final boolean isEditMenuMode =
|
||||
shortcutMenuMode == ShortcutMenuMode.EDIT;
|
||||
holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
|
||||
holder.mSwitchItem.setChecked(isFeatureEnabled());
|
||||
}
|
||||
|
||||
private boolean isFeatureEnabled() {
|
||||
return Settings.Secure.getInt(getContext().getContentResolver(),
|
||||
getKey(), /* settingsValueOff */ 0) == /* settingsValueOn */ 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
|
||||
|
||||
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
|
||||
import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState;
|
||||
import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
||||
|
||||
/**
|
||||
* Extension for {@link AccessibilityServiceTarget} with
|
||||
* {@link AccessibilityFragmentType#VOLUME_SHORTCUT_TOGGLE} type.
|
||||
*/
|
||||
class VolumeShortcutToggleAccessibilityServiceTarget extends AccessibilityServiceTarget {
|
||||
|
||||
VolumeShortcutToggleAccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
|
||||
@NonNull AccessibilityServiceInfo serviceInfo) {
|
||||
super(context,
|
||||
shortcutType,
|
||||
AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE,
|
||||
serviceInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(boolean isChecked) {
|
||||
switch (getShortcutType()) {
|
||||
case ACCESSIBILITY_BUTTON:
|
||||
onCheckedFromAccessibilityButton(isChecked);
|
||||
return;
|
||||
case ACCESSIBILITY_SHORTCUT_KEY:
|
||||
super.onCheckedChanged(isChecked);
|
||||
return;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected shortcut type");
|
||||
}
|
||||
}
|
||||
|
||||
private void onCheckedFromAccessibilityButton(boolean isChecked) {
|
||||
setShortcutEnabled(isChecked);
|
||||
final ComponentName componentName = ComponentName.unflattenFromString(getId());
|
||||
setAccessibilityServiceState(getContext(), componentName, isChecked);
|
||||
|
||||
if (!isChecked) {
|
||||
optOutValueFromSettings(getContext(), UserShortcutType.HARDWARE, getId());
|
||||
|
||||
final String warningText =
|
||||
getContext().getString(R.string.accessibility_uncheck_legacy_item_warning,
|
||||
getLabel());
|
||||
Toast.makeText(getContext(), warningText, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
package com.android.internal.accessibility.util;
|
||||
|
||||
import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
|
||||
import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
|
||||
|
||||
@@ -27,9 +28,11 @@ import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -129,4 +132,27 @@ public final class AccessibilityUtils {
|
||||
? AccessibilityFragmentType.INVISIBLE_TOGGLE
|
||||
: AccessibilityFragmentType.TOGGLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a {@code componentId} service is enabled.
|
||||
*
|
||||
* @param context The current context.
|
||||
* @param componentId The component id that need to be checked.
|
||||
* @return {@code true} if a {@code componentId} service is enabled.
|
||||
*/
|
||||
public static boolean isAccessibilityServiceEnabled(Context context,
|
||||
@NonNull String componentId) {
|
||||
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
|
||||
final List<AccessibilityServiceInfo> enabledServices =
|
||||
am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
|
||||
|
||||
for (AccessibilityServiceInfo info : enabledServices) {
|
||||
final String id = info.getComponentName().flattenToString();
|
||||
if (id.equals(componentId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,10 @@ import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityManager.ShortcutType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
@@ -96,29 +98,6 @@ public final class ShortcutUtils {
|
||||
Settings.Secure.putString(context.getContentResolver(), targetsKey, joiner.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if component id existed in one of {@link UserShortcutType} string from Settings.
|
||||
*
|
||||
* @param context The current context.
|
||||
* @param shortcutTypes A combination of {@link UserShortcutType}.
|
||||
* @param componentId The component id that need to be checked existed in Settings.
|
||||
* @return {@code true} if component id existed in Settings.
|
||||
*/
|
||||
public static boolean hasValuesInSettings(Context context, @UserShortcutType int shortcutTypes,
|
||||
@NonNull String componentId) {
|
||||
boolean exist = false;
|
||||
if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
|
||||
exist = isComponentIdExistingInSettings(context, UserShortcutType.SOFTWARE,
|
||||
componentId);
|
||||
}
|
||||
if (((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE)) {
|
||||
exist |= isComponentIdExistingInSettings(context, UserShortcutType.HARDWARE,
|
||||
componentId);
|
||||
}
|
||||
return exist;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns if component id existed in Settings.
|
||||
*
|
||||
@@ -148,6 +127,21 @@ public final class ShortcutUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a {@code shortcutType} shortcut contains {@code componentId}.
|
||||
*
|
||||
* @param context The current context.
|
||||
* @param shortcutType The preferred shortcut type user selected.
|
||||
* @param componentId The component id that need to be checked.
|
||||
* @return {@code true} if a component id is contained.
|
||||
*/
|
||||
public static boolean isShortcutContained(Context context, @ShortcutType int shortcutType,
|
||||
@NonNull String componentId) {
|
||||
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
|
||||
final List<String> requiredTargets = am.getAccessibilityShortcutTargets(shortcutType);
|
||||
return requiredTargets.contains(componentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@link UserShortcutType} to {@link Settings.Secure} key.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user