上一篇文章【玩转Android主动化】微信老友导出 实现了导出微信老友的功用,有了这些老友,咱们就能够一个一个去进行想要的操作,去判别老友状况了。本章咱们来介绍一下怎样判别老友联系状况。

判别办法

  • 现在已知的判别办法常用的是拉群法假转账法,本篇主要讲解经过假转账的办法去判别老友状况,并供给完好的解决方案。

假转账办法

  • 开端之前先说一下为什么能够经过假转账的办法判别,当你对一个老友进行转账的时分其实微信是会对当时老友联系做一次校验的,假如对方把你拉黑、删去、或许老友账号反常,微信都会弹框提示,假如老友状况正常的就能够进行输入暗码转账了,当然之所以称之为假转账的办法,便是咱们并不会去真实的转账,只是凭借转账这个操作判别状况罢了,不需求输入支付暗码,所以整个进程不触及暗码走漏之类的状况,能够放心运用。

以上便是咱们最终去判别用户状况的地方,那么在判别之前是怎样翻开转账页面的纳,都需求哪些流程呐,接下来就来剖析具体进程。

操作流程

  • 翻开微信 → 点击【通讯录】tab → 点击一个老友(进入老友信息页) → 点击发消息(进入谈天页) → 点击右下角功用按钮(弹出功用区域) → 点击功用区的转账按钮(进入转账页面) → 输入金额 → 点击键盘里的转账按钮 → 触发弹框判别状况 → 回来到主页。

留意:文章中所用的页面节点元素查看便是运用咱们之前文章【玩转Android主动化】布局节点速查器中封装的小工具,不清楚的能够先看看文章介绍。

进程拆分

由于触及到的页面比较多,所以咱们把同一个页面的功用放到一同,每个页面建一个类,这样愈加直观且便利办理。

1、翻开微信

  • 翻开微信的办法跟上一篇文章介绍的相同直接调用咱们的封装好的扩展办法即可
fun Context.goToWx() = Intent(Intent.ACTION_MAIN)
    .apply {
        addCategory(Intent.CATEGORY_LAUNCHER)
        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
        component = ComponentName("com.tencent.mm", "com.tencent.mm.ui.LauncherUI")
    }
    .apply(::startActivity)

2、点击【通讯录】tab

  • 点击通讯录仍然需求先找到通讯录tab的节点,然后触发点击事情即可
\--- className = android.widget.RelativeLayout → text =  → id = com.tencent.mm:id/fj3 → description =  → isClickable = false → isScrollable = false → isEditable = false
 \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
   +--- className = android.widget.RelativeLayout → text =  → id = com.tencent.mm:id/kd_ → description =  → isClickable = true → isScrollable = false → isEditable = false
   |  \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/f1f → description =  → isClickable = false → isScrollable = false → isEditable = false
   |    +--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
   |    |  +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/f2a → description =  → isClickable = false → isScrollable = false → isEditable = false
   |    |  \--- className = android.widget.TextView → text = 1 → id = com.tencent.mm:id/l0c → description =  → isClickable = false → isScrollable = false → isEditable = false
   |    \--- className = android.widget.TextView → text = 微信 → id = com.tencent.mm:id/f2s → description =  → isClickable = false → isScrollable = false → isEditable = false
   +--- className = android.widget.RelativeLayout → text =  → id = com.tencent.mm:id/kd_ → description =  → isClickable = true → isScrollable = false → isEditable = false
   |  \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/f1f → description =  → isClickable = false → isScrollable = false → isEditable = false
   |    +--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
   |    |  \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/f2a → description =  → isClickable = false → isScrollable = false → isEditable = false
   |    \--- className = android.widget.TextView → text = 通讯录 → id = com.tencent.mm:id/f2s → description =  → isClickable = false → isScrollable = false → isEditable = false
   +--- className = android.widget.RelativeLayout → text =  → id = com.tencent.mm:id/kd_ → description =  → isClickable = true → isScrollable = false → isEditable = false
   |  \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/f1f → description =  → isClickable = false → isScrollable = false → isEditable = false
   |    +--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
   |    |  \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/f2a → description =  → isClickable = false → isScrollable = false → isEditable = false
   |    \--- className = android.widget.TextView → text = 发现 → id = com.tencent.mm:id/f2s → description =  → isClickable = false → isScrollable = false → isEditable = false
   \--- className = android.widget.RelativeLayout → text =  → id = com.tencent.mm:id/kd_ → description =  → isClickable = true → isScrollable = false → isEditable = false
     \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/f1f → description =  → isClickable = false → isScrollable = false → isEditable = false
       +--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
       |  \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/f2a → description =  → isClickable = false → isScrollable = false → isEditable = false
       \--- className = android.widget.TextView → text = 我 → id = com.tencent.mm:id/f2s → description =  → isClickable = false → isScrollable = false → isEditable = false
  • 有了节点信息,咱们就去找到text = 通讯录 → id = com.tencent.mm:id/f2s这个节点就能够了,其中运用咱们封装好的findByIdAndText办法,体系原始的办法只要findXXXByIdfindXXXByText。从上面打印的数据能够看出来,底导四个tab的ID是相同的,假如只依据文本匹配的话会不准确,由于体系供给的办法对文本匹配是含糊匹配的,只要包含咱们传入的text都会匹配出来,没有办法精准匹配,所以咱们就寻找指定id的节点,会得到一个数组,里面只包含了这四个tab的数据,然后再依据文本通讯录去匹配就能够得到仅有的tab,最终触发点击事情就能够翻开通讯录页面了。
