使用AccessibilityService实现自动遍历点赞功能

概述:

利用AccessibilityService机制实现了一个比较好玩儿的功能,微信朋友圈自动遍历点赞。即通过不断的滚动+点赞实现把每一条朋友圈都赞一次。

当然其中还要涉及一些判断算法,比如如果这条朋友圈已经赞过就跳过去,以及当前界面没有可赞的朋友圈时执行翻页。其实做起来试错是个很繁冗的过程,这个效果也差不多做了两天。

使用方式:

运行程序-开启无障碍服务,再切换到微信主界面,进入朋友圈,就会自动执行点赞程序了。

效果图如下:

实现原理步骤以及难点:

1.首先要获取到微信朋友圈这个界面的ListView结点,或者通过根节点描述判断是否进入该界面。

2.到了朋友圈界面之后可以执行程序方法体了,但是要有个boolean值判断只能执行一次。

为什么该方法体只能执行一次呢?(代码在下面有),因为如果被动地让onAccessibilityEvent调用我们的方法,会出现很多问题,比如结点刷新过快,多次触发方法导致点赞步骤同时执行N次然后无限死循环,因为onAccessibilityEvent触发太快了,大概0.几毫秒触发一次,所以我最后让方法体只触发一次,再每秒钟休眠1次确保结点有足够的时间刷新,也保证了执行的稳定性。

3.记录下用户自己的名字,比如我的是“至秦的瓜”,然后我在下面每个item的结点里去找到点赞区域,然后找是否有“至秦的瓜”这个字段,有的话说明这条朋友圈已经赞过了,跳过去,没有则执行点赞。

4.点赞程序的执行,则没什么难度了,代码都看得懂,这里就一带而过了。

代码实现:

/**
 * Created by jiangzn on 17/2/6.
 */
public class MyAccessibilityService extends AccessibilityService {

  @Override
  protected void onServiceConnected() {
    LogUtils.d("onServiceConnected");
  }

  String description;

  ArrayList<Integer> topList = new ArrayList<>();

  List<AccessibilityNodeInfo> lvs;

  @Override
  public void onAccessibilityEvent(AccessibilityEvent event) {
    try {

      //微信UI界面的根节点,开始遍历节点
      AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
      if (rootNodeInfo == null) {
        return;
      }
      description = "";
      if (rootNodeInfo.getContentDescription() != null) {
        description = rootNodeInfo.getContentDescription().toString();
      }

      //自动点赞流程
      if (mUserName.equals("")) {
        //Lv
        lvs = rootNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/cn0");
        LogUtils.d("找到的Lv数量: " + lvs.size());
        //如果size不为0,证明当前在朋友圈页面下,开始执行逻辑
        if (lvs.size() != 0) {
          //1.先记录用户名
          List<AccessibilityNodeInfo> userNames =
              rootNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/afa");
          if (userNames.size() != 0) {
            if (userNames.get(0).getParent() != null && userNames.get(0).getParent().getChildCount() == 4) {
              mUserName = userNames.get(0).getText().toString();
              if (!mUserName.equals("") && !ifOnce) {
                LogUtils.d("初始化,只会执行一次");
                LogUtils.d("当前的用户名:" + mUserName);
                ifOnce = true;
                //测试朋友圈点赞
                test3(rootNodeInfo);
              }
            }
          }
        } else {
          ifOnce = false;
          mUserName = "";
        }

      }

    } catch (Exception e) {
      if (e != null && e.getMessage() != null) {
        LogUtils.d("报错:" + e.getMessage().toString());
      }
    }

  }

  String mUserName = "";
  private boolean ifOnce = false;

