问题描述: 1)部分app 存在无障碍服务功能,需要关闭 2)对于客户自研App,自己具备系统签名自己直接开启了无障碍服务并且打开了无障碍服务快捷方式,如何关闭无障碍服务快捷开关
文章目录
问题现象问题描述
屏蔽app无障碍服务显示屏蔽快捷模式方案举例说明:针对酷狗音乐举例说明:针对智能家居 App测试
问题现象
现象如下图所示
问题描述
在无障碍功能列表里面一般都是第三方app,我们不允许客户能够具备这个权限,所以需要屏蔽掉针对自研具备系统签名app,不允许客户自己开启无障碍服务快捷方式、或者 我们每次自己去控制无障碍服务快捷开关
屏蔽app无障碍服务显示
思路如下
搜索无障碍服务相关文字搜索,如下:
通过1 找到无障碍模式布局文件 accessibility_settings.xml 从布局对比可以看到,这个已下载的应用,本身子数据也是动态添加的,其它的快捷菜单子数据是配置的。
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); } 屏蔽快捷模式方案 搜索代码 “快捷方式,找到对应位置” 通过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"); 无障碍服务源码