├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── inspectionProfiles
│ ├── Project_Default.xml
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app-debug.apk
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── alvido_bahor
│ │ └── wechattool
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── kompasim
│ │ │ ├── MainActivity.java
│ │ │ ├── MoneyService.java
│ │ │ ├── MyApplication.java
│ │ │ └── WindowService.java
│ └── res
│ │ ├── drawable
│ │ ├── button_bg.xml
│ │ └── controls_bg.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── window.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ └── accessibility_service_config.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── alvido_bahor
│ └── wechattool
│ └── ExampleUnitTest.java
├── build.gradle
├── demo.png
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## 0. 最终效果
3 |
4 | 
5 |
6 |
7 | ## 1. Accessibility介绍
8 |
9 | > 许多Android使用者因为各种情况导致他们要以不同的方式与手机交互。对于那些由于视力、听力或其它身体原因导致不能方便使用Android智能手机的用户,Android提供了Accessibility功能和服务帮助这些用户更加简单地操作设备,包括文字转语音、触觉反馈、手势操作、轨迹球和手柄操作。开发者可以搭建自己的Accessibility服务,这可以加强应用的可用性,例如声音提示,物理反馈,和其他可选的操作模式。
10 |
11 | ## 2. android无障碍功能
12 |
13 | > 它的具体实现是通过AccessibilityService服务运行在后台中,通过AccessibilityEvent接收指定事件的回调。这样的事件表示用户在界面中的一些状态转换,例如:焦点改变了,一个按钮被点击,等等。这样的服务可以选择请求活动窗口的内容的能力。简单的说AccessibilityService就是一个后台监控服务,当你监控的内容发生改变时,就会调用后台服务的回调方法
14 |
15 | ## 3. AccessibilityService使用
16 |
17 | 编写自己的Service类,重写onServiceConnected()方法、onAccessibilityEvent()方法和onInterrupt()方法
18 | ```java
19 | public class QHBAccessibilityService extends AccessibilityService {
20 |
21 | /**
22 | * 当启动服务的时候就会被调用
23 | */
24 | @Override
25 | protected void onServiceConnected() {
26 | super.onServiceConnected();
27 | }
28 |
29 | /**
30 | * 监听窗口变化的回调
31 | */
32 | @Override
33 | public void onAccessibilityEvent(AccessibilityEvent event) {
34 | int eventType = event.getEventType();
35 | //根据事件回调类型进行处理
36 | }
37 |
38 | /**
39 | * 中断服务的回调
40 | */
41 | @Override
42 | public void onInterrupt() {
43 |
44 | }
45 | }
46 | ```
47 | 下面是对AccessibilityService中常用的方法的介绍
48 |
49 | * disableSelf():禁用当前服务,也就是在服务可以通过该方法停止运行
50 | * findFoucs(int falg):查找拥有特定焦点类型的控件
51 | * getRootInActiveWindow():如果配置能够获取窗口内容,则会返回当前活动窗口的根结点
52 | * getSeviceInfo():获取当前服务的配置信息
53 | * onAccessibilityEvent(AccessibilityEvent event):有关AccessibilityEvent事件的回调函数,系统通过sendAccessibiliyEvent()不断的发送AccessibilityEvent到此处
54 | * performGlobalAction(int action):执行全局操作,比如返回,回到主页,打开最近等操作
55 | * setServiceInfo(AccessibilityServiceInfo info):设置当前服务的配置信息
56 | * getSystemService(String name):获取系统服务
57 | * onKeyEvent(KeyEvent event):如果允许服务监听按键操作,该方法是按键事件的回调,需要注意,这个过程发生了系统处理按键事件之前
58 | * onServiceConnected():系统成功绑定该服务时被触发,也就是当你在设置中开启相应的服务,系统成功的绑定了该服务时会触发,通常我们可以在这里做一些初始化操作
59 | * onInterrupt():服务中断时的回调
60 |
61 | ## 4. 声明服务
62 |
63 | 既然是个后台服务,那么就需要我们在manifests中配置该服务信息
64 | ```xml
65 |
71 |
72 |
73 |
74 |
75 | ```
76 | 我们必须注意:任何一个信息配置错误,都会使该服务无反应
77 |
78 | * android:label:在无障碍列表中显示该服务的名字
79 | * android:permission:需要指定BIND_ACCESSIBILITY_SERVICE权限,这是4.0以上的系统要求的
80 | * intent-filter:这个name是固定不变的
81 |
82 |
83 | ## 5. 配置服务参数
84 |
85 | 配置服务参数是指:配置用来接受指定类型的事件,监听指定package,检索窗口内容,获取事件类型的时间等等。其配置服务参数有两种方法:
86 | * 方法一:安卓4.0之后可以通过meta-data标签指定xml文件进行配置
87 | * 方法二:通过代码动态配置参数
88 |
89 | ### 方法一
90 |
91 | 在原先的manifests中增加meta-data标签指定xml文件
92 |
93 | ```xml
94 |
100 |
101 |
102 |
103 |
104 |
107 |
108 | ```
109 |
110 | 接下来是accessibility_service_config文件的配置
111 |
112 | ```xml
113 |
114 |
122 | ```
123 |
124 | 下面是对xml参数的介绍
125 |
126 | * accessibilityEventTypes:表示该服务对界面中的哪些变化感兴趣,即哪些事件通知,比如窗口打开,滑动,焦点变化,长按等。具体的值可以在AccessibilityEvent类中查到,如typeAllMask表示接受所有的事件通知
127 | * accessibilityFeedbackType:表示反馈方式,比如是语音播放,还是震动
128 | * canRetrieveWindowContent:表示该服务能否访问活动窗口中的内容。也就是如果你希望在服务中获取窗体内容,则需要设置其值为true
129 | * description:对该无障碍功能的描述,具体体现在下图
130 |
131 | * notificationTimeout:接受事件的时间间隔,通常将其设置为100即可
132 | * packageNames:表示对该服务是用来监听哪个包的产生的事件,这里以微信的包名为例
133 |
134 | ### 方法二
135 |
136 | 通过代码为我们的AccessibilityService配置AccessibilityServiceInfo信息,这里我们可以抽取成一个方法进行设置
137 |
138 | ```java
139 | private void settingAccessibilityInfo() {
140 | String[] packageNames = {"com.tencent.mm"};
141 | AccessibilityServiceInfo mAccessibilityServiceInfo = new AccessibilityServiceInfo();
142 | // 响应事件的类型,这里是全部的响应事件(长按,单击,滑动等)
143 | mAccessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
144 | // 反馈给用户的类型,这里是语音提示
145 | mAccessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
146 | // 过滤的包名
147 | mAccessibilityServiceInfo.packageNames = packageNames;
148 | setServiceInfo(mAccessibilityServiceInfo);
149 | }
150 | ```
151 |
152 | 在这里涉及到了AccessibilityServiceInfo类,AccessibilityServiceInfo类被用于配置AccessibilityService信息,该类中包含了大量用于配置的常量字段及用来xml属性,常见的有:accessibilityEventTypes,canRequestFilterKeyEvents,packageNames等等
153 |
154 |
155 |
156 | ## 6. 启动服务
157 |
158 | 这里我们需要在无障碍功能里面手动打开该项功能,否则无法继续进行,通过下面代码可以打开系统的无障碍功能列表
159 |
160 | ```java
161 | Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
162 | startActivity(intent);
163 | ```
164 |
165 | ## 6. 处理事件信息
166 |
167 | 由于我们监听了事件的通知栏和界面等信息,当我们指定packageNames的通知栏或者界面发生变化时,会通过onAccessibilityEvent回调我们的事件,接着进行事件的处理
168 |
169 | ```java
170 | @Override
171 | public void onAccessibilityEvent(AccessibilityEvent event) {
172 | int eventType = event.getEventType();
173 | //根据事件回调类型进行处理
174 | switch (eventType) {
175 | //当通知栏发生改变时
176 | case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
177 |
178 | break;
179 | //当窗口的状态发生改变时
180 | case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
181 |
182 | break;
183 | }
184 | }
185 | ```
186 |
187 | 当我们微信收到通知时,状态栏会有一条推送信息到达,这个时候就会被TYPE_NOTIFICATION_STATE_CHANGED监听,执行里面的内容,当我们切换微信界面时,或者使用微信时,这个时候就会被TYPE_WINDOW_STATE_CHANGED监听,执行里面的内容
188 |
189 | AccessibilityEvent的方法
190 |
191 | * getEventType():事件类型
192 | * getSource():获取事件源对应的结点信息
193 | * getClassName():获取事件源对应类的类型,比如点击事件是有某个Button产生的,那么此时获取的就是Button的完整类名
194 | * getText():获取事件源的文本信息,比如事件是有TextView发出的,此时获取的就是TextView的text属性。如果该事件源是树结构,那么此时获取的是这个树上所有具有text属性的值的集合
195 | * isEnabled():事件源(对应的界面控件)是否处在可用状态
196 | * getItemCount():如果事件源是树结构,将返回该树根节点下子节点的数量
197 |
198 |
199 | ## 7. 获取节点信息
200 |
201 | 获取了界面窗口变化后,这个时候就要获取控件的节点。整个窗口的节点本质是个树结构,通过以下操作节点信息
202 |
203 | * 获取窗口节点(根节点)
204 | ```java
205 | AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
206 | ```
207 |
208 | * 获取指定子节点(控件节点)
209 |
210 | ```java
211 | //通过文本找到对应的节点集合
212 | List list = nodeInfo.findAccessibilityNodeInfosByText(text);
213 | //通过控件ID找到对应的节点集合,如com.tencent.mm:id/gd
214 | List list = nodeInfo.findAccessibilityNodeInfosByViewId(clickId);
215 | ```
216 |
217 | ## 8. 模拟节点点击
218 |
219 | 当我们获取了节点信息之后,对控件节点进行模拟点击、长按等操作,AccessibilityNodeInfo类提供了performAction()方法让我们执行模拟操作,具体操作可看官方文档介绍,这里列举常用的操作
220 |
221 | ```java
222 | //模拟点击
223 | accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
224 | //模拟长按
225 | accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
226 | //模拟获取焦点
227 | accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
228 | //模拟粘贴
229 | accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);
230 | ```
231 |
232 | ## 9. 原理分析
233 |
234 | 1. 收到微信红包的推送信息,在推送信息中判断是否出现”[微信红包]”的消息提示,如果出现则点击进入聊天界面
235 | 2. 通过遍历窗口树节点,发现带有”领取红包”字样的节点,则点击进入,即红包,弹出抢红包界面
236 | 3. 在抢红包界面,通过ID获取”开”按钮的节点,则打开红包
237 | 4. 在红包详情页面,通过ID获取返回键按钮的节点,点击并返回微信聊天界面
238 |
239 |
240 | ## 10. 注意事项
241 |
242 | 1. 由于微信每个版本的按钮ID都是不一样的,在我们的程序中是需要去修改按钮ID,以达到版本的适配
243 | 2. 在获取控件ID的时候,注意其布局是否可点击,否则获取不可点击的控件,会使程序无反应
244 |
245 | ## 11. 获取控件ID
246 |
247 | 当我们手机接入USB线时,在Android Device Monitor中的选择设备并开启Dump View Hierarchy for UI Automator工具,通过它可以获取控件信息
248 |
249 | ## 12. 遇到的一些问题
250 |
251 | * 1 :
252 | > Q: `AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();`总是报空指针,问一下博主遇到过这个问题吗?
253 | > A: 我也遇到你这样的问题了,我觉得是“配置服务参数”那里的,我刚开始用在代码里面配置,也是返回null,我感觉是没有写这个android:canRetrieveWindowContent="true",但是没有找到android:canRetrieveWindowContent="true"对应的设置,所以我就改成在xml里面配置了,就可以了。
254 |
255 | * 2 :
256 | > Q: 请问这些类名com.tencent.mm.ui.LauncherUI是如何获取的?
257 | > A: String className = event.getClassName().toString();使用Log打印出来,打开微信看Log信息
258 |
259 | ## 13. 其他
260 |
261 | [原文:Hensen_的博客](http://img.blog.csdn.net/20161121130452281)
262 |
--------------------------------------------------------------------------------
/app-debug.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/app-debug.apk
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.2"
6 | defaultConfig {
7 | applicationId 'com.kompasim.wechat'
8 | minSdkVersion 19
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | productFlavors {
21 | }
22 | }
23 |
24 | dependencies {
25 | compile fileTree(include: ['*.jar'], dir: 'libs')
26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
27 | exclude group: 'com.android.support', module: 'support-annotations'
28 | })
29 |
30 |
31 | compile 'com.github.zcweng:switch-button:0.0.3@aar'
32 | compile 'com.android.support:appcompat-v7:25.3.0'
33 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
34 | testCompile 'junit:junit:4.12'
35 | }
36 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/alvido_bahor/wechattool/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.example.alvido_bahor.wechattool;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.example.alvido_bahor.wechattool", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
34 |
35 |
38 |
39 |
40 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/java/kompasim/MainActivity.java:
--------------------------------------------------------------------------------
1 | package kompasim;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.provider.Settings;
6 | import android.os.Bundle;
7 | import android.widget.Toast;
8 | import com.example.alvido_bahor.wechattool.R;
9 |
10 |
11 | public class MainActivity extends Activity {
12 |
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_main);
18 | }
19 |
20 | @Override
21 | protected void onResume(){
22 | super.onResume();
23 | Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
24 | startActivity(intent);
25 | Toast.makeText(this, "请开启无障碍服务!", Toast.LENGTH_LONG).show();
26 | finish();
27 | }
28 |
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/kompasim/MoneyService.java:
--------------------------------------------------------------------------------
1 | package kompasim;
2 |
3 | import android.accessibilityservice.AccessibilityService;
4 | import android.accessibilityservice.AccessibilityServiceInfo;
5 | import android.app.Notification;
6 | import android.app.PendingIntent;
7 | import android.content.ClipData;
8 | import android.content.ClipboardManager;
9 | import android.content.Context;
10 | import android.content.Intent;
11 | import android.os.Bundle;
12 | import android.util.Log;
13 | import android.view.accessibility.AccessibilityEvent;
14 | import android.view.accessibility.AccessibilityNodeInfo;
15 | import android.widget.Toast;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.Timer;
20 | import java.util.TimerTask;
21 |
22 | public class MoneyService extends AccessibilityService {
23 |
24 | private List parents = new ArrayList<>();
25 | private boolean isMoneyOpenedAlready = false;
26 |
27 | private String NONE = "none";
28 | private String MONEY = "money";
29 | private String ANSWER = "answer";
30 | private String whatIsIt = NONE;
31 |
32 | public MoneyService() {
33 | }
34 |
35 | @Override
36 | protected void onServiceConnected() {
37 | super.onServiceConnected();
38 | Toast.makeText(this, "左边的按钮抢红包,右边的自动回复! ", Toast.LENGTH_LONG).show();
39 | Log.e("--->", "connect");
40 |
41 | AccessibilityServiceInfo info = new AccessibilityServiceInfo();
42 | // 响应事件的类型,这里是全部的响应事件(长按,单击,滑动等)
43 | info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
44 | // 反馈给用户的类型,这里是语音提示
45 | info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
46 | // 过滤的包名
47 | String[] packageNames = {"com.tencent.mm"};
48 | info.packageNames = packageNames;
49 | setServiceInfo(info);
50 | }
51 |
52 | @Override
53 | public void onAccessibilityEvent(AccessibilityEvent event) {
54 | int eventType = event.getEventType();
55 | switch (eventType) {
56 | //当通知栏发生改变时
57 | case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
58 | Log.e("--->", "wechat");
59 | Log.e("--->", event.getText().toString());
60 | Log.e("--->", event.getClassName().toString());
61 | //
62 | List texts = event.getText();
63 | if (!texts.isEmpty()) {
64 | for (CharSequence text : texts) {
65 | String content = text.toString();
66 | if (content.contains("[微信红包]")) {
67 | if (!MyApplication.money_.isChecked()){
68 | return;
69 | }
70 | whatIsIt = MONEY;
71 | //模拟打开通知栏消息,即打开微信
72 | if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
73 | Notification notification = (Notification) event.getParcelableData();
74 | PendingIntent pendingIntent = notification.contentIntent;
75 | try {
76 | pendingIntent.send();
77 | Log.e("kompasim:", "进入微信1");
78 | } catch (Exception e) {
79 | e.printStackTrace();
80 | }
81 | }
82 | }else {
83 | if (!MyApplication.answer_.isChecked()){
84 | return;
85 | }
86 | whatIsIt = ANSWER;
87 | //模拟打开通知栏消息,即打开微信
88 | if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
89 | Notification notification = (Notification) event.getParcelableData();
90 | PendingIntent pendingIntent = notification.contentIntent;
91 | // kompsdim is it tencent
92 | //
93 | //
94 | try {
95 | pendingIntent.send();
96 | Log.e("kompasim:", "进入微信2");
97 | } catch (Exception e) {
98 | e.printStackTrace();
99 | }
100 | }
101 | }
102 | }
103 | }
104 | break;
105 | //当窗口的状态发生改变时
106 | case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
107 | Log.e("--->", "wechat");
108 | Log.e("text--->", event.getText().toString());
109 | Log.e("class--->", event.getClassName().toString());
110 | // Log.e("kompasim:", getRootInActiveWindow().toString());
111 |
112 |
113 | String className = event.getClassName().toString();
114 | if (className.equals("com.tencent.mm.ui.LauncherUI")) {
115 | //点击最后一个红包
116 | Log.e("what-->", whatIsIt);
117 | if (whatIsIt == MONEY){
118 | if (!MyApplication.money_.isChecked()){
119 | return;
120 | }
121 | Log.e("kompasim", "启动,点击红包");
122 | getLastPacket(event);
123 | } else if (whatIsIt == ANSWER) {
124 | if (!MyApplication.answer_.isChecked()){
125 | return;
126 | }
127 | Log.e("kompasim", "启动,自动回复");
128 | answer(event);
129 | }
130 | } else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.En_fba4b94f")) {
131 | //开红包
132 | Log.e("kompasim", "开红包");
133 | inputClick("com.tencent.mm:id/bjj");
134 | } else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) {
135 | //退出红包
136 | Log.e("kompasim", "抢完,退出红包");
137 | // inputClick("com.tencent.mm:id/gw");
138 | performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
139 | performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
140 | performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
141 | }
142 | break;
143 | default:
144 | break;
145 | }
146 | }
147 |
148 | /**
149 | * 通过ID获取控件,并进行模拟点击
150 | *
151 | * @param clickId
152 | */
153 | private void inputClick(String clickId) {
154 | AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
155 | if (nodeInfo != null) {
156 | List list = nodeInfo.findAccessibilityNodeInfosByViewId(clickId);
157 | for (AccessibilityNodeInfo item : list) {
158 | item.performAction(AccessibilityNodeInfo.ACTION_CLICK);
159 | }
160 | }
161 | }
162 |
163 | private void backHome(){
164 | TimerTask timerTask = new TimerTask() {
165 | @Override
166 | public void run() {
167 | Intent home=new Intent(Intent.ACTION_MAIN);
168 | home.addCategory(Intent.CATEGORY_HOME);
169 | home.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
170 | startActivity(home);
171 | }
172 | };
173 | Timer timer = new Timer();
174 | timer.schedule(timerTask, 1000);
175 | performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
176 | }
177 |
178 | /**
179 | * 获取List中最后一个红包,并进行模拟点击
180 | */
181 | private void getLastPacket(AccessibilityEvent event) {
182 | AccessibilityNodeInfo rootNode = getRootInActiveWindow();
183 | if (rootNode == null) {
184 | Log.e(">>>", "rootNode is null");
185 | return;
186 | } else {
187 | isMoneyOpenedAlready = false;
188 | recycle(rootNode);
189 | }
190 | if (parents.size() > 0) {
191 | parents.get(parents.size() - 1).performAction(AccessibilityNodeInfo.ACTION_CLICK);
192 | parents.clear();
193 | }
194 | }
195 |
196 | /**
197 | * 回归函数遍历每一个节点,并将含有"领取红包"存进List中
198 | *
199 | * @param info
200 | */
201 | public void recycle(AccessibilityNodeInfo info) {
202 | if (info.getChildCount() == 0) {
203 | if (info.getText() != null) {
204 | Log.e("text is : ", info.getText().toString());
205 | if (info.getText().toString().contains( "你领取了")){
206 | isMoneyOpenedAlready = true;
207 | }
208 | if ("领取红包".equals(info.getText().toString()) && (isMoneyOpenedAlready == false)) {
209 | if (info.isClickable()) {
210 | info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
211 | }
212 | AccessibilityNodeInfo parent = info.getParent();
213 | while (parent != null) {
214 | if (parent.isClickable()) {
215 | parents.add(parent);
216 | break;
217 | }
218 | parent = parent.getParent();
219 | }
220 | }
221 | }
222 | } else {
223 | for (int i = info.getChildCount() - 1; i >= 0; i--) {
224 | if (info.getChild(i) != null) {
225 | recycle(info.getChild(i));
226 | }
227 | }
228 | }
229 | }
230 |
231 | public void answer(AccessibilityEvent event){
232 | // 回复我很忙
233 | AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
234 | if (nodeInfo != null) {
235 | List list = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/a3b");
236 | for (AccessibilityNodeInfo item : list) {
237 | Log.e("item-->", item.toString());
238 | // paste
239 | Bundle arguments = new Bundle();
240 | arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
241 | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
242 | arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
243 | true);
244 | item.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
245 | arguments);
246 | item.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
247 | ClipData clip = ClipData.newPlainText("label", "我现在正忙,稍后联系你!");
248 | ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
249 | clipboardManager.setPrimaryClip(clip);
250 | item.performAction(AccessibilityNodeInfo.ACTION_PASTE);
251 | // send
252 | inputClick("com.tencent.mm:id/a3h");
253 | performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
254 | }
255 | }
256 | performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
257 | whatIsIt = NONE;
258 | }
259 |
260 |
261 | @Override
262 | public void onInterrupt() {
263 |
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/app/src/main/java/kompasim/MyApplication.java:
--------------------------------------------------------------------------------
1 | package kompasim;
2 |
3 | import android.app.Application;
4 | import android.content.ComponentName;
5 | import android.content.Intent;
6 | import android.content.ServiceConnection;
7 | import android.os.IBinder;
8 |
9 | import com.suke.widget.SwitchButton;
10 |
11 | /**
12 | * Created by Alvido_bahor on 2017/4/8.
13 | */
14 |
15 | public class MyApplication extends Application {
16 | public static SwitchButton money_ = null;
17 | public static SwitchButton answer_ = null;
18 |
19 | @Override
20 | public void onCreate(){
21 | super.onCreate();
22 | if (!WindowService.flag) {
23 |
24 | ServiceConnection con = new ServiceConnection() {
25 | @Override
26 | public void onServiceConnected(ComponentName name, IBinder service) {
27 |
28 | }
29 |
30 | @Override
31 | public void onServiceDisconnected(ComponentName name) {
32 |
33 | }
34 | };
35 |
36 | Intent windowService = null;
37 | windowService = new Intent(MyApplication.this, WindowService.class);
38 | bindService(windowService, con, BIND_AUTO_CREATE);
39 | startService(windowService);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/kompasim/WindowService.java:
--------------------------------------------------------------------------------
1 | package kompasim;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ObjectAnimator;
5 | import android.app.Service;
6 | import android.content.Intent;
7 | import android.os.Build;
8 | import android.os.IBinder;
9 |
10 |
11 | import android.content.Context;
12 | import android.graphics.PixelFormat;
13 | import android.provider.Settings;
14 | import android.util.DisplayMetrics;
15 | import android.view.Gravity;
16 | import android.view.LayoutInflater;
17 | import android.view.MotionEvent;
18 | import android.view.View;
19 | import android.view.WindowManager;
20 | import android.widget.ImageButton;
21 | import android.widget.LinearLayout;
22 | import android.widget.Toast;
23 |
24 | import com.example.alvido_bahor.wechattool.R;
25 | import com.suke.widget.SwitchButton;
26 |
27 |
28 | public class WindowService extends Service implements SwitchButton.OnCheckedChangeListener {
29 |
30 |
31 | private WindowManager windowManager;
32 | private WindowManager.LayoutParams layoutParams;
33 | //
34 | public static boolean flag = false;//标记悬浮窗是否已经显示
35 | private LinearLayout window;
36 | public ImageButton button;
37 | public LinearLayout controls;
38 | private boolean open = true;//悬浮窗是否是打开状态
39 |
40 | DisplayMetrics metric = new DisplayMetrics();
41 |
42 | public WindowService() {
43 | }
44 |
45 |
46 | @Override
47 | public IBinder onBind(Intent intent) {
48 | return null;
49 | }
50 |
51 |
52 |
53 | @Override
54 | public void onCreate() {
55 | //获取windowManager对象
56 | windowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
57 | windowManager.getDefaultDisplay().getMetrics(metric);
58 | //获取LayoutParams对象
59 | layoutParams = new WindowManager.LayoutParams();
60 | layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;// 系统提示window
61 | layoutParams.format = PixelFormat.TRANSLUCENT;// 支持透明
62 | //layoutParams.format = PixelFormat.RGBA_8888;//实现渐变效果需要
63 | layoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 焦点
64 | layoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;// 模态
65 | layoutParams.width = (int) (300*metric.density);//窗口的宽
66 | layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;//窗口的高
67 | layoutParams.gravity = Gravity.START | Gravity.TOP;
68 | // layoutParams.x = (screenWidth - 600) / 2;//窗口出现位置的偏移量
69 | layoutParams.x = 10;//窗口出现位置的偏移量
70 | layoutParams.y = 500;//窗口出现位置的偏移量
71 | //layoutParams.alpha = 0.1f;//窗口的透明度
72 | final LayoutInflater inflater = LayoutInflater.from(getApplication());
73 | //
74 | //
75 | window = (LinearLayout) inflater.inflate(R.layout.window, null);
76 | button = (ImageButton) window.findViewById(R.id.button);
77 | controls = (LinearLayout) window.findViewById(R.id.controls);
78 |
79 | // SwitchButton app = (SwitchButton )controls.findViewById(R.id.app);
80 | MyApplication.money_ = (SwitchButton )controls.findViewById(R.id.money);
81 | MyApplication.answer_ = (SwitchButton )controls.findViewById(R.id.answer);
82 | // SwitchButton one = (SwitchButton )controls.findViewById(R.id.one);
83 |
84 | // app.setOnCheckedChangeListener(this);
85 | MyApplication.money_.setOnCheckedChangeListener(this);
86 | MyApplication.answer_.setOnCheckedChangeListener(this);
87 | // one.setOnCheckedChangeListener(this);
88 |
89 | //添加desktop_pet布局文件内的图片onTouch监听器
90 | button.setOnTouchListener(new View.OnTouchListener() {
91 | private float startX;//拖动开始之前悬浮窗的x位置
92 | private float startY;//拖动开始之前悬浮窗的y位置
93 | private float lastX;//上个MotionEvent的x位置
94 | private float lastY;//上个MotionEvent的y位置
95 | private float nowX;//这次MotionEvent的x位置
96 | private float nowY;//这次MotionEvent的y位置
97 | private float translateX;//每次拖动产生MotionEvent事件之后窗口所要移动的x轴距离
98 | private float translateY;//每次拖动产生MotionEvent事件的时候窗口所要移动的x轴距离
99 |
100 | @Override
101 | public boolean onTouch(View v, MotionEvent event) {
102 | final int action = event.getAction();
103 | boolean ret = false;
104 | if (action == MotionEvent.ACTION_DOWN) {
105 | lastX = event.getRawX();
106 | lastY = event.getRawY();
107 | startX = layoutParams.x;
108 | startY = layoutParams.y;
109 | } else if (action == MotionEvent.ACTION_MOVE) {
110 | nowX = event.getRawX();
111 | nowY = event.getRawY();
112 | //这次MotionEvent要移动的距离
113 | translateX = (int) (nowX - lastX);
114 | translateY = (int) (nowY - lastY);
115 | layoutParams.x += translateX;
116 | layoutParams.y += translateY;
117 | //更新布局
118 | windowManager.updateViewLayout(window, layoutParams);
119 | lastX = nowX;
120 | lastY = nowY;
121 | } else if (action == MotionEvent.ACTION_UP) {
122 | //跟开始位置比较,检测是否有明显的多动,不是的话返回false,继续执行onClick
123 | boolean a = Math.abs(layoutParams.x - startX) < 5;
124 | boolean b = Math.abs(layoutParams.y - startY) < 5;
125 | //窗口xy移动距离小于我们期望的范围当做onClick,返回true继续执行onClick
126 | if (a && b) {
127 | ret = false;
128 | } else {
129 | layoutParams.x = 0;
130 | ret = true;
131 | }
132 | //刷新布局
133 | windowManager.updateViewLayout(window, layoutParams);
134 | }
135 | return ret;
136 | }
137 | });
138 |
139 |
140 | button.setOnClickListener(new View.OnClickListener() {
141 | @Override
142 | public void onClick(View v) {
143 | ObjectAnimator animator = ObjectAnimator.ofFloat(button, "alpha", 1f, 0f, 1f);
144 | animator.setDuration(1000);
145 | animator.start();
146 | if (open) {
147 | ObjectAnimator anim = ObjectAnimator.ofFloat(controls, "rotationX", 0.0F, 360.0F);
148 | anim.setDuration(500);
149 | anim.addListener(new Animator.AnimatorListener() {
150 | @Override
151 | public void onAnimationStart(Animator animation) {
152 | }
153 |
154 | @Override
155 | public void onAnimationEnd(Animator animation) {
156 | window.removeView(controls);
157 | layoutParams.width = (int) (70*metric.density);
158 | windowManager.updateViewLayout(window, layoutParams);
159 | open = false;
160 | }
161 |
162 | @Override
163 | public void onAnimationCancel(Animator animation) {
164 | }
165 |
166 | @Override
167 | public void onAnimationRepeat(Animator animation) {
168 | }
169 | });
170 | anim.start();
171 | } else {
172 | window.addView(controls);
173 | layoutParams.width = (int) (300*metric.density);
174 | windowManager.updateViewLayout(window, layoutParams);
175 | open = true;
176 | ObjectAnimator.ofFloat(controls, "rotationX", 0.0F, 360.0F).setDuration(500).start();
177 | }
178 | }
179 | });
180 | super.onCreate();
181 | }
182 |
183 |
184 | @Override
185 | public int onStartCommand(Intent intent, int flags, int startId) {
186 | if (!WindowService.flag) {
187 | if (Build.VERSION.SDK_INT >= 23) {
188 | if(!Settings.canDrawOverlays(this)) {
189 | Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
190 | startActivity(i);
191 | } else {
192 | //Android6.0以上
193 | windowManager.addView(window, layoutParams);
194 | WindowService.flag = true;
195 | }
196 | } else {
197 | //Android6.0以下,不用动态声明权限
198 | windowManager.addView(window, layoutParams);
199 | WindowService.flag = true;
200 | }
201 |
202 | }
203 | return super.onStartCommand(intent, flags, startId);
204 | }
205 |
206 | @Override
207 | public void onDestroy() {
208 | if (window.getParent() != null) {
209 | windowManager.removeView(window);
210 | WindowService.flag = false;
211 | }
212 | super.onDestroy();
213 | }
214 |
215 |
216 |
217 | @Override
218 | public void onCheckedChanged(SwitchButton view, boolean isChecked) {
219 | switch (view.getId()) {
220 | case R.id.money:
221 | if (isChecked){
222 | Toast.makeText(this, "بولاق تالىشىش باشلاندى", Toast.LENGTH_SHORT).show();
223 |
224 | }else {
225 | Toast.makeText(this, "بولاق تالىشىش توختىتىلدى", Toast.LENGTH_SHORT).show();
226 |
227 | }
228 | break;
229 | default:
230 | break;
231 | }
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/controls_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/window.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
16 |
26 |
27 |
28 |
29 |
30 |
31 | app:sb_button_color="@color/red"
32 |
33 |
34 |
35 |
36 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #cc0300
7 | #ccbb00
8 | #5cfd82
9 | #000acc
10 | #8a05b2
11 | #0063b4
12 | #f4177b
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 55dp
5 | 55dp
6 | 1dp
7 | 50dp
8 |
9 | 55dp
10 | 55dp
11 |
12 | 30dp
13 | 30dp
14 |
15 | 16dp
16 | 16dp
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WechatTool
3 | 点击这里开启无障碍服务
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/accessibility_service_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/alvido_bahor/wechattool/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.example.alvido_bahor.wechattool;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.1'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/demo.png
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Atypicalim/android-wechat-tool/421d7a83c57649568877f5b5e3ac6aa4d3b45477/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Apr 07 23:38:19 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------