  /**
   * com.tencent.mm:id/cn0
   * 朋友圈点赞 (目前实现手动滚动全部点赞)
   * 上方固定显示的名字:com.tencent.mm:id/afa
   * 下方点赞:显示id:com.tencent.mm:id/cnn
   * 每发现一个【评论按钮】,就去搜索当前同父组件下的点赞区域有没有自己的ID。
   * 如果有就不点赞,如果没有就点赞
   * 这里要改成不通过Id抓取提高稳定性
   *
   * @param rootNodeInfo
   */
  private synchronized void test3(AccessibilityNodeInfo rootNodeInfo) {
    LogUtils.d("当前线程:" + Thread.currentThread());
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    topList.clear();

    if (!mUserName.equals("")) {

      //测试获得评论按钮的父节点,再反推出点赞按钮
      List<AccessibilityNodeInfo> fuBtns =
          rootNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/co0");

      LogUtils.d("fuBtns数量:" + fuBtns.size());

      if (fuBtns.size() != 0) {

        //删掉超出屏幕的fuBtn
        AccessibilityNodeInfo lastFuBtn = fuBtns.get(fuBtns.size() - 1);
        Rect lastFuBtnOutBound = new Rect();
        lastFuBtn.getBoundsInScreen(lastFuBtnOutBound);
        if (lastFuBtnOutBound.top > Config.height) {
          fuBtns.remove(lastFuBtn);
        }

        for (int i = 0; i < fuBtns.size(); i++) {
          AccessibilityNodeInfo fuBtn = fuBtns.get(i);
          LogUtils.d("fuBtn的子节点数量:" + fuBtn.getChildCount());//3-4个
          List<AccessibilityNodeInfo> plBtns = fuBtn.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/cj9");
          LogUtils.d("从这里发现评论按钮:" + plBtns.size());

          if (plBtns.size() == 0) {
            if (lvs.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)) {
              test3(getRootInActiveWindow());
            }
            return;
          }

          AccessibilityNodeInfo plbtn = plBtns.get(0);  //评论按钮
          List<AccessibilityNodeInfo> zanBtns = fuBtn.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/cnn");
          LogUtils.d("从这里发现点赞文字显示区域:" + zanBtns.size());
          if (zanBtns.size() != 0) {
            //2.如果不为空,则查找有没有自己点过赞,有则不点,没有则点
            AccessibilityNodeInfo zanbtn = zanBtns.get(0);
            LogUtils.d("点赞的人是:" + zanbtn.getText().toString());
            if (zanbtn != null && zanbtn.getText() != null &&
                zanbtn.getText().toString().contains(mUserName)) {
              LogUtils.d("*********************这一条已经被赞过辣");
              //判断是否需要翻页,如果当前所有页面的父节点都没点过了,就需要翻页
              boolean ifxuyaofanye = false;
              LogUtils.d("O(≧口≦)O: i=" + i + " fuBtns.size():" + fuBtns.size());
              if (i == fuBtns.size() - 1) {
                ifxuyaofanye = true;
              }
              if (ifxuyaofanye) {
                //滑动前检测一下是否还有没有点过的点
                if (jianceIfLou()) {
                  LogUtils.d("还有遗漏的点!!!!再检查一遍!!!!!!!!!!");
                  test3(getRootInActiveWindow());
                } else {
                  if (lvs.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)) {
                    test3(getRootInActiveWindow());
                    return;
                  }
                }
              }

            } else {
              LogUtils.d("**************************:自己没有赞过!");
              //开始执行点赞流程
              if (plBtns.size() != 0) {
                Rect outBounds = new Rect();
                plbtn.getBoundsInScreen(outBounds);
                int top = outBounds.top;

                //根据top判断如果已经点开了就不重复点开了
                if (topList.contains(top)) {
                  return;
                }
                //com.tencent.mm:id/cj5 赞
                if (plbtn.performAction(AccessibilityNodeInfo.ACTION_CLICK)) {
                  List<AccessibilityNodeInfo> zanlBtns = rootNodeInfo.
                      findAccessibilityNodeInfosByViewId("com.tencent.mm:id/cj3");
                  if (zanlBtns.size() != 0) {
                    if (!topList.contains(top) && zanlBtns.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK)) {
                      topList.add(top);
                      LogUtils.d("topList:" + topList.toString());

                      //判断是否需要翻页,如果当前所有页面的父节点都没点过了,就需要翻页
                      boolean ifxuyaofanye = false;
                      LogUtils.d("O(≧口≦)O: i=" + i + " fuBtns.size():" + fuBtns.size());
                      if (i == fuBtns.size() - 1) {
                        ifxuyaofanye = true;
                      }
                      if (ifxuyaofanye) {
                        //滑动前检测一下是否还有没有点过的点
                        if (jianceIfLou()) {
                          LogUtils.d("还有遗漏的点!!!!再检查一遍!!!!!!!!!!");
                          test3(getRootInActiveWindow());
                        } else {
                          if (lvs.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)) {
                            test3(getRootInActiveWindow());
                            return;
                          }
                        }

                      }

                    }
                  }
                }
              }
            }

          } else {
            LogUtils.d("**************************:点赞区域为空!plBtns.size() :" + plBtns.size());

            //开始执行点赞流程
            if (plBtns.size() != 0) {

              Rect outBounds = new Rect();
              plbtn.getBoundsInScreen(outBounds);
              int top = outBounds.top;

              //根据top判断如果已经点开了就不重复点开了
              if (topList.contains(top)) {
                return;
              }
              //com.tencent.mm:id/cj5 赞
              if (plbtn.performAction(AccessibilityNodeInfo.ACTION_CLICK)) {
                List<AccessibilityNodeInfo> zanlBtns = rootNodeInfo.
                    findAccessibilityNodeInfosByViewId("com.tencent.mm:id/cj3");
                if (zanlBtns.size() != 0) {
                  if (!topList.contains(top) && zanlBtns.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK)) {
                    topList.add(top);
                    LogUtils.d("topList:" + topList.toString());

                    //判断是否需要翻页,如果当前所有页面的父节点都没点过了,就需要翻页
                    boolean ifxuyaofanye = false;
                    LogUtils.d("O(≧口≦)O: i=" + i + " fuBtns.size():" + fuBtns.size());
                    if (i == fuBtns.size() - 1) {
                      ifxuyaofanye = true;
                    }
                    if (ifxuyaofanye) {
                      //滑动前检测一下是否还有没有点过的点
                      if (jianceIfLou()) {
                        LogUtils.d("还有遗漏的点!!!!再检查一遍!!!!!!!!!!");
                        test3(getRootInActiveWindow());
                      } else {
                        if (lvs.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)) {
                          test3(getRootInActiveWindow());
                          return;
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }

    }
  }

  private boolean jianceIfLou() {
    boolean result = false;
    List<AccessibilityNodeInfo> fuBtns =
        getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/co0");
    LogUtils.d("检查的父节点数量:" + fuBtns.size());
    if (fuBtns.size() != 0) {
      for (AccessibilityNodeInfo fuBtn : fuBtns) {
        //点赞区域
        List<AccessibilityNodeInfo> zanBtns = fuBtn.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/cnn");
        LogUtils.d("检查的父节点的点赞区域数量:" + zanBtns.size());
        if (zanBtns.size() != 0) {
          AccessibilityNodeInfo zanbtn = zanBtns.get(0);
          LogUtils.d(" zanbtn.getText().toString():" + zanbtn.getText().toString());
          if (zanbtn != null && zanbtn.getText() != null &&
              zanbtn.getText().toString().contains(mUserName)) {
            result = false;
          } else {
            result = true;
          }
        } else {
          result = true;
        }
      }
    }

    return result;
  }

  @Override
  public void onInterrupt() {
    LogUtils.d("onInterrupt");
  }

}

辅助服务类的配置方法可以参考上文AccessibilityService——实现微信切换账号功能。

目前的代码有两段几乎重复的,这里没有抽离出来了因为之后我还要进一步优化(恩这就是个demo版不想改了。。)

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

(0)

相关推荐

  • Android仿微信朋友圈点赞和评论功能

    最近在做朋友圈的项目,所以写一个Android仿朋友圈点赞和评论功能Demo,代码就是简单实现了一下功能,没有做优化,凑合看 图文排列是用的RecyclerView实现的,弹窗效果是用的自定义的PopupWindow,点赞应该是在本地请求数据库,设置一个flag,获取当前用户的id后,带着id向服务器post一个flag,评论就比较简单了,也是获取当前朋友id(或者昵称),带着内容,向服务器post 贴代码: package com.example.lenovo.dianzandemo; imp

  • Android中Listview点赞功能的实现

    最近这段时间一直在看Android,利用Listview去实现点赞功能,下面给大家介绍下基本思路. 基本思路: 进入界面–>获取数据–> 在Listview中显示–> 通过map集合(position,boolean)保存每一行是否被点击–> 利用实体类去保存相应的对象–> get/set方法进行相应值得改变–> 点击一次,相应的数量加1 只实现了点赞功能,踩和赞基本类似. 具体实现如下: 继承自BaseAdapter package com.gz.test_listv

  • 简单实用的Android UI微博动态点赞效果

    说起空间动态.微博的点赞效果,网上也是很泛滥,各种实现与效果一大堆.而详细实现的部分,讲述的也是参差不齐,另一方面估计也有很多大侠也不屑一顾,觉得完全没必要单独开篇来写和讲解吧.毕竟,也就是两个view和一些简单的动画效果罢了. 单若是只讲这些,我自然也是不愿花这番功夫的.虽然自己很菜,可也不甘于太菜.所以偶尔看到些好东西,可以延伸学写下,我还是很情愿拿出来用用,顺带秀一秀逼格什么的. 不扯太多,先说说今天实现点赞效果用到的自以为不错的两个点: Checkable 用来扩展View实现选中状态切

  • Android自定义view实现仿抖音点赞效果

    前言 学习自定义view,想找点东西耍一下,刚好看到抖音的点赞效果不错,尝试一下. 抖音效果: 话不多说,先上代码: public class Love extends RelativeLayout { private Context mContext; float[] num = {-30, -20, 0, 20, 30};//随机心形图片角度 public Love(Context context) { super(context); initView(context); } public

  • Android控件实现直播App特效之点赞飘心动画

    现在市面上直播类的应用可以说是一抓一大把,随随便便就以什么主题来开发个直播App,说白了就想在这领域分杯羹.在使用这些应用过程中其实不难发现,在所有的直播界面,少不了的就是各种打赏.各种点赞.今天自己就针对点赞功能敲了一下,代码不多,主要是涉及到动画运动轨迹运算,这里需借助 贝塞尔曲线 相关知识,我使用三阶贝塞尔曲线来实现轨迹动画. 运行效果 一.具体实现流程 仔细分析整个点赞过程可以发现,首先是"爱心"的出现动画,然后是"爱心"以类似气泡的形式向上运动. &quo

  • Android项目开发 教你实现Periscope点赞效果

    现在视频应用越来越火,Periscope火起来后,国内也出现了不少跟风者,界面几乎跟Periscope一模一样.Periscope确实不错,点赞的效果也让人眼前一亮,很漂亮,于是乎,我就想着自己实现一下. 最终的效果图如下: 最终效果图.gif 录制的效果不太好,手机运行起来还是挺好看的. 不能说一模一样,但是也差不多了吧!~ 惯例: 通过本文你将学习到什么? 自定义view的一些基础方法以及一些注意点 随机数的使用 插补器的使用 属性动画的高级用法 贝塞尔曲线在Android中的实现以及应用

  • Android自定义ViewGroup实现堆叠头像的点赞Layout

    简介 这样的点赞列表怎么样?之前做社区的时候也有类似的点赞列表,但是没有这样重叠,一个小小的改变,个人感觉逼格提高不少. 这个很有规则,就是后一个头像会覆盖一部分到前一个头像上,头像多了就像一串糖葫芦了. 这个实现起来不难,自定义ViewGroup,关键重写onLayout方法. 关于自定义控件的基础知识可以看一看这个,整理的很详细: https://github.com/GcsSloop/AndroidNote 实现 自定义属性 属性名 说明 默认值 vertivalSpace 行距 4dp

  • Android高级UI特效仿直播点赞动画效果

    本文给大家分享高级UI特效仿直播点赞效果-一个优美炫酷的点赞动画,具体实现代码大家参考本文. 效果图如下: 攻克难点: 心形图片的路径等走向 心形图片的控制范围 部分代码如下: 通过AbstractPathAnimator定义飘心动画控制器 @Override public void start(final View child, final ViewGroup parent) { parent.addView(child, new ViewGroup.LayoutParams(mConfig.

  • Android中使用PopupWindow 仿微信点赞和评论弹出

    微信朋友圈的点赞和评论功能,有2个组成部分:左下角的"更多"按钮:点击该按钮后弹出的对话框: PopupWindow,弹出框使用PopupWindow实现,这是点赞和评论的载体,具体要涉及 PopupWindow 点击非窗口位置和再次点击消失以及显示位置的问题(根据相应更多按钮的位置确定 PopupWindow 的显示位置 package com.example.cmm.helloworld; import android.app.AlertDialog; import android

  • Android 仿微信朋友圈点赞和评论弹出框功能

    贡献/下载源码:https://github.com/mmlovesyy/PopupWindowDemo 本文简单模仿微信朋友圈的点赞和评论弹出框,布局等细节请忽略,着重实现弹出框.发评论,及弹出位置的控制. 1. 微信弹出框 微信朋友圈的点赞和评论功能,有2个组成部分: 点击左下角的"更多"按钮,弹出对话框: 点击评论,弹出输入框,添加评论并在页面中实时显示: 微信朋友圈点赞和评论功能 2. 实际效果 本文将建一个 ListView,在其 Item 中简单模仿微信的布局,然后着重实现

随机推荐