基于AccessibilityService制作的钉钉自动签到程序

标签: 移动开发安卓自动化操作

2015-12-03 09:56 1736人阅读 评论(10) 收藏 举报

分类:

Android(3)

版权声明:本文为博主原创文章,未经博主允许不得转载。

前两天公司开始宣布要使用阿里钉钉来签到啦!!!~~这就意味着,我必须老老实实每天按时签到上班下班了,这真是一个悲伤的消息,可是!!!!那么机智(lan)的我,怎么可能就这么屈服!!!阿里钉钉签到,说到底不就是手机软件签到吗?我就是干移动开发的,做一个小应用每天自动签到不就行了:)

说干就干,首先分析一下,阿里钉钉的签到流程:

打开阿里钉钉->广告页停留2S左右->进入主页->点击“工作”tab->点击“签到”模块->进入签到页面(可能会再次出现广告和对话框)->点击签到

我们操作手机的过程就是这样,要实现这些点击,很自然想起了前段时间做的微信抢红包小应用,利用AccessibilityService服务帮助我们实现这些自动化操作。

以上是分析过程,接下来是我对这个小功能实现的具体方案思路:

将测试手机放公司并且安装这个应用,通过我远程的电话拨打或者短信发送到测试手机(只要能产生广播或者信息的就行),测试手机接受到广播信息,唤醒钉钉,进入钉钉页面,AccessibilityService开始工作,进行一系列点击签到操作,结束操作后退出钉钉,签到完成。

通过以上过程的分析我们大概要用到的知识有以下几块:

唤醒非自己的其他第三方应用

广播

AccessibilityService服务

以下是对这三部分代码实现:

唤醒第三方应用

package net.fenzz.dingplug;

import java.util.List;

import android.content.ComponentName;

import android.content.Context;

import android.content.Intent;

import android.content.pm.PackageInfo;

import android.content.pm.PackageManager;

import android.content.pm.PackageManager.NameNotFoundException;

import android.content.pm.ResolveInfo;

public class Utils {

public static void openCLD(String packageName,Context context) {

PackageManager packageManager = context.getPackageManager();

PackageInfo pi = null;

try {

pi = packageManager.getPackageInfo("com.alibaba.android.rimet", 0);

} catch (NameNotFoundException e) {

}

Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null);

resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER);

resolveIntent.setPackage(pi.packageName);

List<ResolveInfo> apps = packageManager.queryIntentActivities(resolveIntent, 0);

ResolveInfo ri = apps.iterator().next();

if (ri != null ) {

String className = ri.activityInfo.name;

Intent intent = new Intent(Intent.ACTION_MAIN);

intent.addCategory(Intent.CATEGORY_LAUNCHER);

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

ComponentName cn = new ComponentName(packageName, className);

intent.setComponent(cn);

context.startActivity(intent);

}

}

}

接受电话广播并且唤醒钉钉:

mainifest先注册监听器

<!-- 注册监听手机状态 -->

<receiver android:name=".PhoneReceiver">

<intent-filter android:priority="1000" >

<action android:name="android.intent.action.PHONE_STATE" />

</intent-filter>

</receiver>

相关权限

<!-- 读取手机状态的权限 -->

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>

代码

package net.fenzz.dingplug;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.telephony.TelephonyManager;

import android.app.Service;

import android.util.Log;

public class PhoneReceiver extends BroadcastReceiver {

private static final String TAG = "message";

private static boolean mIncomingFlag = false;

private static String mIncomingNumber = null;

@Override

public void onReceive(Context context, Intent intent) {

// 如果是拨打电话

if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {

mIncomingFlag = false;

String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);

Log.i(TAG, "call OUT:" + phoneNumber);

} else {

// 如果是来电

TelephonyManager tManager = (TelephonyManager) context

.getSystemService(Service.TELEPHONY_SERVICE);

switch (tManager.getCallState()) {

case TelephonyManager.CALL_STATE_RINGING:

mIncomingNumber = intent.getStringExtra("incoming_number");

Log.i(TAG, "RINGING :" + mIncomingNumber);

if(mIncomingNumber!=null&&mIncomingNumber.equals(你的手机号)){

Utils.openCLD("com.alibaba.android.rimet", context);

DingService.instance.setServiceEnable();

}

break;

case TelephonyManager.CALL_STATE_OFFHOOK:

if (mIncomingFlag) {

Log.i(TAG, "incoming ACCEPT :" + mIncomingNumber);

}

break;

case TelephonyManager.CALL_STATE_IDLE:

if (mIncomingFlag) {

Log.i(TAG, "incoming IDLE");

}

break;

}

}

}

