GVKun编程网logo

从Android手机的抢红包插件说起(安卓手机抢红包插件)

22

针对从Android手机的抢红包插件说起和安卓手机抢红包插件这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展Androd强化抢红包实现不了自动抢红包、AndroidAccessibility

针对从Android手机的抢红包插件说起安卓手机抢红包插件这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展Androd强化抢红包实现不了自动抢红包、Android AccessibilityService实现微信抢红包插件、Android Wear将数据发送到Android手机,但手机似乎永远不会收到它、Android 微信红包插件等相关知识,希望可以帮助到你。

本文目录一览:

从Android手机的抢红包插件说起(安卓手机抢红包插件)

从Android手机的抢红包插件说起(安卓手机抢红包插件)

为防止盗链,本文首发于于果的博客,转载请注明出处!原文链接:https://www.cnblogs.com/yuxiuyan/p/14524302.html,

前语

最近,Android手机上的手机管家更新了新版本,提供了红包闹钟功能,只要有微信红包或者QQ红包,就会自动提醒。恰逢最近又在做UI自动化的工作,使用到UI Automator框架。几行代码,就可以让手机自动完成某些操作,很有意思,今天就来扒一扒这背后的原理。

UI Automator

首先,官方文档镇楼:https://developer.android.com/training/testing/ui-automator

传统的手工测试,我们需要点击一些控件元素,来查看输出的结果是否符合预期。比如在登录界面,输入正确的用户名和密码,点击登录按钮后,就可以正常登录。

如果这些操作,每一次都需要手工执行的话,是需要大量的人力成本的,比如手机QQ安卓端, 手工用例有上万条。所以就需要大力推广自动化测试。

UI自动化作为测试金字塔的最顶层,承担了端到端的需求回归与灰度验证任务,其重要性不言而喻。

测试金字塔

UI Automator作为一款Google谷歌推出的,用于UI自动化测试的工具,有着优秀的API与社区文档。也是目前主流的Android自动化测试框架。它提供了一系列用于获取手机上页面控件元素和操作元素的方法,非常方便。

注意UI Automator测试框架是基于instrumentation的API,运行在Android JunitRunner 之上,同时UI Automator Test只运行在 Android 4.3(API level 18)以上版本

从一次抢红包说起

想想我们平时抢红包的流程是什么样的呢?

假如你现在正在刷剧,这时候通知栏提醒你微信有红包了,于是你点击通知栏的消息,进入了微信页面,找到了红包,再点击拆红包的按钮,小手一抖,几毛到手。

这么一想,其实这些步骤完全是一个体力活,要是有个机器人能自动抢就好了!

这个机器人的背后就是AccessibilityService,当然它的具体作用我们稍后再讲。

按照我们的现有的逻辑,自动抢红包大致分为以下几个步骤:

  1. 识别获取通知栏的微信红包的通知事件
  2. 点击通知栏的消息
  3. 获取红包的消息
  4. 点击按钮拆红包

这里面最最重要的两个步骤就是识别,操作。接下来我们侃侃这两步。

怎么识别页面控件元素?

首先,我们先来认识一下UI Automator viewer这个工具,位于<android-sdk>/tools/bin目录下,他可以很方便地扫描和分析 Android 设备上当前显示的界面组件,展示一棵完整的控件树,与某一个叶子节点(控件元素)的属性。

UI Automator view工具

从上图我们可以看到,页面的一个登录按钮元素,有自己的text属性,resource-id属性,content-desc属性等等。

UI Automator中,存在uiDevice类,可以通过findobject方法,查看到这些控件元素。

UiObject2 login_btn = uiDevice.findobject(By.desc("登录"));

现在我们深入findobject方法,

    public UiObject2 findobject(BySelector selector) {
        // 这里返回匹配选择器的第一个节点,如果没有找到匹配的话,就返回null
        AccessibilityNodeInfo node = ByMatcher.findMatch(this,selector,getwindowRoots());
        return node != null ? new UiObject2(this,node) : null;
    }

可以看到,这里传入了一个选择器selector,然后在ByMatcherfindMatch方法中查询,如果找到了,就返回一个AccessibilityNodeInfo的node,如果没有找到就返回null。

