您好,欢迎来到源码搜藏!分享精神,快乐你我!提示:担心找不到本站?在百度搜索“源码搜藏”,网址永远不丢失!
  • 首 页
  • 在线工具
  • 当前位置:首页 > 安卓源码 > 技术博客 >

    Android微信自动回复功能

    时间:2016-12-06 22:14 来源:互联网 作者:源码搜藏 浏览:收藏 挑错 推荐 打印

    写在前面: 最近接到老大的一个需求,要求在手机端拦截微信的通知(Notification),从而获得联系人和内容。之后将联系人和内容发送到我们的硬件产品上,展示出来之后,再将我们想回复内容传给微信,并且发送给相应联系人。 老大还提示我需要用 Accessibilit

    写在前面:
    最近接到老大的一个需求,要求在手机端拦截微信的通知(Notification),从而获得联系人和内容。之后将联系人和内容发送到我们的硬件产品上,展示出来之后,再将我们想回复内容传给微信,并且发送给相应联系人。

    老大还提示我需要用AccessibilityService去实现它,当然在此之前我并不知道AccessibilityService是什么鬼,不过没关系, just do IT !

    AccessibilityService

    AccessibilityService官方文档(需翻墙)

    上面这个链接是AccessibilityService的官方文档,可以翻墙点进去了解下,我再给大家总结一下:

    AccessibilityService是Android系统框架提供给安装在设备上应用的一个可选的导航反馈特性。AccessibilityService 可以替代应用与用户交流反馈,比如将文本转化为语音提示,或是用户的手指悬停在屏幕上一个较重要的区域时的触摸反馈等。

    如果感觉上面的描述比较抽象,没关系,也许你见过下面这张图:

    Android微信自动回复功能

    打开你手机的设置–辅助功能中,有很多APP提供的服务,他们都是基于AccessibilityService编写的,AccessibilityService可以侦听你的点击,长按,手势,通知栏的变化等。并且你可以通过很多种方式找到窗体中的EditText,Button等组件,去填充他们,去点击他们来帮你实现自动化的功能。

    像360助手的自动安装功能,它就是侦听着系统安装的APP,然后找到“安装”按钮,实现了自动点击。微信自动抢红包功能,实现方式都是如此。

    配置AccessibilityService

    首先我们在res文件夹下创建xml文件夹,然后创建一个名为auto_reply_service_config的文件,一会我们会在清单文件中引用它。

    Android微信自动回复功能

    代码:

    
    					
    1
    2
    3
    4
    5
    6
    7
    
    					
    <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_description"
    android:notificationTimeout="100"
    android:packageNames="com.tencent.mm" />

    这个文件表示我们对AccessibilityService服务未来侦听的行为做了一些配置,比如 typeNotificationStateChanged 和 typeWindowStateChanged 表示我们需要侦听通知栏的状态变化和窗体状态改变。 android:packageNames=”com.tencent.mm” 这是微信的包名,表示我们只关心微信这一个应用。

    代码不打算带着大家一行一行看了,如果有不明白的,去看看文档,或者下面回复我,我给大家解答~

    创建AccessibilityService

    下面贴出AccessibilityService类的全部代码,注释还算详尽,如有疑问,下方回复。

    
    					
    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
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    
    					
    package com.ileja.autoreply;
     
    import android.accessibilityservice.AccessibilityService;
    import android.annotation.SuppressLint;
    import android.app.ActivityManager;
    import android.app.KeyguardManager;
    import android.app.Notification;
    import android.app.PendingIntent;
    import android.content.ClipData;
    import android.content.ClipboardManager;
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.PowerManager;
    import android.text.TextUtils;
    import android.view.KeyEvent;
    import android.view.accessibility.AccessibilityEvent;
    import android.view.accessibility.AccessibilityNodeInfo;
     
    import java.io.IOException;
    import java.util.List;
     
    public class AutoReplyService extends AccessibilityService {
    private final static String MM_PNAME = "com.tencent.mm";
    boolean hasAction = false;
    boolean locked = false;
    boolean background = false;
    private String name;
    private String scontent;
    AccessibilityNodeInfo itemNodeinfo;
    private KeyguardManager.KeyguardLock kl;
    private Handler handler = new Handler();
     
     
    /**
    * 必须重写的方法,响应各种事件。
    * @param event
    */
    @Override
    public void onAccessibilityEvent(final AccessibilityEvent event) {
    int eventType = event.getEventType();
    android.util.Log.d("maptrix", "get event = " + eventType);
    switch (eventType) {
    case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:// 通知栏事件
    android.util.Log.d("maptrix", "get notification event");
    List<CharSequence> texts = event.getText();
    if (!texts.isEmpty()) {
    for (CharSequence text : texts) {
    String content = text.toString();
    if (!TextUtils.isEmpty(content)) {
    if (isScreenLocked()) {
    locked = true;
    wakeAndUnlock();
    android.util.Log.d("maptrix", "the screen is locked");
    if (isAppForeground(MM_PNAME)) {
    background = false;
    android.util.Log.d("maptrix", "is mm in foreground");
    sendNotifacationReply(event);
    handler.postDelayed(new Runnable() {
    @Override
    public void run() {
    sendNotifacationReply(event);
    if (fill()) {
    send();
    }
    }
    }, 1000);
    } else {
    background = true;
    android.util.Log.d("maptrix", "is mm in background");
    sendNotifacationReply(event);
    }
    } else {
    locked = false;
    android.util.Log.d("maptrix", "the screen is unlocked");
    // 监听到微信红包的notification,打开通知
    if (isAppForeground(MM_PNAME)) {
    background = false;
    android.util.Log.d("maptrix", "is mm in foreground");
    sendNotifacationReply(event);
    handler.postDelayed(new Runnable() {
    @Override
    public void run() {
    if (fill()) {
    send();
    }
    }
    }, 1000);
    } else {
    background = true;
    android.util.Log.d("maptrix", "is mm in background");
    sendNotifacationReply(event);
    }
    }
    }
    }
    }
    break;
    case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
    android.util.Log.d("maptrix", "get type window down event");
    if (!hasAction) break;
    itemNodeinfo = null;
    String className = event.getClassName().toString();
    if (className.equals("com.tencent.mm.ui.LauncherUI")) {
    if (fill()) {
    send();
    }else {
    if(itemNodeinfo != null){
    itemNodeinfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
    handler.postDelayed(new Runnable() {
    @Override
    public void run() {
    if (fill()) {
    send();
    }
    back2Home();
    release();
    hasAction = false;
    }
    }, 1000);
    break;
    }
    }
    }
     
    //bring2Front();
    back2Home();
    release();
    hasAction = false;
    break;
    }
    }
     
    /**
    * 寻找窗体中的“发送”按钮,并且点击。
    */
    @SuppressLint("NewApi")
    private void send() {
    AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
    if (nodeInfo != null) {
    List<AccessibilityNodeInfo> list = nodeInfo
    .findAccessibilityNodeInfosByText("发送");
    if (list != null && list.size() > 0) {
    for (AccessibilityNodeInfo n : list) {
    if(n.getClassName().equals("android.widget.Button") && n.isEnabled())
    {
    n.performAction(AccessibilityNodeInfo.ACTION_CLICK);}
    }
     
    } else {
    List<AccessibilityNodeInfo> liste = nodeInfo
    .findAccessibilityNodeInfosByText("Send");
    if (liste != null && liste.size() > 0) {
    for (AccessibilityNodeInfo n : liste) {
    if(n.getClassName().equals("android.widget.Button") && n.isEnabled())
    {
    n.performAction(AccessibilityNodeInfo.ACTION_CLICK);}
    }
    }
    }
    }
    pressBackButton();
    }
     
    }
     
    /**
    * 模拟back按键
    */
    private void pressBackButton(){
    Runtime runtime = Runtime.getRuntime();
    try {
    runtime.exec("input keyevent " + KeyEvent.KEYCODE_BACK);
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
     
    /**
    *
    * @param event
    */
    private void sendNotifacationReply(AccessibilityEvent event) {
    hasAction = true;
    if (event.getParcelableData() != null
    && event.getParcelableData() instanceof Notification) {
    Notification notification = (Notification) event
    .getParcelableData();
    String content = notification.tickerText.toString();
    String[] cc = content.split(":");
    name = cc[0].trim();
    scontent = cc[1].trim();
     
    android.util.Log.i("maptrix", "sender name =" + name);
    android.util.Log.i("maptrix", "sender content =" + scontent);
     
     
    PendingIntent pendingIntent = notification.contentIntent;
    try {
    pendingIntent.send();
    } catch (PendingIntent.CanceledException e) {
    e.printStackTrace();
    }
    }
    }
     
    @SuppressLint("NewApi")
    private boolean fill() {
    AccessibilityNodeInfo rootNode = getRootInActiveWindow();
    if (rootNode != null) {
    return findEditText(rootNode, "正在忙,稍后回复你");
    }
    return false;
    }
     
     
    private boolean findEditText(AccessibilityNodeInfo rootNode, String content) {
    int count = rootNode.getChildCount();
     
    android.util.Log.d("maptrix", "root class=" + rootNode.getClassName() + ","+ rootNode.getText()+","+count);
    for (int i = 0; i < count; i++) {
    AccessibilityNodeInfo nodeInfo = rootNode.getChild(i);
    if (nodeInfo == null) {
    android.util.Log.d("maptrix", "nodeinfo = null");
    continue;
    }
     
    android.util.Log.d("maptrix", "class=" + nodeInfo.getClassName());
    android.util.Log.e("maptrix", "ds=" + nodeInfo.getContentDescription());
    if(nodeInfo.getContentDescription() != null){
    int nindex = nodeInfo.getContentDescription().toString().indexOf(name);
    int cindex = nodeInfo.getContentDescription().toString().indexOf(scontent);
    android.util.Log.e("maptrix", "nindex=" + nindex + " cindex=" +cindex);
    if(nindex != -1){
    itemNodeinfo = nodeInfo;
    android.util.Log.i("maptrix", "find node info");
    }
    }
    if ("android.widget.EditText".equals(nodeInfo.getClassName())) {
    android.util.Log.i("maptrix", "==================");
    Bundle arguments = new Bundle();
    arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
    AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
    arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
    true);
    nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
    arguments);
    nodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
    ClipData clip = ClipData.newPlainText("label", content);
    ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    clipboardManager.setPrimaryClip(clip);
    nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);
    return true;
    }
     
    if (findEditText(nodeInfo, content)) {
    return true;
    }
    }
     
    return false;
    }
     
    @Override
    public void onInterrupt() {
     
    }
     
    /**
    * 判断指定的应用是否在前台运行
    *
    * @param packageName
    * @return
    */
    private boolean isAppForeground(String packageName) {
    ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
    String currentPackageName = cn.getPackageName();
    if (!TextUtils.isEmpty(currentPackageName) && currentPackageName.equals(packageName)) {
    return true;
    }
     
    return false;
    }
     
     
    /**
    * 将当前应用运行到前台
    */
    private void bring2Front() {
    ActivityManager activtyManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> runningTaskInfos = activtyManager.getRunningTasks(3);
    for (ActivityManager.RunningTaskInfo runningTaskInfo : runningTaskInfos) {
    if (this.getPackageName().equals(runningTaskInfo.topActivity.getPackageName())) {
    activtyManager.moveTaskToFront(runningTaskInfo.id, ActivityManager.MOVE_TASK_WITH_HOME);
    return;
    }
    }
    }
     
    /**
    * 回到系统桌面
    */
    private void back2Home() {
    Intent home = new Intent(Intent.ACTION_MAIN);
     
    home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    home.addCategory(Intent.CATEGORY_HOME);
     
    startActivity(home);
    }
     
     
    /**
    * 系统是否在锁屏状态
    *
    * @return
    */
    private boolean isScreenLocked() {
    KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
    return keyguardManager.inKeyguardRestrictedInputMode();
    }
     
    private void wakeAndUnlock() {
    //获取电源管理器对象
    PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
     
    //获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是调试用的Tag
    PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright");
     
    //点亮屏幕
    wl.acquire(1000);
     
    //得到键盘锁管理器对象
    KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
    kl = km.newKeyguardLock("unLock");
     
    //解锁
    kl.disableKeyguard();
     
    }
     
    private void release() {
     
    if (locked && kl != null) {
    android.util.Log.d("maptrix", "release the lock");
    //得到键盘锁管理器对象
    kl.reenableKeyguard();
    locked = false;
    }
    }
    }

    接着配置清单文件,权限和service的配置比较重要。

    
    					
    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
    
    					
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ileja.autoreply">
     
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.REORDER_TASKS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
     
    <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
    <intent-filter>
    <action android:name="android.intent.action.MAIN" />
     
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    </activity>
     
    <service
    android:name=".AutoReplyService"
    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/auto_reply_service_config"/>
    </service>
    </application>
    </manifest>

    为了使用某些必要的API,最低API level应该是18

    运行程序,打开服务,看看效果如何把~

    Android微信自动回复功能

    接着用其他手机试着发送给我几条微信

    Android微信自动回复功能

    可以看到,自动回复功能就实现了。

    写在后面:

    代码没有给大家详细讲解,不过看注释应该可以看懂个大概。当微信程序切换到后台,或者锁屏(无锁屏密码)时,只要有通知出现,都可以实现自动回复。

    关于AccessibilityService可以监控的行为非常多,所以我觉得可以实现各种各样炫酷的功能,不过我并不建议你打开某些流氓软件的AccessibilityService服务,因为很有可能造成一些安全问题,所以,自己动手写就安全多了嘛。

    Android微信自动回复功能转载http://www.codesocang.com/anzhuoyuanma/boke/33964.html
    标签:网站源码