AccessibilityService服务实现:

相关权限及注册:

<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />

<service

android:name=".DingService"

android:enabled="true"

android:exported="true"

android:label="@string/app_name"

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/red_service_config" />

</service>

需要在res文件夹下新建一个xml文件夹里面放入一个这样的xml配置文件:

<?xml version="1.0" encoding="utf-8"?>

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"

android:accessibilityEventTypes="typeAllMask"

android:accessibilityFeedbackType="feedbackGeneric"

android:accessibilityFlags="flagDefault"

android:canRetrieveWindowContent="true"

android:description="@string/accessibility_description"

android:notificationTimeout="100"

android:packageNames="com.alibaba.android.rimet"

/>

代码:

package net.fenzz.dingplug;

import java.util.ArrayList;

import java.util.List;

import android.accessibilityservice.AccessibilityService;

import android.util.Log;

import android.view.accessibility.AccessibilityEvent;

import android.view.accessibility.AccessibilityNodeInfo;

import android.widget.Toast;

public class DingService extends AccessibilityService {

private String TAG = getClass().getSimpleName();

private  boolean  isFinish = false;

public static DingService instance;

private int index = 1;

/**

* 获取到短信通知

*  0.唤醒屏幕

*  1.打开钉钉

*  2.确保当前页是主页界面

*  3.找到“工作”tab并且点击

*  4.确保到达签到页面

*  5.找到签到按钮,并且点击

*  6.判断签到是否成功

*      1.成功,退出程序

*      2.失败,返回到主页,重新从1开始签到

*/

@Override

public void onAccessibilityEvent(AccessibilityEvent event) {

// TODO Auto-generated method stub

//       final int eventType = event.getEventType();

ArrayList<String> texts = new ArrayList<String>();

Log.i(TAG, "事件---->" + event.getEventType());

if(isFinish){

return;

}

AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();

if(nodeInfo == null) {

Log.w(TAG, "rootWindow为空");

return ;

}

//       nodeInfo.

//       System.out.println("nodeInfo"+nodeInfo);

System.out.println("index:"+index);

switch (index) {

case 1: //进入主页

OpenHome(event.getEventType(),nodeInfo);

break;

case 2: //进入签到页

OpenQianDao(event.getEventType(),nodeInfo);

break;

case 3:

doQianDao(event.getEventType(),nodeInfo);

break;

default:

break;

}

}

private ArrayList<String> getTextList(AccessibilityNodeInfo node,ArrayList<String> textList){

if(node == null) {

Log.w(TAG, "rootWindow为空");

return null;

}

if(textList==null){

textList = new ArrayList<String>();

}

String text = node.getText().toString();

if(text!=null&&text.equals("")){

textList.add(text);

}

//        node.get

return null;

}

private void OpenHome(int type,AccessibilityNodeInfo nodeInfo) {

if(type == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED){

//判断当前是否是钉钉主页

List<AccessibilityNodeInfo> homeList = nodeInfo.findAccessibilityNodeInfosByText("工作");

if(!homeList.isEmpty()){

//点击

boolean isHome = click( "工作");

System.out.println("---->"+isHome);

index = 2;

System.out.println("点击进入主页签到");

}

}

}

private void OpenQianDao(int type,AccessibilityNodeInfo nodeInfo) {

if(type == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED){

//判断当前是否是主页的签到页

List<AccessibilityNodeInfo> qianList = nodeInfo.findAccessibilityNodeInfosByText("工作");

if(!qianList.isEmpty()){

boolean ret = click( "签到");

index = 3;

System.out.println("点击进入签到页面详情");

}

//           index = ret?3:1;

}

}

private void doQianDao(int type,AccessibilityNodeInfo nodeInfo) {

if(type == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED){

//判断当前页是否是签到页

List<AccessibilityNodeInfo> case1 = nodeInfo.findAccessibilityNodeInfosByText("开启我的签到之旅");

if(!case1.isEmpty()){

click("开启我的签到之旅");

System.out.println("点击签到之旅");

}

List<AccessibilityNodeInfo> case2 = nodeInfo.findAccessibilityNodeInfosByText("我知道了");

if(!case2.isEmpty()){

click("我知道了");

System.out.println("点击我知道对话框");

}

List<AccessibilityNodeInfo> case3 = nodeInfo.findAccessibilityNodeInfosByText("签到");

if(!case3.isEmpty()){

Toast.makeText(getApplicationContext(), "发现目标啦!!~~", 1).show();

System.out.println("发现目标啦!");

click("签到");

isFinish = true;

}

}

//      if(type == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED){

//          List<AccessibilityNodeInfo> case3 = nodeInfo.findAccessibilityNodeInfosByText("签到");

//          if(!case3.isEmpty()){

//              Toast.makeText(getApplicationContext(), "发现目标啦!!~~", 1).show();

//          }

//      }

}

//通过文字点击

private boolean click(String viewText){

AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();

if(nodeInfo == null) {

Log.w(TAG, "点击失败,rootWindow为空");

return false;

}

List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(viewText);

if(list.isEmpty()){

//没有该文字的控件

Log.w(TAG, "点击失败,"+viewText+"控件列表为空");

return false;

}else{

//有该控件

//找到可点击的父控件

AccessibilityNodeInfo view = list.get(0);

return onclick(view);  //遍历点击

}

}

private boolean onclick(AccessibilityNodeInfo view){

if(view.isClickable()){

view.performAction(AccessibilityNodeInfo.ACTION_CLICK);

Log.w(TAG, "点击成功");

return true;

}else{

AccessibilityNodeInfo parent = view.getParent();

if(parent==null){

return false;

}

onclick(parent);

}

return false;

}

//点击返回按钮事件

private void back(){

performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);

}

