问题描述:
1)部分app 存在无障碍服务功能,需要关闭
2)对于客户自研App,自己具备系统签名自己直接开启了无障碍服务并且打开了无障碍服务快捷方式,如何关闭无障碍服务快捷开关
文章目录
- 问题现象
- 问题描述
- 屏蔽app无障碍服务显示
- 屏蔽快捷模式方案
- 举例说明:针对酷狗音乐
- 举例说明:针对智能家居 App测试
问题现象
现象如下图所示
问题描述
- 在无障碍功能列表里面一般都是第三方app,我们不允许客户能够具备这个权限,所以需要屏蔽掉
- 针对自研具备系统签名app,不允许客户自己开启无障碍服务快捷方式、或者 我们每次自己去控制无障碍服务快捷开关
屏蔽app无障碍服务显示
思路如下
- 搜索无障碍服务相关文字搜索,如下:
<string name="accessibility_text_and_display_title" msgid="1398507755501334961">"文字和显示"</string>
<string name="accessibility_turn_screen_darker_title" msgid="5986223133285858349">"调暗屏幕"</string>
<string name="interaction_control_category_title" msgid="2696474616743882372">"互动控制"</string>
<string name="accessibility_tap_assistance_title" msgid="1058913033421048284">"时间控件"</string>
<string name="accessibility_system_controls_title" msgid="6403287349518987624">"系统控件"</string>
<string name="user_installed_services_category_title" msgid="2639470729311439731">"已下载的应用"</string>
<string name="experimental_category_title" msgid="898904396646344152">"实验性功能"</string>
- 通过1 找到无障碍模式布局文件 accessibility_settings.xml
从布局对比可以看到,这个已下载的应用,本身子数据也是动态添加的,其它的快捷菜单子数据是配置的。
<PreferenceCategory
android:key="user_installed_services_category"
android:persistent="false"
android:title="@string/user_installed_services_category_title"/>
- 通过2 找到 AccessibilitySettings.java
已下载的内容:CATEGORY_DOWNLOADED_SERVICES
private static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category";
-》 final PreferenceCategory downloadedServicesCategory =
mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES);
-》集合mCategoryToPrefCategoryMap 赋值:
private void initializeAllPreferences() {
for (int i = 0; i < CATEGORIES.length; i++) {
PreferenceCategory prefCategory = findPreference(CATEGORIES[i]);
mCategoryToPrefCategoryMap.put(CATEGORIES[i], prefCategory);
}
}
根据2 中的推测,找到mCategoryToPrefCategoryMap 中,已下载的 赋值地方。
-> final PreferenceCategory downloadedServicesCategory =
mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES);
downloadedServicesCategory 找到它赋值子类的地方:
->
for (int i = 0, count = preferenceList.size(); i < count; ++i) {
final RestrictedPreference preference = preferenceList.get(i);
final ComponentName componentName = preference.getExtras().getParcelable(
EXTRA_COMPONENT_NAME);
PreferenceCategory prefCategory = downloadedServicesCategory;
// Set the appropriate category if the service comes pre-installed.
if (mPreBundledServiceComponentToCategoryMap.containsKey(componentName)) {
prefCategory = mPreBundledServiceComponentToCategoryMap.get(componentName);
}
prefCategory.addPreference(preference);
mServicePreferenceToPreferenceCategoryMap.put(preference, prefCategory);
}
这个地方,通过 componentName 的信息,如果含有了 需要屏蔽的包名 则 continue 即可,不让添加到prefCategory中 即可
修改后的代码如下:
for (int i = 0, count = preferenceList.size(); i < count; ++i) {
final RestrictedPreference preference = preferenceList.get(i);
final ComponentName componentName = preference.getExtras().getParcelable(
EXTRA_COMPONENT_NAME);
String pkgName= componentName.getPackageName();
Log.d(TAG,"pkgName:"+pkgName);
if("com.sohu.inputmethod.sogou".equals(pkgName)){ //这个地方通过包名来判断,进行屏蔽
Log.d(TAG,"ping bi:"+pkgName);
continue;
}
PreferenceCategory prefCategory = downloadedServicesCategory;
// Set the appropriate category if the service comes pre-installed.
if (mPreBundledServiceComponentToCategoryMap.containsKey(componentName)) {
prefCategory = mPreBundledServiceComponentToCategoryMap.get(componentName);
}
prefCategory.addPreference(preference);
mServicePreferenceToPreferenceCategoryMap.put(preference, prefCategory);
}
屏蔽快捷模式方案
- 搜索代码 “快捷方式,找到对应位置”
<string name="accessibility_shortcut_title" msgid="8125867833704517463">"<xliff:g id="SERVICE">%1$s</xliff:g>快捷方式"</string>
- 通过R.string.accessibility_shortcut_title 找到 AccessibilityShortcutPreferenceFragment
找到一些相关的代码
相关代码加载布局
/**
* Base class for accessibility fragments shortcut functions and dialog management.
*/
@VisibleForTesting
void setupEditShortcutDialog(Dialog dialog) {
final View dialogSoftwareView = dialog.findViewById(R.id.software_shortcut);
mSoftwareTypeCheckBox = dialogSoftwareView.findViewById(R.id.checkbox);
setDialogTextAreaClickListener(dialogSoftwareView, mSoftwareTypeCheckBox);
final View dialogHardwareView = dialog.findViewById(R.id.hardware_shortcut);
mHardwareTypeCheckBox = dialogHardwareView.findViewById(R.id.checkbox);
setDialogTextAreaClickListener(dialogHardwareView, mHardwareTypeCheckBox);
updateEditShortcutDialogCheckBox();
}
@Override
public Dialog onCreateDialog(int dialogId) {
final Dialog dialog;
switch (dialogId) {
case DialogEnums.EDIT_SHORTCUT:
final CharSequence dialogTitle = getPrefContext().getString(
R.string.accessibility_shortcut_title, getLabelName());
final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
? AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC_SUW :
AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC;
dialog = AccessibilityDialogUtils.showEditShortcutDialog(
getPrefContext(), dialogType, dialogTitle,
this::callOnAlertDialogCheckboxClicked);
setupEditShortcutDialog(dialog);
return dialog;
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
dialog = AccessibilityGestureNavigationTutorial
.createAccessibilityTutorialDialog(getPrefContext(),
getUserShortcutTypes());
dialog.setCanceledOnTouchOutside(false);
return dialog;
default:
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
}
->AccessibilityGestureNavigationTutorial.java
checkBox 点击事件地方
@Override
public void onToggleClicked(ShortcutPreference preference) {
if (getComponentName() == null) {
return;
}
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),
getComponentName().flattenToString(), AccessibilityUtil.UserShortcutType.SOFTWARE);
if (preference.isChecked()) {
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes,
getComponentName());
showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);
} else {
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes,
getComponentName());
}
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
这里AccessibilityUtil 有两个方法 optInAllValuesToSettings optOutAllValuesFromSettings 而且有 getComponentName 说明是对某个应用进行设置,进去看下具体代码
/**
* Opts in component name into multiple {@code shortcutTypes} colon-separated string in
* Settings.
*
* @param context The current context.
* @param shortcutTypes A combination of {@link UserShortcutType}.
* @param componentName The component name that need to be opted in Settings.
*/
static void optInAllValuesToSettings(Context context, int shortcutTypes,
@NonNull ComponentName componentName) {
if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
optInValueToSettings(context, UserShortcutType.SOFTWARE, componentName);
}
if (((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE)) {
optInValueToSettings(context, UserShortcutType.HARDWARE, componentName);
}
}
/**
* Opts out component name into multiple {@code shortcutTypes} colon-separated string in
* Settings.
*
* @param context The current context.
* @param shortcutTypes A combination of {@link UserShortcutType}.
* @param componentName The component name that need to be opted out from Settings.
*/
static void optOutAllValuesFromSettings(Context context, int shortcutTypes,
@NonNull ComponentName componentName) {
if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
optOutValueFromSettings(context, UserShortcutType.SOFTWARE, componentName);
}
if (((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE)) {
optOutValueFromSettings(context, UserShortcutType.HARDWARE, componentName);
}
}
实际根据AccessibilityUtil.java 日志分析,
举例说明:针对酷狗音乐
打开无障碍快捷方式:
AccessibilityUtil com.android.settings D optInAllValuesToSettings shortcutTypes:1 pkgName:com.kugou.android className:com.kugou.ringtone.permission.accessibilitysuper.service.AccessibilitySuperService
AccessibilityUtil com.android.settings D optInAllValuesToSettings ->optInValueToSettings SOFTWARE
AccessibilityUtil com.android.settings D optInValueToSettings targetKey:accessibility_button_targets targetString:
AccessibilityUtil com.android.settings D optInValueToSettings componentName flattenToStr:com.kugou.android/com.kugou.ringtone.permission.accessibilitysuper.service.AccessibilitySuperService
AccessibilityUtil com.android.settings D optInValueToSettings targetKey:accessibility_button_targets joiner:com.kugou.android/com.kugou.ringtone.permission.accessibilitysuper.service.AccessibilitySuperService
最终 optInValueToSettings 方法,调用:
Settings.Secure.putString(context.getContentResolver(), targetKey, joiner.toString());
Log.d(TAG,"optInValueToSettings targetKey:"+targetKey+" joiner:"+ joiner.toString());
关闭无障碍快捷方式:
AccessibilityUtil com.android.settings D optOutAllValuesFromSettings shortcutTypes:1 pkgName:com.kugou.android className:com.kugou.ringtone.permission.accessibilitysuper.service.AccessibilitySuperService
AccessibilityUtil com.android.settings D optOutValueFromSettings shortcutType:1 pkgName:com.kugou.android className:com.kugou.ringtone.permission.accessibilitysuper.service.AccessibilitySuperService
AccessibilityUtil com.android.settings D optOutValueFromSettings targetKey:accessibility_button_targets targetString:com.kugou.android/com.kugou.ringtone.permission.accessibilitysuper.service.AccessibilitySuperService
AccessibilityUtil com.android.settings D optOutValueFromSettings targetKey:accessibility_button_targets joiner.toString():
最终 optOutValueFromSettings 方法里面调用:
Settings.Secure.putString(context.getContentResolver(), targetKey, joiner.toString());
Log.d(TAG,"optOutValueFromSettings targetKey:"+targetKey+" joiner.toString():"+joiner.toString());
所以,针对 不同app,取消权限 需要调用 对应的targetkey 的value 置空即可
举例说明:针对智能家居 App测试
打开快捷方式 日志如下:
AccessibilityUtil com.android.settings D optInAllValuesToSettings shortcutTypes:3 pkgName:com.deling.launcher className:com.hrs.tools.services.ToolsAccessibilityService
AccessibilityUtil com.android.settings D optInAllValuesToSettings ->optInValueToSettings SOFTWARE
AccessibilityUtil com.android.settings D optInValueToSettings targetKey:accessibility_button_targets targetString:
AccessibilityUtil com.android.settings D optInValueToSettings componentName flattenToStr:com.deling.launcher/com.hrs.tools.services.ToolsAccessibilityService
AccessibilityUtil com.android.settings D optInValueToSettings targetKey:accessibility_button_targets joiner:com.deling.launcher/com.hrs.tools.services.ToolsAccessibilityService
打开快捷方式开关:涉及到相关的componentName 如下:
pkgName:com.deling.launcher className:com.hrs.tools.services.ToolsAccessibilityService
componentName flattenToStr:com.deling.launcher/com.hrs.tools.services.ToolsAccessibilityService
两个app targetKey:accessibility_button_targets 一样的
关闭快捷方式,日志如下:
AccessibilityUtil com.android.settings D optOutAllValuesFromSettings shortcutTypes:3 pkgName:com.deling.launcher className:com.hrs.tools.services.ToolsAccessibilityService
AccessibilityUtil com.android.settings D optOutValueFromSettings shortcutType:1 pkgName:com.deling.launcher className:com.hrs.tools.services.ToolsAccessibilityService
AccessibilityUtil com.android.settings D optOutValueFromSettings targetKey:accessibility_button_targets targetString:com.deling.launcher/com.hrs.tools.services.ToolsAccessibilityService
AccessibilityUtil com.android.settings D optOutValueFromSettings targetKey:accessibility_button_targets joiner.toString():
AccessibilityUtil com.android.settings D optOutValueFromSettings shortcutType:2 pkgName:com.deling.launcher className:com.hrs.tools.services.ToolsAccessibilityService
AccessibilityUtil com.android.settings D optOutValueFromSettings targetKey:accessibility_shortcut_target_service targetString:com.deling.launcher/com.hrs.tools.services.ToolsAccessibilityService
AccessibilityUtil com.android.settings D optOutValueFromSettings targetKey:accessibility_shortcut_target_service joiner.toString():
所以解决方案,每次开机后,或者在哪个位置,关闭对应的快捷方式即可,方法:
Settings.Secure.putString(context.getContentResolver(), targetKey, "");
针对 X7 快捷,实现方式: 取消快捷
Settings.Secure.putString(context.getContentResolver(), "accessibility_button_targets", "");
Settings.Secure.putString(context.getContentResolver(), "accessibility_shortcut_target_service", "");
实际demo 功能验证:
//ToastUtils.showShort("测试快捷模式关闭");
Settings.Secure.putString(getContentResolver(), "accessibility_button_targets", "");
Settings.Secure.putString(getContentResolver(), "accessibility_shortcut_target_service", "");
// ToastUtils.showShort("测试快捷模式打开");
Settings.Secure.putString(getContentResolver(), "accessibility_button_targets", "com.deling.launcher/com.hrs.tools.services.ToolsAccessibilityService");
Settings.Secure.putString(getContentResolver(), "accessibility_shortcut_target_service", "com.deling.launcher/com.hrs.tools.services.ToolsAccessibilityService");
无障碍服务源码