wxAccessibilityService?.findByIdAndText(
                        NodeInfo.BottomNavContactsTabNode.nodeId,
                        NodeInfo.BottomNavContactsTabNode.nodeText
                    ).click()

3、点击一个老友(进入老友信息页)

  • 在通讯录页咱们选中一个老友,为了便利测验,先找到第一个老友
|  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|    \--- className = android.widget.RelativeLayout → text =  → id = com.tencent.mm:id/bqy → description =  → isClickable = false → isScrollable = false → isEditable = false
|      \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/a27 → description =  → isClickable = false → isScrollable = false → isEditable = false
|        \--- className = android.widget.TableLayout → text =  → id = com.tencent.mm:id/hg2 → description =  → isClickable = false → isScrollable = false → isEditable = false
|          \--- className = android.widget.TableRow → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|            \--- className = android.widget.TextView → text = 百里守约 → id = com.tencent.mm:id/hg4 → description =  → isClickable = false → isScrollable = false → isEditable = false
  • 然后调用咱们的办法 wxAccessibilityService?.findByText("百里守约").click()就触发了点击百里守约这个老友了,就会进入到老友的信息页

4、点击发消息(进入谈天页)

  • 进入老友信息页后咱们就就要剖析这个页面的元素,下边展示的节点数据是去掉了一些用不到的信息后的姿态。
+--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/gv4 → description =  → isClickable = false → isScrollable = false → isEditable = false
|  \--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|    \--- className = android.widget.ListView → text =  → id = android:id/list → description =  → isClickable = false → isScrollable = false → isEditable = false
|      +--- className = android.widget.RelativeLayout → text =  → id = com.tencent.mm:id/bq2 → description =  → isClickable = true → isScrollable = false → isEditable = false
|      |  +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/bpy → description =  → isClickable = false → isScrollable = false → isEditable = 
|      |  |  \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/bps → description =  → isClickable = false → isScrollable = false → isEditable = 
|      |  |    +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/bpr → description = 头像 → isClickable = true → isScrollable = false → isEditable 
|      |  |    \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|      |  |      +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|      |  |      |  +--- className = android.widget.TextView → text = 百里守约 → id = com.tencent.mm:id/bq1 → description =  → isClickable = false → isScrollable = false → 
|      |  |      |  +--- className = android.view.View → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|      |  |      |  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|      |  |      +--- className = android.widget.TextView → text = 昵称:  测验 → id = com.tencent.mm:id/bq0 → description =  → isClickable = false → isScrollable = false → 
|      |  |      \--- className = android.widget.TextView → text = 微信号:  wxid_xxxxxx → id = com.tencent.mm:id/bq9 → description =  → isClickable = false → isScrollable = false → isEditable = false
|      +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/bq5 → description =  → isClickable = true → isScrollable = false → isEditable = false
|      |  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|      |    +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/f15 → description =  → isClickable = false → isScrollable = false → isEditable = false
|      |    \--- className = android.widget.TextView → text = 发消息 → id = com.tencent.mm:id/khj → description =  → isClickable = false → isScrollable = false → isEditable = 
|      +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/iwg → description =  → isClickable = true → isScrollable = false → isEditable = false
|      |  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|      |    +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/f15 → description =  → isClickable = false → isScrollable = false → isEditable = false
|      |    \--- className = android.widget.TextView → text = 音视频通话 → id = com.tencent.mm:id/khj → description =  → isClickable = false → isScrollable = false → 
|      +--- className = android.widget.TextView → text =  → id = com.tencent.mm:id/jn6 → description =  → isClickable = true → isScrollable = false → isEditable = false
|      +--- className = android.widget.TextView → text =  → id = android:id/title → description =  → isClickable = true → isScrollable = false → isEditable = false
|      \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = true → isScrollable = false → isEditable = false
|        \--- className = android.widget.TextView → text =  → id = android:id/title → description =  → isClickable = false → isScrollable = false → isEditable = false
  • 在这个页面能够看到微信昵称和微信号,还有发送按钮,咱们在WXContactInfoPage类中界说两个办法,获取老友数据的getUserInfo()和点击发送按钮的clickSendMsg()
            val nickName = wxAccessibilityService
                ?.findById(NodeInfo.ContactInfoNickNameNode.nodeId)
                ?.text
                .default()
            //android.widget.TextView → 微信号: wxid_xxx → com.tencent.mm:id/ini
            val wxCode = wxAccessibilityService
                ?.findById(NodeInfo.ContactInfoWxCodeNode.nodeId)
                ?.text
                ?.split("微信号:")
                ?.getOrNull(1)
                .default()
                .trim()
            if (nickName.isNotBlank() && wxCode.isNotBlank()) {
                WxUserInfo(nickName, wxCode)
            }
        //【发消息】的按钮ID和【音视频通话】的ID相同,所以用文本区分
        wxAccessibilityService.clickByText(NodeInfo.ContactInfoSendMsgNode.nodeText)
  • 经过以上两步操作就能够获取到老友的微信昵称和微信号了,然后点击了【发消息】按钮跳转到谈天详情页