@Override

public void onInterrupt() {

// TODO Auto-generated method stub

}

@Override

protected void onServiceConnected() {

// TODO Auto-generated method stub

super.onServiceConnected();

Log.i(TAG, "service connected!");

Toast.makeText(getApplicationContext(), "连接成功!", 1).show();

instance = this;

}

public void setServiceEnable(){

isFinish = false;

Toast.makeText(getApplicationContext(), "服务可用开启!", 1).show();

index = 1;

}

}

以上基本是所有代码,这个小程序中可以不用Activity组件,也可以加一个小的Activity,用来作为系统的总开关,当然也可以自动检测时间,来判断是否开启服务,这样就不用Activity了,在这个小例子中,我使用了一个小activity,就放了一个button。

项目源码,我稍后上传!

null

时间: 2024-10-25 11:20:10

基于AccessibilityService制作的钉钉自动签到程序的相关文章

基于Sae和Python的flask实现的金山快盘自动签到

第一次写博客园,各位大大请多加照顾哦. 进入正题 功能介绍 架到Sae后,每天定点Sae的服务器就会帮你自动签到金山快盘和自动转盘抽奖.一些记录如下: 金山快盘的签到记录: Sae的Cron的日记: 核心代码 本来想写抓包的过程的,但是感觉很小儿科(如果大家觉得有需要,我再写上来),就不献丑了,直接po上代码. 1 #!/usr/bin/python 2 #-*-coding:utf-8-*- 3 import urllib, urllib2, cookielib, sys 4 5 class

今天教大家苹果系统钉钉打卡怎么作弊 钉钉签到水印照片怎么替换

随着移动互联网的不断深入,很多公司已经不在用传统的指纹打卡机了,采用了软件定位打卡.钉钉作为阿里巴巴的主推产品,已经有越来越多的公司在使用.但是公司死板的考勤制度让很多人觉得很头疼,不知道该如果破解? 今天就给大家推荐一个钉钉虚拟定位的方法,可以考勤打卡位置修改,也可以打卡定位破解. 而且钉钉也是频繁升级,找到一个靠谱的软件,难上加难.而且市面上鱼龙混杂,要仔细擦亮眼睛,关乎到自己的工作,面子,真正不是开玩笑,一旦出现问题,后悔是来不及的. 因此我们工作室最近推出了一款,软件,可以完美解决困扰大

自动打包发布到服务器的持续集成工具:Jenkins+钉钉

