Browse Source

添加 微信自动回复功能。

liuyuqi-dellpc 4 years ago
parent
commit
0d9564fc10

+ 7 - 0
README.md

@@ -0,0 +1,7 @@
+## wxRedPacket
+
+原项目微信红包助手,已实现。那么继续实现微信的其他自动化功能。比如自动回复:
+
+### WcAutoReply
+
+Android 微信自动回复功能,使用的知识为 AccessibilityService

+ 14 - 0
app/src/main/AndroidManifest.xml

@@ -14,6 +14,12 @@
     <uses-permission android:name="android.permission.READ_LOGS"
         tools:ignore="ProtectedPermissions" />
 
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
+        tools:ignore="ProtectedPermissions" />
+    <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"
@@ -43,6 +49,14 @@
             <meta-data android:name="android.accessibilityservice"
                 android:resource="@xml/accessible_service_config"/>
         </service>
+        <service android:name=".services.AutoReplyService"
+            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/accessible_service_config"/>
+        </service>
         <provider
             android:name="android.support.v4.content.FileProvider"
             android:authorities="me.yoqi.app.hongbao.fileProvider"

+ 2 - 2
app/src/main/java/me/yoqi/app/wxredpacket/activities/MainActivity.java

@@ -17,7 +17,7 @@ import android.widget.ImageView;
 import android.widget.TextView;
 import android.widget.Toast;
 
-import com.tencent.bugly.Bugly;
+//import com.tencent.bugly.Bugly;
 
 import java.util.List;
 
@@ -33,7 +33,7 @@ public class MainActivity extends Activity implements AccessibilityManager.Acces
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        Bugly.init(getApplicationContext(), "9410cbd743", false);
+//        Bugly.init(getApplicationContext(), "9410cbd743", false);
         setContentView(R.layout.activity_main);
         pluginStatusText = findViewById(R.id.layout_control_accessibility_text);
         pluginStatusIcon = findViewById(R.id.layout_control_accessibility_icon);

+ 340 - 0
app/src/main/java/me/yoqi/app/wxredpacket/services/AutoReplyService.java

@@ -0,0 +1,340 @@
+package me.yoqi.app.wxredpacket.services;
+
+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");
+                                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, "wxRedPacket: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;
+        }
+    }
+}

+ 2 - 2
gradle.properties

@@ -9,10 +9,10 @@
 
 # Specifies the JVM arguments used for the daemon process.
 # The setting is particularly useful for tweaking memory settings.
-#org.gradle.jvmargs=-Xmx1024m
+org.gradle.jvmargs=-Xmx1024m
 
 # When configured, Gradle will run in incubating parallel mode.
 # This option should only be used with decoupled projects. More details, visit
 # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
 # org.gradle.parallel=true
-#android.useDeprecatedNdk=true
+android.useDeprecatedNdk=true