5、在谈天页点击右下角功用按钮(弹出功用区域)和点击功用区的转账按钮(进入转账页面)

  • 谈天详情页咱们界说WXChattingPage类,这个页面相关的操作都写在一个类中,便于保护,咱们只需求剖分出右下角的功用按钮【➕】和点击展开后功用去里的【转账】按钮

【玩转Android自动化】微信好友状态检查(假转账方式)

className = android.widget.ImageButton → text =  → id = com.tencent.mm:id/b3q → description = 更多功用按钮,已折叠 → isClickable = true → isScrollable = false → isEditable = false
\--- className = android.widget.FrameLayout → text =  → id = com.tencent.mm:id/b44 → description =  → isClickable = false → isScrollable = false → isEditable = false
  \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/b1m → description =  → isClickable = false → isScrollable = false → isEditable = false
    \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
      \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/w6 → description =  → isClickable = false → isScrollable = false → isEditable = false
        +--- className = android.view.ViewGroup → text =  → id = com.tencent.mm:id/w8 → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  +--- className = android.widget.GridView → text =  → id = com.tencent.mm:id/w9 → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = true → isScrollable = false → isEditable = false
        |  |  |  \--- className = android.widget.FrameLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |    +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |    \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      +--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      |  +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/ve → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      |  \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/vf → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      \--- className = android.widget.TextView → text = 红包 → id = com.tencent.mm:id/vg → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = true → isScrollable = false → isEditable = false
        |  |  |  \--- className = android.widget.FrameLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |    +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |    \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      +--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      |  +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/ve → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      |  \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/vf → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      \--- className = android.widget.TextView → text = 转账 → id = com.tencent.mm:id/vg → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = true → isScrollable = false → isEditable = false
        |  |  |  \--- className = android.widget.FrameLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |    +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |    \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      +--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      |  +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/ve → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      |  \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/vf → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  |  |      \--- className = android.widget.TextView → text = 语音输入 → id = com.tencent.mm:id/vg → description =  → isClickable = false → isScrollable = false → isEditable = false
        |  \--- className = android.widget.GridView → text =  → id = com.tencent.mm:id/w9 → description =  → isClickable = false → isScrollable = false → isEditable = false
        \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/w7 → description =  → isClickable = false → isScrollable = false → isEditable = false
          +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/gvo → description =  → isClickable = false → isScrollable = false → isEditable = false
          \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/gvo → description =  → isClickable = false → isScrollable = false → isEditable = false
  • 从上边的数据能够剖分出android.widget.ImageButton → text = → id = com.tencent.mm:id/b3q → description = 更多功用按钮,已折叠 → isClickable = true是咱们需求点击的按钮, 转账 → id = com.tencent.mm:id/vg是功用区转账按钮的节点信息,拿到了节点信息后获取节点并触发点击即可。这儿会在测验中发现,点击的机遇不易太快,假如点的太快会呈现页面未跳转的问题,所以在点击事情时分做一个delay处理,而且转账按钮调用performAction(AccessibilityNodeInfo.ACTION_CLICK)是无效的,或许转账触及到金钱买卖,微信做了一层防护吧,禁掉了事情,不过这儿能够运用模拟点击的办法去替代。

//点击功用展开按钮
wxAccessibilityService.clickById(NodeInfo.ChattingBottomPlusNode.nodeId)
//获取功用区转账按钮节点
val find = wxAccessibilityService?.findByIdAndText(
                    NodeInfo.ChattingTransferMoneyNode.nodeId,
                    NodeInfo.ChattingTransferMoneyNode.nodeText
                )