首先看ByMatcher是什么东东?这是一个实用工具类,通过它的方法,我们可以在一个树形结构中搜索到匹配selector的节点。

findMatch方法很简单,就是一个从根节点开始搜索的树型搜索方法,不用多说。

AccessibilityNodeInfo是什么呢?这相当于一个节点,在AccessibilityService的角度来看,这就是一个可访问到的控件节点。

那这么来看,findMatch的第三个参数,就是传入的控件树的根节点了吗?我们深入看一下这里的getwindowRoots方法的关键代码,

 /** 这里返回活动窗口容器的root节点的列表 */
    AccessibilityNodeInfo[] getwindowRoots() {
        // 等待线程空闲后再执行
        waitForIdle();
        // 初始化一个root节点的集合
        Set<AccessibilityNodeInfo> roots = new HashSet();

        // 通过UiAutomation获取当前最底部的根窗口容器的root节点
        AccessibilityNodeInfo activeRoot = getUiAutomation().getRootInActiveWindow();  // 这里使用UiAutomation的方法
        if (activeRoot != null) {
            roots.add(activeRoot);
        }

        // 多窗口容器的搜索
        if (UiDevice.API_LEVEL_ACTUAL >= Build.VERSION_CODES.LOLLIPOP) {
            for (AccessibilityWindowInfo window : getUiAutomation().getwindows()) { // 这里使用UiAutomation的方法
                AccessibilityNodeInfo root = window.getRoot();
                …………
                roots.add(root);
            }
        }
        return roots.toArray(new AccessibilityNodeInfo[roots.size()]);
    }

这里要提一下, UiAutomation是Google在Android4.3的时候,发布的一个自动化框架,它提供了与系统底层交互的能力。

再往下,我们看看UiAutomationgetwindows方法的关键代码:

    public List<AccessibilityWindowInfo> getwindows() {
      ……
        return AccessibilityInteractionClient.getInstance()
                .getwindows(connectionId);
    }

这里获取了AccessibilityInteractionClient的实例,然后返回了client的getwindows方法结果。然后再看一下这个getwindows方法的关键代码,

    public List<AccessibilityWindowInfo> getwindows(int connectionId) {
            ……
            IAccessibilityServiceConnection connection = getConnection(connectionId);
            if (connection != null) {
                // 首先去查询缓存,如果缓存是有的,直接返回
                List<AccessibilityWindowInfo> windows = sAccessibilityCache.getwindows();
                   ……
                    return windows;
                }
                ……
                 // 如果上面的缓存不存在,就调用connection.getwindows方法
                 windows = connection.getwindows();
                 ……
                if (windows != null) {
                    // 把上面获取到的新的windows放置缓存,并返回
                    sAccessibilityCache.setwindows(windows);
                    return windows;
                }
            } 
             ……
    }

IAccessibilityServiceConnection开始,在IDE中就开始提示Cannot resolve symbol 'IAccessibilityServiceConnection',无法再跳转追踪了。这是因为这个文件属于aidl文件,这是Android中用于跨进程通信的接口文件,其具体源码可以在GoogleSource上面看到,有兴趣的同学可以去看一下:IAccessibilityServiceConnection.aidl。 这说明,到这里,UI Automation进程开始了与AccessibilityService进程的通信。我们把当前的程序可以当做是客户端,那么Android系统服务就是服务端,从这里开始,真正深入到Android系统的核心。在下面,就是Android Native的Library库。

这里,我们可以用时序图总结一下:

获取控件的时序图

怎么操作页面页面元素?

我们现在已经知道了UI Automator是怎么识别控件的,那怎么操作控件元素呢?比如实现控件的自动点击。

我们还是从源码开始入手。比如一个控件元素的点击动作,在UiObject2类中,关键代码如下:

    public void click() {
        mGestureController.performGesture(mGestures.click(getVisibleCenter()));
    }

首先,getVisibleCenter方法可以根据控件节点信息,也就是上面提到的AccessibilityNodeInfo,获取到这个控件节点的中心坐标点。然后把这个坐标点传给mGestureclick方法,这里是为了封装点击动作,最后交给mGestureController对象的performGesture方法去实施这个点击动作。

