Android 上的 Tasker 绝对称得上是 Android 系统的神器之一, 可以完成很复杂多样的自定义任务。这里介绍一下利用他完成某 APP 自动签到的过程。
思路
保证手机正常联网,通过USB调试做以下步骤:
- 解锁屏幕进入桌面
- 打开签到 APP
- 模拟点击 APP 中签到按钮
- 退出签到 APP
- 锁屏
准备
- 能够安装 Magisk 的安卓手机一部
- 安装 Magisk 模块:ADB & Fastboot for Android NDK,Busybox for Android NDK
- 安装 APP:终端模拟器,RE文件管理器,Tasker,Secure Settings
- 装有 adb tools 及 Git for Windows 的 PC 一台(用来计算模拟点击坐标)
- 打开手机 USB 调试模式,并允许通过 USB 调试模拟点击
- 保证Tasker,Secure Settings后台常驻 – 非常重要!!!
唤醒屏幕
在进行模拟点击前,必须保证手机屏幕是唤醒状态,不然点了也没用。网上很多人说的采用adb shell input keyevent 26
这种方法只有在亮屏时有用,相当于按下一次电源键,但熄屏状态并不起作用,所以我采用另一种办法:通过Tasker调用Secure Settings唤醒屏幕。
具体做法是
- 打开Secure Settings然后
enable System+ Modul
- 打开Tasker -> 任务 -> 新建任务 -> 插件 -> Secure Settings -> 配置 -> Actions -> Wake Device
- 手机系统设置中找到设备管理器,激活 Secure Settings
测试:可以在 Tasker 中设定一个延时任务测试下,比如延时三秒后亮屏
滑动解锁
亮屏后默认是系统的锁屏界面,简单起见,我已经把手机解锁密码去掉,只剩下滑动锁。滑动解锁处理起来比较容易,adb shell input swipe x1 y1 x2 y2
即可,所以我们需要定起始和终止两个坐标点,来模拟滑动,继续往下看。
获得屏幕点击的位置坐标
此方法比较土,但胜在只要有adb tools就能用,不需要别的东西。
计算比例
通过命令adb shell getevent -p | grep -e "0035" -e "0036"
获得event
体系里宽和高
我的手机输出如下信息:
0035 : value 0, min 0, max 1080, fuzz 0, flat 0, resolution 0
0036 : value 0, min 0, max 2248, fuzz 0, flat 0, resolution 0
我们需要的就是其中的 max
0035 max 1080 宽
0036 max 2248 高
手机分辨率是已知的,我手机是:1080 x 2248
计算比例:
rateW = 1080(手机屏幕的宽) / 1080(event里0035的max) = 1
rateH = 1920(手机屏幕的高) / 2248(event里0036的max) = 1
计算点击位置坐标
执行命令adb shell getevent | grep -e "0035" -e "0036"
输出如下信息:
/dev/input/event3: 0003 0035 00000430 width
/dev/input/event3: 0003 0036 000005b1 height
把0035和0036后面的位置数据从16进制转化为10进制,祭出 Windows10 自带计算器,选程序员转换之得到十进制坐标为:(1072, 1457)
这是在event
体系里的位置,将其转化为屏幕位置
screenW = width*rateW = 1072*1 = 1072
screenH = height*rateH = 1457*1 = 1457
所以得到刚才点击的屏幕坐标位置是(1072, 1457)
自己adb调试自己
一般我们是在 PC 端利用 adb 调试安卓手机,但实际上安卓自己也可以调试自己,这样可以只用手机就完成模拟点击操作,不需要电脑的帮助。还记得准备工作要求的安装 ADB & Fastboot for Android NDK,Busybox for Android NDK 这个 Magisk 模块么?就是来干这个的。
打开终端模拟器,输入代码:
su 2000 -c 'setprop service.adb.tcp.port 5555' # 开启USB网络调试
su -c 'stop adbd && start adbd' # 重启USB调试服务
adb connect localhost:5555 # 启动adb,接通本机5555端口
出来弹窗选择信任即可
打开签到 APP
这一步你当然可以通过模拟点击来打开相应 APP 来进行后续的操作,但这要求你手机解锁开屏后不能有别的 APP 占据屏幕,否则点击就乱套。这里我采用的是am start -n {包(package)名}/{包名}.{活动(activity)名称}
方法,APP 包名一般都能在对应的市场比如酷安应用详情中看到,钉钉的包名是com.alibaba.android.rimet
。那么如何知道 APP 的 activity 名称呢?继续往下看。
快速获取当前Activity类名
据说有个叫re-sign.jar
的小工具可以干这个,但我这里总是报OOM的错,懒得深究,直接上 adb 敲如下命令
adb shell dumpsys activity activities | findstr "应用包名"
,输入之后信息会有很多,过滤一下,主要找包名.
开头的,比如钉钉是com.alibaba.android.rimet.ui.activity.StartActivity
(版本不同可能会有差异,需要自己琢磨),找到后用如下命令启动 APP:
am start -n com.alibaba.android.rimet/com.alibaba.android.rimet.ui.activity.StartActivity
打开 APP 后模拟点击完签到流程,最后退出
am force-stop com.alibaba.android.rimet
熄屏
屏幕老亮着太费电,还是关了的好,有两种方法
adb shell input keyevent 26
相当于按电源键- Tasker 调用 Secure Settings 插件,选择 Lock Device(步骤类似唤醒屏幕)
只在工作日签到
Tasker 自带的定时功能并不能满足这个需求,我们需要借助第三方 API 接口来判断当天是否为工作日,是工作日才签到。类似的第三方接口很多,有开发能力的机油甚至可以自己写,这里我用的是免费节假日API提供的接口。
整个自动签到脚本内容如下:
#!/system/bin/sh
# file: auto_login
# 具体坐标及延时请视自己手机而定,不要照搬
# 查询当天是否为工作日
# 0 - 工作日,1 - 假日,2 - 节日
DAY=$(curl -s http://tool.bitefu.net/jiari/?d=$(date +%Y%m%d))
# 只有工作日才签到
if [ ${DAY}x == '0x' ]; then
# 延时一秒再模拟滑动解锁
sleep 1
adb shell input swipe 538 1033 555 536
# 延时三秒再打开APP
sleep 3
am start -n com.alibaba.android.rimet/com.alibaba.android.rimet.ui.activity.StartActivity
# 视手机性能及网络影响,APP完全打开需要一定时间,所以延时最好多延几秒
sleep 10
# 模拟点击签到按钮
adb shell input tap 133 730
sleep 5
adb shell input tap 433 830
sleep 5
# 完成点击后停止APP
am force-stop com.alibaba.android.rimet
fi
exit 0
ALL IN
使用 RE 管理器将上述auto_login
移动到/system/xbin/
目录下,改可执行权限。剩下的操作均在 Tasker 应用中完成:
- 新建配置文件,时间选每天上下班时间,关联任务名字随便取
- 唤醒屏幕前最好随机睡段时间(睡多久自己把控)
- 唤醒屏幕
- 调用
auto_login
脚本完成签到流程 - 锁屏
懒人福利:Tasker 配置文件(复制保存为xml后缀文件,可直接在应用内导入还原):
<TaskerData sr="" dvi="1" tv="5.0u7m">
<Profile sr="prof8" ve="2">
<cdate>1531101968896</cdate>
<edate>1531122746414</edate>
<id>8</id>
<mid0>6</mid0>
<nme>上班</nme>
<Time sr="con0">
<fh>8</fh>
<fm>16</fm>
<th>-1</th>
<tm>-1</tm>
</Time>
</Profile>
<Task sr="task6">
<cdate>1531100883059</cdate>
<edate>1531122706060</edate>
<id>6</id>
<nme>自动流程</nme>
<pri>100</pri>
<Action sr="act0" ve="7">
<code>545</code>
<Str sr="arg0" ve="3">%SLEEP_TIME</Str>
<Int sr="arg1" val="3"/>
<Int sr="arg2" val="180"/>
</Action>
<Action sr="act1" ve="7">
<code>123</code>
<Str sr="arg0" ve="3">sleep %SLEEP_TIME</Str>
<Int sr="arg1" val="0"/>
<Int sr="arg2" val="1"/>
<Str sr="arg3" ve="3"/>
<Str sr="arg4" ve="3"/>
<Str sr="arg5" ve="3"/>
<ConditionList sr="if">
<Condition sr="c0" ve="3">
<lhs>%SLEEP_TIME</lhs>
<op>12</op>
<rhs></rhs>
</Condition>
</ConditionList>
</Action>
<Action sr="act2" ve="7">
<code>1563799945</code>
<Bundle sr="arg0">
<Vals sr="val">
<com.intangibleobject.securesettings.plugin.extra.BLURB>Screen & Keyboard Lights On
1 Second</com.intangibleobject.securesettings.plugin.extra.BLURB>
<com.intangibleobject.securesettings.plugin.extra.BLURB-type>java.lang.String</com.intangibleobject.securesettings.plugin.extra.BLURB-type>
<com.intangibleobject.securesettings.plugin.extra.SETTING>wake_device</com.intangibleobject.securesettings.plugin.extra.SETTING>
<com.intangibleobject.securesettings.plugin.extra.SETTING-type>java.lang.String</com.intangibleobject.securesettings.plugin.extra.SETTING-type>
<com.intangibleobject.securesettings.plugin.extra.WAKE_LOCK_DURATION>1000</com.intangibleobject.securesettings.plugin.extra.WAKE_LOCK_DURATION>
<com.intangibleobject.securesettings.plugin.extra.WAKE_LOCK_DURATION-type>java.lang.Long</com.intangibleobject.securesettings.plugin.extra.WAKE_LOCK_DURATION-type>
<com.intangibleobject.securesettings.plugin.extra.WAKE_LOCK_TYPE>full</com.intangibleobject.securesettings.plugin.extra.WAKE_LOCK_TYPE>
<com.intangibleobject.securesettings.plugin.extra.WAKE_LOCK_TYPE-type>java.lang.String</com.intangibleobject.securesettings.plugin.extra.WAKE_LOCK_TYPE-type>
<com.twofortyfouram.locale.intent.extra.BLURB>Screen & Keyboard Lights On
1 Second</com.twofortyfouram.locale.intent.extra.BLURB>
<com.twofortyfouram.locale.intent.extra.BLURB-type>java.lang.String</com.twofortyfouram.locale.intent.extra.BLURB-type>
<net.dinglisch.android.tasker.subbundled>true</net.dinglisch.android.tasker.subbundled>
<net.dinglisch.android.tasker.subbundled-type>java.lang.Boolean</net.dinglisch.android.tasker.subbundled-type>
</Vals>
</Bundle>
<Str sr="arg1" ve="3">com.intangibleobject.securesettings.plugin</Str>
<Str sr="arg2" ve="3">com.intangibleobject.securesettings.plugin.Activities.TabsActivity</Str>
<Int sr="arg3" val="0"/>
</Action>
<Action sr="act3" ve="7">
<code>123</code>
<Str sr="arg0" ve="3">auto_login</Str>
<Int sr="arg1" val="0"/>
<Int sr="arg2" val="1"/>
<Str sr="arg3" ve="3"/>
<Str sr="arg4" ve="3"/>
<Str sr="arg5" ve="3"/>
</Action>
<Action sr="act4" ve="7">
<code>1563799945</code>
<Bundle sr="arg0">
<Vals sr="val">
<com.intangibleobject.securesettings.plugin.extra.BLURB>Lock Device</com.intangibleobject.securesettings.plugin.extra.BLURB>
<com.intangibleobject.securesettings.plugin.extra.BLURB-type>java.lang.String</com.intangibleobject.securesettings.plugin.extra.BLURB-type>
<com.intangibleobject.securesettings.plugin.extra.SETTING>force_lock</com.intangibleobject.securesettings.plugin.extra.SETTING>
<com.intangibleobject.securesettings.plugin.extra.SETTING-type>java.lang.String</com.intangibleobject.securesettings.plugin.extra.SETTING-type>
<com.twofortyfouram.locale.intent.extra.BLURB>Lock Device</com.twofortyfouram.locale.intent.extra.BLURB>
<com.twofortyfouram.locale.intent.extra.BLURB-type>java.lang.String</com.twofortyfouram.locale.intent.extra.BLURB-type>
<net.dinglisch.android.tasker.subbundled>true</net.dinglisch.android.tasker.subbundled>
<net.dinglisch.android.tasker.subbundled-type>java.lang.Boolean</net.dinglisch.android.tasker.subbundled-type>
</Vals>
</Bundle>
<Str sr="arg1" ve="3">com.intangibleobject.securesettings.plugin</Str>
<Str sr="arg2" ve="3">com.intangibleobject.securesettings.plugin.Activities.TabsActivity</Str>
<Int sr="arg3" val="0"/>
</Action>
</Task>
</TaskerData>
发现自动签到方案还是不甚完美:签到成功与否没有消息通知。
所以就想到用 IFTTT 结合 Telegram 来搞一搞这个签到反馈,实现的效果是这样的:
需要在 IFTTT 中开一个 applets
脚本加点新内容:
#!/system/bin/sh
# file: auto_login
# 具体坐标及延时请视自己手机而定,不要照搬
# 查询当天是否为工作日
# 0 - 工作日,1 - 假日,2 - 节日
DAY=$(curl -s http://tool.bitefu.net/jiari/?d=$(date +%Y%m%d))
# webhook url
IFTTT="https://maker.ifttt.com/trigger/sign_in_office/with/key/{your-key}"
send_msg(){
curl -X POST -H "Content-Type: application/json" -d '{"value1": "'$1'"}' $IFTTT
}
if [ ${DAY}x == 'x' ]; then
send_msg "网络故障或节假日API不可用"
elif [ ${DAY}x == '0x' ]; then
# 延时一秒再模拟滑动解锁
sleep 1
adb shell input swipe 538 1033 555 536
# 延时三秒再打开APP
sleep 3
am start -n com.alibaba.android.rimet/com.alibaba.android.rimet.ui.activity.StartActivity
# 视手机性能及网络影响,APP完全打开需要一定时间,所以延时最好多延几秒
sleep 10
# 模拟点击签到按钮
adb shell input tap 133 730
sleep 5
adb shell input tap 433 830
sleep 5
# 完成点击后停止APP
am force-stop com.alibaba.android.rimet
send_msg "签到完成"
else
send_msg "节假日不用签到"
fi
exit 0
搞定!
参考
- Run adb command inside the terminal emulator or programmatically without root
- 使用工具re-sign.jar获取android的包名和主类名
- Android使用ADB启动应用程序
- 免费节假日API
- Android快速获取当前Activity类名的三种方法
- Android adb shell 获得点击屏幕的位置坐标
- Tasker FAQ
- Magisk – The Universal Systemless Interface, to create an altered mask of the system without changing the system itself.
原文链接:https://blog.ferstar.org/post/use-tasker-do-some-funny-things/