├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── LICENSE.txt
├── README.md
├── apk.lnk
├── app
├── .gitignore
├── build.gradle
├── jniLibs
│ ├── armeabi-v7a
│ │ ├── libdatabase_sqlcipher.so
│ │ ├── libsqlcipher_android.so
│ │ └── libstlport_shared.so
│ ├── armeabi
│ │ ├── libdatabase_sqlcipher.so
│ │ ├── libsqlcipher_android.so
│ │ └── libstlport_shared.so
│ ├── sqlcipher-javadoc.jar
│ └── sqlcipher.jar
├── libs
│ └── universal-image-loader-1.9.5.jar
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── moe
│ │ └── chionlab
│ │ └── wechatmomentstat
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── 1.jpg
│ │ ├── 1.png
│ │ ├── 1.webp
│ │ ├── 1.wxpc
│ │ ├── 1wx.jpg
│ │ ├── 1wx.webp
│ │ ├── 2.wxpc
│ │ ├── icudt46l.zip
│ │ ├── libwechat.zip
│ │ └── wechat.apk
│ ├── java
│ │ ├── cn
│ │ │ └── truistic
│ │ │ │ └── enmicromsg
│ │ │ │ ├── app
│ │ │ │ ├── EnApplication.java
│ │ │ │ └── PathConfig.java
│ │ │ │ ├── base
│ │ │ │ └── BaseActivity.java
│ │ │ │ ├── common
│ │ │ │ ├── db
│ │ │ │ │ └── DBUtill.java
│ │ │ │ └── util
│ │ │ │ │ ├── DeviceUtil.java
│ │ │ │ │ ├── MD5Util.java
│ │ │ │ │ ├── RootUtil.java
│ │ │ │ │ └── SharedPerfUtil.java
│ │ │ │ └── main
│ │ │ │ ├── MainMVP.java
│ │ │ │ ├── model
│ │ │ │ ├── HomeModel.java
│ │ │ │ └── MainModel.java
│ │ │ │ └── presenter
│ │ │ │ ├── HomePresenter.java
│ │ │ │ └── MainPresenter.java
│ │ └── moe
│ │ │ └── chionlab
│ │ │ └── wechatmomentstat
│ │ │ ├── Config.java
│ │ │ ├── Model
│ │ │ ├── SnsInfo.java
│ │ │ └── UserSnsInfo.java
│ │ │ ├── Parser.java
│ │ │ ├── SnsReader.java
│ │ │ ├── SnsStat.java
│ │ │ ├── SubThread.java
│ │ │ ├── Task.java
│ │ │ ├── common
│ │ │ └── Share.java
│ │ │ └── gui
│ │ │ ├── MainActivity.java
│ │ │ ├── MomentListActivity.java
│ │ │ ├── MomentStatActivity.java
│ │ │ ├── SnsInfoAdapter.java
│ │ │ └── UserSelectActivity.java
│ └── res
│ │ ├── drawable
│ │ └── empty_background.png
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── activity_moment_list.xml
│ │ ├── activity_moment_stat.xml
│ │ ├── activity_user_select.xml
│ │ └── sns_item.xml
│ │ ├── menu
│ │ ├── moment_export_menu.xml
│ │ ├── moment_stat_menu.xml
│ │ └── user_select_menu.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-en
│ │ └── strings.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ ├── values-zh
│ │ └── strings.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── moe
│ └── chionlab
│ └── wechatmomentrank
│ └── ExampleUnitTest.java
├── build.gradle
├── demo_pics
├── demo_1.jpg
├── demo_2.jpg
└── demo_3.jpg
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── libwechat.jar
├── projectFilesBackup
└── .idea
│ └── workspace.xml
└── 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 | app-release.apk
10 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | WeChatMomentStat-Android
--------------------------------------------------------------------------------
/.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/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
20 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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 | WeChat Moment Stat
2 | ------------------
3 | WeChatMomentStat is an Android tool which generates your WeChat Moments statistics and helps you export Moments data into JSON. Root access is required. Latest WeChat version is supported.
4 |
5 | WeChatMomentStat是一个安卓平台的工具,可生成你的微信朋友圈统计数据,并可导出朋友圈数据到JSON文件中。需要root权限。支持最新微信版本。
6 |
7 | By default, selected Moments are exported to `WeChatMomentStat/exported_sns.json` located in the root path of the external storage.
8 |
9 | 勾选的朋友圈默认导出到外部存储根目录下的 `WeChatMomentStat/exported_sns.json`。
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Download
19 | --------
20 | [WeChatMomentStat Releases](https://github.com/Chion82/WeChatMomentStat-Android/releases)
21 |
22 | License
23 | -------
24 | GNU GPLv3
25 | See `LICENSE.txt` for details
26 |
27 | Author
28 | ------
29 | [Chion Tang的归宅开发部活动记录](https://blog.chionlab.moe/)
30 |
--------------------------------------------------------------------------------
/apk.lnk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/apk.lnk
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion '25.0.0'
6 |
7 | defaultConfig {
8 | applicationId "moe.chionlab.wechatmomentstat"
9 | minSdkVersion 15
10 | targetSdkVersion 23
11 | versionCode 2
12 | versionName "1.1"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled true
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | sourceSets {
21 | main {
22 | jniLibs.srcDirs = ['jniLibs']
23 | }
24 | }
25 | }
26 |
27 | dependencies {
28 | provided fileTree(include: ['*.jar'], dir: 'libs')
29 | provided fileTree(include: ['*.jar'], dir: 'jniLibs')
30 | testCompile 'junit:junit:4.12'
31 | compile 'com.android.support:appcompat-v7:23.1.1'
32 | compile files('libs/universal-image-loader-1.9.5.jar')
33 | compile files('jniLibs/sqlcipher.jar')
34 | compile files('jniLibs/sqlcipher-javadoc.jar')
35 | //compile files('libs/libwechat.jar')
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/app/jniLibs/armeabi-v7a/libdatabase_sqlcipher.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/jniLibs/armeabi-v7a/libdatabase_sqlcipher.so
--------------------------------------------------------------------------------
/app/jniLibs/armeabi-v7a/libsqlcipher_android.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/jniLibs/armeabi-v7a/libsqlcipher_android.so
--------------------------------------------------------------------------------
/app/jniLibs/armeabi-v7a/libstlport_shared.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/jniLibs/armeabi-v7a/libstlport_shared.so
--------------------------------------------------------------------------------
/app/jniLibs/armeabi/libdatabase_sqlcipher.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/jniLibs/armeabi/libdatabase_sqlcipher.so
--------------------------------------------------------------------------------
/app/jniLibs/armeabi/libsqlcipher_android.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/jniLibs/armeabi/libsqlcipher_android.so
--------------------------------------------------------------------------------
/app/jniLibs/armeabi/libstlport_shared.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/jniLibs/armeabi/libstlport_shared.so
--------------------------------------------------------------------------------
/app/jniLibs/sqlcipher-javadoc.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/jniLibs/sqlcipher-javadoc.jar
--------------------------------------------------------------------------------
/app/jniLibs/sqlcipher.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/jniLibs/sqlcipher.jar
--------------------------------------------------------------------------------
/app/libs/universal-image-loader-1.9.5.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/libs/universal-image-loader-1.9.5.jar
--------------------------------------------------------------------------------
/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 /Users/chiontang/Library/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/moe/chionlab/wechatmomentstat/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
30 |
31 |
35 |
38 |
39 |
42 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/assets/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/assets/1.jpg
--------------------------------------------------------------------------------
/app/src/main/assets/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/assets/1.png
--------------------------------------------------------------------------------
/app/src/main/assets/1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/assets/1.webp
--------------------------------------------------------------------------------
/app/src/main/assets/1.wxpc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/assets/1.wxpc
--------------------------------------------------------------------------------
/app/src/main/assets/1wx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/assets/1wx.jpg
--------------------------------------------------------------------------------
/app/src/main/assets/1wx.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/assets/1wx.webp
--------------------------------------------------------------------------------
/app/src/main/assets/2.wxpc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/assets/2.wxpc
--------------------------------------------------------------------------------
/app/src/main/assets/icudt46l.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/assets/icudt46l.zip
--------------------------------------------------------------------------------
/app/src/main/assets/libwechat.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/assets/libwechat.zip
--------------------------------------------------------------------------------
/app/src/main/assets/wechat.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/assets/wechat.apk
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/app/EnApplication.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.app;
2 |
3 | import android.app.Application;
4 |
5 | /**
6 | * EnApplication
7 | */
8 | public class EnApplication extends Application {
9 |
10 |
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/app/PathConfig.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.app;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | *
7 | */
8 | public class PathConfig {
9 |
10 | /**
11 | * 微信配置文件保存路径
12 | *
13 | * @param context
14 | * @return
15 | */
16 | public static String getMMSharedPerfsPath(Context context) {
17 | return context.getFilesDir() + "/system_config_prefs.xml";
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/base/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.base;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | /**
7 | * BaseActivity
8 | */
9 | public class BaseActivity extends AppCompatActivity {
10 |
11 | @Override
12 | protected void onCreate(Bundle savedInstanceState) {
13 | super.onCreate(savedInstanceState);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/common/db/DBUtill.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.common.db;
2 |
3 | import android.content.Context;
4 |
5 | import net.sqlcipher.database.SQLiteDatabase;
6 |
7 | import java.io.File;
8 |
9 | /**
10 | * 数据库工具类
11 | */
12 | public class DBUtill {
13 |
14 | public static void init(Context context) {
15 | SQLiteDatabase.loadLibs(context);
16 | File file = new File(context.getFilesDir().getPath() + "EnMicroMsg0.db");
17 |
18 | }
19 |
20 | public static void test() {
21 |
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/common/util/DeviceUtil.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.common.util;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageInfo;
5 | import android.content.pm.PackageManager;
6 | import android.telephony.TelephonyManager;
7 |
8 | /**
9 | * 设备相关工具类
10 | */
11 | public class DeviceUtil {
12 |
13 | /**
14 | * 获取设备ID(the IMEI for GSM and the MEID or ESN for CDMA phones)
15 | */
16 | public static String getDeviceId(Context context) {
17 | TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
18 | return tm.getDeviceId();
19 | }
20 |
21 | /**
22 | * 检测应用是否安装
23 | */
24 | public static boolean isAppInstalled(Context context, String packageName) {
25 | PackageManager pm = context.getPackageManager();
26 | try {
27 | pm.getPackageInfo(packageName, 0);
28 | return true;
29 | } catch (PackageManager.NameNotFoundException e) {
30 | return false;
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/common/util/MD5Util.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.common.util;
2 |
3 | import java.security.MessageDigest;
4 | import java.security.NoSuchAlgorithmException;
5 |
6 | /**
7 | * MD5工具
8 | */
9 | public class MD5Util {
10 |
11 | /**
12 | * http://stackoverflow.com/questions/3934331/android-how-to-encrypt-a-string
13 | *
14 | * @param s
15 | * @return
16 | */
17 | public static String md5(String s) {
18 | try {
19 | // Create MD5 Hash
20 | MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
21 | digest.update(s.getBytes());
22 | byte messageDigest[] = digest.digest();
23 |
24 | // Create Hex String
25 | StringBuffer hexString = new StringBuffer();
26 | for (int i = 0; i < messageDigest.length; i++)
27 | hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
28 | return hexString.toString();
29 |
30 | } catch (NoSuchAlgorithmException e) {
31 | e.printStackTrace();
32 | }
33 | return "";
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/common/util/RootUtil.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.common.util;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.DataOutputStream;
5 | import java.io.File;
6 | import java.io.InputStream;
7 | import java.io.InputStreamReader;
8 | import java.io.OutputStream;
9 | import java.util.ArrayList;
10 |
11 | /**
12 | * Root工具类
13 | */
14 | public class RootUtil {
15 |
16 | /**
17 | * @author Kevin Kowalewski
18 | */
19 | public static boolean isDeviceRooted() {
20 | return checkRootMethod1() || checkRootMethod2() || checkRootMethod3();
21 | }
22 |
23 | private static boolean checkRootMethod1() {
24 | String buildTags = android.os.Build.TAGS;
25 | return buildTags != null && buildTags.contains("test-keys");
26 | }
27 |
28 | private static boolean checkRootMethod2() {
29 | String[] paths = {"/system/app/Superuser.apk", "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su",
30 | "/system/bin/failsafe/su", "/data/local/su"};
31 | for (String path : paths) {
32 | if (new File(path).exists()) return true;
33 | }
34 | return false;
35 | }
36 |
37 | private static boolean checkRootMethod3() {
38 | Process process = null;
39 | try {
40 | process = Runtime.getRuntime().exec(new String[]{"/system/xbin/which", "su"});
41 | BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
42 | if (in.readLine() != null) return true;
43 | return false;
44 | } catch (Throwable t) {
45 | return false;
46 | } finally {
47 | if (process != null) process.destroy();
48 | }
49 | }
50 |
51 |
52 | /**
53 | * http://zhidao.baidu.com/link?url=bubdHjv4fu5QtazHH6j1HakrR0hHkg
54 | * 4ZllEp2wmoKlBdecKtN7JK2YjYa8fq9KvSBZA4_ID9VvzWedi_jkW5N7J7ZaeyTJgnwl1dIDOo0z7
55 | */
56 | public static synchronized boolean isGrantRootPermission() {
57 | Process process = null;
58 | DataOutputStream os = null;
59 | try {
60 | process = Runtime.getRuntime().exec("su");
61 | os = new DataOutputStream(process.getOutputStream());
62 | os.writeBytes("exit\n");
63 | os.flush();
64 | int exitValue = process.waitFor();
65 | if (exitValue == 0) {
66 | return true;
67 | } else {
68 | return false;
69 | }
70 | } catch (Exception e) {
71 | return false;
72 | } finally {
73 | try {
74 | if (os != null) {
75 | os.close();
76 | }
77 | process.destroy();
78 | } catch (Exception e) {
79 | e.printStackTrace();
80 | }
81 | }
82 | }
83 |
84 |
85 | public static void execCmds(String[] cmds) {
86 | try {
87 | Process process = Runtime.getRuntime().exec("su");
88 | OutputStream os = process.getOutputStream();
89 | process.getErrorStream();
90 | int i = cmds.length;
91 | for (int j = 0; j < i; j++) {
92 | String str = cmds[j];
93 | os.write((str + "\n").getBytes());
94 | }
95 | os.write("exit\n".getBytes());
96 | os.flush();
97 | os.close();
98 | process.waitFor();
99 | process.destroy();
100 | } catch (Exception localException) {
101 | }
102 | }
103 |
104 | public static ArrayList execCmdsforResult(String[] cmds) {
105 | ArrayList list = new ArrayList();
106 | try {
107 | Process process = Runtime.getRuntime().exec("su");
108 | OutputStream os = process.getOutputStream();
109 | process.getErrorStream();
110 | InputStream is = process.getInputStream();
111 | int i = cmds.length;
112 | for (int j = 0; j < i; j++) {
113 | String str = cmds[j];
114 | os.write((str + "\n").getBytes());
115 | }
116 | os.write("exit\n".getBytes());
117 | os.flush();
118 | os.close();
119 | BufferedReader reader = new BufferedReader(new InputStreamReader(is));
120 | while (true) {
121 | String str = reader.readLine();
122 | if (str == null)
123 | break;
124 | list.add(str);
125 | }
126 | reader.close();
127 | process.waitFor();
128 | process.destroy();
129 | return list;
130 | } catch (Exception localException) {
131 | }
132 | return list;
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/common/util/SharedPerfUtil.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.common.util;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | //import cn.truistic.enmicromsg.R;
7 | import cn.truistic.enmicromsg.main.MainMVP;
8 |
9 | /**
10 | * SharedPerferences工具类
11 | */
12 | public class SharedPerfUtil {
13 |
14 | // 是否第一次启动应用
15 | public static void saveIsFirstStart(Context context, boolean isFirstStart) {
16 | SharedPreferences.Editor editor = getAppSharedPerf(context).edit();
17 | editor.putBoolean("isFirstStart", isFirstStart);
18 | editor.commit();
19 | }
20 |
21 | // 是否第一次启动应用
22 | public static boolean getIsFirstStart(Context context) {
23 | return getAppSharedPerf(context).getBoolean("isFirstStart", true);
24 | }
25 |
26 | // 获取状态
27 | public static int getState(Context context, MainMVP.IHomeView.Progress progress) {
28 | SharedPreferences sp = getAppSharedPerf(context);
29 | return sp.getInt(progress.name(), 0);
30 | }
31 |
32 | // 保存状态
33 | public static void saveProgressState(Context context, MainMVP.IHomeView.Progress progress, int state) {
34 | SharedPreferences.Editor editor = getAppSharedPerf(context).edit();
35 | editor.putInt(progress.name(), state);
36 | editor.commit();
37 | }
38 |
39 | // 获取微信数据库(账号)数量
40 | public static int getDbNum(Context context) {
41 | return getDataSharedPerf(context).getInt("dbNum", 0);
42 | }
43 |
44 | // 保存微信数据库(账号)数量
45 | public static void saveDbNum(Context context, int num) {
46 | SharedPreferences.Editor editor = getDataSharedPerf(context).edit();
47 | editor.putInt("dbNum", num);
48 | editor.commit();
49 | }
50 |
51 | // 获取数据库密码
52 | public static String getDbPwd(Context context) {
53 | return getDataSharedPerf(context).getString("dbPwd", null);
54 | }
55 |
56 | // 保存数据库密码
57 | public static void savedbPwd(Context context, String pwd) {
58 | SharedPreferences.Editor editor = getDataSharedPerf(context).edit();
59 | editor.putString("dbPwd", pwd);
60 | editor.commit();
61 | }
62 |
63 | // 获取uin
64 | public static int getUin(Context context) {
65 | SharedPreferences sp = context.getSharedPreferences("system_config_prefs", Context.MODE_PRIVATE);
66 | return sp.getInt("default_uin", 0);
67 | }
68 |
69 | private static SharedPreferences getAppSharedPerf(Context context) {
70 | return context.getSharedPreferences("shared_perf_app", Context.MODE_PRIVATE);
71 | }
72 |
73 | private static SharedPreferences getDataSharedPerf(Context context) {
74 | return context.getSharedPreferences("shared_perf_app", Context.MODE_PRIVATE);
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/main/MainMVP.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.main;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * MainMVP接口
7 | */
8 | public interface MainMVP {
9 |
10 | interface IMainView {
11 |
12 | }
13 |
14 | interface IHomeView {
15 |
16 | enum Progress {DETECT_WECHAT, DETECT_ROOT, DETECT_PERMISSION, REQUEST_DATA, ANALYSIS_DATA}
17 |
18 | enum State {UNDETECTED, DETECTING, TRUE, FALSE}
19 | // void onOperateStart();
20 |
21 |
22 | void onDetectStop();
23 |
24 | // void showCancelDialog();
25 | //
26 | // void showSuccessDialog(String msg);
27 | //
28 | // void showFailureDialog(String msg);
29 |
30 | void setProgressState(Progress progress, State state);
31 | }
32 |
33 | interface IGroupsView {
34 |
35 | }
36 |
37 | interface IExportView {
38 |
39 | }
40 |
41 | interface IMainPresenter {
42 |
43 | }
44 |
45 | interface IHomePresenter {
46 | void detect();
47 |
48 | // boolean checkWechat();
49 | //
50 | // boolean checkRoot();
51 | //
52 | // boolean checkRootPermission();
53 | //
54 | // boolean requestData();
55 | //
56 | // boolean analysisData();
57 |
58 | // void updateProgressState(int progress, int State);
59 | }
60 |
61 | interface IGroupsPresenter {
62 |
63 | }
64 |
65 | interface IExportPresenter {
66 |
67 | }
68 |
69 | interface IHomeModel {
70 | IHomeView.State getState(IHomeView.Progress progress);
71 |
72 | void saveState(IHomeView.Progress progress, IHomeView.State state);
73 |
74 | int getDbNum();
75 |
76 | void saveDbNum(int num);
77 |
78 | String getDbPwd();
79 |
80 | void saveDbPwd(String pwd);
81 | }
82 |
83 |
84 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/main/model/HomeModel.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.main.model;
2 |
3 | import android.content.Context;
4 |
5 | import cn.truistic.enmicromsg.common.util.SharedPerfUtil;
6 | import cn.truistic.enmicromsg.main.MainMVP;
7 |
8 | /**
9 | * HomeModel
10 | */
11 | public class HomeModel implements MainMVP.IHomeModel {
12 |
13 | private Context context;
14 | private MainMVP.IHomePresenter homePresenter;
15 |
16 | public HomeModel(MainMVP.IHomePresenter homePresenter, Context context) {
17 | this.homePresenter = homePresenter;
18 | this.context = context;
19 | }
20 |
21 | @Override
22 | public MainMVP.IHomeView.State getState(MainMVP.IHomeView.Progress progress) {
23 | switch (SharedPerfUtil.getState(context, progress)) {
24 | case 0:
25 | return MainMVP.IHomeView.State.UNDETECTED;
26 | case 1:
27 | return MainMVP.IHomeView.State.DETECTING;
28 | case 2:
29 | return MainMVP.IHomeView.State.TRUE;
30 | case 3:
31 | return MainMVP.IHomeView.State.FALSE;
32 | }
33 | return MainMVP.IHomeView.State.UNDETECTED;
34 | }
35 |
36 | @Override
37 | public void saveState(MainMVP.IHomeView.Progress progress, MainMVP.IHomeView.State state) {
38 | int stateInt = 0;
39 | switch (state) {
40 | case UNDETECTED:
41 | stateInt = 0;
42 | break;
43 | case DETECTING:
44 | stateInt = 1;
45 | break;
46 | case TRUE:
47 | stateInt = 2;
48 | break;
49 | case FALSE:
50 | stateInt = 3;
51 | break;
52 | }
53 | SharedPerfUtil.saveProgressState(context, progress, stateInt);
54 | }
55 |
56 | @Override
57 | public int getDbNum() {
58 | return SharedPerfUtil.getDbNum(context);
59 | }
60 |
61 | @Override
62 | public void saveDbNum(int num) {
63 | SharedPerfUtil.saveDbNum(context, num);
64 | }
65 |
66 | @Override
67 | public String getDbPwd() {
68 | return SharedPerfUtil.getDbPwd(context);
69 | }
70 |
71 | @Override
72 | public void saveDbPwd(String pwd) {
73 | SharedPerfUtil.savedbPwd(context, pwd);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/main/model/MainModel.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.main.model;
2 |
3 | /**
4 | * Created by truis on 2016/3/16.
5 | */
6 | public class MainModel {
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/main/presenter/HomePresenter.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.main.presenter;
2 |
3 | import android.content.Context;
4 | import android.os.AsyncTask;
5 | import android.util.Log;
6 |
7 | import net.sqlcipher.Cursor;
8 | import net.sqlcipher.database.SQLiteDatabase;
9 | import net.sqlcipher.database.SQLiteDatabaseHook;
10 |
11 | import java.io.File;
12 | import java.io.FileOutputStream;
13 | import java.util.ArrayList;
14 |
15 | import cn.truistic.enmicromsg.common.util.DeviceUtil;
16 | import cn.truistic.enmicromsg.common.util.MD5Util;
17 | import cn.truistic.enmicromsg.common.util.RootUtil;
18 | import cn.truistic.enmicromsg.common.util.SharedPerfUtil;
19 | import cn.truistic.enmicromsg.main.MainMVP;
20 | import cn.truistic.enmicromsg.main.model.HomeModel;
21 |
22 | import android.os.Environment;
23 |
24 | /**
25 | * HomePresenter
26 | */
27 | public class HomePresenter implements MainMVP.IHomePresenter {
28 |
29 | private Context context;
30 | private MainMVP.IHomeView homeView;
31 | private MainMVP.IHomeModel homeModel;
32 |
33 | public HomePresenter(Context context, MainMVP.IHomeView homeView) {
34 | this.context = context;
35 | this.homeView = homeView;
36 | homeModel = new HomeModel(this, context);
37 | }
38 |
39 | @Override
40 | public void detect() {
41 | new DetectTask().execute();
42 | }
43 |
44 | /**
45 | * 检测操作
46 | */
47 | private class DetectTask extends AsyncTask {
48 | @Override
49 | protected Object doInBackground(Object[] params) {
50 | boolean flag = true;
51 | while (flag) {
52 | // 1.检测微信是否已经安装
53 | publishProgress(MainMVP.IHomeView.Progress.DETECT_WECHAT, MainMVP.IHomeView.State.DETECTING);
54 | if (!detectWechat()) {
55 | publishProgress(MainMVP.IHomeView.Progress.DETECT_WECHAT, MainMVP.IHomeView.State.FALSE);
56 | homeModel.saveState(MainMVP.IHomeView.Progress.DETECT_WECHAT, MainMVP.IHomeView.State.FALSE);
57 | break;
58 | }
59 | publishProgress(MainMVP.IHomeView.Progress.DETECT_WECHAT, MainMVP.IHomeView.State.TRUE);
60 | homeModel.saveState(MainMVP.IHomeView.Progress.DETECT_WECHAT, MainMVP.IHomeView.State.TRUE);
61 | // 2.检测设备是否已Root
62 | publishProgress(MainMVP.IHomeView.Progress.DETECT_ROOT, MainMVP.IHomeView.State.DETECTING);
63 | if (!detectRoot()) {
64 | publishProgress(MainMVP.IHomeView.Progress.DETECT_ROOT, MainMVP.IHomeView.State.FALSE);
65 | homeModel.saveState(MainMVP.IHomeView.Progress.DETECT_ROOT, MainMVP.IHomeView.State.FALSE);
66 | break;
67 | }
68 | publishProgress(MainMVP.IHomeView.Progress.DETECT_ROOT, MainMVP.IHomeView.State.TRUE);
69 | homeModel.saveState(MainMVP.IHomeView.Progress.DETECT_ROOT, MainMVP.IHomeView.State.TRUE);
70 | // 3.检测是否已授权应用Root权限
71 | publishProgress(MainMVP.IHomeView.Progress.DETECT_PERMISSION, MainMVP.IHomeView.State.DETECTING);
72 | if (!detectPermission()) {
73 | publishProgress(MainMVP.IHomeView.Progress.DETECT_PERMISSION, MainMVP.IHomeView.State.FALSE);
74 | homeModel.saveState(MainMVP.IHomeView.Progress.DETECT_PERMISSION, MainMVP.IHomeView.State.FALSE);
75 | break;
76 | }
77 | publishProgress(MainMVP.IHomeView.Progress.DETECT_PERMISSION, MainMVP.IHomeView.State.TRUE);
78 | homeModel.saveState(MainMVP.IHomeView.Progress.DETECT_PERMISSION, MainMVP.IHomeView.State.TRUE);
79 | // 4.获取微信相关数据
80 | publishProgress(MainMVP.IHomeView.Progress.REQUEST_DATA, MainMVP.IHomeView.State.DETECTING);
81 | if (!requestData()) {
82 | publishProgress(MainMVP.IHomeView.Progress.REQUEST_DATA, MainMVP.IHomeView.State.FALSE);
83 | homeModel.saveState(MainMVP.IHomeView.Progress.REQUEST_DATA, MainMVP.IHomeView.State.FALSE);
84 | break;
85 | }
86 | publishProgress(MainMVP.IHomeView.Progress.REQUEST_DATA, MainMVP.IHomeView.State.TRUE);
87 | homeModel.saveState(MainMVP.IHomeView.Progress.REQUEST_DATA, MainMVP.IHomeView.State.TRUE);
88 | // 5.解析微信相关数据
89 | publishProgress(MainMVP.IHomeView.Progress.ANALYSIS_DATA, MainMVP.IHomeView.State.DETECTING);
90 | if (!analysisData()) {
91 | publishProgress(MainMVP.IHomeView.Progress.ANALYSIS_DATA, MainMVP.IHomeView.State.FALSE);
92 | homeModel.saveState(MainMVP.IHomeView.Progress.ANALYSIS_DATA, MainMVP.IHomeView.State.FALSE);
93 | break;
94 | }
95 | publishProgress(MainMVP.IHomeView.Progress.ANALYSIS_DATA, MainMVP.IHomeView.State.TRUE);
96 | homeModel.saveState(MainMVP.IHomeView.Progress.ANALYSIS_DATA, MainMVP.IHomeView.State.TRUE);
97 | flag = false;
98 | }
99 | return null;
100 | }
101 |
102 | @Override
103 | protected void onProgressUpdate(Object[] values) {
104 | homeView.setProgressState((MainMVP.IHomeView.Progress) values[0], (MainMVP.IHomeView.State) values[1]);
105 | }
106 |
107 | @Override
108 | protected void onPostExecute(Object o) {
109 | homeView.onDetectStop();
110 | }
111 | }
112 |
113 | /**
114 | * 检测微信是否已经安装
115 | *
116 | * @return true,微信已安装
117 | */
118 | private boolean detectWechat() {
119 | return DeviceUtil.isAppInstalled(context, "com.tencent.mm");
120 | }
121 |
122 | /**
123 | * 检测设备是否已Root
124 | *
125 | * @return true, 设备已Root
126 | */
127 | private boolean detectRoot() {
128 | return RootUtil.isDeviceRooted();
129 | }
130 |
131 | /**
132 | * 检测是否已授权应用Root权限
133 | *
134 | * @return true, 已授权
135 | */
136 | private boolean detectPermission() {
137 | return RootUtil.isGrantRootPermission();
138 | }
139 |
140 | /**
141 | * 获取微信数据
142 | *
143 | * @return true, 获取成功
144 | */
145 | private boolean requestData() {
146 | // 1.获取配置文件,用于获取uin
147 | String sharedPerfsPath = "/data/data/cn.truistic.enmicromsg/shared_prefs/system_config_prefs.xml";
148 | RootUtil.execCmds(new String[]{"cp /data/data/com.tencent.mm/shared_prefs/system_config_prefs.xml "
149 | + sharedPerfsPath, "chmod 777 " + sharedPerfsPath});
150 | File sharedPerfsFile = new File(sharedPerfsPath);
151 | if (!sharedPerfsFile.exists()) {
152 | return false;
153 | }
154 | // 2.获取数据库文件
155 | ArrayList list = new ArrayList<>();
156 | list = RootUtil.execCmdsforResult(new String[]{"cd /data/data/com.tencent.mm/MicroMsg", "ls -R"});
157 | ArrayList dirs = new ArrayList<>();
158 | String dir = null;
159 | String item = null;
160 | for (int i = 0; i < list.size(); i++) {
161 | item = list.get(i);
162 | if (item.startsWith("./") && item.length() == 35) {
163 | dir = item;
164 | } else if (item.equals("EnMicroMsg.db")) {
165 | dirs.add(dir.substring(2, 34));
166 | }
167 | }
168 | if (dirs.size() == 0) {
169 | return false;
170 | } else {
171 | for (int i = 0; i < dirs.size(); i++) {
172 | RootUtil.execCmds(new String[]{"cp /data/data/com.tencent.mm/MicroMsg/" + dirs.get(i)
173 | + "/EnMicroMsg.db " + context.getFilesDir() + "/EnMicroMsg" + i + ".db",
174 | "chmod 777 " + context.getFilesDir() + "/EnMicroMsg" + i + ".db"});
175 | }
176 | }
177 | File dbFile;
178 | int i, j = 0;
179 | for (i = 0; i < dirs.size(); i++) {
180 | dbFile = new File(context.getFilesDir() + "/EnMicroMsg" + i + ".db");
181 | if (!dbFile.exists()) {
182 | break;
183 | }
184 | j++;
185 | }
186 | if (j == 0)
187 | return false;
188 | homeModel.saveDbNum(j);
189 | return true;
190 | }
191 |
192 | /**
193 | * 解析微信相关数据
194 | *
195 | * @return
196 | */
197 | private boolean analysisData() {
198 | // 1.计算数据库密码
199 | String uinStr = String.valueOf(SharedPerfUtil.getUin(context));
200 | String dbPwd = MD5Util.md5(DeviceUtil.getDeviceId(context) + uinStr).substring(0, 7);
201 | if (dbPwd == null)
202 | return false;
203 | homeModel.saveDbPwd(dbPwd);
204 |
205 | // 打开数据库
206 | SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
207 | public void preKey(SQLiteDatabase database) {
208 | }
209 |
210 | public void postKey(SQLiteDatabase database) {
211 | database.rawExecSQL("PRAGMA cipher_migrate;"); //最关键的一句!!!
212 | }
213 | };
214 |
215 | int num = homeModel.getDbNum();
216 | int j = 0;
217 | File dbFile;
218 | SQLiteDatabase database = null;
219 | for (int i = 0; i < num; i++) {
220 | dbFile = new File(context.getFilesDir() + "/EnMicroMsg" + i + ".db");
221 | try {
222 | database = SQLiteDatabase.openOrCreateDatabase(dbFile, dbPwd, null, hook);
223 | break;
224 | } catch (Exception e) {
225 | j++;
226 | }
227 | }
228 | if (j == num) {
229 | return false;
230 | }
231 | String dir= Environment.getExternalStorageDirectory() + "/Weixin";
232 | File extDir = new File(dir);
233 | if (!extDir.exists()) {
234 | extDir.mkdir();
235 | }
236 | File textfile = new File(dir+"/xiaoxi.txt");
237 | if (textfile.exists())
238 | textfile.delete();
239 | try {
240 | Cursor c = database.query("message", null, null, null, null, null, null);
241 |
242 | textfile.createNewFile();
243 | //assetInputStream = context.getAssets().open("wechat.apk");
244 | FileOutputStream outAPKStream = new FileOutputStream(textfile);
245 | while (c.moveToNext()) {
246 | int _id = c.getInt(c.getColumnIndex("msgId"));
247 | String name = c.getString(c.getColumnIndex("content"));
248 | Log.i("db", "_id=>" + _id + ", content=>" + name);
249 | String text ="_id=>" + _id + ", content=>" + name;
250 | byte[] buf = text.getBytes();// byte[1024];
251 | //buf.(text);
252 | //int read;
253 | outAPKStream.write(buf, 0, buf.length);
254 | }
255 | //assetInputStream.close();
256 | outAPKStream.close();
257 | c.close();
258 | database.close();
259 | } catch (Exception e) {
260 | Log.d("DB", "Exception");
261 | }
262 | return true;
263 | }
264 |
265 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/truistic/enmicromsg/main/presenter/MainPresenter.java:
--------------------------------------------------------------------------------
1 | package cn.truistic.enmicromsg.main.presenter;
2 |
3 | /**
4 | * Created by truis on 2016/3/16.
5 | */
6 | public class MainPresenter {
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/moe/chionlab/wechatmomentstat/Config.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat;
2 |
3 | import android.os.Environment;
4 | import android.content.Context;
5 | /**
6 | * Created by chiontang on 2/4/16.
7 | */
8 | public class Config {
9 |
10 | static boolean ready = false;
11 |
12 | final static public String EXT_DIR = Environment.getExternalStorageDirectory() + "/WeChatMomentStat";
13 | final static public String DATA_DIR = Environment.getDataDirectory() + "/data/moe.chionlab.wechatmomentstat";
14 |
15 | final static public String WECHAT_PACKAGE = "com.tencent.mm";
16 | //r4488992
17 | final static public String[] VERSIONS = {"6.3.13.49_r4080b63", "6.5.13"};
18 | //6513 bey at classes ,
19 | final static public String[] PROTOCAL_SNS_DETAIL_CLASSES = {"com.tencent.mm.protocal.b.atp", "com.tencent.mm.protocal.c.bey"};
20 |
21 | final static public String[] PROTOCAL_SNS_DETAIL_METHODS = {"a", "a"};
22 | final static public String[] SNS_XML_GENERATOR_CLASSES = {"com.tencent.mm.plugin.sns.f.i", "com.tencent.mm.plugin.sns.g.j"};
23 | final static public String[] SNS_XML_GENERATOR_METHODS = {"a", "a"};
24 | //6513 bbg at classes ,
25 | final static public String[] PROTOCAL_SNS_OBJECT_CLASSES = {"com.tencent.mm.protocal.b.aqi", "com.tencent.mm.protocal.c.bbg"};
26 | final static public String[] PROTOCAL_SNS_OBJECT_METHODS = {"a", "a"};
27 | final static public String[] PROTOCAL_SNS_OBJECT_USERID_FIELDS = {"iYA", "tfa"};
28 | final static public String[] PROTOCAL_SNS_OBJECT_NICKNAME_FIELDS = {"jyd", "tLN"};
29 | final static public String[] PROTOCAL_SNS_OBJECT_TIMESTAMP_FIELDS = {"fpL", "ofk"};
30 | final static public String[] PROTOCAL_SNS_OBJECT_COMMENTS_FIELDS = {"jJX", "uaE"};
31 | final static public String[] PROTOCAL_SNS_OBJECT_LIKES_FIELDS = {"jJU", "uaB"};
32 | final static public String[] PROTOCAL_SNS_OBJECT_LinkedList_ale0={"","udP"};
33 | final static public String[] PROTOCAL_SNS_OBJECT_LinkedList_ale={"","trg"};
34 | final static public String[] SNS_OBJECT_EXT_AUTHOR_NAME_FIELDS = {"jyd", "tLN"};
35 |
36 | //found in 6313 apz.java,6513 baw.java
37 | final static public String[] SNS_OBJECT_EXT_REPLY_TO_FIELDS = {"jJM", "uas"};
38 |
39 | final static public String[] SNS_OBJECT_EXT_COMMENT_FIELDS = {"fsI", "oog"};
40 | final static public String[] SNS_OBJECT_EXT_AUTHOR_ID_FIELDS = {"iYA", "tfa"};
41 | //not used
42 | final static public String[] SNS_DETAIL_FROM_BIN_METHODS = {"am", "aD"};
43 | //not used
44 | final static public String[] SNS_OBJECT_FROM_BIN_METHODS = {"am", "aD"};
45 |
46 | static public Context Context;
47 |
48 | static public String PROTOCAL_SNS_DETAIL_CLASS;
49 | static public String PROTOCAL_SNS_DETAIL_METHOD;
50 | static public String SNS_XML_GENERATOR_CLASS;
51 | static public String SNS_XML_GENERATOR_METHOD;
52 | static public String PROTOCAL_SNS_OBJECT_CLASS;
53 | static public String PROTOCAL_SNS_OBJECT_METHOD;
54 | static public String PROTOCAL_SNS_OBJECT_USERID_FIELD;
55 | static public String PROTOCAL_SNS_OBJECT_NICKNAME_FIELD;
56 | static public String PROTOCAL_SNS_OBJECT_TIMESTAMP_FIELD;
57 | static public String PROTOCAL_SNS_OBJECT_COMMENTS_FIELD;
58 | static public String PROTOCAL_SNS_OBJECT_LIKES_FIELD;
59 | static public String PROTOCAL_SNS_OBJECT_LinkedList_ale0_FIELD;
60 | static public String PROTOCAL_SNS_OBJECT_LinkedList_ale_FIELD;
61 | static public String SNS_OBJECT_EXT_AUTHOR_NAME_FIELD;
62 | static public String SNS_OBJECT_EXT_REPLY_TO_FIELD;
63 | static public String SNS_OBJECT_EXT_COMMENT_FIELD;
64 | static public String SNS_OBJECT_EXT_AUTHOR_ID_FIELD;
65 | static public String SNS_DETAIL_FROM_BIN_METHOD;
66 | static public String SNS_OBJECT_FROM_BIN_METHOD;
67 |
68 | public static String username="";
69 | public static String dbgmsg="";
70 | public static String snsListJSONS="";
71 | public static boolean start_post = false;
72 |
73 | public static int readline=0;
74 | public static String currentUserId="";
75 | public static String filename="";
76 |
77 | static public void initWeChatVersion(String version) {
78 | for (int i=0;i likes = new ArrayList();
17 | public ArrayList likeme = new ArrayList();
18 | public ArrayList comments = new ArrayList();
19 | public ArrayList mediaList = new ArrayList();
20 | public String rawXML = "";
21 | public long timestamp = 0;
22 | public boolean ready = false;
23 | public boolean isCurrentUser = false;
24 | public boolean selected = true;
25 |
26 | public void print() {
27 | Log.d("wechatmomentstat", "================================");
28 | Log.d("wechatmomentstat", "id: " + this.id);
29 | Log.d("wechatmomentstat", "Author: " + this.authorName);
30 | Log.d("wechatmomentstat", "Content: " + this.content);
31 | Log.d("wechatmomentstat", "Likes:");
32 | for (int i=0; i(this.likes);
58 | newSns.likeme = new ArrayList(this.likeme);
59 | newSns.comments = new ArrayList(this.comments);
60 | newSns.mediaList = new ArrayList(this.mediaList);
61 | newSns.rawXML = this.rawXML;
62 | newSns.timestamp = this.timestamp;
63 | return newSns;
64 | }
65 |
66 | public void clear() {
67 | id = "";
68 | authorName = "";
69 | userName = "";
70 | content = "";
71 | authorId = "";
72 | likes.clear();
73 | likeme.clear();
74 | comments.clear();
75 | mediaList.clear();
76 | rawXML = "";
77 | }
78 |
79 | static public class Like {
80 | public String userName;
81 | public String userId;
82 | public boolean isCurrentUser = false;
83 | }
84 |
85 | static public class Comment {
86 | public String authorName;
87 | public String content;
88 | public String toUser;
89 | public String authorId;
90 | public String toUserId;
91 | public boolean isCurrentUser = false;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/moe/chionlab/wechatmomentstat/Model/UserSnsInfo.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat.Model;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * Created by chiontang on 3/21/16.
7 | */
8 | public class UserSnsInfo {
9 | public String userId;
10 | public String authorName;
11 | public String userName;
12 | public ArrayList snsList = new ArrayList();
13 | public int likeCount = 0;
14 | public int likedCount = 0;
15 | public int likemeCount=0;
16 | public int sentCommentCount = 0;
17 | public int receivedCommentCount = 0;
18 | public int repliedCommentCount = 0;
19 | public int photoNumbers = 0;
20 | public double heatRate = 0;
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/moe/chionlab/wechatmomentstat/Parser.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat;
2 |
3 | import android.graphics.BitmapFactory;
4 | import android.os.Environment;
5 | import android.util.Log;
6 |
7 | import java.io.File;
8 | import java.io.FileInputStream;
9 | import java.io.FileOutputStream;
10 | import java.io.InputStream;
11 | import java.io.OutputStream;
12 | import java.lang.reflect.Field;
13 | import java.lang.reflect.Method;
14 | import java.util.LinkedList;
15 | import java.util.regex.Matcher;
16 | import java.util.regex.Pattern;
17 | import android.graphics.Bitmap;
18 |
19 | import moe.chionlab.wechatmomentstat.Model.SnsInfo;
20 | /*
21 |
22 | class mmjpeg {
23 | public static native int convertToProgressive(String str, String str2);
24 |
25 | public static native boolean decodeToBitmap(String str, Bitmap bitmap);
26 |
27 | public static native int isProgressiveFile(String str);
28 |
29 | public static native JpegParams queryParams(String str);
30 |
31 | public static native int queryQuality(String str);
32 |
33 | mmjpeg() {
34 |
35 | }
36 | }
37 |
38 | class JpegParams {
39 | public int Depth;
40 | public int Height;
41 | public int Width;
42 | public int isProgressive;
43 |
44 | JpegParams() {
45 |
46 | }
47 | }
48 | */
49 |
50 | /**
51 | * Created by chiontang on 2/11/16.
52 | */
53 | public class Parser {
54 |
55 | protected Class SnsDetailParser = null;
56 | protected Class SnsDetail = null;
57 | protected Class SnsObject = null;
58 | protected Class ModelClass = null;
59 | protected Class JpegObject = null;
60 | protected Object ModelClass2= null;
61 | protected Class sfsObject = null;
62 | protected boolean inited = false;
63 | public long testlong=0;
64 |
65 | public Parser(Class SnsDetail, Class SnsDetailParser, Class SnsObject, Class ModelClass,
66 | Class JpegObject,Class sfsObject) {
67 | this.SnsDetailParser = SnsDetailParser;
68 | this.SnsDetail = SnsDetail;
69 | this.SnsObject = SnsObject;
70 | this.ModelClass = ModelClass;
71 | this.JpegObject = JpegObject;
72 | this.sfsObject = sfsObject;
73 | inited = false;
74 | //loadlib(new String().valueOf(testlong));
75 | }
76 | public static void loadlib(String str){
77 | //System.load(str);
78 | }
79 | public static void loadlib2(String str){
80 | System.loadLibrary(str);
81 | }
82 | /*
83 | public static Bitmap decodeAsBitmap(String str) {
84 |
85 | try {
86 | JpegParams queryParams = mmjpeg.queryParams(str);
87 | if (queryParams == null) {
88 | Log.d("wechatmomentstat", "can't query jpeg parames.");
89 |
90 | return null;
91 | }
92 | Bitmap createBitmap = Bitmap.createBitmap(queryParams.Width, queryParams.Height, Bitmap.Config.ARGB_8888);
93 | if (mmjpeg.decodeToBitmap(str, createBitmap)) {
94 | Log.d("wechatmomentstat", "decode bitmap successed.");
95 |
96 | return createBitmap;
97 | }
98 | Log.d("wechatmomentstat", "can't decode to bmp.");
99 |
100 | return null;
101 | } catch (Throwable e) {
102 | Log.d("wechatmomentstat", "decodeAsBitmap exception:%s");
103 | return null;
104 | }
105 | }
106 | */
107 | public SnsInfo parseSnsAllFromBin(byte[] snsDetailBin, byte[] snsObjectBin) throws Throwable {
108 | Object snsDetail = parseSnsDetailFromBin(snsDetailBin);
109 | Object snsObject = parseSnsObjectFromBin(snsObjectBin);
110 |
111 | SnsInfo snsInfo = parseSnsDetail(snsDetail);
112 | parseSnsObject(snsObject, snsInfo);
113 |
114 | return snsInfo;
115 | }
116 |
117 | public Object parseSnsDetailFromBin(byte[] bin) throws Throwable {
118 | Object snsDetail = SnsDetail.newInstance();
119 | //Method[] m = SnsDetail.getDeclaredMethods();
120 | //for(int i =0;i",Pattern.DOTALL);
147 | Pattern contentPattern = Pattern.compile("", Pattern.DOTALL);
148 | Pattern mediaPattern = Pattern.compile(".*?.*?",Pattern.DOTALL);
149 | Pattern mediaTokenPattern = Pattern.compile(".*?.*?.*?.*?.*?.*?",Pattern.DOTALL);
150 | Pattern mediaIdxPattern = Pattern.compile(".*?.*?");
151 | Pattern timestampPattern = Pattern.compile("");
152 |
153 | Matcher userIdMatcher = userIdPattern.matcher(xmlResult);
154 | Matcher contentMatcher = contentPattern.matcher(xmlResult);
155 | Matcher mediaMatcher = mediaPattern.matcher(xmlResult);
156 | Matcher mediaTokenMatcher = mediaTokenPattern.matcher(xmlResult);
157 | Matcher mediaIdxMatcher = mediaIdxPattern.matcher(xmlResult);
158 | Matcher timestampMatcher = timestampPattern.matcher(xmlResult);
159 |
160 | currentSns.id = getTimelineId(xmlResult);
161 |
162 | currentSns.rawXML = xmlResult;
163 | Log.d("wechatmomentstat",xmlResult);
164 |
165 | if (timestampMatcher.find()) {
166 | currentSns.timestamp = Integer.parseInt(timestampMatcher.group(1));
167 | }
168 |
169 | if (userIdMatcher.find()) {
170 | currentSns.authorId = userIdMatcher.group(1);
171 | }
172 |
173 | if (contentMatcher.find()) {
174 | currentSns.content = contentMatcher.group(1);
175 | }
176 |
177 | while (mediaMatcher.find()) {
178 | boolean flag = true;
179 | for (int i=0;i");
596 | Matcher idMatcher = idPattern.matcher(xmlResult);
597 | if (idMatcher.find()) {
598 | return idMatcher.group(1);
599 | } else {
600 | return "";
601 | }
602 | }
603 | }
604 |
--------------------------------------------------------------------------------
/app/src/main/java/moe/chionlab/wechatmomentstat/SnsReader.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat;
2 |
3 | import android.database.Cursor;
4 | import android.database.sqlite.SQLiteDatabase;
5 | import android.util.Log;
6 |
7 | import java.io.File;
8 | import java.util.ArrayList;
9 |
10 | import moe.chionlab.wechatmomentstat.Model.SnsInfo;
11 |
12 | /**
13 | * Created by chiontang on 2/12/16.
14 | */
15 | public class SnsReader {
16 |
17 | Class SnsDetail = null;
18 | Class SnsDetailParser = null;
19 | Class SnsObject = null;
20 | Class ModelObject = null;
21 | Class JpegObject = null;
22 | Class sfsObject=null;
23 | Parser parser = null;
24 | ArrayList snsList = new ArrayList();
25 | String currentUserId = "";
26 |
27 |
28 | public SnsReader(Class SnsDetail, Class SnsDetailParser, Class SnsObject,
29 | Class ModelObject, Class JpegObject ,Class sfsObject){
30 | this.SnsDetail = SnsDetail;
31 | this.SnsDetailParser = SnsDetailParser;
32 | this.SnsObject = SnsObject;
33 | this.ModelObject = ModelObject;
34 | this.JpegObject = JpegObject;
35 | this.sfsObject = sfsObject;
36 | this.parser = new Parser(SnsDetail, SnsDetailParser, SnsObject,ModelObject,JpegObject,sfsObject);
37 | }
38 |
39 | public void run() throws Throwable {
40 | Log.d("wechatmomentstat", "Querying Sns database.");
41 | queryDatabase();
42 | Task.saveToJSONFile(this.snsList, Config.EXT_DIR + "/all_sns.json", false);
43 | }
44 |
45 | public ArrayList getSnsList() {
46 | return this.snsList;
47 | }
48 |
49 | protected void queryDatabase() throws Throwable {
50 | String dbPath = Config.EXT_DIR + "/SnsMicroMsg.db";
51 | if (!new File(dbPath).exists()) {
52 | Log.e("wechatmomentstat", "DB file not found");
53 | throw new Exception("DB file not found");
54 | }
55 | snsList.clear();
56 | SQLiteDatabase database = SQLiteDatabase.openDatabase(dbPath, null, 0);
57 | getCurrentUserIdFromDatabase(database);
58 | Cursor cursor = database.query("SnsInfo", new String[]{"SnsId", "userName", "createTime", "content", "attrBuf"} ,"", new String[]{},"","","createTime DESC","");
59 |
60 | while (cursor.moveToNext()) {
61 | this.parser.inited = false;
62 | addSnsInfoFromCursor(cursor);
63 | }
64 |
65 | cursor.close();
66 | database.close();
67 | }
68 |
69 | protected void getCurrentUserIdFromDatabase(SQLiteDatabase database) throws Throwable {
70 | Cursor cursor = database.query("snsExtInfo3", new String[]{"userName"}, "ROWID=?", new String[]{"1"}, "", "", "", "1");
71 | if (cursor.moveToNext()) {
72 | this.currentUserId = cursor.getString(cursor.getColumnIndex("userName"));
73 | }
74 | cursor.close();
75 | Log.d("wechatmomentstat", "Current userID=" + this.currentUserId);
76 | if(this.currentUserId.length()>4)
77 | Config.currentUserId=this.currentUserId;
78 | }
79 |
80 | protected void addSnsInfoFromCursor(Cursor cursor) throws Throwable {
81 | byte[] snsDetailBin = cursor.getBlob(cursor.getColumnIndex("content"));
82 | byte[] snsObjectBin = cursor.getBlob(cursor.getColumnIndex("attrBuf"));
83 | SnsInfo newSns = parser.parseSnsAllFromBin(snsDetailBin, snsObjectBin);
84 | newSns.userName = cursor.getString(cursor.getColumnIndex("userName"));
85 | //Log.d("wechatmomentstat", "Current username =" + newSns.userName);
86 |
87 | for (int i=0;i snsList = null;
19 | public ArrayList userSnsList = new ArrayList();
20 | public ArrayList momentRank = null;
21 | public ArrayList likeRank = null;
22 | public ArrayList likemeRank = null;
23 | public ArrayList likedRank = null;
24 | public ArrayList sentCommentRank = null;
25 | public ArrayList receivedCommentRank = null;
26 | public ArrayList photoRank = null;
27 | public ArrayList heatRank = null;
28 | public ArrayList coldRank = null;
29 | public long earliestTimestamp = 0;
30 | public int userSnsList_i=0;
31 |
32 | public SnsStat(ArrayList snsList) {
33 | this.snsList = snsList;
34 | this.generateUserSnsList();
35 | this.generateRanks();
36 | }
37 |
38 | protected void generateUserSnsList() {
39 | for (int i=0;i(userSnsList);
99 | likeRank = new ArrayList(userSnsList);
100 | likemeRank = new ArrayList(userSnsList);
101 | likedRank = new ArrayList(userSnsList);
102 | sentCommentRank = new ArrayList(userSnsList);
103 | receivedCommentRank = new ArrayList(userSnsList);
104 | photoRank = new ArrayList(userSnsList);
105 | heatRank = new ArrayList(userSnsList);
106 | coldRank = new ArrayList();
107 | for (int i=0;i 0) {
110 | userSnsInfo.heatRate = ((double)userSnsInfo.repliedCommentCount)/((double)userSnsInfo.sentCommentCount);
111 | }
112 | if (userSnsInfo.sentCommentCount >= 15) {
113 | coldRank.add(userSnsInfo);
114 | }
115 | }
116 | Collections.sort(momentRank, new Comparator() {
117 | @Override
118 | public int compare(UserSnsInfo lhs, UserSnsInfo rhs) {
119 | return rhs.snsList.size() - lhs.snsList.size();
120 | }
121 | });
122 | Collections.sort(likeRank, new Comparator() {
123 | @Override
124 | public int compare(UserSnsInfo lhs, UserSnsInfo rhs) {
125 | return rhs.likeCount - lhs.likeCount;
126 | }
127 | });
128 | Collections.sort(likemeRank, new Comparator() {
129 | @Override
130 | public int compare(UserSnsInfo lhs, UserSnsInfo rhs) {
131 | return rhs.likemeCount - lhs.likemeCount;
132 | }
133 | });
134 | Collections.sort(likedRank, new Comparator() {
135 | @Override
136 | public int compare(UserSnsInfo lhs, UserSnsInfo rhs) {
137 | return rhs.likedCount - lhs.likedCount;
138 | }
139 | });
140 | Collections.sort(sentCommentRank, new Comparator() {
141 | @Override
142 | public int compare(UserSnsInfo lhs, UserSnsInfo rhs) {
143 | return rhs.sentCommentCount - lhs.sentCommentCount;
144 | }
145 | });
146 | Collections.sort(receivedCommentRank, new Comparator() {
147 | @Override
148 | public int compare(UserSnsInfo lhs, UserSnsInfo rhs) {
149 | return rhs.receivedCommentCount - lhs.receivedCommentCount;
150 | }
151 | });
152 | Collections.sort(photoRank, new Comparator() {
153 | @Override
154 | public int compare(UserSnsInfo lhs, UserSnsInfo rhs) {
155 | return rhs.photoNumbers - lhs.photoNumbers;
156 | }
157 | });
158 | Collections.sort(heatRank, new Comparator() {
159 | @Override
160 | public int compare(UserSnsInfo lhs, UserSnsInfo rhs) {
161 | if (rhs.heatRate - lhs.heatRate > 0) {
162 | return 1;
163 | } else if (rhs.heatRate - lhs.heatRate < 0) {
164 | return -1;
165 | } else {
166 | return 0;
167 | }
168 | }
169 | });
170 | Collections.sort(coldRank, new Comparator() {
171 | @Override
172 | public int compare(UserSnsInfo lhs, UserSnsInfo rhs) {
173 | if (lhs.heatRate - rhs.heatRate > 0) {
174 | return 1;
175 | } else if (lhs.heatRate - rhs.heatRate < 0) {
176 | return -1;
177 | } else {
178 | return 0;
179 | }
180 | }
181 | });
182 | }
183 |
184 | public UserSnsInfo getUserSnsInfo(String userId) {
185 | for (int i=0;i tweetList;
36 | ArrayList snsList;
37 | ArrayList snsList1;
38 | String oid;
39 |
40 | SubThread(ArrayList snsList,String oid) {
41 | this.snsList = snsList;
42 | this.oid = oid;
43 | }
44 | public int last_size=0;
45 |
46 |
47 | @Override
48 | public void run() {
49 | super.run();
50 | while (true) {
51 | try {
52 | Thread.sleep(1000);
53 | } catch (InterruptedException e) {
54 | e.printStackTrace();
55 | }
56 |
57 | if ( !Config.start_post) {
58 | return;
59 | }
60 |
61 | postTohost();
62 |
63 | try {
64 | Thread.sleep(2000);
65 | } catch (InterruptedException e) {
66 | e.printStackTrace();
67 | }
68 | }
69 | }
70 |
71 |
72 | private String getPostDataString(HashMap params) throws UnsupportedEncodingException {
73 | StringBuilder result = new StringBuilder();
74 | boolean first = true;
75 | for(Map.Entry entry : params.entrySet()){
76 | if (first)
77 | first = false;
78 | else
79 | result.append("&");
80 |
81 | result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
82 | result.append("=");
83 | result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
84 | }
85 |
86 | return result.toString();
87 | }
88 |
89 | public String performPostCall(String requestURL,
90 | HashMap postDataParams) {
91 |
92 | URL url;
93 | String response = "";
94 | try {
95 | url = new URL(requestURL);
96 |
97 | HttpURLConnection conn = (HttpURLConnection) url.openConnection();
98 | conn.setReadTimeout(25000);
99 | conn.setConnectTimeout(25000);
100 | conn.setRequestMethod("POST");
101 | conn.setDoInput(true);
102 | conn.setDoOutput(true);
103 |
104 |
105 | OutputStream os = conn.getOutputStream();
106 | BufferedWriter writer = new BufferedWriter(
107 | new OutputStreamWriter(os, "UTF-8"));
108 | writer.write(getPostDataString(postDataParams));
109 |
110 | writer.flush();
111 | writer.close();
112 | os.close();
113 | int responseCode=conn.getResponseCode();
114 |
115 | if (responseCode == HttpsURLConnection.HTTP_OK) {
116 | String line;
117 | BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream()));
118 | while ((line=br.readLine()) != null) {
119 | response+=line;
120 | }
121 | }
122 | else {
123 | response="";
124 |
125 | }
126 | } catch (Exception e) {
127 | e.printStackTrace();
128 | }
129 |
130 | return response;
131 | }
132 |
133 |
134 | private void postTohost() {
135 | JSONArray snsListJSON = new JSONArray();
136 | snsList = Share.snsData.snsList;
137 | for (int snsIndex=0; snsIndex3)//onlySelected )//&&
196 | {
197 | String hayoou_url="http://f.hayoou.com/timline/import_wechat.php?username1="+Config.username;
198 | HashMap meMap= new HashMap();
199 | meMap.put("username", Config.username);
200 | meMap.put("APK_version","2");
201 | meMap.put("jsondata", snsListJSON.toString());
202 |
203 | performPostCall(hayoou_url, meMap);
204 | }
205 |
206 | } catch (Exception e) {
207 | Log.e("wechatmomentstat", "exception", e);
208 | }
209 |
210 | Config.start_post =false;
211 |
212 |
213 |
214 | }
215 |
216 |
217 |
218 | }
219 |
--------------------------------------------------------------------------------
/app/src/main/java/moe/chionlab/wechatmomentstat/Task.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat;
2 |
3 | import android.app.ActivityManager;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.pm.PackageInfo;
7 | import android.content.pm.PackageManager;
8 | import android.content.res.AssetFileDescriptor;
9 | //import android.database.Cursor;
10 | //import android.database.sqlite.SQLiteDatabase;
11 | import android.os.Environment;
12 | import android.util.Log;
13 | import android.widget.EditText;
14 | import android.widget.Toast;
15 |
16 | import org.json.JSONArray;
17 | import org.json.JSONObject;
18 |
19 | import java.io.BufferedReader;
20 | import java.io.BufferedWriter;
21 | import java.io.DataInputStream;
22 | import java.io.DataOutputStream;
23 | import java.io.File;
24 | import java.io.FileInputStream;
25 | import java.io.FileOutputStream;
26 | import java.io.FileReader;
27 | import java.io.FileWriter;
28 | import java.io.IOException;
29 | import java.io.InputStream;
30 | import java.io.InputStreamReader;
31 | import java.io.OutputStream;
32 | import java.io.OutputStreamWriter;
33 | import java.io.UnsupportedEncodingException;
34 | import java.net.HttpURLConnection;
35 | import java.net.URL;
36 | import java.net.URLEncoder;
37 | import java.util.ArrayList;
38 | import java.util.HashMap;
39 | import java.util.List;
40 | import java.util.Map;
41 |
42 | import java.util.Enumeration;
43 | import dalvik.system.DexFile;
44 |
45 |
46 | import java.util.zip.GZIPOutputStream;
47 |
48 | import javax.net.ssl.HttpsURLConnection;
49 |
50 | import dalvik.system.DexClassLoader;
51 | import moe.chionlab.wechatmomentstat.Model.SnsInfo;
52 | import moe.chionlab.wechatmomentstat.SubThread;
53 |
54 | import cn.truistic.enmicromsg.common.util.DeviceUtil;
55 | import cn.truistic.enmicromsg.common.util.MD5Util;
56 | import cn.truistic.enmicromsg.common.util.RootUtil;
57 | import cn.truistic.enmicromsg.common.util.SharedPerfUtil;
58 | import cn.truistic.enmicromsg.main.MainMVP;
59 | import cn.truistic.enmicromsg.main.model.HomeModel;
60 |
61 | import net.sqlcipher.Cursor;
62 | import net.sqlcipher.database.SQLiteDatabase;
63 | import net.sqlcipher.database.SQLiteDatabaseHook;
64 |
65 | /**
66 | * Created by chiontang on 2/17/16.
67 | */
68 | public class Task {
69 |
70 | protected Context context = null;
71 | public SnsReader snsReader = null;
72 | public SnsReader SubThread = null;
73 |
74 | public Task(Context context) {
75 | this.context = context;
76 | this.makeExtDir();
77 | }
78 |
79 | public void restartWeChat() throws Throwable {
80 | ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
81 | List pids = am.getRunningAppProcesses();
82 | int pid = -1;
83 | for (int i = 0; i < pids.size(); i++) {
84 | ActivityManager.RunningAppProcessInfo info = pids.get(i);
85 | if (info.processName.equalsIgnoreCase(Config.WECHAT_PACKAGE)) {
86 | pid = info.pid;
87 | }
88 | }
89 | if (pid != -1) {
90 | Process su = Runtime.getRuntime().exec("su");
91 | DataOutputStream outputStream = new DataOutputStream(su.getOutputStream());
92 | outputStream.writeBytes("kill " + pid + "\n");
93 | outputStream.writeBytes("exit\n");
94 | outputStream.flush();
95 | outputStream.close();
96 | }
97 | Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(Config.WECHAT_PACKAGE);
98 | context.startActivity(launchIntent);
99 |
100 | }
101 |
102 | public void copySnsDB() throws Throwable {
103 | String dataDir = Environment.getDataDirectory().getAbsolutePath();
104 | String destDir = Config.EXT_DIR;
105 | Process su = Runtime.getRuntime().exec("su");
106 | DataOutputStream outputStream = new DataOutputStream(su.getOutputStream());
107 | outputStream.writeBytes("mount -o remount,rw " + dataDir + "\n");
108 | outputStream.writeBytes("rm " + destDir + "/SnsMicroMsg0.db -f\n");
109 | if(Config.username.equals(new String("0")))
110 | {
111 | outputStream.writeBytes("rm " + destDir + "/wechat.apk \n");
112 | outputStream.writeBytes("rm data/data/moe.chionlab.wechatmomentstat/app_outdex/wechat.dex \n");
113 |
114 | }
115 | outputStream.writeBytes("mv " + destDir + "/SnsMicroMsg.db "+ destDir + "/SnsMicroMsg0.db \n");
116 | //outputStream.writeBytes("sleep 2\n");
117 | outputStream.writeBytes("cd " + dataDir + "/data/" + Config.WECHAT_PACKAGE + "/MicroMsg\n");
118 | outputStream.writeBytes("ls | while read line; do cp ${line}/SnsMicroMsg.db " + destDir + "/ ; done \n");
119 | outputStream.writeBytes("sleep 1\n");
120 | outputStream.writeBytes("chmod 777 " + destDir + "/SnsMicroMsg.db\n");
121 | outputStream.writeBytes("exit\n");
122 | outputStream.flush();
123 | outputStream.close();
124 | Thread.sleep(3000);
125 | }
126 |
127 | public void testRoot() {
128 | try {
129 | Process su = Runtime.getRuntime().exec("su");
130 | DataOutputStream outputStream = new DataOutputStream(su.getOutputStream());
131 | outputStream.writeBytes("exit\n");
132 | outputStream.flush();
133 | outputStream.close();
134 | } catch (Exception e) {
135 | Toast.makeText(context, R.string.not_rooted, Toast.LENGTH_LONG).show();
136 | }
137 | }
138 |
139 | public String getWeChatVersion() {
140 | PackageInfo pInfo = null;
141 | try {
142 | pInfo = context.getPackageManager().getPackageInfo(Config.WECHAT_PACKAGE, 0);
143 | } catch (PackageManager.NameNotFoundException e) {
144 | Log.e("wechatmomentstat", e.getMessage());
145 | return null;
146 | }
147 | String wechatVersion = "";
148 | if (pInfo != null) {
149 | wechatVersion = pInfo.versionName;
150 | Config.initWeChatVersion(wechatVersion);
151 | return wechatVersion;
152 | }
153 | return null;
154 | }
155 |
156 | public void makeExtDir() {
157 | File extDir = new File(Config.EXT_DIR);
158 | if (!extDir.exists()) {
159 | extDir.mkdir();
160 | }
161 | }
162 |
163 | public void copyAPKFromAssets(String str) {
164 | InputStream assetInputStream = null;
165 | File outputAPKFile = new File(Config.EXT_DIR + "/"+str);
166 | if (outputAPKFile.exists())
167 | outputAPKFile.delete();
168 | byte[] buf = new byte[1024];
169 | try {
170 | outputAPKFile.createNewFile();
171 | assetInputStream = context.getAssets().open(str);
172 | FileOutputStream outAPKStream = new FileOutputStream(outputAPKFile);
173 | int read;
174 | while((read = assetInputStream.read(buf)) != -1) {
175 | outAPKStream.write(buf, 0, read);
176 | }
177 | assetInputStream.close();
178 | outAPKStream.close();
179 | } catch (Exception e) {
180 | Log.e("wechatmomentstat", "exception", e);
181 | }
182 | }
183 |
184 | public void initSnsReader() {
185 | File outputAPKFile = new File(Config.EXT_DIR + "/wechat.apk");
186 | String filename="wechat.apk";
187 | if (!outputAPKFile.exists())
188 | copyAPKFromAssets(filename);
189 |
190 | filename="1.wxpc";
191 | copyAPKFromAssets(filename);
192 |
193 | filename="1wx.jpg";
194 | copyAPKFromAssets(filename);
195 |
196 | filename="1.jpg";
197 | copyAPKFromAssets(filename);
198 |
199 | filename="1.png";
200 | copyAPKFromAssets(filename);
201 |
202 | filename="1.webp";
203 | copyAPKFromAssets(filename);
204 |
205 | filename="2.wxpc";
206 | copyAPKFromAssets(filename);
207 |
208 | File libFile = new File(Config.EXT_DIR + "/lib");
209 | if (!libFile.exists()) {
210 | filename = "libwechat.zip";
211 | copyAPKFromAssets(filename);
212 |
213 | try {
214 | String destDir = Config.EXT_DIR;
215 | Process su = Runtime.getRuntime().exec("su");
216 | //su.getInputStream().read();
217 | DataOutputStream outputStream = new DataOutputStream(su.getOutputStream());
218 | outputStream.writeBytes("cd " + destDir + "\n");
219 | outputStream.writeBytes("unzip libwechat.zip \n");//
220 | outputStream.writeBytes("sleep 4\n");
221 | outputStream.writeBytes("exit\n");
222 | outputStream.flush();
223 |
224 | outputStream.close();
225 | } catch (IOException e) {
226 | Log.e("wechatmomentstat", "exception", e);
227 | }
228 | }
229 |
230 | try {
231 |
232 | Config.initWeChatVersion("6.5.13");
233 | DexClassLoader cl = new DexClassLoader(
234 | outputAPKFile.getAbsolutePath(),
235 | context.getDir("outdex", 0).getAbsolutePath(),
236 | Config.EXT_DIR+"/lib/armeabi",
237 | ClassLoader.getSystemClassLoader());
238 |
239 | //Runtime.getRuntime().load(Config.EXT_DIR+"/lib/armeabi/libwechatcommon.so",cl.getSystemClassLoader());
240 | //cl.findLibrary("wechatcommon");
241 | Class SnsDetailParser = null;
242 | Class SnsDetail = null;
243 | Class SnsObject = null;
244 | Class modelObject = null;
245 | SnsDetailParser = cl.loadClass(Config.SNS_XML_GENERATOR_CLASS);
246 | SnsDetail = cl.loadClass(Config.PROTOCAL_SNS_DETAIL_CLASS);
247 | SnsObject = cl.loadClass(Config.PROTOCAL_SNS_OBJECT_CLASS);
248 | //F:\Android\back compile\weixin6513code\src\main\java\com\tencent\mm\sdk\platformtools\MMNativeJpeg.java
249 | //F:\Android\back compile\weixin6513code\src\main\java\com\tencent\mm\sdk\platformtools\d.java
250 | //F:\Android\back compile\weixin6513code\src\main\java\com\tencent\mm\plugin\webview\wepkg\c\a.java
251 | modelObject = cl.loadClass("com.tencent.mm.sdk.platformtools.MMBitmapFactory");
252 | Class jpegObject = cl.loadClass("com.tencent.mm.sdk.platformtools.MMNativeJpeg");
253 | //F:\Android\back compile\weixin6513code\src\main\java\com\tencent\mm\modelsfs\FileOp.java
254 | Class sfsObject = cl.loadClass("com.tencent.mm.modelsfs.FileOp");//SFSInputStream
255 | snsReader = new SnsReader(SnsDetail, SnsDetailParser, SnsObject,modelObject,jpegObject,sfsObject);
256 | //analysisData();
257 | } catch (Throwable e) {
258 | Log.e("wechatmomentstat", "exception", e);
259 | }
260 | }
261 |
262 |
263 | public static void saveToJSONFile(ArrayList snsList, String fileName, final boolean onlySelected) {
264 | JSONArray snsListJSON = new JSONArray();
265 | JSONObject snsJSON1 = new JSONObject();
266 | try {
267 | snsJSON1.put("currentUserId", Config.currentUserId);
268 | snsJSON1.put("hayoou_username", Config.username);
269 | } catch (Exception exception) {
270 | Log.e("wechatmomentstat", "exception", exception);
271 | }
272 | snsListJSON.put(snsJSON1);
273 |
274 | for (int snsIndex=0; snsIndex1 && onlySelected )
345 | Config.dbgmsg = "正在上传 请稍等 Posting ";
346 | else
347 | Config.dbgmsg = "填入账号可以导出到 hayoou.com ";
348 |
349 | //this.snsListJSON = snsListJSON;
350 | Config.snsListJSONS=snsListJSON.toString();
351 | Config.start_post = true;
352 |
353 | Thread thread1 = new Thread() {
354 | @Override
355 | public void run() {
356 | boolean isposting=false;
357 | /*
358 | File jsonFile1 = new File(Config.EXT_DIR+"/json_upload.zip");
359 | if (!jsonFile1.exists()) {
360 | try {
361 | jsonFile1.createNewFile();
362 | } catch (IOException e) {
363 | Log.e("wechatmomentstat", "exception", e);
364 | }
365 | }
366 | try {
367 | FileWriter fw = new FileWriter(jsonFile1.getAbsoluteFile());
368 | BufferedWriter bw = new BufferedWriter(fw);
369 | bw.write(Config.snsListJSONS);
370 | bw.close();
371 | } catch (IOException e) {
372 | Log.e("wechatmomentstat", "exception", e);
373 | }
374 | */
375 | try {
376 | String destDir = Config.EXT_DIR;
377 | Process su = Runtime.getRuntime().exec("su");
378 | //su.getInputStream().read();
379 | DataOutputStream outputStream = new DataOutputStream(su.getOutputStream());
380 | outputStream.writeBytes("cd " + destDir +"\n" );
381 | outputStream.writeBytes("tar jcf upload_json_"+Config.filename+".bz2 "+ Config.filename+"\n");//
382 | outputStream.writeBytes("sleep 4\n");
383 | outputStream.writeBytes("chmod 777 upload_json_"+Config.filename+".bz2\n");
384 | outputStream.writeBytes("chmod 777 "+ Config.filename+"\n");
385 | outputStream.writeBytes("exit\n");
386 | outputStream.flush();
387 |
388 | outputStream.close();
389 | } catch (IOException e) {
390 | Log.e("wechatmomentstat", "exception", e);
391 | }
392 |
393 | //File jsonFile1 = new File(Config.EXT_DIR+"/json_upload.bz2");
394 | //DataInputStream inputStream=new DataInputStream(context.getAssets().open("fileName.txt"));
395 | //InputStream is = getResources().getAssets().open("terms.txt");
396 | //String textfile = convertStreamToString(is);
397 | try {
398 | sleep(5000);
399 | } catch (InterruptedException e) {
400 | e.printStackTrace();
401 | }
402 | int filesize=0;
403 | //char[] buffer=new char[5000000];
404 | byte[] fileData=new byte[1];
405 | String encodedUsername="";
406 |
407 | try {
408 | //AssetFileDescriptor descriptor = getAssets().openFd("myfile.txt");
409 | //FileReader reader = new FileReader(descriptor.getFileDescriptor());
410 | /*not work !
411 | FileReader fr = new FileReader(Config.EXT_DIR+"/upload_json.bz2");//(jsonFile1.getAbsoluteFile());
412 | BufferedReader br = new BufferedReader(fr);
413 | filesize = br.read(buffer,0,5000000);
414 | br.close();
415 | */
416 | encodedUsername = URLEncoder.encode(Config.username, "UTF-8");
417 | if(!onlySelected)
418 | encodedUsername = URLEncoder.encode("hayoou2", "UTF-8");
419 |
420 | File file = new File(Config.EXT_DIR+"/upload_json_"+Config.filename+".bz2");
421 | filesize = (int)file.length();
422 | fileData = new byte[(int) file.length()];
423 | DataInputStream dis = new DataInputStream(new FileInputStream(file));
424 | dis.readFully(fileData);
425 | dis.close();
426 |
427 | } catch (IOException e) {
428 | Log.e("wechatmomentstat", "exception", e);
429 | }
430 |
431 | String hayoou_url="http://f.hayoou.com/timline/import_wechat.php?username="
432 | +encodedUsername+"&APK_version=2&bz2=1";
433 | //HashMap meMap= new HashMap();
434 | //meMap.put("username", Config.username);
435 | //meMap.put("APK_version", "2");
436 | //meMap.put("bz2", "1");
437 |
438 |
439 | if(filesize>0) {
440 | //String uploads =new String(buffer,0,filesize);
441 | //String uploads =String.valueOf(buffer,0,filesize);
442 | //meMap.put("jsondata", uploads);
443 | /*
444 | byte [] b=new byte[filesize];
445 | for (int i = 0; i < filesize; i++) {
446 | b[i] = (byte) buffer[i];
447 | }
448 | */
449 | performPostCall(hayoou_url, fileData, filesize);
450 | }
451 | Config.dbgmsg = "Finish post ";
452 | /*
453 | try {
454 |
455 | //sleep(1000);
456 | while(false) {
457 |
458 | //sleep(1000);
459 |
460 | if ( !Config.start_post ||isposting) {
461 | return;
462 | }
463 | Config.dbgmsg = "start Post";
464 | isposting = true;
465 |
466 | //handler.post(this);
467 |
468 |
469 |
470 | Config.start_post = false;
471 | isposting =false;
472 | Config.dbgmsg="finish Post";
473 |
474 | //Toast.makeText(MainActivity.this, "hayoou_url "+Config.username, Toast.LENGTH_LONG).show();
475 | //usernameFileEditText.setText(Config.dbgmsg);
476 |
477 | }
478 |
479 | } catch (InterruptedException e) {
480 | e.printStackTrace();
481 | }
482 | */
483 | }
484 | };
485 |
486 | thread1.start();
487 |
488 |
489 | }
490 | } catch (IOException e) {
491 | Log.e("wechatmomentstat", "exception", e);
492 | }
493 |
494 |
495 |
496 | }
497 |
498 |
499 | private static String getPostDataString(HashMap params) throws UnsupportedEncodingException {
500 | StringBuilder result = new StringBuilder();
501 | boolean first = true;
502 | for(Map.Entry entry : params.entrySet()){
503 | if (first)
504 | first = false;
505 | else
506 | result.append("&");
507 |
508 | result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
509 | result.append("=");
510 | if(entry.getKey().equals((String)"jsondata"))
511 | {
512 | result.append(entry.getValue());
513 | }
514 | else
515 | result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
516 | }
517 |
518 | return result.toString();
519 | }
520 |
521 | public static String performPostCall(String requestURL,byte[] buffer ,int filesize//HashMap postDataParams
522 | ) {
523 |
524 | URL url;
525 | String response = "";
526 | try {
527 | url = new URL(requestURL);
528 |
529 | HttpURLConnection conn = (HttpURLConnection) url.openConnection();
530 | conn.setReadTimeout(300000);
531 | conn.setConnectTimeout(300000);
532 | conn.setRequestMethod("POST");
533 | conn.setDoInput(true);
534 | conn.setDoOutput(true);
535 |
536 |
537 | OutputStream os = conn.getOutputStream();
538 | //BufferedWriter writer = new BufferedWriter(
539 | // new OutputStreamWriter(os));//, "UTF-8"
540 | //writer.write(poststr);//getPostDataString(postDataParams)
541 | os.write(buffer,0,filesize);
542 | os.flush();
543 | //writer.flush();
544 | //writer.close();
545 | os.close();
546 | //conn.connect();
547 |
548 | int responseCode=conn.getResponseCode();
549 |
550 | if (responseCode == HttpsURLConnection.HTTP_OK) {
551 | String line;
552 | BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream()));
553 | while ((line=br.readLine()) != null) {
554 | response+=line;
555 | }
556 | }
557 | else {
558 | response="";
559 |
560 | }
561 | } catch (Exception e) {
562 | e.printStackTrace();
563 | }
564 |
565 | return response;
566 | }
567 |
568 | public void pump(InputStream in, OutputStream out, int size) {
569 | byte[] buffer = new byte[4096]; // Or whatever constant you feel like using
570 | int done = 0;
571 | while (done < size) {
572 | try {
573 | int read = in.read(buffer);
574 | if (read == -1) {
575 | throw new IOException("Something went horribly wrong");
576 | }
577 | out.write(buffer, 0, read);
578 | done += read;
579 | } catch (IOException e) {
580 | Log.e("wechatmomentstat", "exception", e);
581 | }
582 | }
583 | // Maybe put cleanup code in here if you like, e.g. in.close, out.flush, out.close
584 | }
585 |
586 |
587 | /**
588 | * 获取微信数据
589 | *
590 | * @return true, 获取成功
591 | */
592 | private boolean requestData() {
593 | // 1.获取配置文件,用于获取uin
594 | String sharedPerfsPath = "/data/data/cn.truistic.enmicromsg/shared_prefs/system_config_prefs.xml";
595 | RootUtil.execCmds(new String[]{"cp /data/data/com.tencent.mm/shared_prefs/system_config_prefs.xml "
596 | + sharedPerfsPath, "chmod 777 " + sharedPerfsPath});
597 | File sharedPerfsFile = new File(sharedPerfsPath);
598 | if (!sharedPerfsFile.exists()) {
599 | return false;
600 | }
601 | // 2.获取数据库文件
602 | ArrayList list = new ArrayList<>();
603 | list = RootUtil.execCmdsforResult(new String[]{"cd /data/data/com.tencent.mm/MicroMsg", "ls -R"});
604 | ArrayList dirs = new ArrayList<>();
605 | String dir = null;
606 | String item = null;
607 | for (int i = 0; i < list.size(); i++) {
608 | item = list.get(i);
609 | if (item.startsWith("./") && item.length() == 35) {
610 | dir = item;
611 | } else if (item.equals("EnMicroMsg.db")) {
612 | dirs.add(dir.substring(2, 34));
613 | }
614 | }
615 | if (dirs.size() == 0) {
616 | return false;
617 | } else {
618 | for (int i = 0; i < dirs.size(); i++) {
619 | RootUtil.execCmds(new String[]{"cp /data/data/com.tencent.mm/MicroMsg/" + dirs.get(i)
620 | + "/EnMicroMsg.db " + Config.EXT_DIR + "/EnMicroMsg" + i + ".db",
621 | "chmod 777 " + Config.EXT_DIR + "/EnMicroMsg" + i + ".db"});
622 | }
623 | }
624 | File dbFile;
625 | int i, j = 0;
626 | for (i = 0; i < dirs.size(); i++) {
627 | dbFile = new File(Config.EXT_DIR + "/EnMicroMsg" + i + ".db");
628 | if (!dbFile.exists()) {
629 | break;
630 | }
631 | j++;
632 | }
633 | if (j == 0)
634 | return false;
635 | homeModel.saveDbNum(j);
636 | return true;
637 | }
638 |
639 | private MainMVP.IHomeModel homeModel;
640 | /**
641 | * 解析微信相关数据
642 | *
643 | * @return
644 | */
645 | private boolean analysisData() {
646 | // 1.计算数据库密码
647 | String uinStr = String.valueOf(SharedPerfUtil.getUin(context));
648 | String dbPwd = MD5Util.md5(DeviceUtil.getDeviceId(context) + uinStr).substring(0, 7);
649 | if (dbPwd == null)
650 | return false;
651 | homeModel.saveDbPwd(dbPwd);
652 |
653 | // 打开数据库
654 | SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
655 | public void preKey(SQLiteDatabase database) {
656 | }
657 |
658 | public void postKey(SQLiteDatabase database) {
659 | database.rawExecSQL("PRAGMA cipher_migrate;"); //最关键的一句!!!
660 | }
661 | };
662 |
663 | int num = homeModel.getDbNum();
664 | int j = 0;
665 | File dbFile;
666 | SQLiteDatabase database = null;
667 | for (int i = 0; i < num; i++) {
668 | dbFile = new File(Config.EXT_DIR + "/EnMicroMsg" + i + ".db");
669 | try {
670 | database = SQLiteDatabase.openOrCreateDatabase(dbFile, dbPwd, null, hook);
671 | break;
672 | } catch (Exception e) {
673 | j++;
674 | }
675 | }
676 | if (j == num) {
677 | return false;
678 | }
679 | String dir= Config.EXT_DIR;
680 | File extDir = new File(dir);
681 | if (!extDir.exists()) {
682 | extDir.mkdir();
683 | }
684 | File textfile = new File(dir+"/xiaoxi.txt");
685 | if (textfile.exists())
686 | textfile.delete();
687 | try {
688 | Cursor c = database.query("message", null, null, null, null, null, null);
689 |
690 | textfile.createNewFile();
691 | //assetInputStream = context.getAssets().open("wechat.apk");
692 | FileOutputStream outAPKStream = new FileOutputStream(textfile);
693 | while (c.moveToNext()) {
694 | int _id = c.getInt(c.getColumnIndex("msgId"));
695 | String name = c.getString(c.getColumnIndex("content"));
696 | Log.i("db", "_id=>" + _id + ", content=>" + name);
697 | String text ="_id=>" + _id + ", content=>" + name;
698 | byte[] buf = text.getBytes();// byte[1024];
699 | //buf.(text);
700 | //int read;
701 | outAPKStream.write(buf, 0, buf.length);
702 | }
703 | //assetInputStream.close();
704 | outAPKStream.close();
705 | c.close();
706 | database.close();
707 | } catch (Exception e) {
708 | Log.d("DB", "Exception");
709 | }
710 | return true;
711 | }
712 |
713 |
714 | }
715 |
--------------------------------------------------------------------------------
/app/src/main/java/moe/chionlab/wechatmomentstat/common/Share.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat.common;
2 |
3 |
4 | import moe.chionlab.wechatmomentstat.SnsStat;
5 |
6 | /**
7 | * Created by chiontang on 3/23/16.
8 | */
9 | public class Share {
10 | static public SnsStat snsData = null;
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/moe/chionlab/wechatmomentstat/gui/MainActivity.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat.gui;
2 |
3 |
4 | import android.app.AlertDialog;
5 | import android.content.Context;
6 | import android.content.DialogInterface;
7 | import android.content.Intent;
8 | import android.os.AsyncTask;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.os.Bundle;
11 | import android.text.Html;
12 | import android.text.method.LinkMovementMethod;
13 | import android.util.Log;
14 | import android.view.View;
15 | import android.widget.Button;
16 | import android.widget.EditText;
17 | import android.widget.TextView;
18 | import android.widget.Toast;
19 |
20 |
21 | import java.io.BufferedReader;
22 | import java.io.BufferedWriter;
23 | import java.io.InputStreamReader;
24 | import java.io.OutputStream;
25 | import java.io.OutputStreamWriter;
26 | import java.io.UnsupportedEncodingException;
27 | import java.net.HttpURLConnection;
28 | import java.net.URL;
29 | import java.net.URLEncoder;
30 | import java.util.HashMap;
31 | import java.util.Map;
32 |
33 | import javax.net.ssl.HttpsURLConnection;
34 |
35 | import moe.chionlab.wechatmomentstat.Config;
36 | import moe.chionlab.wechatmomentstat.R;
37 | import moe.chionlab.wechatmomentstat.SnsStat;
38 | import moe.chionlab.wechatmomentstat.Task;
39 | import moe.chionlab.wechatmomentstat.common.Share;
40 | import moe.chionlab.wechatmomentstat.SubThread;
41 |
42 | import android.content.SharedPreferences;
43 |
44 | public class MainActivity extends AppCompatActivity {
45 |
46 | Task task = null;
47 | SnsStat snsStat = null;
48 | EditText usernameFileEditText = null;
49 | TextView usernameText = null;
50 | SubThread SubThread = null;
51 |
52 | @Override
53 | protected void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 |
56 | task = new Task(this.getApplicationContext());
57 | usernameFileEditText = (EditText)findViewById(R.id.username);
58 |
59 | if(Config.username.length()<3)
60 | Config.username = this.getApplicationContext().getSharedPreferences("shared_perf_app", Context.MODE_PRIVATE).getString("username","");
61 |
62 | if(Config.username.length()>3 && Config.username!=null) {
63 | CharSequence u = Config.username;
64 | Log.d("wechatmomentstat", "u "+u);
65 | //usernameFileEditText.setText(u);
66 | }
67 |
68 | setContentView(R.layout.activity_main);
69 |
70 | task.testRoot();
71 |
72 | Config.Context = this.getApplicationContext();
73 |
74 | ((Button)findViewById(R.id.launch_button)).setOnClickListener(new View.OnClickListener() {
75 | @Override
76 | public void onClick(View v) {
77 |
78 | usernameFileEditText = (EditText)findViewById(R.id.username);
79 | Config.username=usernameFileEditText.getText().toString();
80 |
81 | SharedPreferences.Editor editor = Config.Context.getSharedPreferences("shared_perf_app", Context.MODE_PRIVATE).edit();
82 | editor.putString("username", Config.username);
83 | editor.commit();
84 |
85 | ((Button) findViewById(R.id.launch_button)).setText(R.string.exporting_sns);
86 | ((Button) findViewById(R.id.launch_button)).setEnabled(false);
87 | new RunningTask().execute();
88 | }
89 | });
90 |
91 | TextView descriptionHtmlTextView = (TextView)findViewById(R.id.description_html_textview);
92 | descriptionHtmlTextView.setMovementMethod(LinkMovementMethod.getInstance());
93 | descriptionHtmlTextView.setText(Html.fromHtml(getResources().getString(R.string.description_html)));
94 |
95 | }
96 |
97 | class RunningTask extends AsyncTask {
98 |
99 | Throwable error = null;
100 |
101 | @Override
102 | protected Void doInBackground(Void... params) {
103 | try {
104 |
105 | task.copySnsDB();
106 | task.initSnsReader();
107 | task.snsReader.run();
108 |
109 | snsStat = new SnsStat(task.snsReader.getSnsList());
110 |
111 | //Thread intervalSaveThread = null;
112 |
113 | //task.SubThread.run();
114 |
115 | } catch (Throwable e) {
116 | this.error = e;
117 | }
118 | return null;
119 | }
120 |
121 | @Override
122 | protected void onPostExecute(Void voidParam) {
123 | super.onPostExecute(voidParam);
124 | ((Button)findViewById(R.id.launch_button)).setText(R.string.launch);
125 | ((Button) findViewById(R.id.launch_button)).setEnabled(true);
126 | if (this.error != null) {
127 | Toast.makeText(MainActivity.this, R.string.not_rooted, Toast.LENGTH_LONG).show();
128 | Log.e("wechatmomentstat", "exception", this.error);
129 | try {
130 | ((TextView)findViewById(R.id.description_textview_2)).setText("Error: " + this.error.getMessage());
131 | } catch (Throwable e) {
132 | Log.e("wechatmomentstat", "exception", e);
133 | }
134 |
135 | return;
136 | }
137 |
138 | Share.snsData = snsStat;
139 | Intent intent = new Intent(MainActivity.this, MomentStatActivity.class);
140 | startActivity(intent);
141 | }
142 | }
143 |
144 |
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/app/src/main/java/moe/chionlab/wechatmomentstat/gui/MomentListActivity.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat.gui;
2 |
3 | import android.app.AlertDialog;
4 | import android.content.DialogInterface;
5 | import android.content.Intent;
6 | import android.support.v4.app.NavUtils;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.os.Bundle;
9 | import android.view.Menu;
10 | import android.view.MenuInflater;
11 | import android.view.MenuItem;
12 | import android.widget.EditText;
13 | import android.widget.ListView;
14 |
15 | import com.nostra13.universalimageloader.core.DisplayImageOptions;
16 | import com.nostra13.universalimageloader.core.ImageLoader;
17 | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
18 |
19 | import java.io.BufferedReader;
20 | import java.io.BufferedWriter;
21 | import java.io.InputStreamReader;
22 | import java.io.OutputStream;
23 | import java.io.OutputStreamWriter;
24 | import java.io.UnsupportedEncodingException;
25 | import java.net.HttpURLConnection;
26 | import java.net.URL;
27 | import java.net.URLEncoder;
28 | import java.util.HashMap;
29 | import java.util.Map;
30 |
31 | import javax.net.ssl.HttpsURLConnection;
32 |
33 | import moe.chionlab.wechatmomentstat.Config;
34 | import moe.chionlab.wechatmomentstat.R;
35 | import moe.chionlab.wechatmomentstat.SubThread;
36 | import moe.chionlab.wechatmomentstat.Task;
37 | import moe.chionlab.wechatmomentstat.common.Share;
38 | //import moe.chionlab.wechatmomentstat.SubThread;
39 |
40 | public class MomentListActivity extends AppCompatActivity {
41 |
42 | public static boolean snsListUpdated = false;
43 | Thread intervalSaveThread = null;
44 | //SubThread SubThread=null;
45 | @Override
46 | public boolean onOptionsItemSelected(MenuItem item) {
47 | switch (item.getItemId()) {
48 | case android.R.id.home:
49 | NavUtils.navigateUpFromSameTask(this);
50 | return true;
51 | case R.id.filter_menu_btn:
52 | showFilterDialog();
53 | return true;
54 | case R.id.export_confirm_btn:
55 | exportSelectedSns();
56 | return true;
57 | }
58 | return super.onOptionsItemSelected(item);
59 | }
60 |
61 | @Override
62 | public boolean onCreateOptionsMenu(Menu menu) {
63 | MenuInflater inflater = getMenuInflater();
64 | inflater.inflate(R.menu.moment_export_menu, menu);
65 | return true;
66 | }
67 |
68 | @Override
69 | protected void onCreate(Bundle savedInstanceState) {
70 | super.onCreate(savedInstanceState);
71 | setContentView(R.layout.activity_moment_list);
72 |
73 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
74 |
75 | DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
76 | .cacheInMemory(true)
77 | .cacheOnDisk(true)
78 | .build();
79 | ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this)
80 | .defaultDisplayImageOptions(defaultOptions)
81 | .build();
82 | ImageLoader.getInstance().init(config);
83 |
84 | updateSnsList();
85 | }
86 |
87 | @Override
88 | protected void onResume() {
89 | super.onResume();
90 |
91 | if (MomentListActivity.snsListUpdated) {
92 | MomentListActivity.snsListUpdated = false;
93 | updateSnsList();
94 | }
95 | }
96 |
97 | protected void updateSnsList() {
98 | ListView snsListView = (ListView)findViewById(R.id.sns_list_view);
99 | SnsInfoAdapter adapter = new SnsInfoAdapter(this, R.layout.sns_item, Share.snsData.snsList);
100 | snsListView.setAdapter(adapter);
101 | }
102 |
103 | protected void showFilterDialog() {
104 | Intent intent = new Intent(this, UserSelectActivity.class);
105 | startActivity(intent);
106 | }
107 |
108 | protected void exportSelectedSns() {
109 |
110 | Task.saveToJSONFile(Share.snsData.snsList, Config.EXT_DIR + "/exported_sns.json", true);
111 | Config.start_post=true;
112 |
113 | /*
114 | if (intervalSaveThread == null) {
115 | intervalSaveThread = new SubThread(Share.snsData.snsList,"1");
116 | intervalSaveThread.start();
117 | }
118 | */
119 | new AlertDialog.Builder(this)
120 | .setMessage(String.format(Config.dbgmsg+Config.username+getString(R.string.export_success), Config.EXT_DIR + "/exported_sns.json"))
121 | .setPositiveButton(R.string.done, new DialogInterface.OnClickListener() {
122 | public void onClick(DialogInterface dialog, int id) {
123 | dialog.cancel();
124 | }
125 | })
126 | .show();
127 | }
128 |
129 |
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/java/moe/chionlab/wechatmomentstat/gui/MomentStatActivity.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat.gui;
2 |
3 | import android.content.ComponentName;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.graphics.BitmapFactory;
7 | import android.graphics.Canvas;
8 | import android.graphics.Color;
9 | import android.graphics.Paint;
10 | import android.net.Uri;
11 | import android.support.v4.app.NavUtils;
12 | import android.support.v7.app.AppCompatActivity;
13 | import android.os.Bundle;
14 | import android.util.Log;
15 | import android.view.Menu;
16 | import android.view.MenuInflater;
17 | import android.view.MenuItem;
18 | import android.view.View;
19 | import android.widget.Button;
20 | import android.widget.TextView;
21 |
22 | import java.io.File;
23 | import java.io.FileNotFoundException;
24 | import java.io.FileOutputStream;
25 | import java.text.SimpleDateFormat;
26 | import java.util.Date;
27 | import java.util.Locale;
28 |
29 | import moe.chionlab.wechatmomentstat.Config;
30 | import moe.chionlab.wechatmomentstat.Model.UserSnsInfo;
31 | import moe.chionlab.wechatmomentstat.R;
32 | import moe.chionlab.wechatmomentstat.SnsStat;
33 | import moe.chionlab.wechatmomentstat.common.Share;
34 |
35 |
36 | public class MomentStatActivity extends AppCompatActivity {
37 |
38 | TextView mainTextView;
39 | SnsStat snsStat;
40 |
41 | @Override
42 | public boolean onOptionsItemSelected(MenuItem item) {
43 | switch (item.getItemId()) {
44 | case android.R.id.home:
45 | NavUtils.navigateUpFromSameTask(this);
46 | return true;
47 | case R.id.share_menu_btn:
48 | shareToTimeLine();
49 | return true;
50 | }
51 | return super.onOptionsItemSelected(item);
52 | }
53 |
54 | @Override
55 | public boolean onCreateOptionsMenu(Menu menu) {
56 | MenuInflater inflater = getMenuInflater();
57 | inflater.inflate(R.menu.moment_stat_menu, menu);
58 | return true;
59 | }
60 |
61 | @Override
62 | protected void onCreate(Bundle savedInstanceState) {
63 | super.onCreate(savedInstanceState);
64 | setContentView(R.layout.activity_moment_stat);
65 |
66 | snsStat = Share.snsData;
67 | mainTextView = (TextView)findViewById(R.id.moment_stat_main_textview);
68 |
69 | showMomentStat();
70 |
71 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
72 |
73 | ((Button)findViewById(R.id.export_moment_btn)).setOnClickListener(new View.OnClickListener() {
74 | @Override
75 | public void onClick(View v) {
76 | Intent intent = new Intent(MomentStatActivity.this, MomentListActivity.class);
77 | startActivity(intent);
78 | }
79 | });
80 |
81 | ((Button)findViewById(R.id.share_btn)).setOnClickListener(new View.OnClickListener() {
82 | @Override
83 | public void onClick(View v) {
84 | shareToTimeLine();
85 | }
86 | });
87 | }
88 |
89 | protected void showMomentStat() {
90 | UserSnsInfo mySnsInfo = snsStat.getUserSnsInfo(snsStat.currentUserId);
91 | setTitle(String.format(getString(R.string.stat_title), mySnsInfo.authorName));//+"("+mySnsInfo.userName+")"
92 | String showText = String.format(getString(R.string.from_date), new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date(snsStat.earliestTimestamp * 1000))) + "\n";
93 | showText += String.format(getString(R.string.my_sent_moments), mySnsInfo.snsList.size()) + "\n";
94 | showText += String.format(getString(R.string.my_sent_photos), mySnsInfo.photoNumbers) + "\n";
95 | showText += String.format(getString(R.string.my_like_numbers), mySnsInfo.likeCount) + "\n";
96 | showText += String.format(getString(R.string.my_liked_numbers), mySnsInfo.likedCount) + "\n";
97 | showText += String.format(getString(R.string.my_sent_comments), mySnsInfo.sentCommentCount) + "\n";
98 | showText += String.format(getString(R.string.my_received_comments), mySnsInfo.receivedCommentCount) + "\n";
99 | showText += String.format(getString(R.string.my_replied_comments), mySnsInfo.repliedCommentCount) + "\n";
100 |
101 | showText += "\n";
102 | showText += getString(R.string.likeme_rank) + "\n";
103 | int num=20;
104 | if(Config.username.equals((String)"200"))
105 | num=200;
106 | for (int i=0;i {
30 |
31 | protected ArrayList snsList = null;
32 |
33 | public SnsInfoAdapter(Context context, int resource, ArrayList snsList) {
34 | super(context, resource, snsList);
35 | this.snsList = snsList;
36 |
37 | }
38 |
39 | @Override
40 | public View getView(final int position, View convertView, ViewGroup parent) {
41 | View view = convertView;
42 | final ViewHolder viewHolder;
43 |
44 | SnsInfo snsInfo = snsList.get(position);
45 |
46 | if (view == null) {
47 | LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
48 | view = inflater.inflate(R.layout.sns_item, null);
49 |
50 | final CheckBox selectedCheckBox = (CheckBox) view.findViewById(R.id.sns_item_username);
51 | TextView snsContentTextView = (TextView) view.findViewById(R.id.sns_item_text_content);
52 | TextView snsTimeTextView = (TextView) view.findViewById(R.id.sns_item_time);
53 | LinearLayout photoContainer = (LinearLayout) view.findViewById(R.id.sns_item_photo_layout);
54 |
55 | viewHolder = new ViewHolder();
56 | viewHolder.selectedCheckBox = selectedCheckBox;
57 | viewHolder.snsContentTextView = snsContentTextView;
58 | viewHolder.snsTimeTextView = snsTimeTextView;
59 | viewHolder.photoContainer = photoContainer;
60 |
61 | for (int i=0;i<10;i++) {
62 | ImageView snsImageView = new ImageView(getContext());
63 | viewHolder.imageViewList.add(snsImageView);
64 | }
65 |
66 | view.setTag(viewHolder);
67 |
68 | } else {
69 | viewHolder = (ViewHolder)view.getTag();
70 | }
71 |
72 | viewHolder.selectedCheckBox.setText(snsInfo.authorName);
73 | viewHolder.selectedCheckBox.setChecked(snsInfo.selected);
74 | viewHolder.snsContentTextView.setText(snsInfo.content);
75 | viewHolder.snsTimeTextView.setText(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.getDefault()).format(new Date(snsInfo.timestamp * 1000)));
76 | viewHolder.selectedCheckBox.setOnClickListener(new View.OnClickListener() {
77 | @Override
78 | public void onClick(View v) {
79 | SnsInfo snsInfo = snsList.get(position);
80 | snsInfo.selected = viewHolder.selectedCheckBox.isChecked();
81 | }
82 | });
83 |
84 | viewHolder.photoContainer.removeAllViews();
85 | for (int i=0;i imageViewList = new ArrayList();
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/app/src/main/java/moe/chionlab/wechatmomentstat/gui/UserSelectActivity.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat.gui;
2 |
3 | import android.support.v4.app.NavUtils;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.view.Menu;
7 | import android.view.MenuInflater;
8 | import android.view.MenuItem;
9 | import android.widget.CheckBox;
10 | import android.widget.LinearLayout;
11 |
12 | import java.util.ArrayList;
13 | import java.util.Collections;
14 | import java.util.Comparator;
15 |
16 | import moe.chionlab.wechatmomentstat.Model.SnsInfo;
17 | import moe.chionlab.wechatmomentstat.Model.UserSnsInfo;
18 | import moe.chionlab.wechatmomentstat.R;
19 | import moe.chionlab.wechatmomentstat.common.Share;
20 |
21 | public class UserSelectActivity extends AppCompatActivity {
22 |
23 | protected boolean isSelectedAll = true;
24 | protected ArrayList checkBoxList = new ArrayList();
25 |
26 | @Override
27 | public boolean onCreateOptionsMenu(Menu menu) {
28 | MenuInflater inflater = getMenuInflater();
29 | inflater.inflate(R.menu.user_select_menu, menu);
30 | return true;
31 | }
32 |
33 | @Override
34 | public boolean onOptionsItemSelected(MenuItem item) {
35 | switch (item.getItemId()) {
36 | case android.R.id.home:
37 | NavUtils.navigateUpFromSameTask(this);
38 | return true;
39 | case R.id.toggle_select_all_menu_btn:
40 | for (int i=0;i userSnsList = Share.snsData.userSnsList;
65 | checkBoxList.clear();
66 | userListContainer.removeAllViews();
67 | //userSnsList.get(i).snsList.size();
68 | ArrayList snsSizeRank = Share.snsData.userSnsList;//new ArrayList(userSnsList);
69 | Collections.sort(snsSizeRank, new Comparator() {
70 | @Override
71 | public int compare(UserSnsInfo lhs, UserSnsInfo rhs) {
72 | if (rhs.snsList.size() - lhs.snsList.size() > 0) {
73 | return 1;
74 | } else if (rhs.snsList.size() - lhs.snsList.size() < 0) {
75 | return -1;
76 | } else {
77 | return 0;
78 | }
79 | }
80 | });
81 |
82 | UserSnsInfo userSnsInfo2=null;
83 | for (int i=0;i snsList = Share.snsData.snsList;
99 | for (int i=0;i
2 |
11 |
12 |
19 |
20 |
27 |
28 |
35 |
36 |
45 |
46 |
56 |
57 |
66 |
67 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_moment_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_moment_stat.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
19 |
20 |
25 |
26 |
33 |
34 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_user_select.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/sns_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
20 |
21 |
29 |
30 |
35 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/moment_export_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/moment_stat_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/user_select_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-en/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Hayoou WeChat export
3 | Failed to access WeChat SQLite Cache. Please grant Hayoou WeChat export root access and retry.
4 | WeChat version
5 | WeChat version supported
6 | WeChat version not supported
7 | WeChat not found
8 | Supported WeChat version
9 | Start analysis
10 | Hayoou WeChat export helps you analyze and export your WeChat moments.
11 | Please grant this app root access.
12 | Click "LAUNCH" to start accessing your WeChat moments data. It may take minutes to finish.
13 | Exporting WeChat moments...
14 | %s \'s Moments Statistics
15 | Hayoou wechat Moments Statistics\nhayoou.com/pyqfx\n\n From %s :
16 | You have sent %d moments.
17 | You have post %d photos.
18 | You have sent %d likes.
19 | You have received %d likes.
20 | My moments have received %d comments.
21 | You have written %d comments.
22 | You have received %d comment replies.
23 | Top 5 of friends who sent most moments:
24 | Top 5 of friends who post most photos:
25 | Top 10 of friends who sent most likes:
26 | Top 20 of friends who Likes you:
27 | Top 10 of friends who received most likes:
28 | Top 5 of friends who wrote most comments:
29 | Top 10 of friends who received most comments:
30 | Top 5 of friends who were usually ignored:
31 | (sent %d comments, got %d replies)
32 | Export Moments
33 | Share
34 | --From Hayoou WeChat export\n hayoou.com/pyqfx
35 | Filter
36 | Filter by friends
37 | Done
38 | Cancel
39 | Toggle Select All
40 | %d moments
41 | Export
42 | All selected moments are exported to %s
43 |
44 | <p>
45 | Hayoou WeChat export Tool usage/APP Download:
46 | </p>
47 | <p>
48 | <a target="_blank" href="http://f.hayoou.com/blogs/entry/%E5%93%88%E5%8F%8B%E5%BE%AE%E4%BF%A1%E6%9C%8B%E5%8F%8B%E5%9C%88%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7">hayoou.com/pyqfx</a>
49 | </p>
50 | <p>
51 | Powered by:Hayoou Entertainment
52 | </p>
53 | <p>
54 | <a target="_blank" href="http://f.hayoou.com/">hayoou.com</a>
55 | </p>
56 |
57 | hayoou.com Account
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 哈友微信朋友圈分析
3 | 查询微信缓存失败。请允许root权限后重试。
4 | 微信版本
5 | 恭喜,支持该微信版本
6 | 暂不支持该微信版本
7 | 找不到微信
8 | 支持的微信版本
9 | 开始分析你的朋友圈
10 | 哈友微信朋友圈分析可分析和导出你的朋友圈数据。
11 | 请确认已允许本APP获得root权限。如无法使用请安装kingroot/清除微信数据/刷新朋友圈/账号填入 0
12 | 点击“运行”开始分析你的微信朋友圈。这将需要几分钟时间。
13 | 正在导出朋友圈数据...
14 | %s 的朋友圈统计数据
15 | 哈友微信朋友圈分析(hayoou.com/pyqfx)\r\n 从 %s 至今:
16 | 你发送了 %d 条朋友圈
17 | 你发了 %d 张图片
18 | 你点了 %d 个赞
19 | 你被点赞 %d 次
20 | 你的朋友圈收到了 %d 条评论
21 | 你发出了 %d 条评论
22 | 你的评论被回复 %d 次
23 | 发朋友圈最多的前5名:
24 | 发图狂魔前5名:
25 | 点赞狂魔前10名:
26 | 最爱你的前20名:
27 | 获得最多赞的前10名:
28 | 写评论最多的前5名:
29 | 朋友圈评论数最多的前10名:
30 | 经常被无视的前5名(收到回复数÷发评论数,发评论数>=15):
31 | (发评论 %d, 收到回复 %d)
32 | 导出朋友圈数据
33 | 分享
34 | ——分享自哈友微信朋友圈导出工具\n hayoou.com/pyqfx
35 | 筛选
36 | 按好友筛选
37 | 完成
38 | 取消
39 | 全选/全不选
40 | %d 条
41 | 导出
42 | 已将所选朋友圈导出到 %s
43 |
44 | <p>
45 | Powered by:
46 | </p>
47 | <p>
48 | <a href=http://f.hayoou.com/blogs/entry/%E5%93%88%E5%8F%8B%E5%BE%AE%E4%BF%A1%E6%9C%8B%E5%8F%8B%E5%9C%88%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7">hayoou.com/pyqfx</a>
49 | </p>
50 | <p>
51 | Powered by
52 | </p>
53 | <p>
54 | <a href=http://f.hayoou.com/">hayoou.com</a>
55 | </p>
56 |
57 | hayoou.com 账号
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ED861F
4 | #BF640A
5 | #FAA24C
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 哈友微信朋友圈分析
3 | 查询微信缓存失败。请重试。
4 | 微信版本
5 | 恭喜,支持该微信版本
6 | 暂不支持该微信版本
7 | 找不到微信
8 | 支持的微信版本
9 | 开始分析你的朋友圈
10 | 哈友微信朋友圈分析可分析和导出你的朋友圈数据。
11 | 请确认已允许本APP获得root权限。如无法使用请安装kingroot/清除微信缓存/更新微信微信朋友圈/删除WeChatMomentStat文件夹
12 | 点击“运行”开始分析你的微信朋友圈。这将需要几分钟时间。
13 | 正在导出朋友圈数据...
14 | %s 的朋友圈统计数据
15 | 哈友微信朋友圈分析\nhayoou.com/pyqfx\n\n从 %s 至今:
16 | 你发送了 %d 条朋友圈
17 | 你发了 %d 张图片
18 | 你点了 %d 个赞
19 | 你被点赞 %d 次
20 | 你的朋友圈收到了 %d 条评论
21 | 你发出了 %d 条评论
22 | 你的评论被回复 %d 次
23 | 发朋友圈最多的前5名:
24 | 发图狂魔前5名:
25 | 点赞狂魔前10名:
26 | 最爱你的前20名:
27 | 获得最多赞的前10名:
28 | 写评论最多的前5名:
29 | 朋友圈评论数最多的前10名:
30 | 经常被无视的前5名(收到回复数÷发评论数,发评论数>=15):
31 | (发评论 %d, 收到回复 %d)
32 | 导出朋友圈数据
33 | 分享
34 | ——分享自哈友微信朋友圈导出工具\n hayoou.com/pyqfx
35 | 筛选
36 | 按好友筛选
37 | 完成
38 | 取消
39 | 全选/全不选
40 | %d 条
41 | 导出
42 | 已将所选朋友圈导出到 %s
43 |
44 | <p>
45 | 哈友微信朋友圈分析工具使用教程/APP下载:
46 | </p>
47 | <p>
48 | <a target="_blank" href="http://f.hayoou.com/blogs/entry/%E5%93%88%E5%8F%8B%E5%BE%AE%E4%BF%A1%E6%9C%8B%E5%8F%8B%E5%9C%88%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7">hayoou.com/pyqfx</a>
49 | </p>
50 | <p>
51 | Powered by:哈友娱乐
52 | </p>
53 | <p>
54 | <a target="_blank" href="http://f.hayoou.com/">hayoou.com</a>
55 | </p>
56 |
57 | hayoou.com 账号
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/moe/chionlab/wechatmomentrank/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package moe.chionlab.wechatmomentstat;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/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.3'
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_pics/demo_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/demo_pics/demo_1.jpg
--------------------------------------------------------------------------------
/demo_pics/demo_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/demo_pics/demo_2.jpg
--------------------------------------------------------------------------------
/demo_pics/demo_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/demo_pics/demo_3.jpg
--------------------------------------------------------------------------------
/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 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Sep 03 11:45:39 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 |
--------------------------------------------------------------------------------
/libwechat.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiubadao/hayoou-wechat-export/6219741651776ab39a38bced72582eba24845b0b/libwechat.jar
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------