对于mGestureclick方法,这个mGesture是一个构造工厂,它的click方法直接生成了一个PointerGesture对象,这个对象表示的是执行手势操作时的动作。比如手势的开始坐标点,结束坐标点,持续时间,移动方向,速度等等。

重点看一下mGestureController对象的performGesture方法,其关键代码如下:

public void performGesture(PointerGesture ... gestures) {
          …………
         // 执行传入的手势操作动作
        MotionEvent event;   // 这个是关于运动事件
        for (……) {
                 …………
               // 初始化运动事件,并调用UI Automation的injectInputEvent注入事件,异步执行
                event = getMotionEvent(……);
                getDevice().getUiAutomation().injectInputEvent(event,true);
                …………
            }
          …………
        }
}

这里可以看到事件的注入,也是通过UI Automation来完成的。看一下injectInputEvent方法的关键代码,

    public boolean injectInputEvent(InputEvent event,boolean sync) {
        …………
        // 异步执行,这段代码之前有关于锁的操作
        return mUiAutomationConnection.injectInputEvent(event,sync);
        …………
    }

我们发现也是通过一个connection来执行操作的,这个connection对象对应的iuiAutomationConnection类,也属于一个aidl文件。

这里也放一个时序图,

点击事件的时序图

AccessibilityService

AccessibilityService根据官方说明,是指开发者通过增加类似contentDescription的属性,从而在不修改代码的情况下,让残障人士能够获得使用体验的优化,大家可以打开AccessibilityService来试一下,点击区域,可以有语音或者触摸的提示,帮助残障人士使用App。

当然,现在国内,AccessibilityService已经被玩儿坏了,越来越多的App借用AccessibilityService来实现了一些其它功能,甚至是灰色产品。

在国内,通过AccessibilityService实现的功能包括免Root自动安装,自动抢红包,微信消息自动回复等等黑科技。

当然也有一些恶意功能,比如软件防卸载。当用户想要卸载你的App的时候,一般会来到设置界面,找到你的App然后选择卸载,那么如果我们监控这个页面,如果发现是自己的App,就直接退出,这样不就无法卸载了吗?是的,简简单单,但是背后的恶意却让人心寒。

使用AccessibilityService做自动化的步骤

大家看了上面的分析,可能对自动化有了一点兴趣,其实归纳起来,步骤很简单:

  1. 分析整个操作流程,拆解成关于每个控件的识别与操作。
  2. 利用uiautomatorviewer等工具,查看对应UI控件的属性,进行唯一性识别
  3. 编写代码,查找到元素后,进行点击等操作
  4. 兼容性处理

结语

大家经常说“面试造火箭,工作拧螺丝”。其实大家平时工作可能都是“拧拧螺丝”,但是站在个人职业发展角度来看,是不可取的。只有不断挖深自己的技术护城河,才能提高个人的不可替代性。在“拧螺丝”的时候,我们不妨抬头看看,整个“火箭”是的构造。

工作拧螺丝

参考

https://juejin.cn/post/6844903456809943053

https://developer.android.com/reference/android/app/UiAutomation

https://testerhome.com/topics/1887

https://blog.csdn.net/luoyanglizi/article/details/51980630

https://www.kancloud.cn/digest/uiautomatorpriciple/192698

Android层次结构

Androd强化抢红包实现不了自动抢红包

Androd强化抢红包实现不了自动抢红包

List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("拆红包"); Log.i(TAG, "list:" + list.size()+""); for(AccessibilityNodeInfo n : list) {
    Log.i(TAG, "n:" + n);  n.performAction(AccessibilityNodeInfo.ACTION_CLICK); }

Android AccessibilityService实现微信抢红包插件

Android AccessibilityService实现微信抢红包插件

在你的手机更多设置或者高级设置中,我们会发现有个无障碍的功能,很多人不知道这个功能具体是干嘛的,其实这个功能是为了增强用户界面以帮助残障人士,或者可能暂时无法与设备充分交互的人们

它的具体实现是通过AccessibilityService服务运行在后台中,通过AccessibilityEvent接收指定事件的回调。这样的事件表示用户在界面中的一些状态转换,例如:焦点改变了,一个按钮被点击,等等。这样的服务可以选择请求活动窗口的内容的能力。简单的说AccessibilityService就是一个后台监控服务,当你监控的内容发生改变时,就会调用后台服务的回调方法