钉钉,智能移动办公平台工具. Jenkins,是一个开源的持续集成的服务器,开源帮助我们自动构建各类项目. 持续集成,检测代码逻辑,高效率自动化发布工程项目. 安装Jenkins在服务器:Jenkins具体安装与构建部署使用教程 发布后台API配置:图片太大 发布前端Vue项目:图片太大 原文地址:https://www.cnblogs.com/yancongyang/p/10832561.html

关于钉钉如何修改位置打卡签到,在家打卡,钉钉虚拟位置运用教程

上班一族都很头疼这件事,上班打卡,迟到了扣工资,心痛.....关于钉钉如何进行虚拟定位然后正常打卡,有一下方案推荐给大家,大家可以也去试一试! ①系统定位软件 此软件是基于苹果系统代码,编写的,无需越狱直接使用软件某云自提,链接: https://pan.baidu.com/s/13ZSedj3Z78lNmNh5PzJe9w 提取码: f2gj 下载安装itunes 打开moveaway 文件夹中的injecttool 打开moveaway程序附:如何查看 自 己的系统是32位还是64位? 按w

【转】[钉钉通知系列]Jenkins发布后自动通知

转载请注明出处:https://www.cnblogs.com/jianxuanbing/p/7211006.html 阅读目录 一.前言 二.使用钉钉推送的优势 三.配置 一.前言 最近使用Jenkins进行自动化部署,但是发布署后,并没有相应的通知,虽然有邮件发送通知,但是发现邮件会受限于大家接受的设置,导致不能及时看到相关的发布内容.由于之前有用Gitlab推送消息到钉钉的方式,因此考虑Jenkins是否能通知到钉钉的实现方式. 二.使用钉钉推送的优势 实时提醒项目参与人员信息的更新 便于

钉钉实践--自动提醒打卡

起因:每周需统计补卡人员,1次20元,鉴于此增加每日18:30钉钉群提醒功能 实现:钉钉群添加"自定义机器人" 按照文档https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.karFPe&treeId=257&articleId=105735&docType=1 去调试,最后部署到jenkins实现每日job执行 原理:利用接口,推送自定义消息 原文地址:https://www.cnbl

用Python实现阿里钉钉机器人读取数据库内容自动发群通知

最近想把一些预警数据信息按照一定的要求自动发送到移动端APP,最终把目标放在了腾讯的微信和阿里的钉钉软件上,由于刚开始学习python,于是编程工具想用python来实现.微信使用群体最广,通过一天的研究用itchat库已经实现,但由于itchat需要用web微信方式登录,发现微信对新注册的用户关闭了web微信功能,于是考虑用备选方案阿里钉钉来实现,其实阿里钉钉虽然没有微信用户群体庞大,但是在企业应用方面比微信强大了太多,很多企业已经开始开始用钉钉作为内部沟通工具. 一.工作准备 1.Pytho

“破局者”氚云——低代码应用搭建工具,基于钉钉敏捷搭建企业专属应用

关键词:低代码开发.氚云.中心企业信息化.丰富模板.移动办公 通过信息化来提升管理效率,减低管理成本,增强企业竞争力,是绝大部分企业在发展过程中都绕不开的关键点. 对于大小企业而言,专业的IT人才配备以及充沛的资金支持能够保证信息化管理的有效推行.而更多的企业在信息化布局过程中却面临层层困局,软件购买费用过高,企业费用预算有限;配置专业的IT人员,企业运营成本攀升;购买的系统与业务匹配度低,自定义升级难-- 作为一款低代码(Low-Code)应用搭建工具,氚云恰恰能轻松破解企业信息化布局中遇到的

Jenkins发布后自动通知【钉钉】

阅读目录 一.前言 二.使用钉钉推送的优势 三.配置 一.前言 最近使用Jenkins进行自动化部署,但是发布署后,并没有相应的通知,虽然有邮件发送通知,但是发现邮件会受限于大家接受的设置,导致不能及时看到相关的发布内容.由于之前有用Gitlab推送消息到钉钉的方式,因此考虑Jenkins是否能通知到钉钉的实现方式. 二.使用钉钉推送的优势 实时提醒项目参与人员信息的更新 便于查看 三.配置 3.1 钉钉的配置 3.1.1 进入钉钉群 进入某个群->点击机器人图标注意:如果你不是群主,且群主开启