if (find != null) {
    //点击按钮
    wxAccessibilityService?.gestureClick(find)
}
//模拟点击代码封装
fun AccessibilityService.gestureClick(node: AccessibilityNodeInfo) {
    val nodeBounds = Rect().apply(node::getBoundsInScreen)
    val x = nodeBounds.centerX().toFloat()
    val y = nodeBounds.centerY().toFloat()
    dispatchGesture(
        GestureDescription.Builder().apply {
            addStroke(
                GestureDescription.StrokeDescription(
                    Path().apply { moveTo(x, y) },
                    0L,
                    200L
                )
            )
        }.build(),
        object : AccessibilityService.GestureResultCallback() {
            override fun onCompleted(gestureDescription: GestureDescription?) {
                super.onCompleted(gestureDescription)
            }
        },
        null
    )
}

6、转账页相关操作

  • 经过上面的操作就进入到转账页面了,接下来就在转账页面进行判别了。在转账页其实还有一个点需求特别关注一下,便是转账目标那里假如显现的是转账给 百里守约(xx名),微信昵称后边假如显现的有真实姓名的话,也是能够阐明是正常老友联系的,这一点能够经过反复测验去验证。所以一旦监测到昵称后边有真名就无需在进行输入金额进行假转账判别了,省了后续的操作。

  • 先全体看一下转账页的节点信息吧

+--- className = android.widget.FrameLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
  \--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
    +--- className = android.widget.ScrollView → text =  → id = com.tencent.mm:id/iwq → description =  → isClickable = false → isScrollable = false → isEditable = false
    |  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
    |    +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/euv → description =  → isClickable = false → isScrollable = false → isEditable = false
    |    |  +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
    |    |  |  +--- className = android.widget.TextView → text = 转账给 百里守约 → id = com.tencent.mm:id/inh → description =  → isClickable = false → isScrollable = false → 
    |    |  |  \--- className = android.widget.TextView → text = 微信号:  wxid_xxxxxx → id = com.tencent.mm:id/ini → description =  → isClickable = false → isScrollable = false
    |    |  \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/ing → description = 头像 → isClickable = false → isScrollable = false → isEditable = false
    |    \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/ah2 → description =  → isClickable = false → isScrollable = false → isEditable = false
    |      \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/in8 → description =  → isClickable = false → isScrollable = false → isEditable = false
    |        +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/imt → description =  → isClickable = false → isScrollable = false → isEditable = false
    |        |  +--- className = android.widget.TextView → text = 转账金额 → id = com.tencent.mm:id/inz → description =  → isClickable = false → isScrollable = false → isEditable 
    |        |  \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/gym → description =  → isClickable = false → isScrollable = false → isEditable = false
    |        |    \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/gyl → description =  → isClickable = false → isScrollable = false → isEditable = false
    |        |      +--- className = android.widget.TextView → text =  → id = com.tencent.mm:id/ljn → description =  → isClickable = false → isScrollable = false → isEditable = false
    |        |      \--- className = android.widget.EditText → text =  → id = com.tencent.mm:id/lg_ → description =  → isClickable = true → isScrollable = false → isEditable = true
    |        +--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
    |        |  \--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
    |        |    \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/lh_ → description =  → isClickable = false → isScrollable = false → isEditable = false
    |        \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/im3 → description =  → isClickable = false → isScrollable = false → isEditable = false
    |          \--- className = android.widget.TextView → text = 增加转账阐明 → id = com.tencent.mm:id/je → description =  → isClickable = true → isScrollable = false → isEditable 
    \--- className = android.widget.RelativeLayout → text =  → id = com.tencent.mm:id/lrk → description =  → isClickable = false → isScrollable = false → isEditable = false
      \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/ffr → description =  → isClickable = true → isScrollable = false → isEditable = false
        +--- className = android.view.View → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
        \--- className = android.view.ViewGroup → text =  → id = com.tencent.mm:id/fg4 → description =  → isClickable = false → isScrollable = false → isEditable = false
          +--- className = android.widget.TextView → text = 1 → id = com.tencent.mm:id/ffg → description =  → isClickable = true → isScrollable = false → isEditable = false
          +--- className = android.widget.TextView → text = 2 → id = com.tencent.mm:id/ffh → description =  → isClickable = true → isScrollable = false → isEditable = false
          +--- className = android.widget.TextView → text = 3 → id = com.tencent.mm:id/ffi → description =  → isClickable = true → isScrollable = false → isEditable = false
          +--- className = android.widget.Button → text =  → id = com.tencent.mm:id/ffw → description = 删去 → isClickable = true → isScrollable = false → isEditable = false
          |  \--- className = android.widget.ImageView → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
          +--- className = android.widget.TextView → text = 4 → id = com.tencent.mm:id/ffj → description =  → isClickable = true → isScrollable = false → isEditable = false
          +--- className = android.widget.TextView → text = 5 → id = com.tencent.mm:id/ffk → description =  → isClickable = true → isScrollable = false → isEditable = false
          +--- className = android.widget.TextView → text = 6 → id = com.tencent.mm:id/ffl → description =  → isClickable = true → isScrollable = false → isEditable = false
          +--- className = android.widget.TextView → text = 7 → id = com.tencent.mm:id/ffm → description =  → isClickable = true → isScrollable = false → isEditable = false
          +--- className = android.widget.TextView → text = 8 → id = com.tencent.mm:id/ffn → description =  → isClickable = true → isScrollable = false → isEditable = false
          +--- className = android.widget.TextView → text = 9 → id = com.tencent.mm:id/ffo → description =  → isClickable = true → isScrollable = false → isEditable = false
          +--- className = android.widget.TextView → text = 0 → id = com.tencent.mm:id/fff → description =  → isClickable = true → isScrollable = false → isEditable = false
          +--- className = android.widget.TextView → text = . → id = com.tencent.mm:id/fg3 → description =  → isClickable = true → isScrollable = false → isEditable = false
          \--- className = android.widget.Button → text = 转账 → id = com.tencent.mm:id/ffp → description =  → isClickable = true → isScrollable = false → isEditable = false