AccessibilityService使用

1.1 创建服务类

编写自己的Service类,重写onServiceConnected()方法、onAccessibilityEvent()方法和onInterrupt()方法

public class QHBAccessibilityService extends AccessibilityService {

 /**
 * 当启动服务的时候就会被调用
 */
 @Override
 protected void onServiceConnected() {
 super.onServiceConnected();
 }

 /**
 * 监听窗口变化的回调
 */
 @Override
 public void onAccessibilityEvent(AccessibilityEvent event) {
 int eventType = event.getEventType();
 //根据事件回调类型进行处理
 }

 /**
 * 中断服务的回调
 */
 @Override
 public void onInterrupt() {

 }
}

下面是对AccessibilityService中常用的方法的介绍

disableSelf():禁用当前服务,也就是在服务可以通过该方法停止运行
findFoucs(int falg):查找拥有特定焦点类型的控件
getRootInActiveWindow():如果配置能够获取窗口内容,则会返回当前活动窗口的根结点
getSeviceInfo():获取当前服务的配置信息
onAccessibilityEvent(AccessibilityEvent event):有关AccessibilityEvent事件的回调函数,系统通过sendAccessibiliyEvent()不断的发送AccessibilityEvent到此处
performGlobalAction(int action):执行全局操作,比如返回,回到主页,打开最近等操作
setServiceInfo(AccessibilityServiceInfo info):设置当前服务的配置信息
getSystemService(String name):获取系统服务
onKeyEvent(KeyEvent event):如果允许服务监听按键操作,该方法是按键事件的回调,需要注意,这个过程发生了系统处理按键事件之前
onServiceConnected():系统成功绑定该服务时被触发,也就是当你在设置中开启相应的服务,系统成功的绑定了该服务时会触发,通常我们可以在这里做一些初始化操作
onInterrupt():服务中断时的回调

1.2 声明服务

既然是个后台服务,那么就需要我们在manifests中配置该服务信息

<service
 android:name=".AccessibilityService.QHBAccessibilityService"
 android:enabled="true"
 android:exported="true"
 android:label="@string/label"
 android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
 <intent-filter>
 <action android:name="android.accessibilityservice.AccessibilityService" />
 </intent-filter>
</service>

我们必须注意:任何一个信息配置错误,都会使该服务无反应

android:label:在无障碍列表中显示该服务的名字

android:permission:需要指定BIND_ACCESSIBILITY_SERVICE权限,这是4.0以上的系统要求的
intent-filter:这个name是固定不变的

1.3 配置服务参数

配置服务参数是指:配置用来接受指定类型的事件,监听指定package,检索窗口内容,获取事件类型的时间等等。其配置服务参数有两种方法:

方法一:安卓4.0之后可以通过Meta-data标签指定xml文件进行配置
方法二:通过代码动态配置参数

1.3.1 方法一

在原先的manifests中增加Meta-data标签指定xml文件

<service
 android:name=".AccessibilityService.QHBAccessibilityService"
 android:enabled="true"
 android:exported="true"
 android:label="@string/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/accessibility_service_config" />
</service>

接下来是accessibility_service_config文件的配置

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
 android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged|typeWindowsChanged"
 android:accessibilityFeedbackType="FeedbackGeneric"
 android:accessibilityFlags="flagDefault"
 android:canRetrieveWindowContent="true"
 android:description="@string/description"
 android:notificationTimeout="100"
 android:packageNames="com.tencent.mm" />

下面是对xml参数的介绍

accessibilityEventTypes:表示该服务对界面中的哪些变化感兴趣,即哪些事件通知,比如窗口打开,滑动,焦点变化,长按等。具体的值可以在AccessibilityEvent类中查到,如typeAllMask表示接受所有的事件通知
accessibilityFeedbackType:表示反馈方式,比如是语音播放,还是震动
canRetrieveWindowContent:表示该服务能否访问活动窗口中的内容。也就是如果你希望在服务中获取窗体内容,则需要设置其值为true
description:对该无障碍功能的描述,具体体现在下图

notificationTimeout:接受事件的时间间隔,通常将其设置为100即可
packageNames:表示对该服务是用来监听哪个包的产生的事件,这里以微信的包名为例

