利用AccessibilityService(安卓辅助)下载微信朋友圈照片,解决微信朋友圈照片长按无效的问题,无需root,不涉及微信底层

由于项目需要开发一个朋友圈克隆的功能,经过2天的研究,终于有所作为,先上图:

先说说微信朋友圈克隆的思路,主要是通过AccessibilityService辅助类对微信界面的改变进行监控,然后进入到指定朋友的朋友圈,复制指定朋友的朋友圈内容,然后在自己的朋友圈发布复制过来的内容即可。这一切操作都是自动的,无需手动点击。

朋友圈的内容复制分为三类:第一类:纯文本朋友圈。第二类:图片+文本,纯图片,视频+文本,纯视频。第三类:外部链接+文本,纯外部链接。第一类和第三类都很容易完成,第二类的视频+文本和纯视频都很容易就进行了复制和转发。当在开发第二类图片+文本和纯图片时,遇到图片无法长按弹出下载提示层的情况,而且我一个一个node的尝试都无法进行长按和点击事件。我甚至考虑到用手势事件,但是手势事件在版本24以上才可用,并不是很理想,所以只能想别的办法实现。所以就有了这篇文章的诞生。

下面我们进入主题:我的思路是从微信的缓存目录获取图片,当一张图片点击查看时,会在本地目录生成一个文件,该文件在SD卡的“tencent/MicroMsg/(这里是一长串数字和字母的文件夹)/sns/”目录下面。所以只需要在点击开该图片的时候监听该文件夹的文件变动,然后找到该变动的文件,复制到“tencent/MicroMsg/WeiXin”目录下,然后通知图库刷新即可,思路确定,代码开干,我直接贴几个关键类的代码:

文件帮助类,主要是获取微信的缓存路径,和拷贝缓存文件到WeiXin目录

