通过AccessibilityService(无障碍服务)自动在设置界面打开App所需要的对应权限

本文介绍了如何利用Android的AccessibilityService类来自动设置应用权限,特别是在华为Honor9i(Android 9.0)设备上。通过监听系统设置界面的事件,模拟用户操作,如点击和滑动,逐步开启所需权限。代码示例展示了监听不同界面元素并执行相应操作的逻辑,但需要注意的是,由于不同手机设置界面的差异,实际应用中需要针对不同机型进行适配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近看到一个项目比如说需要允许访问应用权限、以及录屏等权限的时候 在手机运行的时候点击跳转至无障碍界面点击打开无障碍服务,然后调转至设置界面,然后手机就自动的一步一步的打开App所需的权限,看起来特别牛逼和智能的亚子。了解了一下发现通过AccessibilityService这个类可以完成上述需求。但是由于每款手机的设置界面是不相同的,所以需要做不同的机型判断适配机型。我的是在华为 Honor 9i(Android 9.0系统)中完成自动设置权限的功能的。

关于AccessibilityService类我就不做过多的解释了,直接撸代码

1.在清单文件的application中添加:

    <service
        android:name="com.hxf.childapp.AutoDebugService"
        android:enabled="true"
        android:exported="true"
        android:label="你的包名"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService"/>
        </intent-filter>
        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/autodebug"/>
    </service>

2.配置需要监听的包名和行为,也就是@xml/autodebug文件:在xml文件夹中创建autodebug文件。

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask|typeWindowStateChanged|typeWindowContentChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagReportViewIds"
    android:canPerformGestures="true"
    android:notificationTimeout="100"
    android:canRetrieveWindowContent="true"
    android:packageNames="com.android.settings" />

这里说一下 android:packageNames中的值就是需要监听的应用。

3.创建AutoDebugService类继承自AccessibilityService类:

public class AutoDebugService extends AccessibilityService {
	
}

4.重写方法:

@Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
    
    }

需要重写的核心方法 所有事件都是从这个方法监听的,比如界面改变,点击,长按等事件。

5.通过事件判断是否监听:

if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event.getEventType() || AccessibilityEvent.TYPE_VIEW_CLICKED == event.getEventType()) {

}

此处我是通过界面改变和点击事件进行过滤,逻辑都是写在此回调方法中,

以下就是全部代码,只供参考思路,不同的手机是需要适配的。

public class AutoDebugService extends AccessibilityService {
    int index = 0;

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event.getEventType() || AccessibilityEvent.TYPE_VIEW_CLICKED == event.getEventType()) {
            if (step == 0) {
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event.getEventType()) {
                    event("com.android.settings:id/dashboard_container", "帐户");
                    Log.e("lee", step + "*******");
                }
            } else if (step == 1) {
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event.getEventType()) {
                    Log.e("lee", step + "*******");
                    back();
                }
            } else if (step == 2) {
                if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event.getEventType()) {
                    back();
                }
            } else if (step == 3) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event.getEventType()) {
                    event("com.android.settings:id/dashboard_container", "安全和隐私");
                }
            } else if (step == 4) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 5) {
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 6) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                event("com.android.settings:id/dashboard_container", "桌面和壁纸");
            } else if (step == 7) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                event("com.android.settings:id/list", "桌面风格");
            } else if (step == 8) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                event("com.android.settings:id/normal_l_ui_sector", "标准风格");
            } else if (step == 9) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 10) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 11) {
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 12) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                event("com.android.settings:id/dashboard_container", "应用");
            } else if (step == 13) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                event("com.android.settings:id/list", "应用分身");
            } else if (step == 14) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                check("com.android.settings:id/clone_app_switche");
            } else if (step == 15) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 16) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 17) {
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 18) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                event("com.android.settings:id/dashboard_container", "应用");
            } else if (step == 19) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                event("com.android.settings:id/list", "应用启动管理");
            } else if (step == 20) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                check("com.huawei.systemmanager:id/app_control_switch");
            } else if (step == 21) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 22) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 23) {
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 24) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                event("com.android.settings:id/dashboard_container", "系统");
            } else if (step == 25) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                event("com.android.settings:id/list", "开发人员选项");
            } else if (step == 26) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                event("com.android.settings:id/list", "USB 调试");
            } else if (step == 27) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 28) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            } else if (step == 29) {
                Log.e("lee", step + "*******");
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                back();
            }
        }
    }

    /**
     * 通过Id获取AccessibilityNodeInfo
     *
     * @param id
     * @return
     */
    private List<AccessibilityNodeInfo> findNodesById(String id) {
        AccessibilityNodeInfo root = getRootInActiveWindow();
        if (root != null) {
            return root.findAccessibilityNodeInfosByViewId(id);
        }
        return null;
    }

    /**
     * 通过Text获取AccessibilityNodeInfo
     *
     * @param text
     * @return
     */
    private List<AccessibilityNodeInfo> findNodesByText(String text) {
        AccessibilityNodeInfo root = getRootInActiveWindow();
        if (root != null) {
            return root.findAccessibilityNodeInfosByText(text);
        }
        return null;
    }

    /**
     * 处理事件
     *
     * @param id
     * @param text
     */
    private void event(String id, String text) {
        List<AccessibilityNodeInfo> byIdList = findNodesById(id);
        List<AccessibilityNodeInfo> byTextList = findNodesByText(text);
        if (byTextList != null && byTextList.size() > 0) {
            AccessibilityNodeInfo info = byTextList.get(0);
            if (!info.isChecked()) {
                info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
                step++;
            } else {
                byTextList.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK);
                step++;
            }
        } else {
            if (byIdList != null && byIdList.size() > 0) {
                byIdList.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
            }
        }
    }

    private void check(String id) {
        List<AccessibilityNodeInfo> byIdList = findNodesById(id);
        if (byIdList != null && byIdList.size() > 0) {
            for (int i = 0; i < byIdList.size(); i++) {
                if (!byIdList.get(i).isChecked()) {
                    byIdList.get(i).performAction(AccessibilityNodeInfo.ACTION_CLICK);
                }
            }
        }
        step++;
    }


    /**
     * 返回上一层级
     */
    private void back() {
        performGlobalAction(GLOBAL_ACTION_BACK);
        step++;
    }
}