1.3.2 方法二

通过代码为我们的AccessibilityService配置AccessibilityServiceInfo信息,这里我们可以抽取成一个方法进行设置

private void settingAccessibilityInfo() {
 String[] packageNames = {"com.tencent.mm"};
 AccessibilityServiceInfo mAccessibilityServiceInfo = new AccessibilityServiceInfo();
 // 响应事件的类型,这里是全部的响应事件(长按,单击,滑动等)
 mAccessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
 // 反馈给用户的类型,这里是语音提示
 mAccessibilityServiceInfo.FeedbackType = AccessibilityServiceInfo.FeedBACK_SPOKEN;
 // 过滤的包名
 mAccessibilityServiceInfo.packageNames = packageNames;
 setServiceInfo(mAccessibilityServiceInfo);
}

在这里涉及到了AccessibilityServiceInfo类,AccessibilityServiceInfo类被用于配置AccessibilityService信息,该类中包含了大量用于配置的常量字段及用来xml属性,常见的有:accessibilityEventTypes,canRequestFilterKeyEvents,packageNames等等

1.4 启动服务

这里我们需要在无障碍功能里面手动打开该项功能,否则无法继续进行,通过下面代码可以打开系统的无障碍功能列表

Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);

1.5 处理事件信息

由于我们监听了事件的通知栏和界面等信息,当我们指定packageNames的通知栏或者界面发生变化时,会通过onAccessibilityEvent回调我们的事件,接着进行事件的处理

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
 int eventType = event.getEventType();
 //根据事件回调类型进行处理
 switch (eventType) {
 //当通知栏发生改变时
 case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:

  break;
 //当窗口的状态发生改变时
 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:

  break;
 }
}

当我们微信收到通知时,状态栏会有一条推送信息到达,这个时候就会被TYPE_NOTIFICATION_STATE_CHANGED监听,执行里面的内容,当我们切换微信界面时,或者使用微信时,这个时候就会被TYPE_WINDOW_STATE_CHANGED监听,执行里面的内容

AccessibilityEvent的方法

getEventType():事件类型
getSource():获取事件源对应的结点信息
getClassName():获取事件源对应类的类型,比如点击事件是有某个Button产生的,那么此时获取的就是Button的完整类名
getText():获取事件源的文本信息,比如事件是有TextView发出的,此时获取的就是TextView的text属性。如果该事件源是树结构,那么此时获取的是这个树上所有具有text属性的值的集合
isEnabled():事件源(对应的界面控件)是否处在可用状态
getItemCount():如果事件源是树结构,将返回该树根节点下子节点的数量

1.6 获取节点信息

获取了界面窗口变化后,这个时候就要获取控件的节点。整个窗口的节点本质是个树结构,通过以下操作节点信息

1.6.1 获取窗口节点(根节点)

AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();

1.6.2 获取指定子节点(控件节点)

//通过文本找到对应的节点集合
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(text);
//通过控件ID找到对应的节点集合,如com.tencent.mm:id/gd
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(clickId);

1.7 模拟节点点击

当我们获取了节点信息之后,对控件节点进行模拟点击、长按等操作,AccessibilityNodeInfo类提供了performAction()方法让我们执行模拟操作,具体操作可看官方文档介绍,这里列举常用的操作

//模拟点击
accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
//模拟长按
accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
//模拟获取焦点
accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
//模拟粘贴
accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);

抢红包插件实现

2.1 原理分析

1、收到微信红包的推送信息,在推送信息中判断是否出现”[微信红包]”的消息提示,如果出现则点击进入聊天界面
2、通过遍历窗口树节点,发现带有”领取红包”字样的节点,则点击进入,即红包,弹出抢红包界面
3、在抢红包界面,通过ID获取”开”按钮的节点,则打开红包
4、在红包详情页面,通过ID获取返回键按钮的节点,点击并返回微信聊天界面

2.2 注意事项

1、由于微信每个版本的按钮ID都是不一样的,在我们的程序中是需要去修改按钮ID,以达到版本的适配
2、在获取控件ID的时候,注意其布局是否可点击,否则获取不可点击的控件,会使程序无反应