package com.xuqing.jnitest;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.os.FileObserver;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileUtil {
    /**
     * 微信文件存储根路径
     */
    private final static String WE_CHAT_FILE_PATH_ROOT = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tencent/MicroMsg";
    /**
     * 微信图片缓存文件存储路径
     */
    private final static String WE_CHAT_IMAGE_CACHE_PATH = createWeChatImageCachePath();
    /**
     * 微信缓存文件名头部字符串
     */
    private final static String WE_CHAT_IMAGE_CACHE_FILE_NAME_HEAD_STRING = "snsb";
    /**
     * 微信图片保存路径
     */
    private final static String WE_CHAT_IMAGE_FILE_SAVE_PATH = WE_CHAT_FILE_PATH_ROOT+"/WeiXin";

    /**
     * 获取微信缓存目录
     * @return
     */
    public static String getWeChatImageCachePath(){
        return WE_CHAT_IMAGE_CACHE_PATH;
    }

    /**
     * 拷贝缓存目录下的文件到weixin目录下
     * @param path
     * @param context
     */
    public static void saveCacheToImage(String path, Context context){
        try {
            if (path.substring(path.lastIndexOf("/")+1).startsWith(WE_CHAT_IMAGE_CACHE_FILE_NAME_HEAD_STRING)) {
                FileInputStream inputStream = new FileInputStream(new File(path));
                File saveFile=new File(WE_CHAT_IMAGE_FILE_SAVE_PATH,System.currentTimeMillis()+".jpg");
                FileOutputStream outputStream=new FileOutputStream(saveFile);
                byte[] bytes=new byte[1024];
                int read=0;
                while ((read=inputStream.read(bytes))!=-1){
                    outputStream.write(bytes,0,read);
                }
                inputStream.close();
                outputStream.close();

                //保存成功,更新图库
                Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                Uri uri = Uri.fromFile(saveFile);
                intent.setData(uri);
                context.sendBroadcast(intent);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 获取微信图片缓存路径
     *
     * @return
     */
    private static String createWeChatImageCachePath() {
        try {
            File file = new File(WE_CHAT_FILE_PATH_ROOT);
            if (file.exists()) {
                File[] files = file.listFiles();
                if (files != null) {
                    String longPath = null;
                    for (File childFile : files) {
                        String tempPath = childFile.getAbsolutePath();
                        if (longPath == null) {
                            longPath = tempPath;
                        } else if (longPath.length() < tempPath.length()) {
                            longPath = tempPath;
                        }
                    }
                    if (longPath != null) {
                        return longPath + "/sns";
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

微信缓存目录文件变动监听类,主要是把微信文件夹内文件的变动第一时间告诉我们,主要是用的FileObserver这个类的功能,具体用法请自行百度该类。

package org.luckyzz.wxhelper.service;
import android.content.Context;
import android.os.FileObserver;
import android.util.Log;

import org.luckyzz.wxhelper.common.FileUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class SDCardFileObserver extends FileObserver {
    List mObservers;
    String mPath;
    String tempPath;
    Context mContext;
    public SDCardFileObserver(String path, Context context) {
        super(path);
        mPath = path;
        mContext=context;
    }
    @Override
    public void onEvent(int event, String path) {
        switch (event) {
            case FileObserver.ACCESS:
                Log.i("RecursiveFileObserver", "ACCESS: " + path);
                break;
            case FileObserver.ATTRIB:
                Log.i("RecursiveFileObserver", "ATTRIB: " + path);
                break;
            case FileObserver.CLOSE_NOWRITE:
                Log.i("RecursiveFileObserver", "CLOSE_NOWRITE: " + path);
                break;
            case FileObserver.CLOSE_WRITE:
                Log.i("RecursiveFileObserver", "CLOSE_WRITE: " + path);
                break;
            case FileObserver.CREATE:
                Log.i("RecursiveFileObserver", "CREATE: " + path);
                break;
            case FileObserver.DELETE:
                Log.i("RecursiveFileObserver", "DELETE: " + path);
                break;
            case FileObserver.DELETE_SELF:
                Log.i("RecursiveFileObserver", "DELETE_SELF: " + path);
                break;
            case FileObserver.MODIFY:
                Log.i("RecursiveFileObserver", "MODIFY: " + path);
                break;
            case FileObserver.MOVE_SELF:
                Log.i("RecursiveFileObserver", "MOVE_SELF: " + path);
                break;
            case FileObserver.MOVED_FROM:
                Log.i("RecursiveFileObserver", "MOVED_FROM: " + path);
                break;
            case FileObserver.MOVED_TO:
                Log.i("RecursiveFileObserver", "MOVED_TO: " + path);
                break;
            case FileObserver.OPEN:
                Log.i("RecursiveFileObserver", "OPEN: " + path);
                break;
            default:
                Log.i("RecursiveFileObserver", "DEFAULT(" + event + " : " + path);
                break;
        }

        switch (event){
            case CREATE:
            case OPEN:
            case ACCESS:
                if (tempPath==null||!tempPath.equals(path)){
                    tempPath=path;
                    //这里去把缓存拷贝到WeiXin文件夹    
                    FileUtil.saveCacheToImage(path,mContext);
                }
                break;
        }
    }

    @Override
    public void startWatching() {
        Log.i("RecursiveFileObserver", "开始监听......");
        if (mObservers != null)
            return;

        mObservers = new ArrayList();
        Stack stack = new Stack();
        stack.push(mPath);

        while (!stack.isEmpty()) {
            String parent = (String)stack.pop();
            mObservers.add(new SDCardChildFileObserver(parent));
            File path = new File(parent);
            File[] files = path.listFiles();
            if (null == files)
                continue;
            for (File f : files) {
                if (f.isDirectory() && !f.getName().equals(".")
                        && !f.getName().equals("..")) {
                    stack.push(f.getPath());
                }
            }
        }

        for (int i = 0; i < mObservers.size(); i++) {
            SDCardChildFileObserver sfo = (SDCardChildFileObserver) mObservers.get(i);
            sfo.startWatching();
        }
    }

    @Override
    public void stopWatching() {
        Log.i("RecursiveFileObserver", "停止监听......");
        if (mObservers == null)
            return;

        for (int i = 0; i < mObservers.size(); i++) {
            SDCardChildFileObserver sfo = (SDCardChildFileObserver) mObservers.get(i);
            sfo.stopWatching();
        }

        mObservers.clear();
        mObservers = null;
    }

    public class SDCardChildFileObserver extends FileObserver {
        String mPath;
        public SDCardChildFileObserver(String path) {
            super(path);
            mPath=path;
        }

        @Override
        public void onEvent(int event, String path) {
            String newPath = mPath +"/"+ path;
            SDCardFileObserver.this.onEvent(event, newPath);
        }
    }
}

调用的地方,我直接贴我的代码的一部分了:

//处理图片
                        List imgNodeList = service.getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/eiz");
                        //处理视频
                        List videoNodeList = service.getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/aou");
                        AccessibilityNodeInfo imgVideoParentNode = null;
                        if (imgNodeList != null && imgNodeList.size() > 0) {
                            imgVideoParentNode = imgNodeList.get(0);
                            if (imgVideoParentNode != null) {
                                img_num = imgVideoParentNode.getChildCount();
                            }
                        } else if (videoNodeList != null && videoNodeList.size() > 0) {
                            imgVideoParentNode = videoNodeList.get(0);
                            img_num = 1;
                        }
                        WxHelperApp.sdCardFileObserver.startWatching();
                        for (int i = 0; i < img_num; i++) {
                            AccessibilityNodeInfo imgNode = null;
                            if (imgNodeList != null && imgNodeList.size() > 0) {
                                imgNode = imgVideoParentNode.getChild(i);
                            } else if (videoNodeList != null && videoNodeList.size() > 0) {
                                imgNode = imgVideoParentNode;
                            }
                            if (imgNode != null) {
                                AccessibilityNodeInfo imgClickNode = ASUtil.findClickNode(imgNode);
                                if (imgClickNode != null) {
                                    boolean imgB = imgClickNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                                    Thread.sleep(1000);
                                    if (imgB) {
                                        List scrollImgNodeList = service.getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/eg1");
                                        List videoViewNodeList = service.getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/vd");
                                        if (scrollImgNodeList != null && scrollImgNodeList.size() > 0) {//保存图片
                                            //TODO 这里需要考虑1秒图片还没打开的情况
                                            service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
                                            Thread.sleep(1000);
                                        } else if (videoViewNodeList != null && videoViewNodeList.size() > 0) {//保存视频
                                            AccessibilityNodeInfo videoLongClick = videoViewNodeList.get(0);
                                            boolean imgGalleryLongB = videoLongClick.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
                                            Thread.sleep(1000);
                                            if (imgGalleryLongB) {
                                                List saveVideoNodeList = service.getRootInActiveWindow().findAccessibilityNodeInfosByText("保存视频");
                                                AccessibilityNodeInfo saveNode = saveVideoNodeList.get(0);
                                                if (saveNode != null) {
                                                    AccessibilityNodeInfo clickSaveNode = ASUtil.findClickNode(saveNode);
                                                    if (clickSaveNode != null) {
                                                        boolean saveB = clickSaveNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                                                        Thread.sleep(1000);
                                                        if (saveB) {
                                                            videoLongClick.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                                                            Thread.sleep(1000);
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        WxHelperApp.sdCardFileObserver.stopWatching();
                        //复制完成
                        ctx.setState(RETURN_MAIN_PAGE);

主要是这两句

WxHelperApp.sdCardFileObserver.startWatching();
WxHelperApp.sdCardFileObserver.stopWatching();

这样就实现了微信朋友圈的图片的自动保存,主要的代码都已贴上,项目代码由于还在开发,不能发出来,主要是思路,希望对遇到该问题的你有所帮助。

你可能感兴趣的:(android,java,FileObserver,微信朋友圈,微信朋友圈图片下载,微信朋友圈克隆)