├── .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 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 |