6.1、判别昵称后边是否有真名

  • 经过节点信息能够看到微信昵称是text = 转账给 百里守约 → id = com.tencent.mm:id/inh,只需获取这个节点取出text,假如呈现 转账给 测验(**名) 即昵称后边有加星的真名就阐明老友正常,否则还需求后边进一步验证
val friendName = wxAccessibilityService
    ?.findById(NodeInfo.RemittanceUserNode.nodeId)
    ?.text
    .default()
    .split("转账给")
    .getOrNull(1)
    .default()
    .trim()
if (friendName.isNotBlank()) {
    val isNormal = friendName.contains("(*") && friendName.endsWith(")")
    if (isNormal) {
        WxUserInfo(friendName, wxCode, FriendStatus.NORMAL)
    }
}

6.2、输入金额

  • 假如经过微信昵称无法判别出来,就要经过假转账的办法了,需求先输入转账金额,咱们这儿就输入最小金额0.01即可。想要对一个可修改的节点输入内容能够用如下办法
fun AccessibilityNodeInfo.inputText(input: String): Boolean {
    val arguments = Bundle().apply {
        putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, input)
    }
    return performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
}
  • 所以获取输入框节点className = android.widget.EditText → text = → id = com.tencent.mm:id/lg_ → description = → isClickable = true → isScrollable = false → isEditable = true然后主动输入金额,代码如下
wxAccessibilityService?.findById(NodeInfo.RemittanceInputMoneyNode.nodeId)?.inputText("0.01")

6.3、点击键盘里的转账按钮

  • 输入完金额后就开端点击转账按钮了,获取按钮触发点击的代码咱们现已写过许多次了,直接看代码吧
wxAccessibilityService.clickById(NodeInfo.RemittanceTransferMoneyNode.nodeId)

6.4、触发弹框判别状况

点击完转账按钮后就会向微信服务器发送请求进行老友联系的判别,并弹出对应的弹框。

  • 被拉黑(弹框内容及节点信息)

【玩转Android自动化】微信好友状态检查(假转账方式)

+--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guj → description =  → isClickable = false → isScrollable = false → isEditable = false
|  \--- className = android.widget.ScrollView → text =  → id = com.tencent.mm:id/o4_ → description =  → isClickable = false → isScrollable = false → isEditable = false
|    \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|      \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guq → description =  → isClickable = false → isScrollable = false → isEditable = false
|        +--- className = android.widget.TextView → text = 请承认你和他(她)的老友联系是否正常 → id = com.tencent.mm:id/guo → description =  → isClickable = false → isScrollable = false → isEditable = false
|        \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|          \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
\--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guc → description =  → isClickable = false → isScrollable = false → isEditable = false
  \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guh → description =  → isClickable = false → isScrollable = false → isEditable = false
    +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/c7p → description =  → isClickable = false → isScrollable = false → isEditable = false
    \--- className = android.widget.Button → text = 我知道了 → id = com.tencent.mm:id/guw → description =  → isClickable = true → isScrollable = false → isEditable = false
  • 被删去(弹框内容及节点信息)

【玩转Android自动化】微信好友状态检查(假转账方式)

+--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guj → description =  → isClickable = false → isScrollable = false → isEditable = false
|  \--- className = android.widget.ScrollView → text =  → id = com.tencent.mm:id/o4_ → description =  → isClickable = false → isScrollable = false → isEditable = false
|    \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|      \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guq → description =  → isClickable = false → isScrollable = false → isEditable = false
|        +--- className = android.widget.TextView → text = 你不是收款方老友,对方增加你为老友后才干建议转账 → id = com.tencent.mm:id/guo → description =  → isClickable = false → isScrollable = false → isEditable = false
|        \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|          \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
\--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guc → description =  → isClickable = false → isScrollable = false → isEditable = false
  \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guh → description =  → isClickable = false → isScrollable = false → isEditable = false
    +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/c7p → description =  → isClickable = false → isScrollable = false → isEditable = false
    \--- className = android.widget.Button → text = 我知道了 → id = com.tencent.mm:id/guw → description =  → isClickable = true → isScrollable = false → isEditable = false
  • 老友账号反常(弹框内容及节点信息)

