小米手机MIUI安装APK时自动获取安装权限(自动点击权限框)
这篇主要是记录一下在完全没学过Android的情况下硬拗完的这个APK,拖了很久查了很多资料才勉强写完,比较垃圾但还是实现功能了。记录的过程我也尽量把知识点贴出来。
一开始是看了一个大佬的分享贴(testerhome的帖子,但是现在论坛封了,等过后再贴大佬的链接),决定改写这个apk。大佬原先写的代码不适用于MIUI系统,稍微改了一下,再增加了跳转获取Accessibility权限部分。(这部分代码也参考了网上的大佬,我有空再找找记得哪个链接就贴哪个,毕竟查了太多资料了)
这个版本现在还很稚嫩,过后有空还会完善。
贴上GitHub地址:https://github.com/congyingHHZ/AutoInstall
有需要的朋友可以自己下载源码
测试设备:小米10
工具:Android Studio 11.0.10
环境:window10
软件介绍
这个软件主要是为了解决MIUI系统(todo:尝试兼容各家定制Android)在APK安装过程中总是需要手动允许才能继续安装流程这个比较烦人的事情。并且也为了以后接入自动化测试,遇到安装APK可以自动完成。
软件大致分成2个部分
- 判断是否有AccessibilityService权限,如果没有权限则引导跳转到AccessibilityService权限设置页面
- 利用AccessibilityService对安装过程的弹窗进行自动点击,已完成自动化安装
代码
一、利用AccessibilityService对权限弹窗的“允许”等按钮进行点击(从核心内容开始说起, AccessibilityUtil.java)
1. 创建一个类继承AccessibilityService
无障碍服务AccessibilityService可以主动接收到系统的事件,通过对事件的判断,知道是否有进行安装操作,并且可以对控件进行点击等操作。
public class AutoInstallService extends AccessibilityService{
// do something
}
2. 当接收到系统事件后,调用onAccessibilityEvent方法
在这个方法里对收到的系统事件进行判断,如果判断为系统正在安装软件那么就调用自己写performInstallation去完成自动点击操作。
public void onAccessibilityEvent(AccessibilityEvent event) {
/*
* 回调方法,当事件发生时会从这里进入,在这里判断需要捕获的内容,
* 可通过下面这句log将所有事件详情打印出来,分析决定怎么过滤。
*/
log("!!onAccessibilityEvent!!");
//log(event.toString());
AccessibilityNodeInfo noteInfo = event.getSource();
log("===noteInfo!===");
if (event.getSource() == null) {
log("<null> event source");
return;
}
AccessibilityNodeInfo rowNode = getRootInActiveWindow();
log("===rowNode!===");
//log(rowNode.toString());
int eventType = event.getEventType();
log(eventType+"");
log(event.getPackageName().toString());
if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
&& (event.getPackageName().equals(PACKAGE_INSTALLER_MIUI) | event.getPackageName().equals(PACKAGE_INSTALLER_MIUI_adb))) {
boolean r = performInstallation(event);
log("Action Perform: " + r);
}else if(eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
event.getPackageName().equals(PACKAGE_INSTALLER_MIUI)){
log("!!input TYPE_WINDOW_CONTENT_CHANGED !!");
boolean r = performInstallation(event);
log("Action Perform: " + r);
}
}
相关知识点:
- event.getSource()
接收的系统事件类型是AccessibilityEvent,通过getSource()方法可以获取到事件信息
AccessibilityNodeInfo noteInfo = event.getSource(); - getRootInActiveWindow()
获取节点信息
AccessibilityNodeInfo rowNode = getRootInActiveWindow();
之后通过节点信息判断页面有没有我们想要的内容,如“允许安装”这些
3.event.getEventType()
获取事件的类型,返回值是INT, TYPE_VIEW_CLICKED, TYPE_VIEW_LONG_CLICKED, TYPE_VIEW_SELECTED……
event.getPackageName()
获取事件产生的应用,也就是这个事件是哪个应用产生的。
int eventType = event.getEventType();
if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
&& (event.getPackageName().equals(PACKAGE_INSTALLER_MIUI) | event.getPackageName().equals(PACKAGE_INSTALLER_MIUI_adb))){
...
}else if(eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
event.getPackageName().equals(PACKAGE_INSTALLER_MIUI)){
}
传进来的AccessibilityEvent,可能是发生了点击事件、长按事件等等,但是如果安装软件弹出来权限框肯定是页面变化,所以这里判断even是不是页面变化的类型(TYPE_WINDOW_STATE_CHANGED,TYPE_WINDOW_CONTENT_CHANGED )才执行接下来的操作。
同时因为系统可能还会有其他软件导致的页面变化事件,因此这里还判断了是不是安装软件产生的页面变化。MIUI中安装应用的包名是PACKAGE_INSTALLER_MIUI,通过adb安装应用的包名是PACKAGE_INSTALLER_MIUI_adb。
3. 确定是进入安装流程后开始执行点击获取权限操作,跳转到performInstallation方法
private boolean performInstallation(AccessibilityEvent event) {
List<AccessibilityNodeInfo> nodeInfoList;
/*
* 有的手机会弹2次,有的只弹一次,在替换安装时会出现确定按钮,
* 为了大而全,下面定义了比较多的内容,可按需增减。
*/
log("!!performInstallation!!");
String[] labels = new String[]{"本次允许","允许", "确定", "继续安装", "下一步", "完成","安装"};
for (String label : labels) {
log(label);
nodeInfoList = event.getSource().findAccessibilityNodeInfosByText(label);
if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
boolean performed = performClick(nodeInfoList);
if (performed) return true;
}
}
return false;
}
这部分没有什么可说的,就是获取页面内容,然后循环判断有没有存在“允许”之类的字符,这些都要要点击的节点。
4. 如果页面有需要点击节点,也就是弹出的权限框,则跳转到performClick()执行点击操作。
@RequiresApi(api = Build.VERSION_CODES.N)
private boolean performClick(List<AccessibilityNodeInfo> nodeInfoList) {
for (AccessibilityNodeInfo node : nodeInfoList) {
/*
* 这里还可以根据node的类名来过滤,大多数是button类,这里也是为了大而全,
* 判断只要是可点击的是可用的就点。
*/
if (node.isClickable() && node.isEnabled()) {
return node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
else if(node.getClassName() == "android.widget.Button"){
Log.d(TAG,"clickByNode");
return clickByNode(node);
}
}
return false;
}
这一段代码逻辑也很简单就是传进来上一步在当前页面发现的所有node,然后遍历这个list,如果是按钮可以点击则点击该node。
相关知识点:
- node.isClickable() && node.isEnabled()
AccessibilityNode如果是按钮就具有isClickable()和isEnabled()属性 - node.getClassName() == "android.widget.Button"
这里比原版代码多加了一个判断,因为小米安装过程弹出权限框的“允许安装”按钮(还是“继续安装”?不记得了,反正就是有个按钮点不了)不是isClickable的,导致无法继续往下走执行点击。但是这个node的类名是"android.widget.Button",所以补充判断再进行点击操作。当然原来isClickable属性的按钮点击方法也用不了了,这里也添加的这种类型node的点击方法。
5. 执行点击操作。这部分是新增的,针对node.performAction()无法实现点击的情况。
public final boolean clickByNode(AccessibilityNodeInfo nodeInfo){
if (nodeInfo == null){
return false;
}
// if (nodeInfo.getClassName() != "android.widget.Button"){
// return false;
// }
Rect rect = new Rect();
nodeInfo.getBoundsInScreen(rect);
int x = (rect.left + rect.right)/2;
int y = (rect.top + rect.bottom)/2;
Point point = new Point(x,y);
GestureDescription.Builder builder = new GestureDescription.Builder();
Path path = new Path();
path.moveTo(point.x,point.y);
builder.addStroke(new GestureDescription.StrokeDescription(path,100,50));
//path:路径 startTime:从手势开始到开始笔画的时间
final GestureDescription gesture = builder.build();
return dispatchGesture(gesture,
new GestureResultCallback(){
@Override
public void onCompleted(GestureDescription gestureDescription){
super.onCompleted(gestureDescription);
}
@Override
public void onCancelled(GestureDescription gestureDescription){
super.onCancelled(gestureDescription);
}
},null);
}
这一段主要是利用了GestureDescription,这个api是Android7.0之后引入的,所以必须在这个方法前增加 @RequiresApi(api = Build.VERSION_CODES.N)。
利用GestureDescription可以实现在不root手机的情况下进行模拟手势操作。
相关知识点:
- GestureDescription.dispatchGesture()
小米手机MIUI安装APK时自动获取安装权限(自动点击权限框)的更多相关文章
- 安装APK时SO库的选择策略
此文已由作者尹彬彬授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 0X0 前言 在Android系统中,当我们安装apk文件的时候,lib目录下的so文件会被解压到app的原 ...
- 安装apk时出现错误Failure [INSTALL_FAILED_DEXOPT]问题解决的方法
在android4.0源码里面编译出来apk后,用adb install (或adb install -r 重装)安装时,报错[INSTALL_FAILED_DEXOPT]. xu@xu-PC:~$ ...
- 安装APK时引发INSTALL_PARSE_FAILED_MANIFEST_MALFORMED错误的几种可能(申明:来源于网络)
安装APK时引发INSTALL_PARSE_FAILED_MANIFEST_MALFORMED错误的几种可能(申明:来源于网络) 地址:https://my.oschina.net/freestyle ...
- Android系统移植与调试之------->安装apk时出现错误Failure [INSTALL_FAILED_DEXOPT]问题解决的方法
在android4.0源码里面编译出来apk后,用adb install (或adb install -r 重装)安装时,报错[INSTALL_FAILED_DEXOPT]. xu@xu-PC:~$ ...
- 解决小米手机USB安装apk时AS报错:INSTALL_FAILED_USER_RESTRICTED
今天,直接用AS在小米手机上运行安装的时候总是报错:INSTALL_FAILED_USER_RESTRICTED,于是乎,通过以下方式解决: 在开发者选项将USB安装打开,然后,哈,解决了.记录一下.
- 关于heritrix安装配置时出现”必须限制口令文件读取访问权限”的解决方法
转载:http://www.floatinglife.cn/关于heritrix安装配置时出现必须限制口令文件读取访问 最近开始写一个RSS聚合程序,需要爬虫支持,于是就整来heritrix,没想到, ...
- Windows下安装程序时提示未安装Microsoft Net FrameWork 2.0
问题描述 安装程序时碰到如下: 现在基本都是用win7.win10系统,缺少环境大多数都是因为系统没有启用. 解决方法 控制面板 - 程序 - 启用或关闭Windows功能 - 把第一项'NET Fr ...
- android手机上安装apk时出现解析包错误的一个解决办法
今天下午在学习安卓开发时,学习开发文档中的gridview时,在模拟器上调试程序一切正常,如下图所示: 但当将bin目录下的HelloGridView.apk拷贝到M8安卓系统后进行安装时,出现了“解 ...
- 关于部分Android手机安装apk,无法获取正常的logo
最近出现过类似的问题,主要出现是在,MediaPad X1 7.0和MediaPad M1 8.0. 发布应用的时候明明配置好了图标的,但是始终找不到原因,郁闷了好几个小时,也浪费了好几个小时. 如果 ...
- 【原创】RPM安装软件时解决依赖性问题(自动解决依赖型)
满足以下3个条件才能自动解决依赖性: 1.使用rpmdb -redhat(在安装时会自动弹出依赖性错误) 2.所有互相依赖的软件都必须在同一个目录下面. 3.调用-aid参数.
随机推荐
- 内网Linux下安装Nginx1.23,添加stream模块实现tcp/udp代理转发
环境:centos7.6 ngx_stream_core_module 这个模块在1.9.0版本后将被启用.但是并不会默认安装,需要在编译时通过指定 --with-stream 参数来激活这个模块,w ...
- SSL certificate problem: unable to get local issuer certificate 错误解决
终端报了如下错误git SSL certificate problem: unable to get local issuer certificate 这个问题是由于没有配置信任的服务器HTTPS验证 ...
- spider_爬取斗图啦所有表情包(图片保存)
"""爬取斗图吧里面的所有表情包知识点总结: 一.使用requests库进行爬取,随机请求头(网站反爬措施少.挂个请求头足矣) 二.具体思路: 1.先爬取所有的图片url ...
- springboot上传图片
springboot上传图片 新建一个springboot项目: 在java/main/com/ljx 创建一个controller.fileController类 内容如下: package com ...
- centos7下的防火墙部署
iptables以及firewalld其实都是防火墙管理工具,说白一点,就是一个服务,用来保障公网与内网 iptables 由于centos7没有自带,需要自己安装 安装 yum install ip ...
- ASP.NET中maxRequestLength和maxAllowedContentLength的区别;上传大文件设置IIS7文件上传的最大大小
https://blog.csdn.net/qq_23663693/article/details/89920039 maxRequestLength表示ASP支持的最大请求大小,而maxAllowe ...
- python peewee
from peewee import MySQLDatabase, Model from peewee import CharField, IntegerField, BooleanField # 引 ...
- 【服务器数据恢复】RAID6数据恢复案例
服务器数据恢复环境:一台Web服务器中有一组由8块磁盘组建的raid6磁盘阵列,用来运行数据库和存储普通办公文件. 服务器故障:服务器raid6磁盘阵列中有两块硬盘离线,但是管理员没有注意到这种情况, ...
- execsnoop
作用 execsnoop通过ftrace实时监控进程的exec()行为,输出短时进程的信息,包括进程 PID.父进程 PID.命令行参数以及执行的结果. 安装execsnoop git clone - ...
- wpf 自定义Messagebox时,对话框显示不居中问题
在自定义Messagebox(有属性Window.SizeToContent="WidthAndHeight")时,对话框显示不居中,经过尝试,应设置如下: msgBox.Wind ...