2.3 获取控件ID

当我们手机接入USB线时,在Android Device Monitor中的选择设备并开启Dump View Hierarchy for UI Automator工具,通过它可以获取控件信息

获取”开”按钮ID和返回按钮ID

2.4 代码实现

注意:这里使用的是微信最新6.3.30版本的控件ID,如果是其他版本的请自行适配

/**
 * =====作者=====
 * 许英俊
 * =====时间=====
 * 2016/11/19.
 */
public class QHBAccessibilityService extends AccessibilityService {

 private List<AccessibilityNodeInfo> parents;

 /**
  * 当启动服务的时候就会被调用
  */
 @Override
 protected void onServiceConnected() {
  super.onServiceConnected();
  parents = new ArrayList<>();
 }

 /**
  * 监听窗口变化的回调
  */
 @Override
 public void onAccessibilityEvent(AccessibilityEvent event) {
  int eventType = event.getEventType();
  switch (eventType) {
   //当通知栏发生改变时
   case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
    List<CharSequence> texts = event.getText();
    if (!texts.isEmpty()) {
     for (CharSequence text : texts) {
      String content = text.toString();
      if (content.contains("[微信红包]")) {
       //模拟打开通知栏消息,即打开微信
       if (event.getParcelableData() != null &&
         event.getParcelableData() instanceof Notification) {
        Notification notification = (Notification) event.getParcelableData();
        PendingIntent pendingIntent = notification.contentIntent;
        try {
         pendingIntent.send();
         Log.e("demo","进入微信");
        } catch (Exception e) {
         e.printstacktrace();
        }
       }
      }
     }
    }
    break;
   //当窗口的状态发生改变时
   case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
    String className = event.getClassName().toString();
    if (className.equals("com.tencent.mm.ui.LauncherUI")) {
     //点击最后一个红包
     Log.e("demo","点击红包");
     getLastPacket();
    } else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) {
     //开红包
     Log.e("demo","开红包");
     inputClick("com.tencent.mm:id/bg7");
    } else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) {
     //退出红包
     Log.e("demo","退出红包");
     inputClick("com.tencent.mm:id/gd");
    }
    break;
  }
 }

 /**
  * 通过ID获取控件,并进行模拟点击
  * @param clickId
  */
 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
 private void inputClick(String clickId) {
  AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
  if (nodeInfo != null) {
   List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(clickId);
   for (AccessibilityNodeInfo item : list) {
    item.performAction(AccessibilityNodeInfo.ACTION_CLICK);
   }
  }
 }

 /**
  * 获取List中最后一个红包,并进行模拟点击
  */
 private void getLastPacket() {
  AccessibilityNodeInfo rootNode = getRootInActiveWindow();
  recycle(rootNode);
  if(parents.size()>0){
   parents.get(parents.size() - 1).performAction(AccessibilityNodeInfo.ACTION_CLICK);
  }
 }

 /**
  * 回归函数遍历每一个节点,并将含有"领取红包"存进List中
  *
  * @param info
  */
 public void recycle(AccessibilityNodeInfo info) {
  if (info.getChildCount() == 0) {
   if (info.getText() != null) {
    if ("领取红包".equals(info.getText().toString())) {
     if (info.isClickable()) {
      info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
     }
     AccessibilityNodeInfo parent = info.getParent();
     while (parent != null) {
      if (parent.isClickable()) {
       parents.add(parent);
       break;
      }
      parent = parent.getParent();
     }
    }
   }
  } else {
   for (int i = 0; i < info.getChildCount(); i++) {
    if (info.getChild(i) != null) {
     recycle(info.getChild(i));
    }
   }
  }
 }

 /**
  * 中断服务的回调
  */
 @Override
 public void onInterrupt() {

 }
}

当收到红包发送的时候,Log的打印信息

11-21 13:53:06.275 2909-2909/com.handsome.boke2 E/demo: 进入微信
11-21 13:53:06.921 2909-2909/com.handsome.boke2 E/demo: 点击红包
11-21 13:53:07.883 2909-2909/com.handsome.boke2 E/demo: 开红包
11-21 13:53:08.732 2909-2909/com.handsome.boke2 E/demo: 退出红包

