由于项目需要开发一个朋友圈克隆的功能,经过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();
这样就实现了微信朋友圈的图片的自动保存,主要的代码都已贴上,项目代码由于还在开发,不能发出来,主要是思路,希望对遇到该问题的你有所帮助。