调用的方法,先判断是否打开了无障碍服务:
如果打开了就跳转到设置界面进行一步一步的设置

if (isAccessibilitySettingsOn(AutoDebugService.class.getName())) {//打开了无障碍权限
                Intent intent = new Intent(Settings.ACTION_SETTINGS);
                startActivity(intent);
            }

未打开的话就去打开:

 Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivityForResult(intent, REQUEST_ACCESSIBLE_PERMISSION);

以上就完成了自动设置权限的功能。 如果不知道视图的id 可以使用sdk自带的DDMS工具查看视图。

### Android 中基于 `attr` 属性的无障碍适配 #### 使用 `android:accessibilityFeedbackType` 为了定义服务反馈类型,在声明无障碍服务时可以设置 `android:accessibilityFeedbackType` 属性。此属性指定了当该服务激活后,系统应采用何种方式向用户提供反馈[^1]。 对于希望提供通用类型的反馈场景而言: ```xml <service android:name=".MyAccessibilityService"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService"/> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service> ``` 在对应的 XML 配置文件中指定如下内容来启用泛型反馈模式: ```xml <?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeAllMask" android:packageNames="com.example.package" android:accessibilityFeedbackType="feedbackGeneric" <!-- 泛型反馈 --> .../> ``` #### 动态注册无障碍服务 针对某些特定需求的应用程序可能需要动态开启或关闭无障碍功能。这可以通过修改系统的安全设置实现,不过需要注意的是这种方式通常仅适用于具有适当权限的系统级应用[^2]。 代码片段展示了如何通过编程手段使能一个具体的无障碍服务实例: ```java Settings.Secure.putString(contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "com.test.app/com.test.app.service.TestService"); Settings.Secure.putInt(contentResolver, Settings.Secure.ACCESSIBILITY_ENABLED, 1); ``` 上述方法允许应用程序根据运行时条件灵活控制无障碍特性的工作状态,但应当谨慎处理以防止潜在的安全风险。 #### 提供语音提示给用户 为了让视障人士更好地理解界面变化情况,开发者可以在交互发生之后立即调用 `View#announceForAccessibility()` 方法发出声音通知[^3]。 下面是一个简单的例子说明怎样在一个按钮点击事件处理器内部加入这样的逻辑: ```kotlin binding?.addCoffee?.setOnClickListener { coffeeRepo.increment() showCount() // 当咖啡数量增加到8的时候给予语音提醒 binding?.amountConsumed?.announceForAccessibility("Limit is increased to 8") } ``` 这种方法能够显著提升用户体验,尤其是在那些依赖触觉输入为主的设备上更为重要。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值