() {{
36 | put(strChatroomId, strChatroomId);
37 | }});
38 | Object[] objectParamiVar = new Object[]{objectiVar, 0};
39 |
40 | //创建静态实例对象au.DF(),转换为com.tencent.mm.ab.o对象
41 | Class> classG = XposedHelpers.findClassIfExists("com.tencent.mm.kernel.g", loadPackageParam.classLoader);
42 | Object objectG = XposedHelpers.callStaticMethod(classG, "Eh");
43 | Object objectdpP = XposedHelpers.getObjectField(objectG, "dpP");
44 |
45 |
46 | //查找au.DF().a()方法
47 | Class> classDF = XposedHelpers.findClassIfExists("com.tencent.mm.ab.o", loadPackageParam.classLoader);
48 | Class> classI = XposedHelpers.findClassIfExists("com.tencent.mm.ab.l", loadPackageParam.classLoader);
49 | Method methodA = XposedHelpers.findMethodExactIfExists(classDF, "a", classI, int.class);
50 |
51 | //调用发消息方法
52 | try {
53 | XposedBridge.invokeOriginalMethod(methodA, objectdpP, objectParamiVar);
54 | XposedBridge.log("invokeOriginalMethod()执行成功");
55 | } catch (Exception e) {
56 | XposedBridge.log("调用微信消息回复方法异常");
57 | XposedBridge.log(e);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_about.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
20 |
21 |
25 |
26 |
30 |
31 |
36 |
37 |
47 |
48 |
55 |
56 |
60 |
61 |
65 |
66 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/java/net/dalu2048/wechatgenius/ui/user/LoginActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ************************************************************
3 | * 文件:LoginActivity.java 模块:app 项目:WeChatGenius
4 | * 当前修改时间:2018年08月20日 17:50:43
5 | * 上次修改时间:2018年08月20日 17:50:42
6 | * 作者:大路
7 | * Copyright (c) 2018
8 | * ************************************************************
9 | */
10 |
11 | package net.dalu2048.wechatgenius.ui.user;
12 |
13 | import android.app.Activity;
14 | import android.content.Intent;
15 | import android.os.Bundle;
16 | import android.util.Log;
17 | import android.view.LayoutInflater;
18 | import android.view.View;
19 | import android.widget.EditText;
20 | import android.widget.TextView;
21 | import android.widget.Toast;
22 |
23 | import com.qmuiteam.qmui.util.QMUIKeyboardHelper;
24 | import com.qmuiteam.qmui.util.QMUIStatusBarHelper;
25 | import com.qmuiteam.qmui.widget.QMUITopBar;
26 |
27 | import net.dalu2048.wechatgenius.MainActivity;
28 | import net.dalu2048.wechatgenius.R;
29 | import net.dalu2048.wechatgenius.ui.AboutActivity;
30 | import net.dalu2048.wechatgenius.util.RegexUtils;
31 | import net.dalu2048.wechatgenius.util.StringUtils;
32 |
33 | import butterknife.BindView;
34 | import butterknife.ButterKnife;
35 | import butterknife.OnClick;
36 |
37 | public class LoginActivity extends Activity {
38 | @BindView(R.id.topbar) QMUITopBar mTopBar;
39 | private final String TAG = getClass().getSimpleName();
40 |
41 | @Override
42 | protected void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | // 沉浸式状态栏
45 | QMUIStatusBarHelper.translucent(this);
46 | View root = LayoutInflater.from(this).inflate(R.layout.activity_login, null);
47 | ButterKnife.bind(this, root);
48 | //初始化状态栏
49 | initTopBar();
50 | //设置view
51 | setContentView(root);
52 |
53 | }
54 |
55 | //初始化状态栏
56 | private void initTopBar() {
57 | mTopBar.addRightImageButton(R.mipmap.icon_topbar_about, R.id.empty_button)
58 | .setOnClickListener(new View.OnClickListener() {
59 | @Override
60 | public void onClick(View view) {
61 | Intent intent = new Intent(LoginActivity.this, AboutActivity.class);
62 | startActivity(intent);
63 | }
64 | });
65 | mTopBar.setTitle(getResources().getString(R.string.activity_title_main));
66 | }
67 |
68 | //登录按钮
69 | @OnClick({R.id.button_login})
70 | void onClickButton(View view) {
71 | switch (view.getId()) {
72 | case R.id.button_login:
73 | onClickLogin(view);
74 | break;
75 | }
76 | }
77 |
78 | //关于、联系我们,文字点击事件处理
79 | @OnClick({R.id.textview_about})
80 | void onClickTextView(View view) {
81 | Intent intent = null;
82 | // Log.d(TAG, "登录页面,文字:" + ((TextView) view).getText() + " 被点击了。");
83 | switch (view.getId()) {
84 | case R.id.textview_about:
85 | intent = new Intent(LoginActivity.this, AboutActivity.class);
86 | break;
87 | }
88 | if (intent != null) {
89 | startActivity(intent);
90 | }
91 | }
92 |
93 | //登录按钮事件
94 | private void onClickLogin(View view) {
95 | String strName = ((EditText) findViewById(R.id.edittext_username)).getText().toString().trim();
96 | String strPassword = ((EditText) findViewById(R.id.edittext_password)).getText().toString().trim();
97 |
98 | if (!RegexUtils.isUsername(strName)) {
99 | Toast.makeText(this, "账号格式不正确!", Toast.LENGTH_SHORT).show();
100 | QMUIKeyboardHelper.showKeyboard((EditText) findViewById(R.id.edittext_username), 1500);
101 | return;
102 | }
103 | if (strPassword.length() < 6 || strPassword.length() > 20) {
104 | Toast.makeText(this, "密码长度为6到20位!", Toast.LENGTH_SHORT).show();
105 | QMUIKeyboardHelper.showKeyboard((EditText) findViewById(R.id.edittext_password), 1500);
106 | return;
107 | }
108 |
109 | Intent intent = new Intent(this, MainActivity.class);
110 | startActivity(intent);
111 | //结束本页面
112 | finish();
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/app/src/main/java/net/dalu2048/wechatgenius/MainXposed.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ************************************************************
3 | * 文件:MainXposed.java 模块:app 项目:WeChatGenius
4 | * 当前修改时间:2018年08月19日 17:06:09
5 | * 上次修改时间:2018年08月19日 17:06:09
6 | * 作者:大路
7 | * Copyright (c) 2018
8 | * ************************************************************
9 | */
10 |
11 | package net.dalu2048.wechatgenius;
12 |
13 | import android.content.ContentValues;
14 |
15 | import net.dalu2048.wechatgenius.xposed.WechatUtils;
16 |
17 | import de.robv.android.xposed.IXposedHookLoadPackage;
18 | import de.robv.android.xposed.XC_MethodHook;
19 | import de.robv.android.xposed.XC_MethodReplacement;
20 | import de.robv.android.xposed.XposedBridge;
21 | import de.robv.android.xposed.XposedHelpers;
22 | import de.robv.android.xposed.callbacks.XC_LoadPackage;
23 |
24 | public final class MainXposed implements IXposedHookLoadPackage {
25 | //微信数据库包名称
26 | private static final String WECHAT_DATABASE_PACKAGE_NAME = "com.tencent.wcdb.database.SQLiteDatabase";
27 | //聊天精灵客户端包名称
28 | private static final String WECHATGENIUS_PACKAGE_NAME = "net.dalu2048.wechatgenius";
29 | //微信主进程名
30 | private static final String WECHAT_PROCESS_NAME = "com.tencent.mm";
31 |
32 | @Override
33 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
34 |
35 | //region hook模块是否激活
36 | if (lpparam.packageName.equals(WECHATGENIUS_PACKAGE_NAME)) {
37 | //hook客户端APP的是否激活返回值。替换为true。
38 | Class> classAppUtils = XposedHelpers.findClassIfExists(WECHATGENIUS_PACKAGE_NAME + ".util.AppUtils", lpparam.classLoader);
39 | if (classAppUtils != null) {
40 | XposedHelpers.findAndHookMethod(classAppUtils,
41 | "isModuleActive",
42 | XC_MethodReplacement.returnConstant(true));
43 | XposedBridge.log("成功hook住net.xxfeng.cc.util.AppUtils的isModuleActive方法。");
44 | }
45 | return;
46 | }
47 | //endregion
48 |
49 | if (!lpparam.processName.equals(WECHAT_PROCESS_NAME)) {
50 | return;
51 | }
52 | XposedBridge.log("进入微信进程:" + lpparam.processName);
53 | //调用 hook数据库插入。
54 | hookDatabaseInsert(lpparam);
55 | }
56 |
57 | //hook数据库插入操作
58 | private void hookDatabaseInsert(final XC_LoadPackage.LoadPackageParam loadPackageParam) {
59 | Class> classDb = XposedHelpers.findClassIfExists(WECHAT_DATABASE_PACKAGE_NAME, loadPackageParam.classLoader);
60 | if (classDb == null) {
61 | XposedBridge.log("hook数据库insert操作:未找到类" + WECHAT_DATABASE_PACKAGE_NAME);
62 | return;
63 | }
64 | XposedHelpers.findAndHookMethod(classDb,
65 | "insertWithOnConflict",
66 | String.class, String.class, ContentValues.class, int.class,
67 | new XC_MethodHook() {
68 | @Override
69 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
70 | String tableName = (String) param.args[0];
71 | ContentValues contentValues = (ContentValues) param.args[2];
72 | if (tableName == null || tableName.length() == 0 || contentValues == null) {
73 | return;
74 | }
75 | //过滤掉非聊天消息
76 | if (!tableName.equals("message")) {
77 | return;
78 | }
79 | //打印出日志
80 | // printInsertLog(tableName, (String) param.args[1], contentValues, (Integer) param.args[3]);
81 |
82 | //提取消息内容
83 | //1:表示是自己发送的消息
84 | int isSend = contentValues.getAsInteger("isSend");
85 | //消息内容
86 | String strContent = contentValues.getAsString("content");
87 | //说话人ID
88 | String strTalker = contentValues.getAsString("talker");
89 | //收到消息,进行回复(要判断不是自己发送的、不是群消息、不是公众号消息,才回复)
90 | if (isSend != 1 && !strTalker.endsWith("@chatroom") && !strTalker.startsWith("gh_")) {
91 | WechatUtils.replyTextMessage(loadPackageParam, "回复:" + strContent, strTalker);
92 | }
93 | }
94 | });
95 | }
96 |
97 | //输出插入操作日志
98 | private void printInsertLog(String tableName, String nullColumnHack, ContentValues contentValues, int conflictValue) {
99 | String[] arrayConflicValues =
100 | {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
101 | if (conflictValue < 0 || conflictValue > 5) {
102 | return;
103 | }
104 | XposedBridge.log("Hook数据库insert。table:" + tableName
105 | + ";nullColumnHack:" + nullColumnHack
106 | + ";CONFLICT_VALUES:" + arrayConflicValues[conflictValue]
107 | + ";contentValues:" + contentValues);
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/app/src/main/java/net/dalu2048/wechatgenius/constant/RegexConstants.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ************************************************************
3 | * 文件:RegexConstants.java 模块:app 项目:WeChatGenius
4 | * 当前修改时间:2018年08月20日 18:55:07
5 | * 上次修改时间:2018年08月06日 11:55:28
6 | * 作者:大路
7 | * Copyright (c) 2018
8 | * ************************************************************
9 | */
10 |
11 | package net.dalu2048.wechatgenius.constant;
12 |
13 | /**
14 | *
15 | * author: Blankj
16 | * blog : http://blankj.com
17 | * time : 2017/03/13
18 | * desc : constants of regex
19 | *
20 | */
21 | public final class RegexConstants {
22 |
23 | /**
24 | * Regex of simple mobile.
25 | */
26 | public static final String REGEX_MOBILE_SIMPLE = "^[1]\\d{10}$";
27 | /**
28 | * Regex of exact mobile.
29 | * china mobile: 134(0-8), 135, 136, 137, 138, 139, 147, 150, 151, 152, 157, 158, 159, 178, 182, 183, 184, 187, 188, 198
30 | * china unicom: 130, 131, 132, 145, 155, 156, 166, 171, 175, 176, 185, 186
31 | * china telecom: 133, 153, 173, 177, 180, 181, 189, 199
32 | * global star: 1349
33 | * virtual operator: 170
34 | */
35 | public static final String REGEX_MOBILE_EXACT = "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(16[6])|(17[0,1,3,5-8])|(18[0-9])|(19[8,9]))\\d{8}$";
36 | /**
37 | * Regex of telephone number.
38 | */
39 | public static final String REGEX_TEL = "^0\\d{2,3}[- ]?\\d{7,8}";
40 | /**
41 | * Regex of id card number which length is 15.
42 | */
43 | public static final String REGEX_ID_CARD15 = "^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$";
44 | /**
45 | * Regex of id card number which length is 18.
46 | */
47 | public static final String REGEX_ID_CARD18 = "^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9Xx])$";
48 | /**
49 | * Regex of email.
50 | */
51 | public static final String REGEX_EMAIL = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
52 | /**
53 | * Regex of url.
54 | */
55 | public static final String REGEX_URL = "[a-zA-z]+://[^\\s]*";
56 | /**
57 | * Regex of Chinese character.
58 | */
59 | public static final String REGEX_ZH = "^[\\u4e00-\\u9fa5]+$";
60 | /**
61 | * Regex of username.
62 | * scope for "a-z", "A-Z", "0-9", "_", "Chinese character"
63 | * can't end with "_"
64 | * length is between 6 to 20
65 | */
66 | public static final String REGEX_USERNAME = "^[\\w\\u4e00-\\u9fa5]{2,20}(? \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/java/net/dalu2048/wechatgenius/entity/AppInfo.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ************************************************************
3 | * 文件:AppInfo.java 模块:app 项目:WeChatGenius
4 | * 当前修改时间:2018年08月20日 11:38:43
5 | * 上次修改时间:2018年08月20日 11:38:42
6 | * 作者:大路
7 | * Copyright (c) 2018
8 | * ************************************************************
9 | */
10 |
11 | package net.dalu2048.wechatgenius.entity;
12 |
13 | import android.content.Context;
14 | import android.os.Build;
15 |
16 | import net.dalu2048.wechatgenius.constant.SupportVersion;
17 | import net.dalu2048.wechatgenius.util.AppUtils;
18 | import net.dalu2048.wechatgenius.util.ShellUtil;
19 | import net.dalu2048.wechatgenius.util.StringUtils;
20 |
21 | public final class AppInfo {
22 | //Android系统版本
23 | private String mAndroidVersionName;
24 | private boolean mIsDeviceRooted; //设备是否ROOT
25 | private boolean mIsSupportAndroid; //是否支持该版本Android
26 | //微信精灵版本号
27 | private String mVersionName;
28 | private int mVersionCode;
29 | //Xposed是否安装
30 | private boolean mIsXposedInstall;
31 | private String mXposedVersionName;
32 | //微信是否安装、版本号
33 | private boolean mIsWechatInstall;
34 | private String mWechatVersionName;
35 | private boolean mIsSupportWechat;
36 |
37 | //region 单例
38 | //单例
39 | private static volatile AppInfo mInstance = null;
40 |
41 | private AppInfo() {
42 | }
43 |
44 | public static AppInfo getInstance() {
45 | if (mInstance == null) {
46 | synchronized (AppInfo.class) {
47 | if (mInstance == null) {
48 | mInstance = new AppInfo();
49 | }
50 | }
51 | }
52 | return mInstance;
53 | }
54 |
55 | //endregion
56 |
57 | //检测设备环境
58 | public void ValidateEnvironment(Context context) {
59 | //判断root
60 | mIsDeviceRooted = ShellUtil.checkRootPermission();
61 | //Android系统版本
62 | mAndroidVersionName = Build.VERSION.RELEASE;
63 |
64 | //获取微信精灵版本号
65 | mVersionName = AppUtils.getVersionName(context);
66 | mVersionCode = AppUtils.getVersionCode(context);
67 | //判断微信
68 | mWechatVersionName = AppUtils.getAppVersionName(context, AppUtils.PACKAGE_NAME_WECHAT);
69 | mIsWechatInstall = !StringUtils.isEmpty(getWechatVersionName());
70 | //判断Xposed是否安装
71 | mXposedVersionName = AppUtils.getAppVersionName(context, AppUtils.PACKAGE_NAME_XPOSED);
72 | mIsXposedInstall = !StringUtils.isEmpty(getXposedVersionName());
73 |
74 | //检测安卓版本是否符合要求
75 | mIsSupportAndroid = isContainAndroidVersion();
76 | //判断微信版本是否符合要求
77 | mIsSupportWechat = mIsWechatInstall && isContainWechatVersion();
78 | }
79 |
80 | //获取微信版本是否包含在支持列表中
81 | private boolean isContainWechatVersion() {
82 | if (mWechatVersionName == null) {
83 | return false;
84 | }
85 | for (int i = 0; i < SupportVersion.wechatSupport.length; i++) {
86 | if (mWechatVersionName.equals(SupportVersion.wechatSupport[i])) {
87 | return true;
88 | }
89 | }
90 | return false;
91 | }
92 |
93 | //获取Android版本是否包含在支持列表中
94 | private boolean isContainAndroidVersion() {
95 | if (mAndroidVersionName == null) {
96 | return false;
97 | }
98 | for (int i = 0; i < SupportVersion.androidSupport.length; i++) {
99 | if (mAndroidVersionName.equals(SupportVersion.androidSupport[i])) {
100 | return true;
101 | }
102 | }
103 | return false;
104 | }
105 |
106 | //获取Xposed模块是否激活
107 | public boolean isXposedActive() {
108 | return AppUtils.isModuleActive();
109 | }
110 |
111 | public boolean isXposedInstall() {
112 | return mIsXposedInstall;
113 | }
114 |
115 | public void setXposedInstall(boolean xposedInstall) {
116 | mIsXposedInstall = xposedInstall;
117 | }
118 |
119 | public String getXposedVersionName() {
120 | return mXposedVersionName;
121 | }
122 |
123 | public void setXposedVersionName(String xposedVersionName) {
124 | mXposedVersionName = xposedVersionName;
125 | }
126 |
127 | public String getAndroidVersionName() {
128 | return mAndroidVersionName;
129 | }
130 |
131 | public void setAndroidVersionName(String androidVersionName) {
132 | mAndroidVersionName = androidVersionName;
133 | }
134 |
135 | public boolean isDeviceRooted() {
136 | return mIsDeviceRooted;
137 | }
138 |
139 | public void setDeviceRooted(boolean deviceRooted) {
140 | mIsDeviceRooted = deviceRooted;
141 | }
142 |
143 | public boolean isSupportAndroid() {
144 | return mIsSupportAndroid;
145 | }
146 |
147 | public void setSupportAndroid(boolean supportAndroid) {
148 | mIsSupportAndroid = supportAndroid;
149 | }
150 |
151 | public boolean isWechatInstall() {
152 | return mIsWechatInstall;
153 | }
154 |
155 | public void setWechatInstall(boolean wechatInstall) {
156 | mIsWechatInstall = wechatInstall;
157 | }
158 |
159 | public String getWechatVersionName() {
160 | return mWechatVersionName;
161 | }
162 |
163 | public void setWechatVersionName(String wechatVersionName) {
164 | mWechatVersionName = wechatVersionName;
165 | }
166 |
167 | public boolean isSupportWechat() {
168 | return mIsSupportWechat;
169 | }
170 |
171 | public void setSupportWechat(boolean supportWechat) {
172 | mIsSupportWechat = supportWechat;
173 | }
174 |
175 | public String getVersionName() {
176 | return mVersionName;
177 | }
178 |
179 | public void setVersionName(String versionName) {
180 | mVersionName = versionName;
181 | }
182 |
183 | public int getVersionCode() {
184 | return mVersionCode;
185 | }
186 |
187 | public void setVersionCode(int versionCode) {
188 | mVersionCode = versionCode;
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/java/net/dalu2048/wechatgenius/ui/AboutActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ************************************************************
3 | * 文件:AboutActivity.java 模块:app 项目:WeChatGenius
4 | * 当前修改时间:2018年08月20日 19:34:19
5 | * 上次修改时间:2018年08月20日 19:34:18
6 | * 作者:大路
7 | * Copyright (c) 2018
8 | * ************************************************************
9 | */
10 |
11 | package net.dalu2048.wechatgenius.ui;
12 |
13 | import android.app.Activity;
14 | import android.content.ClipData;
15 | import android.content.ClipboardManager;
16 | import android.content.Context;
17 | import android.content.Intent;
18 | import android.net.Uri;
19 | import android.os.Bundle;
20 | import android.util.Log;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.widget.TextView;
24 | import android.widget.Toast;
25 |
26 | import com.qmuiteam.qmui.util.QMUIStatusBarHelper;
27 | import com.qmuiteam.qmui.widget.QMUITopBar;
28 | import com.qmuiteam.qmui.widget.grouplist.QMUICommonListItemView;
29 | import com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView;
30 |
31 | import net.dalu2048.wechatgenius.MainActivity;
32 | import net.dalu2048.wechatgenius.R;
33 | import net.dalu2048.wechatgenius.entity.AppInfo;
34 | import net.dalu2048.wechatgenius.util.AppUtils;
35 |
36 | import java.text.SimpleDateFormat;
37 | import java.util.Locale;
38 |
39 | import butterknife.BindView;
40 | import butterknife.ButterKnife;
41 |
42 | public class AboutActivity extends Activity {
43 | @BindView(R.id.topbar) QMUITopBar mTopBar;
44 | @BindView(R.id.version) TextView mVersionTextView;
45 | @BindView(R.id.about_list) QMUIGroupListView mAboutGroupListView;
46 | @BindView(R.id.copyright) TextView mCopyrightTextView;
47 |
48 | final String TAG = getClass().getSimpleName();
49 |
50 | @Override
51 | protected void onCreate(Bundle savedInstanceState) {
52 | super.onCreate(savedInstanceState);
53 | // 沉浸式状态栏
54 | QMUIStatusBarHelper.translucent(this);
55 | View root = LayoutInflater.from(this).inflate(R.layout.activity_about, null);
56 | ButterKnife.bind(this, root);
57 | //初始化状态栏
58 | initTopBar();
59 | //设置view
60 | setContentView(root);
61 |
62 | //初始化about列表内容
63 | initAboutList();
64 | }
65 |
66 | //初始化状态栏
67 | private void initTopBar() {
68 | mTopBar.addLeftBackImageButton().setOnClickListener(new View.OnClickListener() {
69 | @Override
70 | public void onClick(View view) {
71 | finish();
72 | }
73 | });
74 | mTopBar.setTitle(getString(R.string.activity_title_about));
75 | }
76 |
77 | //列表项点击事件处理
78 | View.OnClickListener mOnClickListenerListItem = new View.OnClickListener() {
79 | @Override
80 | public void onClick(View view) {
81 | //region 列表项点击事件
82 | QMUICommonListItemView listItemView = (QMUICommonListItemView) view;
83 | int tag = (int) listItemView.getTag();
84 | Intent intent;
85 |
86 | switch (tag) {
87 | case R.id.listitem_tag_1: //复制微信
88 | copyToClipboard("微信", listItemView.getDetailText().toString());
89 | break;
90 | case R.id.listitem_tag_2: //复制QQ
91 | copyToClipboard("QQ", listItemView.getDetailText().toString());
92 | break;
93 | case R.id.listitem_tag_3: //访问GitHub网址
94 | openWebsite(listItemView.getDetailText().toString());
95 | break;
96 | }
97 | //endregion
98 | }
99 | };
100 |
101 | //访问网址
102 | private void openWebsite(String websiteUrl) {
103 | Intent intent = new Intent(Intent.ACTION_VIEW);
104 | intent.setData(Uri.parse(websiteUrl));
105 | startActivity(intent);
106 | }
107 |
108 | //复制数据到剪贴板
109 | private void copyToClipboard(String label, String string) {
110 | //复制
111 | ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
112 | ClipData clipData = ClipData.newPlainText(label, string);
113 | if (clipboardManager != null) {
114 | clipboardManager.setPrimaryClip(clipData);
115 | Toast.makeText(this, "成功复制" + label, Toast.LENGTH_SHORT).show();
116 | } else {
117 | Toast.makeText(this, "获取剪贴板失败,无法复制", Toast.LENGTH_SHORT).show();
118 | }
119 | }
120 |
121 |
122 | //初始化QMUIGroupListView
123 | private void initAboutList() {
124 | //初始化LOGO下面的名称和版本号
125 | mVersionTextView.setText(String.format("%s V%s", getResources().getString(R.string.activity_title_main),
126 | AppUtils.getVersionName(this)));
127 |
128 | //作者
129 | QMUICommonListItemView itemAuthor = mAboutGroupListView.createItemView("作者");
130 | itemAuthor.setDetailText("大路");
131 | //微信
132 | QMUICommonListItemView itemWechat = mAboutGroupListView.createItemView("微信");
133 | itemWechat.setAccessoryType(QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON);
134 | itemWechat.setDetailText("dalu2048");
135 | itemWechat.setTag(R.id.listitem_tag_1);
136 | //QQ
137 | QMUICommonListItemView itemQQ = mAboutGroupListView.createItemView("QQ");
138 | itemQQ.setAccessoryType(QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON);
139 | itemQQ.setDetailText("1801099979");
140 | itemQQ.setTag(R.id.listitem_tag_2);
141 | //GitHub主页
142 | QMUICommonListItemView itemGitHub = mAboutGroupListView.createItemView("GitHub主页");
143 | itemGitHub.setOrientation(QMUICommonListItemView.VERTICAL);
144 | itemGitHub.setDetailText("https://github.com/dalu2048/WeChatGenius");
145 | itemGitHub.setAccessoryType(QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON);
146 | itemGitHub.setTag(R.id.listitem_tag_3);
147 | QMUIGroupListView.newSection(this)
148 | .setDescription(getString(R.string.about_description))
149 | .addItemView(itemAuthor, null)
150 | .addItemView(itemWechat, mOnClickListenerListItem)
151 | .addItemView(itemQQ, mOnClickListenerListItem)
152 | .addItemView(itemGitHub, mOnClickListenerListItem)
153 | .addTo(mAboutGroupListView);
154 |
155 | //初始化页面底部的版权
156 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy", Locale.CHINA);
157 | String currentYear = dateFormat.format(new java.util.Date());
158 | mCopyrightTextView.setText(String.format(getResources().getString(R.string.about_copyright), currentYear));
159 |
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
20 |
21 |
25 |
26 |
32 |
33 |
42 |
43 |
47 |
48 |
55 |
56 |
66 |
67 |
76 |
77 |
88 |
89 |
99 |
100 |
101 |
102 |
108 |
109 |
110 |
114 |
115 |
126 |
127 |
131 |
132 |
133 |
134 |
135 |
136 |
144 |
145 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/app/src/main/java/net/dalu2048/wechatgenius/util/RegexUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ************************************************************
3 | * 文件:RegexUtils.java 模块:app 项目:WeChatGenius
4 | * 当前修改时间:2018年08月20日 18:55:01
5 | * 上次修改时间:2018年08月06日 11:55:28
6 | * 作者:大路
7 | * Copyright (c) 2018
8 | * ************************************************************
9 | */
10 |
11 | package net.dalu2048.wechatgenius.util;
12 |
13 | import net.dalu2048.wechatgenius.constant.RegexConstants;
14 |
15 | import java.util.ArrayList;
16 | import java.util.Collections;
17 | import java.util.List;
18 | import java.util.regex.Matcher;
19 | import java.util.regex.Pattern;
20 |
21 | /**
22 | *
23 | * author: Blankj
24 | * blog : http://blankj.com
25 | * time : 2016/08/02
26 | * desc : utils about regex
27 | *
28 | */
29 | public final class RegexUtils {
30 |
31 | private RegexUtils() {
32 | throw new UnsupportedOperationException("u can't instantiate me...");
33 | }
34 |
35 | ///////////////////////////////////////////////////////////////////////////
36 | // If u want more please visit http://toutiao.com/i6231678548520731137
37 | ///////////////////////////////////////////////////////////////////////////
38 |
39 | /**
40 | * Return whether input matches regex of simple mobile.
41 | *
42 | * @param input The input.
43 | * @return {@code true}: yes
{@code false}: no
44 | */
45 | public static boolean isMobileSimple(final CharSequence input) {
46 | return isMatch(RegexConstants.REGEX_MOBILE_SIMPLE, input);
47 | }
48 |
49 | /**
50 | * Return whether input matches regex of exact mobile.
51 | *
52 | * @param input The input.
53 | * @return {@code true}: yes
{@code false}: no
54 | */
55 | public static boolean isMobileExact(final CharSequence input) {
56 | return isMatch(RegexConstants.REGEX_MOBILE_EXACT, input);
57 | }
58 |
59 | /**
60 | * Return whether input matches regex of telephone number.
61 | *
62 | * @param input The input.
63 | * @return {@code true}: yes
{@code false}: no
64 | */
65 | public static boolean isTel(final CharSequence input) {
66 | return isMatch(RegexConstants.REGEX_TEL, input);
67 | }
68 |
69 | /**
70 | * Return whether input matches regex of id card number which length is 15.
71 | *
72 | * @param input The input.
73 | * @return {@code true}: yes
{@code false}: no
74 | */
75 | public static boolean isIDCard15(final CharSequence input) {
76 | return isMatch(RegexConstants.REGEX_ID_CARD15, input);
77 | }
78 |
79 | /**
80 | * Return whether input matches regex of id card number which length is 18.
81 | *
82 | * @param input The input.
83 | * @return {@code true}: yes
{@code false}: no
84 | */
85 | public static boolean isIDCard18(final CharSequence input) {
86 | return isMatch(RegexConstants.REGEX_ID_CARD18, input);
87 | }
88 |
89 | /**
90 | * Return whether input matches regex of email.
91 | *
92 | * @param input The input.
93 | * @return {@code true}: yes
{@code false}: no
94 | */
95 | public static boolean isEmail(final CharSequence input) {
96 | return isMatch(RegexConstants.REGEX_EMAIL, input);
97 | }
98 |
99 | /**
100 | * Return whether input matches regex of url.
101 | *
102 | * @param input The input.
103 | * @return {@code true}: yes
{@code false}: no
104 | */
105 | public static boolean isURL(final CharSequence input) {
106 | return isMatch(RegexConstants.REGEX_URL, input);
107 | }
108 |
109 | /**
110 | * Return whether input matches regex of Chinese character.
111 | *
112 | * @param input The input.
113 | * @return {@code true}: yes
{@code false}: no
114 | */
115 | public static boolean isZh(final CharSequence input) {
116 | return isMatch(RegexConstants.REGEX_ZH, input);
117 | }
118 |
119 | /**
120 | * Return whether input matches regex of username.
121 | * scope for "a-z", "A-Z", "0-9", "_", "Chinese character"
122 | * can't end with "_"
123 | * length is between 6 to 20
.
124 | *
125 | * @param input The input.
126 | * @return {@code true}: yes
{@code false}: no
127 | */
128 | public static boolean isUsername(final CharSequence input) {
129 | return isMatch(RegexConstants.REGEX_USERNAME, input);
130 | }
131 |
132 | /**
133 | * Return whether input matches regex of date which pattern is "yyyy-MM-dd".
134 | *
135 | * @param input The input.
136 | * @return {@code true}: yes
{@code false}: no
137 | */
138 | public static boolean isDate(final CharSequence input) {
139 | return isMatch(RegexConstants.REGEX_DATE, input);
140 | }
141 |
142 | /**
143 | * Return whether input matches regex of ip address.
144 | *
145 | * @param input The input.
146 | * @return {@code true}: yes
{@code false}: no
147 | */
148 | public static boolean isIP(final CharSequence input) {
149 | return isMatch(RegexConstants.REGEX_IP, input);
150 | }
151 |
152 | /**
153 | * Return whether input matches the regex.
154 | *
155 | * @param regex The regex.
156 | * @param input The input.
157 | * @return {@code true}: yes
{@code false}: no
158 | */
159 | public static boolean isMatch(final String regex, final CharSequence input) {
160 | return input != null && input.length() > 0 && Pattern.matches(regex, input);
161 | }
162 |
163 | /**
164 | * Return the list of input matches the regex.
165 | *
166 | * @param regex The regex.
167 | * @param input The input.
168 | * @return the list of input matches the regex
169 | */
170 | public static List getMatches(final String regex, final CharSequence input) {
171 | if (input == null) return Collections.emptyList();
172 | List matches = new ArrayList<>();
173 | Pattern pattern = Pattern.compile(regex);
174 | Matcher matcher = pattern.matcher(input);
175 | while (matcher.find()) {
176 | matches.add(matcher.group());
177 | }
178 | return matches;
179 | }
180 |
181 | /**
182 | * Splits input around matches of the regex.
183 | *
184 | * @param input The input.
185 | * @param regex The regex.
186 | * @return the array of strings computed by splitting input around matches of regex
187 | */
188 | public static String[] getSplits(final String input, final String regex) {
189 | if (input == null) return new String[0];
190 | return input.split(regex);
191 | }
192 |
193 | /**
194 | * Replace the first subsequence of the input sequence that matches the
195 | * regex with the given replacement string.
196 | *
197 | * @param input The input.
198 | * @param regex The regex.
199 | * @param replacement The replacement string.
200 | * @return the string constructed by replacing the first matching
201 | * subsequence by the replacement string, substituting captured
202 | * subsequences as needed
203 | */
204 | public static String getReplaceFirst(final String input,
205 | final String regex,
206 | final String replacement) {
207 | if (input == null) return "";
208 | return Pattern.compile(regex).matcher(input).replaceFirst(replacement);
209 | }
210 |
211 | /**
212 | * Replace every subsequence of the input sequence that matches the
213 | * pattern with the given replacement string.
214 | *
215 | * @param input The input.
216 | * @param regex The regex.
217 | * @param replacement The replacement string.
218 | * @return the string constructed by replacing each matching subsequence
219 | * by the replacement string, substituting captured subsequences
220 | * as needed
221 | */
222 | public static String getReplaceAll(final String input,
223 | final String regex,
224 | final String replacement) {
225 | if (input == null) return "";
226 | return Pattern.compile(regex).matcher(input).replaceAll(replacement);
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/app/src/main/java/net/dalu2048/wechatgenius/util/ShellUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ************************************************************
3 | * 文件:ShellUtil.java 模块:app 项目:WeChatGenius
4 | * 当前修改时间:2018年08月20日 12:52:17
5 | * 上次修改时间:2018年08月06日 11:44:57
6 | * 作者:大路
7 | * Copyright (c) 2018
8 | * ************************************************************
9 | */
10 |
11 | package net.dalu2048.wechatgenius.util;
12 | import java.io.BufferedReader;
13 | import java.io.DataOutputStream;
14 | import java.io.IOException;
15 | import java.io.InputStreamReader;
16 | import java.util.List;
17 |
18 | /**
19 | * ShellUtil
20 | *
21 | * Check root
22 | * - {@link ShellUtil#checkRootPermission()}
23 | *
24 | *
25 | * Execte command
26 | * - {@link ShellUtil#execCommand(String, boolean)}
27 | * - {@link ShellUtil#execCommand(String, boolean, boolean)}
28 | * - {@link ShellUtil#execCommand(List, boolean)}
29 | * - {@link ShellUtil#execCommand(List, boolean, boolean)}
30 | * - {@link ShellUtil#execCommand(String[], boolean)}
31 | * - {@link ShellUtil#execCommand(String[], boolean, boolean)}
32 | *
33 | *
34 | * @author Trinea 2013-5-16
35 | */
36 | public class ShellUtil {
37 |
38 | public static final String COMMAND_SU = "su";
39 | public static final String COMMAND_SH = "sh";
40 | public static final String COMMAND_EXIT = "exit\n";
41 | public static final String COMMAND_LINE_END = "\n";
42 |
43 | private ShellUtil() {
44 | throw new AssertionError();
45 | }
46 |
47 | /**
48 | * check whether has root permission
49 | */
50 | public static boolean checkRootPermission() {
51 | return execCommand("echo root", true, false).result == 0;
52 | }
53 |
54 | /**
55 | * execute shell command, default return result msg
56 | *
57 | * @param command command
58 | * @param isRoot whether need to run with root
59 | * @see ShellUtil#execCommand(String[], boolean, boolean)
60 | */
61 | public static CommandResult execCommand(String command, boolean isRoot) {
62 | return execCommand(new String[] { command }, isRoot, true);
63 | }
64 |
65 | /**
66 | * execute shell commands, default return result msg
67 | *
68 | * @param commands command list
69 | * @param isRoot whether need to run with root
70 | * @see ShellUtil#execCommand(String[], boolean, boolean)
71 | */
72 | public static CommandResult execCommand(List commands, boolean isRoot) {
73 | return execCommand(commands == null ? null : commands.toArray(new String[] {}), isRoot, true);
74 | }
75 |
76 | /**
77 | * execute shell commands, default return result msg
78 | *
79 | * @param commands command array
80 | * @param isRoot whether need to run with root
81 | * @see ShellUtil#execCommand(String[], boolean, boolean)
82 | */
83 | public static CommandResult execCommand(String[] commands, boolean isRoot) {
84 | return execCommand(commands, isRoot, true);
85 | }
86 |
87 | /**
88 | * execute shell command
89 | *
90 | * @param command command
91 | * @param isRoot whether need to run with root
92 | * @param isNeedResultMsg whether need result msg
93 | * @see ShellUtil#execCommand(String[], boolean, boolean)
94 | */
95 | public static CommandResult execCommand(String command, boolean isRoot, boolean isNeedResultMsg) {
96 | return execCommand(new String[] { command }, isRoot, isNeedResultMsg);
97 | }
98 |
99 | /**
100 | * execute shell commands
101 | *
102 | * @param commands command list
103 | * @param isRoot whether need to run with root
104 | * @param isNeedResultMsg whether need result msg
105 | * @see ShellUtil#execCommand(String[], boolean, boolean)
106 | */
107 | public static CommandResult execCommand(List commands, boolean isRoot,
108 | boolean isNeedResultMsg) {
109 | return execCommand(commands == null ? null : commands.toArray(new String[] {}), isRoot,
110 | isNeedResultMsg);
111 | }
112 |
113 | /**
114 | * execute shell commands
115 | *
116 | * @param commands command array
117 | * @param isRoot whether need to run with root
118 | * @param isNeedResultMsg whether need result msg
119 | * @return
120 | * - if isNeedResultMsg is false, {@link CommandResult#successMsg} is null and
121 | * {@link CommandResult#errorMsg} is null.
122 | * - if {@link CommandResult#result} is -1, there maybe some excepiton.
123 | *
124 | */
125 | public static CommandResult execCommand(String[] commands, boolean isRoot,
126 | boolean isNeedResultMsg) {
127 | int result = -1;
128 | if (commands == null || commands.length == 0) {
129 | return new CommandResult(result, null, null);
130 | }
131 |
132 | Process process = null;
133 | BufferedReader successResult = null;
134 | BufferedReader errorResult = null;
135 | StringBuilder successMsg = null;
136 | StringBuilder errorMsg = null;
137 |
138 | DataOutputStream os = null;
139 | try {
140 | process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH);
141 | os = new DataOutputStream(process.getOutputStream());
142 | for (String command : commands) {
143 | if (command == null) {
144 | continue;
145 | }
146 |
147 | // don't use os.writeBytes(commmand), avoid chinese charset error
148 | os.write(command.getBytes());
149 | os.writeBytes(COMMAND_LINE_END);
150 | os.flush();
151 | }
152 | os.writeBytes(COMMAND_EXIT);
153 | os.flush();
154 |
155 | result = process.waitFor();
156 | // get command result
157 | if (isNeedResultMsg) {
158 | successMsg = new StringBuilder();
159 | errorMsg = new StringBuilder();
160 | successResult = new BufferedReader(new InputStreamReader(process.getInputStream()));
161 | errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream()));
162 | String s;
163 | while ((s = successResult.readLine()) != null) {
164 | successMsg.append(s);
165 | }
166 | while ((s = errorResult.readLine()) != null) {
167 | errorMsg.append(s);
168 | }
169 | }
170 | } catch (Exception e) {
171 | e.printStackTrace();
172 | } finally {
173 | try {
174 | if (os != null) {
175 | os.close();
176 | }
177 |
178 | if (successResult != null) {
179 | successResult.close();
180 | }
181 |
182 | if (errorResult != null) {
183 | errorResult.close();
184 | }
185 | } catch (IOException e) {
186 | e.printStackTrace();
187 | }
188 |
189 | if (process != null) {
190 | process.destroy();
191 | }
192 | }
193 |
194 | return new CommandResult(result, successMsg == null ? null : successMsg.toString(),
195 | errorMsg == null ? null : errorMsg.toString());
196 | }
197 |
198 | /**
199 | * result of command
200 | *
201 | * - {@link CommandResult#result} means result of command, 0 means normal, else means error,
202 | * same to excute in
203 | * linux shell
204 | * - {@link CommandResult#successMsg} means success message of command result
205 | * - {@link CommandResult#errorMsg} means error message of command result
206 | *
207 | *
208 | * @author Trinea 2013-5-16
209 | */
210 | public static class CommandResult {
211 |
212 | /** result of command **/
213 | public int result;
214 | /** success message of command result **/
215 | public String successMsg;
216 | /** error message of command result **/
217 | public String errorMsg;
218 |
219 | public CommandResult(int result) {
220 | this.result = result;
221 | }
222 |
223 | public CommandResult(int result, String successMsg, String errorMsg) {
224 | this.result = result;
225 | this.successMsg = successMsg;
226 | this.errorMsg = errorMsg;
227 | }
228 |
229 | @Override
230 | public String toString() {
231 | return "CommandResult{"
232 | + "result="
233 | + result
234 | + ", successMsg='"
235 | + successMsg
236 | + '\''
237 | + ", errorMsg='"
238 | + errorMsg
239 | + '\''
240 | + '}';
241 | }
242 | }
243 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
26 |
27 |
33 |
34 |
40 |
41 |
49 |
50 |
58 |
59 |
67 |
68 |
78 |
79 |
86 |
87 |
96 |
97 |
102 |
103 |
107 |
108 |
116 |
117 |
128 |
129 |
140 |
141 |
150 |
151 |
155 |
156 |
162 |
163 |
168 |
169 |
170 |
--------------------------------------------------------------------------------