【玩转Android自动化】微信好友状态检查(假转账方式)

+--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guj → description =  → isClickable = false → isScrollable = false → isEditable = false
|  \--- className = android.widget.ScrollView → text =  → id = com.tencent.mm:id/o4_ → description =  → isClickable = false → isScrollable = false → isEditable = false
|    \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|      \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guq → description =  → isClickable = false → isScrollable = false → isEditable = false
|        +--- className = android.widget.TextView → text = 对方微信号已被限制登录,为保障你的资金安全,暂时无法完结买卖。 → id = com.tencent.mm:id/guo → description =  → isClickable = false → isScrollable = false → isEditable = false
|        \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|          \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
\--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guc → description =  → isClickable = false → isScrollable = false → isEditable = false
  \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/guh → description =  → isClickable = false → isScrollable = false → isEditable = false
    +--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/c7p → description =  → isClickable = false → isScrollable = false → isEditable = false
    \--- className = android.widget.Button → text = 我知道了 → id = com.tencent.mm:id/guw → description =  → isClickable = true → isScrollable = false → isEditable = false
  • 正常(弹框内容及节点信息)

【玩转Android自动化】微信好友状态检查(假转账方式)

  • 自己未实名认证时分会跳转新页面,让你去实名认证,不过既然是个人微信,根本上都现已实名过了,这种状况就不具体列举出来了,不过在代码逻辑层还是要做的。

【玩转Android自动化】微信好友状态检查(假转账方式)

  • 有了上面几种状况咱们就能够来判别了,由于类型众多,咱们界说一个枚举CheckStatusstatus是老友状况,mark便是咱们要匹配的字符,这些字符都是仅有的
enum class CheckStatus(val status: String, val mark: String) {
    BLACK("被拉黑", "请承认你和他(她)的老友联系是否正常"),
    DELETE("被删去", "你不是收款方老友,对方增加你为老友后才干建议转账"),
    ACCOUNT_EXCEPTION("帐号反常", "对方微信号已被限制登录,为保障你的资金安全,暂时无法完结买卖"),
    NORMAL("正常", "付款办法"),
    PWD_PAY("正常", "请输入支付暗码"),
    NO_AUTH("自己未实名认证", "实名认证"),
}
  • 然后便是获取当时窗口的一切节点进行遍历查找枚举中的字符,回来判别后的状况
val find =wxAccessibilityService?.findByContainsText(false, CheckStatus.values().map { it.mark })
if (find != null) {
    val status = CheckStatus.values().find { find.text.contains(it.mark) }
}

7、回来到主页

  • 经过以上操作后就把一个用户的状况检测完了,然后回来主页即可。是不是觉得整个流程很麻烦,说实话进程的确繁琐,可是咱们是抱着学习的态度做的,要点在于学习无障碍模式相关的操作,所以流程繁琐不是问题,重要的是咱们能从中学到什么。

依照惯例做法文章至此也算是接近结尾了,不过咱们干事是寻求完好性的,怎样局限于简单剖析流程纳,只是检测一个指定的老友其实没什么卵用,怎样去主动化检测一切老友才是要点,接下来会持续介绍主动循环检测的办法。

主动循环检测悉数老友

1、依据事前预备的老友列表去检测

  • 之前文章【玩转Android主动化】微信老友导出里现已把微信老友列表拿到了,咱们是不是就能够经过遍历老友列表去循环执行了呢,答案是能够的
    suspend fun startCheckFromList(userList: List<String>) {
        App.instance().goToWx()
        //判别当时是否进入到微信
        val inWxApp = WXHomePage.waitEnterWxApp()
        //假如不在微信里面就没必要持续执行了
        if (!inWxApp) return
        //判别当时是否现已到微信主页
        val isHome = WXHomePage.backToHome()
        if (!isHome) return
        //点击底部通讯录Tab 点两次是为了让通讯录老友列表回到顶部初始状况
        val clickContactsTab = WXHomePage.clickContactsTab(true)
        if (!clickContactsTab) return
        //经过循环遍历老友一次执行检测办法
        userList.forEach { singleTask(it) }
        if (WXHomePage.backToHome()) {
            wxAccessibilityService?.pressBackButton()
        }
        App.instance().toast("检测完毕,请回到APP查看老友状况")
    }
  • 虽说上边的方案也能满足需求,可是还不够好,每次都要先获取一遍老友列表才干开端检测,无疑是进一步增加了检测时刻(假转账办法本来就流程复杂现已很耗时了),那么有其他办法不经过获取列表让他主动一个一个去执行纳,肯定是能够的,接下来就一同来剖析一下吧

