这篇主要是记录一下在完全没学过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);
}
}

相关知识点:

  1. event.getSource()

    接收的系统事件类型是AccessibilityEvent,通过getSource()方法可以获取到事件信息

    AccessibilityNodeInfo noteInfo = event.getSource();
  2. 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。

相关知识点:

  1. node.isClickable() && node.isEnabled()

    AccessibilityNode如果是按钮就具有isClickable()和isEnabled()属性
  2. 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手机的情况下进行模拟手势操作。

相关知识点:

  1. GestureDescription.dispatchGesture()

小米手机MIUI安装APK时自动获取安装权限(自动点击权限框)的更多相关文章

  1. 安装APK时SO库的选择策略

    此文已由作者尹彬彬授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 0X0 前言 在Android系统中,当我们安装apk文件的时候,lib目录下的so文件会被解压到app的原 ...

  2. 安装apk时出现错误Failure [INSTALL_FAILED_DEXOPT]问题解决的方法

    在android4.0源码里面编译出来apk后,用adb install (或adb install -r 重装)安装时,报错[INSTALL_FAILED_DEXOPT]. xu@xu-PC:~$  ...

  3. 安装APK时引发INSTALL_PARSE_FAILED_MANIFEST_MALFORMED错误的几种可能(申明:来源于网络)

    安装APK时引发INSTALL_PARSE_FAILED_MANIFEST_MALFORMED错误的几种可能(申明:来源于网络) 地址:https://my.oschina.net/freestyle ...

  4. Android系统移植与调试之------->安装apk时出现错误Failure [INSTALL_FAILED_DEXOPT]问题解决的方法

    在android4.0源码里面编译出来apk后,用adb install (或adb install -r 重装)安装时,报错[INSTALL_FAILED_DEXOPT]. xu@xu-PC:~$ ...

  5. 解决小米手机USB安装apk时AS报错:INSTALL_FAILED_USER_RESTRICTED

    今天,直接用AS在小米手机上运行安装的时候总是报错:INSTALL_FAILED_USER_RESTRICTED,于是乎,通过以下方式解决: 在开发者选项将USB安装打开,然后,哈,解决了.记录一下.

  6. 关于heritrix安装配置时出现”必须限制口令文件读取访问权限”的解决方法

    转载:http://www.floatinglife.cn/关于heritrix安装配置时出现必须限制口令文件读取访问 最近开始写一个RSS聚合程序,需要爬虫支持,于是就整来heritrix,没想到, ...

  7. Windows下安装程序时提示未安装Microsoft Net FrameWork 2.0

    问题描述 安装程序时碰到如下: 现在基本都是用win7.win10系统,缺少环境大多数都是因为系统没有启用. 解决方法 控制面板 - 程序 - 启用或关闭Windows功能 - 把第一项'NET Fr ...

  8. android手机上安装apk时出现解析包错误的一个解决办法

    今天下午在学习安卓开发时,学习开发文档中的gridview时,在模拟器上调试程序一切正常,如下图所示: 但当将bin目录下的HelloGridView.apk拷贝到M8安卓系统后进行安装时,出现了“解 ...

  9. 关于部分Android手机安装apk,无法获取正常的logo

    最近出现过类似的问题,主要出现是在,MediaPad X1 7.0和MediaPad M1 8.0. 发布应用的时候明明配置好了图标的,但是始终找不到原因,郁闷了好几个小时,也浪费了好几个小时. 如果 ...

  10. 【原创】RPM安装软件时解决依赖性问题(自动解决依赖型)

    满足以下3个条件才能自动解决依赖性: 1.使用rpmdb -redhat(在安装时会自动弹出依赖性错误) 2.所有互相依赖的软件都必须在同一个目录下面. 3.调用-aid参数.

随机推荐

  1. 【Linux命令】在Linux服务器上与windows通过SCP命令互传文件时出现的问题排查过程

    1,在linux 执行 scp 1.txt adminitrator@10.10.10.10:/d:/后,报连接超时 原因:windows不支持ssh,可以安装支持SSH服务的工具,如:winsshd ...

  2. CSS实现开门效果

    .door{ position: relative; width: 450px; height: 300px; border: 1px solid #000; margin: 100px auto; ...

  3. Thread记录

    项目用到了线程 所以写出来留作以后复习线程Thread类包含在System.Threading命名空间有关线程的操作主要包含在这个类中现在总结一下Thread的常用方法和属性 Start([参数])/ ...

  4. Python_基础_Print_转义字符和原字符

    转义字符和原字符 print('hello\nworld') #转义功能的首字母 n-->newline的首字母表示换行 print('hello\tworld') #\t即表示一个制表位 pr ...

  5. 下载安装sqlyog

    sqlyog下载地址: https://github.com/webyog/sqlyog-community/wiki/Downloads 下载社区版,然后傻瓜式安装

  6. C# load and unload dll

    1. Invoker Any c# project Create a new application domain Create a proxy within the domain Unload th ...

  7. ctfshow web入门 命令执行 web29-36

    29-36 全是基于get传参执行 eval() 函数,均采用黑名单匹配,不同点在于黑名单的变化 web29 1 error_reporting(0); 2 if(isset($_GET['c'])) ...

  8. DP4398 是一个立体声 24 位/192kHz 数模转换芯片-替代CS4398

    DP4398 是一个立体声 24 位/192kHz 数模转换芯片.该 D/A 系统包括数字去加重.半分贝步长音量控制.ATAPI 通道混频.可选择的快速和慢速数字插补滤波器和过采样多位增量 Sigma ...

  9. wireshark抓包海康威视摄像头

    1:不清楚海康威视摄像头IP地址:网线直连电脑,打开wireshark抓包 2:抓包在source能看到  Hangzhou类似   说明是摄像头.ARP协议  然后192.168.0.251 是摄像 ...

  10. NanoPi R1 安装 python环境 及opencv

    (友善NanoPi  1G RAM/8GB eMMC) 安装python2/python3  pip/pip3 环境 sudo apt-get install python sudo apt-get ...