你可能会想到做一些窃取信息的软件,比如获取QQ密码、支付宝密码等等,哈哈,凡是EditText中设置inputType为password类型的,都无法获取其输入值

2.5 源码下载:http://xiazai.jb51.net/201611/yuanma/Androidwxpackage(jb51.net).rar

本文已被整理到了《Android微信开发教程汇总》,欢迎大家学习阅读。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

Android Wear将数据发送到Android手机,但手机似乎永远不会收到它

Android Wear将数据发送到Android手机,但手机似乎永远不会收到它

简短摘要:我正在尝试使用PutDataRequest和Googleapiclient将 Android Wear手表中的数据发送到Android手机.日志似乎表明数据已成功发送,但onDataChanged永远不会触发.我使用的是Android Studio 1.0.2.我没有使用任何模拟器,而是使用我自己的Android Wear手表 – 我已配对并通过手机上的Android Wear设备和Android Wear应用程序启用调试.在手机和Wear的AndroidManifest.xml上,我都包含了com.google.android.gms.version.

在Android手机(Android 4.4.4版本)上,我使用了一个侦听器服务,该服务通过AndroidManifest.xml绑定,并通过手机上的主要活动启动.从日志中,我可以确认在手机上成功创建了服务,但是没有收到任何数据(onDataChanged永远不会触发 – 准确地说).

<!-- Phone manifest,registers the listener -->
    <service android:name=".DataLayerListenerService" >
        <intent-filter>
            <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
        </intent-filter>
    </service>

这是在手机上运行的监听服务:

public class DataLayerListenerService extends WearableListenerService {
    private static final String TAG = DataLayerListenerService.class.getName();
    private Googleapiclient mGoogleapiclient;

    private static final String WEaraBLE_DATA_PATH = "/audio";

    @Override
    public void onCreate() {
        // I can see this fires properly on the Android mobile phone
        Logger.d(TAG,"onCreate");
    }

    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {
        // This never fires on the Android mobile phone,even though Wear says data was sent successfully
        Logger.d(TAG,"on change");
    }
}

在Wear设备上,我有一个主要活动,可以创建一个Google Api客户端.我使用UI按钮从音频生成输入(代码未显示),我知道这是因为日志记录正常工作.然后我尝试将这些数据从磨损设备发送到手机.在日志中,我看到“结果可用.状态:状态{statusCode = SUCCESS,resolution = null}”(我使用结果回调来跟踪).

public class MainActivity extends Activity implements
    Googleapiclient.ConnectionCallbacks,Googleapiclient.OnConnectionFailedListener {
    private static final String TAG = MainActivity.class.getName();
    private static final int SPEECH_REQUEST_CODE = 1;

    private static final int RECORDER_SAMPLERATE = 44100;
    private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_STEREO;
    private static final int RECORDER_AUdio_ENCODING = AudioFormat.ENCODING_PCM_16BIT;

    private TextView mTextView;
    private AudioRecord recorder;
    private int bufferSize = 0;
    private Thread recordingThread = null;
    private Googleapiclient mGoogleapiclient;
    private volatile boolean isRecording;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG,"Creating MainActivity");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setonLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                mTextView = (TextView) stub.findViewById(R.id.text);
            }
        });

        mGoogleapiclient = new Googleapiclient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();
    }

    // Connect to the data layer when the Activity starts
    @Override
    protected void onStart() {
        super.onStart();
        mGoogleapiclient.connect();
    }

    protected void onResume() {
        if (null != mGoogleapiclient && !mGoogleapiclient.isConnected()) {
            mGoogleapiclient.connect();
        }
        super.onResume();
    }

    @Override
    protected void onStop() {
        if (null != mGoogleapiclient && mGoogleapiclient.isConnected()) {
            mGoogleapiclient.disconnect();
        }
        super.onStop();
    }

    // Placeholders for required connection callbacks
    @Override
    public void onConnectionSuspended(int cause) {
        Log.d(TAG,"Connection suspended");
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.d(TAG,"Connection Failed");
    }

    @Override
    public void onConnected(Bundle connectionHint) {
        Log.d(TAG,"Connected successfully");
    }

    // This is invoked from the UI,via a helper method not shown. Logs show the method is invoked fine.
    private void processRawAudioData() {
        byte data[] = new byte[bufferSize];
        int read = 0;
        while(isRecording) {
            read = recorder.read(data,bufferSize);

            if(AudioRecord.ERROR_INVALID_OPERATION != read) {
                Log.d(TAG,"Successfully read " + data.length + " bytes of audio");
                Log.d(TAG,"Initial ten bytes: " + data[0] + data[1] + data[2] + data[3]
                    + data[4] + data[5] + data[6] + data[7] + data[8] + data[9] + data[10]);

                Asset myAsset = Asset.createFromBytes(data);
                PutDataRequest request = PutDataRequest.create("/audio");
                // might need to change time each time for other end to see change.
                request.putAsset("profileImage",myAsset);
                PendingResult<DataApi.DataItemResult> result =
                    Wearable.DataApi.putDataItem(mGoogleapiclient,request);
                result.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
                    @Override
                    public void onResult(DataApi.DataItemResult dataItemResult) {
                     // LOGS SHOW STATUS "MainActivity﹕ result available. Status: Status{statusCode=SUCCESS,resolution=null}"   
                     Log.d(TAG,"result available. Status: " + dataItemResult.getStatus());
                    }
                });
            }
        }
    }
}