2、主动从上到下一个一个查找老友并检测

  • 首要咱们知道微信通讯录页面展示的是老友列表,要想去主动遍历,咱们需求找到一个突破口,该怎样去取出来第一个老友纳,之前文章剖析过通讯录页的节点信息,知道这个页面是用RecyclerView做的,他的节点ID是com.tencent.mm:id/js是仅有的,列表中老友节点是TextView,ID是com.tencent.mm:id/hg4,所以咱们找到RecyclerView然后遍历他的子节点中ID为com.tencent.mm:id/hg4的节点然后取出来第一个老友便是咱们第一次要检测的人,那么怎样主动找到第二个第三个纳,思索顷刻发现仅有的突破口便是咱们现已知道上一个老友是谁了,有了上一个老友信息咱们就能够在列表中去定位到上一个老友的索引位置,然后索引+1便是下一个老友的索引了。
/**
 * scrollViewId 是recyclerview节点的id
 * childViewId 是每个用户节点的id
 * lastText 是上一个老友昵称
 * filterTexts 是咱们需求过滤的数据
 */
private fun AccessibilityService?.getNextNodeByCurrentText(
    scrollViewId: String,
    childViewId: String,
    lastText: String?,
    filterTexts: List<String> = listOf()
): AccessibilityNodeInfo? {
    this ?: return null
    //获取RecyclerView的节点
    val parent: AccessibilityNodeInfo = rootInActiveWindow.findNodeById(scrollViewId) ?: return null
    //经过RecyclerView获取他的子节点,同时过滤掉不需求的老友(由于通讯录列表里有微信团队和文件传输帮手和自己)
    val find = parent.findNodesById(childViewId).filterNot { filterTexts.contains(it.text.default()) }
    return if (lastText.isNullOrBlank()) {
        //当时没传入上一个老友,阐明是第一次检测,需求取第一个
        find.firstOrNull()
    } else {
        //找到上一个用户的索引
        val lastIndex = find.indexOfFirst { it.text.default() == lastText }
        if (lastIndex > -1) {
            //找到了就取他下一个值(这儿假如上一个老友正好是的最终一个的话,后边没数据了,索引加一是取不到值的,所以也会回来空)
            find.getOrNull(lastIndex + 1)
        } else {
            //没有找到取第一个,这儿解释一下为什么会没有找到要取第一个
            //是由于假如传入的是上一页的最终一个老友,翻滚一屏后当时页或许会没有上一页的数据,所以就找不到了,自然便是取第一个了
            find.firstOrNull()
        }
    }
}
  • 上面代码的注释现已写得非常清晰了,假如你足够心细的话,就会对上述代码有一些疑问,方才也说了会呈现下一个值取不到的状况,而呈现取不到值的时分阐明当时页现已检测完了,所以想要持续往下去查找老友就需求翻滚列表了,让RecyclerView翻滚一下然后再去取值即可,既然是翻滚肯定是会翻滚到最底部的,该怎样判别是否翻滚到底纳,持续完善代码如下
suspend fun AccessibilityService?.scrollToFindNextNodeByCurrentText(
    scrollViewId: String,
    childViewId: String,
    lastText: String?,
    filterTexts: List<String> = listOf()
): AccessibilityNodeInfo? {
    this ?: return null
    var find = getNextNodeByCurrentText(scrollViewId, childViewId, lastText, filterTexts)
    var isEnd = false
    while (find == null && !isEnd) {
        val parent: AccessibilityNodeInfo = rootInActiveWindow.findNodeById(scrollViewId) ?: return null
        parent.scrollForward()
        delay(200)
        val tryFind = getNextNodeByCurrentText(scrollViewId, childViewId, lastText, filterTexts)
        find = tryFind
        isEnd = tryFind == null
    }
    return find
}

完好流程

  • 主动获取老友的办法咱们也写完了,现在就把整个流程串起来吧,中心触及到许多的细节就不再这儿展开详细介绍了,能够看源码自己领会领会。

    suspend fun quickCheck() {
        lastCheckUser = null
        App.instance().goToWx()
        //判别当时是否进入到微信
        val inWxApp = WXHomePage.waitEnterWxApp()
        if (!inWxApp) return
        //回到微信主页
        val isHome = WXHomePage.backToHome()
        if (!isHome) return
        //点击我的tab
        val clickMineTab = WXHomePage.clickMineTab()
        if (!clickMineTab) return
        //取出自己的微信昵称和微信号码,用来在后边主动查找老友时分过滤自己
        myWxInfo = WXMinePage.getMyWxInfo()
        //点击底部通讯录Tab
        val clickContactsTab = WXHomePage.clickContactsTab(true)
        if (!clickContactsTab) return
        val start = System.currentTimeMillis()
        //真实的检测流程
        quickCheckTask()
        val end = System.currentTimeMillis()
        FriendStatusHelper.taskCallBack?.onTaskEnd(end - start)
        if (WXHomePage.backToHome()) {
            wxAccessibilityService?.pressBackButton()
        }
        App.instance().toast("检测完毕,请回到APP查看老友状况")
    }
    private suspend fun quickCheckTask() {
        //每次都是从微信主页开端的,回到微信主页
        val isHome = WXHomePage.backToHome()
        if (!isHome) return
        //点击底部通讯录Tab
        val clickContactsTab = WXHomePage.clickContactsTab()
        if (!clickContactsTab) return
        //判别是否从微信服务器拉到了用户信息
        val isShowUserList = WXContactPage.inPage()
        if (!isShowUserList) return
        //当时在通讯录页面,点击某一个用户
        val findUser = WXContactPage.scrollToClickNextNodeByCurrentText(lastCheckUser?.nickName)
        //当找不到老友的时分就阐明检测完毕,停止流程
        if (findUser.isNullOrBlank()) return
        //判别是否进入到通讯录用户信息页
        val inPage = WXContactInfoPage.inPage()
        if (!inPage) return
        val userInfo = WXContactInfoPage.getUserInfo() ?: return
        //点击发消息按钮
        val clickSendMsg = WXContactInfoPage.clickSendMsg()
        if (!clickSendMsg) return
        //查看当时是否进入到谈天页(测验中发现有时分点击有效,可是没有进入到谈天页,一切在重试一次)
        val check = WXChattingPage.checkInPage()
        if (!check) {
            //再次点击发消息按钮
            val click = WXContactInfoPage.clickSendMsg()
            if (!click) return
        }
        //点击谈天页功用区按钮
        val clickMoreOption = WXChattingPage.clickMoreOption()
        if (!clickMoreOption) return
        //点击谈天页功用区转账按钮
        val clickTransferMoney = WXChattingPage.clickTransferMoney()
        if (!clickTransferMoney) return
        //第一次判别是否是正常老友
        val friendStatus = WXRemittancePage.checkIsNormalFriend()
        if (friendStatus != null && friendStatus.status == FriendStatus.NORMAL) {
            //提早判别出成果,此次任务完毕
            userInfo.status = friendStatus.status
            lastCheckUser = userInfo
            FriendStatusHelper.addCheckResult(userInfo)
            //当次检测完毕持续下一位检测
            quickCheckTask()
            return
        }
        //输入转账金额
        val inputMoney = WXRemittancePage.inputMoney()
        if (!inputMoney) return
        //点击转账按钮
        val clickTransfer = WXRemittancePage.clickTransferMoney()
        if (!clickTransfer) return
        //查看页面元素判别老友状况
        val status = WXRemittancePage.checkStatus()
        friendStatus!!.status = status.toFriendStatus()
        userInfo.status = friendStatus.status
        lastCheckUser = userInfo
        FriendStatusHelper.addCheckResult(userInfo)
        if (status == CheckStatus.BLACK || status == CheckStatus.DELETE || status == CheckStatus.ACCOUNT_EXCEPTION) {
            //被删去或许被拉黑和对方帐号反常的 会弹框,需求点击【我知道了】 封闭弹框
            WXRemittancePage.clickIKnow()
        }
        //当次检测完毕持续下一位检测
        quickCheckTask()
    }
  • 执行一遍成果如下,检测成果也能够在APP中查看。
nickName: 测验账号1  wxCode: wxid_111111  status: 正常
nickName: 测验账号2  wxCode: wxid_222222  status: 被删去
nickName: 测验账号3  wxCode: wxid_333333  status: 被拉黑
nickName: 测验账号4  wxCode: wxid_444444  status: 账号反常

最终

  • 至此整个剖析流程和一套完好的方案现已悉数介绍完了,相信你能仔细看到这儿的话根本现已对假转账法的进程完全掌握了,想体验的话能够下载源码编译一下或许体验现已编译好的安装包试试作用吧

感兴趣的能够下载demo体验一下,在阅览源码进程遇到任何问题欢迎提Issues,假如对你有帮助,期望动动你的发财小手点个赞呗

预告

  • 看了这篇文章你会发现这种办法检测很繁琐,也很耗时,老友过多的话检测起来是需求许多时刻的,我的三百多老友检测一遍大概要四十来分钟,自己玩玩还能够,不能经常用来检测,究竟太TM耽误时刻了。接下来咱们就来介绍一下【玩转Android主动化】微信老友状况查看(拉群办法),相信你会更感兴趣。