解决方法

为了让WearableListenerService触发’onDataChanged’事件,主应用程序和可穿戴应用程序中的applicationId必须匹配(build.gradle文件).

另外,你不能发送静态数据,数据必须改变.这意味着PutDataMapRequest对象中的数据必须更改.

Android 微信红包插件

Android 微信红包插件

下面是小编 jb51.cc 通过网络收集整理的代码片段。

小编小编现在分享给大家,也给大家做个参考。

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void handleWindowChange(AccessibilityNodeInfo nodeInfo) {
        switch (Stage.getInstance().getCurrentStage()) {
            case Stage.opening_STAGE:
                // 调试信息,打印TTL
                // Log.d("TTL",String.valueOf(ttl));

                /* 如果打开红包失败且还没到达最大尝试次数,重试 */
                if (openHongbao(nodeInfo) == -1 && ttl < MAX_TTL) return;

                ttl = 0;
                Stage.getInstance().entering(Stage.FETCHED_STAGE);
                performMyGlobalAction(GLOBAL_ACTION_BACK);
                if (nodesToFetch.size() == 0) handleWindowChange(nodeInfo);
                break;
            case Stage.OPENED_STAGE:
                ListsuccessNodes = nodeInfo.findAccessibilityNodeInfosByText("红包详情");
                if (successNodes.isEmpty() && ttl < MAX_TTL) {
                    ttl += 1;
                    return;
                }
                ttl = 0;
                Stage.getInstance().entering(Stage.FETCHED_STAGE);
                performMyGlobalAction(GLOBAL_ACTION_BACK);
                break;
            case Stage.FETCHED_STAGE:
                /* 先消灭待抢红包队列中的红包 */
                if (nodesToFetch.size() > 0) {
                    /* 从最下面的红包开始戳 */
                    AccessibilityNodeInfo node = nodesToFetch.remove(nodesToFetch.size() - 1);
                    if (node.getParent() != null) {
                        String id = getHongbaoHash(node);

                        if (id == null) return;

                        fetchedIdentifiers.add(id);

                        // 调试信息,在每次打开红包后打印出已经获取的红包
                        // Log.d("fetched",Arrays.toString(fetchedIdentifiers.toArray()));

                        Stage.getInstance().entering(Stage.opening_STAGE);
                        node.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
                    }
                    return;
                }

                Stage.getInstance().entering(Stage.FETCHING_STAGE);
                fetchHongbao(nodeInfo);
                Stage.getInstance().entering(Stage.FETCHED_STAGE);
                break;
        }
    }

以上是小编(jb51.cc)为你收集整理的全部代码内容,希望文章能够帮你解决所遇到的程序开发问题。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给程序员好友。

关于从Android手机的抢红包插件说起安卓手机抢红包插件的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于Androd强化抢红包实现不了自动抢红包、Android AccessibilityService实现微信抢红包插件、Android Wear将数据发送到Android手机,但手机似乎永远不会收到它、Android 微信红包插件等相关内容,可以在本站寻找。

本文标签: