diff --git a/README.md b/README.md index 1746adc..02efe16 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ 感谢原作者 dss16694/WechatFp , 这么给力的项目 -#注意: 这是Xposed插件 +## 注意: 这是Xposed插件 使用步骤: 1. 下载并安装插件: https://github.com/eritpchy/WechatFp/releases/download/1.3/WechatFp-1.3-release.apk diff --git a/app/build.gradle b/app/build.gradle index bffb72d..522980f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,7 +10,9 @@ android { minSdkVersion 14 targetSdkVersion 26 versionCode 6 - versionName "1.3.0" + versionName "1.4.0" + buildConfigField "String", "APP_SETTINGS_NAME", "\"指紋設置\"" + buildConfigField "String", "APP_PRODUCT_NAME", "\"WeChatFp\"" } buildTypes { release { @@ -35,7 +37,7 @@ android.applicationVariants.all { variant -> println "Running app..." exec { executable = ADB_PATH - args = ['shell', "am start -n ${variant.applicationId}/.SettingsActivity"] + args = ['shell', "am start -n ${variant.applicationId}/.activity.HomeActivity"] } } } @@ -62,6 +64,7 @@ task debugWechat(dependsOn: "installDebug") { dependencies { implementation 'com.android.support:support-v4:26.0.2' implementation 'com.wei.android.lib:fingerprintidentify:1.2.1' - implementation 'com.crossbowffs.remotepreferences:remotepreferences:0.5' + implementation 'com.google.code.gson:gson:2.8.1' + implementation 'com.squareup.okhttp3:okhttp:3.8.1' provided 'de.robv.android.xposed:api:53' } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8d5d2fa..d5e3bbd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,16 +1,18 @@ - - - + + + + + - + @@ -20,22 +22,19 @@ + - - + + + - - + diff --git a/app/src/main/assets/xposed_init b/app/src/main/assets/xposed_init index 67df8a2..5e724e7 100644 --- a/app/src/main/assets/xposed_init +++ b/app/src/main/assets/xposed_init @@ -1 +1 @@ -com.yyxx.wechatfp.WalletBaseUI \ No newline at end of file +com.yyxx.wechatfp.xposed.WalletBaseUI \ No newline at end of file diff --git a/app/src/main/java/com/yyxx/wechatfp/Constant.java b/app/src/main/java/com/yyxx/wechatfp/Constant.java new file mode 100644 index 0000000..1bffa2d --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/Constant.java @@ -0,0 +1,16 @@ +package com.yyxx.wechatfp; + +/** + * Created by Jason on 2017/9/10. + */ + +public class Constant { + + public static final String PACKAGE_BANE_WECHAT = "com.tencent.mm"; + public static final String HELP_URL_WECHAT = "https://github.com/eritpchy/WechatFp/blob/master/doc/WeChat/README.md"; + public static final String PROJECT_URL = "https://github.com/eritpchy/WechatFp"; + public static final String UPDATE_URL_GITHUB = "https://api.github.com/repos/eritpchy/WechatFp/releases/latest"; + public static final String DONATE_ID_ALIPAY = "https://qr.alipay.com/FKX012222QIU52C6LATAB7"; + public static final String DONATE_ID_WECHAT = "wxp://f2f0-1_Hm7XAY7SXA3B_0R0v-VJD71jYCWog"; + +} diff --git a/app/src/main/java/com/yyxx/wechatfp/ObfuscationHelper.java b/app/src/main/java/com/yyxx/wechatfp/ObfuscationHelper.java deleted file mode 100644 index 94592b5..0000000 --- a/app/src/main/java/com/yyxx/wechatfp/ObfuscationHelper.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.yyxx.wechatfp; - -import de.robv.android.xposed.XposedHelpers; -import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; - -public class ObfuscationHelper { - public static int versionint=0; - public static int version_code=0; - - public static class MM_Classes { - public static Class PayUI,FetchUI,Payview,WalletBaseUI; - - private static void init(int idx, LoadPackageParam lpparam) throws Throwable { - PayUI = XposedHelpers.findClass("com.tencent.mm.plugin.wallet.pay.ui." + new String[]{"WalletPayUI","WalletPayUI","WalletPayUI","WalletPayUI"}[idx], lpparam.classLoader); - Payview = XposedHelpers.findClass("com.tencent.mm.plugin.wallet_core.ui." + new String[]{"l","l","l","l"}[idx], lpparam.classLoader); - FetchUI = XposedHelpers.findClass("com.tencent.mm.plugin.wallet.balance.ui." + new String[]{"WalletBalanceFetchPwdInputUI","WalletBalanceFetchPwdInputUI","WalletBalanceFetchPwdInputUI","WalletBalanceFetchPwdInputUI"}[idx], lpparam.classLoader); - WalletBaseUI = XposedHelpers.findClass("com.tencent.mm.wallet_core.ui." + new String[]{"WalletBaseUI","WalletBaseUI","WalletBaseUI","WalletBaseUI"}[idx], lpparam.classLoader); - } - } - - public static class MM_Fields { - public static String PaypwdEditText; - public static String PaypwdView; - public static String PayInputView; - public static String PayTitle; - public static String Passwd_Text; - - private static void init(int idx) throws Throwable { - PaypwdView = new String[]{"qVO","ryk","ryM","rLB"}[idx]; - PaypwdEditText = new String[]{"vyO","wjm","wjX","wDJ"}[idx]; - PayInputView = new String[]{"mOL","nnG","nnZ","nol"}[idx]; - PayTitle = new String[]{"qVK","ryg","ryI","rLw"}[idx]; - Passwd_Text = new String[]{"qVK","ryz","rzb","rLQ"}[idx]; - } - } - public static class MM_Res { - public static int Finger_icon; - public static int Finger_title; - public static int passwd_title; - - private static void init(int idx) throws Throwable { - Finger_icon=new int[]{2130838280,2130838289,2130838289,2130838298}[idx]; - Finger_title=new int[]{2131236833,2131236918,2131236918,2131236964}[idx]; - passwd_title=new int[]{2131236838,2131236923,2131236923,2131236969}[idx]; - } - } - - - public static boolean init(int versioncode, String versionName, LoadPackageParam lpparam) throws Throwable { - version_code = versioncode; - int versionIndex = isSupportedVersion(versioncode, versionName); - if (versionIndex < 0) { - return false; - } - MM_Classes.init(versionIndex, lpparam); - MM_Fields.init(versionIndex); - MM_Res.init(versionIndex); - return true; - } - - - public static int isSupportedVersion(int versioncode, String versionName) { - if(versionName.contains("6.5.8")){ - versionint=0; - return 0; - } - if(versionName.contains("6.5.10") && versioncode == 1080){ - versionint=1; - return 1; - } - if(versionName.contains("6.5.10") && versioncode == 1061){ - versionint=2; - return 2; - } - if(versionName.contains("6.5.13") && versioncode == 1100){ - versionint=3; - return 3; - } - return -1; - } -} diff --git a/app/src/main/java/com/yyxx/wechatfp/SettingsActivity.java b/app/src/main/java/com/yyxx/wechatfp/SettingsActivity.java deleted file mode 100644 index 0479638..0000000 --- a/app/src/main/java/com/yyxx/wechatfp/SettingsActivity.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.yyxx.wechatfp; - - -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.provider.Settings; -import android.util.Log; -import android.widget.Toast; - -import com.wei.android.lib.fingerprintidentify.FingerprintIdentify; -import com.yyxx.wechatfp.Utils.AESHelper; - - -/** - * A {@link PreferenceActivity} that presents a set of application settings. On - * handset devices, settings are presented as a single list. On tablets, - * settings are split by category, with category headers shown to the left of - * the list of settings. - *

- * See - * Android Design: Settings for design guidelines and the Settings - * API Guide for more information on developing a Settings UI. - */ -public class SettingsActivity extends PreferenceActivity implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { - /** - * A preference value change listener that updates the preference's summary - * to reflect its new value. - */ - private SharedPreferences prefs; - private EditTextPreference mPaypwd; - private CheckBoxPreference mEnable; - private FingerprintIdentify mFingerprintIdentify; - private static final String MOD_PREFS = "fp_settings"; - - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - prefs = XPreferenceProvider.getRemoteSharedPreference(this); - addPreferencesFromResource(R.xml.preference); - mEnable = (CheckBoxPreference) findPreference("enable_fp"); - mPaypwd = (EditTextPreference) findPreference("paypwd"); - mPaypwd.setText(prefs.getString("paypwd", "")); - mEnable.setChecked(prefs.getBoolean("enable_fp", false)); - mPaypwd.setOnPreferenceChangeListener(this); - mEnable.setOnPreferenceChangeListener(this); - mEnable.setOnPreferenceClickListener(this); - mPaypwd.setOnPreferenceClickListener(this); - mFingerprintIdentify = new FingerprintIdentify(this); - if (!mFingerprintIdentify.isHardwareEnable()) { - Toast.makeText(this, "指纹传感器不可用,请确认本机已配备指纹传感器", Toast.LENGTH_SHORT).show(); - mEnable.setChecked(false); - mEnable.setEnabled(false); - mPaypwd.setEnabled(false); - } else { - if (!mFingerprintIdentify.isRegisteredFingerprint()) { - Toast.makeText(this, "未录入指纹,请在设置中录入有效指纹", Toast.LENGTH_SHORT).show(); - mEnable.setChecked(false); - mEnable.setEnabled(false); - mPaypwd.setEnabled(false); - } - } - - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (preference == mPaypwd) { - SharedPreferences.Editor mEditor = prefs.edit(); - - String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID); - Log.e("deviceid", ANDROID_ID); - - String pwd = (String) newValue; - Log.e("deviceid", AESHelper.encrypt(pwd, ANDROID_ID)); - if (pwd.length() > 10) { - mEditor.putString("paypwd", pwd); - } else { - mEditor.putString("paypwd", AESHelper.encrypt(pwd, ANDROID_ID)); - } - return mEditor.commit(); - } - if (preference == mEnable) { - SharedPreferences.Editor mEditor = prefs.edit(); - mEditor.putBoolean("enable_fp", (boolean) newValue); - return mEditor.commit(); - } - return false; - } - - @Override - public boolean onPreferenceClick(Preference preference) { - if (preference == mPaypwd) { - mPaypwd.setText(prefs.getString("paypwd", "")); - } - return false; - } -} - diff --git a/app/src/main/java/com/yyxx/wechatfp/XPreferenceProvider.java b/app/src/main/java/com/yyxx/wechatfp/XPreferenceProvider.java deleted file mode 100644 index 97966d7..0000000 --- a/app/src/main/java/com/yyxx/wechatfp/XPreferenceProvider.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.yyxx.wechatfp; - -import android.content.Context; -import android.content.SharedPreferences; - -import com.crossbowffs.remotepreferences.RemotePreferenceProvider; -import com.crossbowffs.remotepreferences.RemotePreferences; - -public class XPreferenceProvider extends RemotePreferenceProvider { - - public static final String AUTHORITY = "com.yyxx.wechatfp.XPreferenceProvider"; - public static final String PREF_NAME = "main_prefs"; - - private static SharedPreferences sSharedPreferenceInstance; - - public static SharedPreferences getRemoteSharedPreference(Context context) { - if (sSharedPreferenceInstance == null) { - synchronized (XPreferenceProvider.class) { - if (sSharedPreferenceInstance == null) { - sSharedPreferenceInstance = new RemotePreferences(context, AUTHORITY, PREF_NAME); - } - } - } - return sSharedPreferenceInstance; - } - - public XPreferenceProvider() { - super(AUTHORITY, new String[] {PREF_NAME}); - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/yyxx/wechatfp/activity/HomeActivity.java b/app/src/main/java/com/yyxx/wechatfp/activity/HomeActivity.java new file mode 100644 index 0000000..da56fc9 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/activity/HomeActivity.java @@ -0,0 +1,68 @@ +package com.yyxx.wechatfp.activity; + + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.text.TextUtils; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.Toast; + +import com.yyxx.wechatfp.R; +import com.yyxx.wechatfp.adapter.PreferenceAdapter; +import com.yyxx.wechatfp.network.updateCheck.UpdateFactory; +import com.yyxx.wechatfp.util.Task; +import com.yyxx.wechatfp.util.UrlUtil; + +import java.util.ArrayList; +import java.util.List; + +import static com.yyxx.wechatfp.Constant.HELP_URL_WECHAT; +import static com.yyxx.wechatfp.Constant.PROJECT_URL; + +public class HomeActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { + + public static final String SETTINGS_NAME_HELP_WECHAT = "微信指纹"; + private static final String SETTINGS_NAME_CHECKUPDATE = "檢查更新"; + private static final String SETTINGS_NAME_WEBSIDE = "項目主頁"; + + private PreferenceAdapter mListAdapter; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.home); + + ListView listView = (ListView) findViewById(R.id.list); + List list = new ArrayList<>(); + list.add(new PreferenceAdapter.Data(SETTINGS_NAME_HELP_WECHAT, "查看使用教程")); + list.add(new PreferenceAdapter.Data(SETTINGS_NAME_CHECKUPDATE, "點擊檢查软件更新")); + list.add(new PreferenceAdapter.Data(SETTINGS_NAME_WEBSIDE, "訪問項目主頁")); + mListAdapter = new PreferenceAdapter(list); + listView.setAdapter(mListAdapter); + listView.setOnItemClickListener(this); + Task.onMain(1000L, new Runnable() { + @Override + public void run() { + UpdateFactory.doUpdateCheck(HomeActivity.this); + } + }); + } + + @Override + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + PreferenceAdapter.Data data = mListAdapter.getItem(i); + if (data == null || TextUtils.isEmpty(data.title)) { + return; + } + if (SETTINGS_NAME_HELP_WECHAT.equals(data.title)) { + WebActivity.openUrl(this, HELP_URL_WECHAT); + } else if (SETTINGS_NAME_CHECKUPDATE.equals(data.title)) { + UpdateFactory.doUpdateCheck(this, false, true); + } else if (SETTINGS_NAME_WEBSIDE.equals(data.title)) { + UrlUtil.openUrl(this, PROJECT_URL); + Toast.makeText(this, "如果您擁有Github賬戶, 別忘了給我的項目+個Star噢", Toast.LENGTH_LONG).show(); + } + } +} + diff --git a/app/src/main/java/com/yyxx/wechatfp/activity/WebActivity.java b/app/src/main/java/com/yyxx/wechatfp/activity/WebActivity.java new file mode 100644 index 0000000..dd9dff2 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/activity/WebActivity.java @@ -0,0 +1,45 @@ +package com.yyxx.wechatfp.activity; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.text.TextUtils; +import android.webkit.WebView; + +import com.yyxx.wechatfp.util.log.L; + + +/** + * Created by Jason on 2017/9/10. + */ + +public class WebActivity extends AppCompatActivity { + + public static final String TAG = WebActivity.class.getName(); + + public static void openUrl(Context context, String url) { + try { + Intent intent = new Intent(context, WebActivity.class); + intent.putExtra("url", url); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } catch (Exception e) { + L.e(e); + } + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + String url = intent.getStringExtra("url"); + + WebView webView = new WebView(this); + if (!TextUtils.isEmpty(url)) { + webView.loadUrl(url); + } + setContentView(webView); + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/adapter/PreferenceAdapter.java b/app/src/main/java/com/yyxx/wechatfp/adapter/PreferenceAdapter.java new file mode 100644 index 0000000..c061231 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/adapter/PreferenceAdapter.java @@ -0,0 +1,157 @@ +package com.yyxx.wechatfp.adapter; + +import android.content.Context; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.yyxx.wechatfp.util.DpUtil; +import com.yyxx.wechatfp.util.StyleUtil; + +import java.util.ArrayList; +import java.util.List; + +import static com.yyxx.wechatfp.util.StyleUtil.LINE_COLOR_DEFAULT; +import static com.yyxx.wechatfp.util.StyleUtil.TEXT_COLOR_SECONDARY; +import static com.yyxx.wechatfp.util.StyleUtil.TEXT_SIZE_SMALL; + +/** + * Created by Jason on 2017/9/9. + */ + +public class PreferenceAdapter extends BaseAdapter { + + + private final List mData; + + public PreferenceAdapter(List mData) { + this.mData = new ArrayList<>(mData); + } + + @Override + public int getCount() { + return mData.size(); + } + + @Override + public Data getItem(int i) { + return mData.get(i); + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public View getView(int position, View view, ViewGroup viewGroup) { + if (view == null) { + view = new ViewHolder(viewGroup.getContext()).itemView; + } + ViewHolder viewHolder = (ViewHolder) view.getTag(); + viewHolder.bind(position, (Data) getItem(position)); + return view; + } + + private class ViewHolder { + + private final LinearLayout itemView; + private final TextView titleText; + private final TextView subTitleText; + private final CheckBox selectBox; + private final View lineView; + + public ViewHolder(Context context) { + itemView = new LinearLayout(context); + itemView.setOrientation(LinearLayout.VERTICAL); + + LinearLayout rootHorizontalLayout = new LinearLayout(context); + rootHorizontalLayout.setOrientation(LinearLayout.HORIZONTAL); + rootHorizontalLayout.setWeightSum(1); + rootHorizontalLayout.setGravity(Gravity.CENTER_VERTICAL); + + LinearLayout verticalLayout = new LinearLayout(context); + verticalLayout.setOrientation(LinearLayout.VERTICAL); + + int defTextVPadding = DpUtil.dip2px(context, 2); + int defVPadding = DpUtil.dip2px(context, 15); + int defHPadding = DpUtil.dip2px(context, 5); + + titleText = new TextView(context); + StyleUtil.apply(titleText); + titleText.setPadding(0, defTextVPadding, 0, defTextVPadding); + + subTitleText = new TextView(context); + StyleUtil.apply(subTitleText); + subTitleText.setTextColor(TEXT_COLOR_SECONDARY); + subTitleText.setTextSize(TEXT_SIZE_SMALL); + subTitleText.setPadding(0, defTextVPadding, 0, defTextVPadding + defHPadding); + + + verticalLayout.setPadding(defHPadding, defVPadding, defHPadding, 0); + verticalLayout.addView(titleText); + verticalLayout.addView(subTitleText); + + selectBox = new CheckBox(context); + selectBox.setClickable(false); + selectBox.setFocusable(false); + selectBox.setFocusableInTouchMode(false); + LinearLayout.LayoutParams selectBoxLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + selectBoxLayoutParams.setMargins(0,0,defHPadding,0); + + rootHorizontalLayout.addView(verticalLayout, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1)); + rootHorizontalLayout.addView(selectBox, selectBoxLayoutParams); + + lineView = new View(context); + lineView.setBackgroundColor(LINE_COLOR_DEFAULT); + + itemView.addView(rootHorizontalLayout, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + itemView.addView(lineView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1)); + itemView.setTag(this); + } + + public void bind(int position, Data data) { + if (data == null) { + return; + } + int count = getCount(); + if (position < count - 1) { + lineView.setVisibility(View.VISIBLE); + } else { + lineView.setVisibility(View.GONE); + } + if (data.isSelection) { + selectBox.setVisibility(View.VISIBLE); + } else { + selectBox.setVisibility(View.GONE); + } + + selectBox.setChecked(data.selectionState); + titleText.setText(data.title); + subTitleText.setText(data.subTitle); + } + } + + public static class Data { + + public String title; + public String subTitle; + public boolean isSelection; + public boolean selectionState; + + public Data(String title, String subTitle) { + this(title, subTitle, false, false); + } + + public Data(String title, String subTitle, boolean isSelection, boolean selectionState) { + this.title = title; + this.subTitle = subTitle; + this.isSelection = isSelection; + this.selectionState = selectionState; + } + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/listener/OnDismissListener.java b/app/src/main/java/com/yyxx/wechatfp/listener/OnDismissListener.java new file mode 100644 index 0000000..68397d1 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/listener/OnDismissListener.java @@ -0,0 +1,12 @@ +package com.yyxx.wechatfp.listener; + +import android.view.View; + +/** + * Created by Jason on 2017/9/9. + */ + +public interface OnDismissListener { + + void onDismiss(View v); +} diff --git a/app/src/main/java/com/yyxx/wechatfp/network/inf/IUpdateCheck.java b/app/src/main/java/com/yyxx/wechatfp/network/inf/IUpdateCheck.java new file mode 100644 index 0000000..b728f07 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/network/inf/IUpdateCheck.java @@ -0,0 +1,9 @@ +package com.yyxx.wechatfp.network.inf; + +/** + * Created by Jason on 2017/9/9. + */ + +public interface IUpdateCheck { + void doUpdateCheck(); +} diff --git a/app/src/main/java/com/yyxx/wechatfp/network/inf/UpdateResultListener.java b/app/src/main/java/com/yyxx/wechatfp/network/inf/UpdateResultListener.java new file mode 100644 index 0000000..1f084c7 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/network/inf/UpdateResultListener.java @@ -0,0 +1,12 @@ +package com.yyxx.wechatfp.network.inf; + +/** + * Created by Jason on 2017/9/9. + */ + +public interface UpdateResultListener { + + void onNoUpdate(); + void onNetErr(); + void onHasUpdate(String version, String content, String pageUrl, String downloadUrl); +} diff --git a/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/BaseUpdateChecker.java b/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/BaseUpdateChecker.java new file mode 100644 index 0000000..ef902de --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/BaseUpdateChecker.java @@ -0,0 +1,60 @@ +package com.yyxx.wechatfp.network.updateCheck; + +import com.yyxx.wechatfp.network.inf.IUpdateCheck; +import com.yyxx.wechatfp.network.inf.UpdateResultListener; +import com.yyxx.wechatfp.util.Task; + +/** + * Created by Jason on 2017/9/9. + */ + +public abstract class BaseUpdateChecker implements IUpdateCheck, UpdateResultListener { + + private UpdateResultListener mResultListener; + + public BaseUpdateChecker(UpdateResultListener listener) { + mResultListener = listener; + } + + @Override + public void onNoUpdate() { + Task.onMain(new Runnable() { + @Override + public void run() { + UpdateResultListener listener = mResultListener; + if (listener == null) { + return; + } + listener.onNoUpdate(); + } + }); + } + + @Override + public void onNetErr() { + Task.onMain(new Runnable() { + @Override + public void run() { + UpdateResultListener listener = mResultListener; + if (listener == null) { + return; + } + listener.onNetErr(); + } + }); + } + + @Override + public void onHasUpdate(final String version, final String content, final String pageUrl, final String downloadUrl) { + Task.onMain(new Runnable() { + @Override + public void run() { + UpdateResultListener listener = mResultListener; + if (listener == null) { + return; + } + listener.onHasUpdate(version, content, pageUrl, downloadUrl); + } + }); + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/UpdateFactory.java b/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/UpdateFactory.java new file mode 100644 index 0000000..cf66af9 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/UpdateFactory.java @@ -0,0 +1,91 @@ +package com.yyxx.wechatfp.network.updateCheck; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.text.TextUtils; +import android.widget.Toast; + +import com.yyxx.wechatfp.network.inf.UpdateResultListener; +import com.yyxx.wechatfp.network.updateCheck.github.GithubUpdateChecker; +import com.yyxx.wechatfp.util.Config; +import com.yyxx.wechatfp.util.UrlUtil; +import com.yyxx.wechatfp.util.log.L; + +/** + * Created by Jason on 2017/9/10. + */ + +public class UpdateFactory { + + public static void doUpdateCheck(final Context context) { + doUpdateCheck(context, true, false); + } + + public static void doUpdateCheck(final Context context, final boolean quite, final boolean dontSkip) { + if (!quite) { + Toast.makeText(context, "正在檢查更新", Toast.LENGTH_LONG).show(); + + } + new GithubUpdateChecker(new UpdateResultListener() { + @Override + public void onNoUpdate() { + if (!quite) { + Toast.makeText(context, "暫無更新", Toast.LENGTH_LONG).show(); + } + } + + @Override + public void onNetErr() { + if (!quite) { + Toast.makeText(context, "網絡錯誤, 檢查更新失敗", Toast.LENGTH_LONG).show(); + } + + } + + @Override + public void onHasUpdate(final String version, String content, final String pageUrl, String downloadUrl) { + if (!dontSkip) { + if (isSkipVersion(context, version)) { + L.d("已跳過版本: " + version); + return; + } + } + AlertDialog.Builder builder = new AlertDialog.Builder(context).setTitle("發現新版本 " + version); + builder.setMessage(content); + builder.setNeutralButton("跳過這個版本", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + new Config(context).setSkipVersion(version); + } + }); + builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + + } + }); + builder.setPositiveButton("前往更新頁", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + UrlUtil.openUrl(context, pageUrl); + } + }); + + builder.show(); + } + }).doUpdateCheck(); + } + + private static boolean isSkipVersion(Context context, String targetVersion) { + Config config = new Config(context); + String skipVersion = config.getSkipVersion(); + if (TextUtils.isEmpty(skipVersion)) { + return false; + } + if (String.valueOf(targetVersion).equals(skipVersion)) { + return true; + } + return false; + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/github/GithubUpdateChecker.java b/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/github/GithubUpdateChecker.java new file mode 100644 index 0000000..60a3017 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/github/GithubUpdateChecker.java @@ -0,0 +1,79 @@ +package com.yyxx.wechatfp.network.updateCheck.github; + +import com.google.gson.Gson; +import com.yyxx.wechatfp.BuildConfig; +import com.yyxx.wechatfp.Constant; +import com.yyxx.wechatfp.network.inf.UpdateResultListener; +import com.yyxx.wechatfp.network.updateCheck.BaseUpdateChecker; +import com.yyxx.wechatfp.network.updateCheck.github.bean.GithubLatestInfo; +import com.yyxx.wechatfp.util.DateUtil; +import com.yyxx.wechatfp.util.StringUtil; +import com.yyxx.wechatfp.util.log.L; + +import java.io.IOException; +import java.util.Date; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +/** + * Created by Jason on 2017/9/9. + */ + +public class GithubUpdateChecker extends BaseUpdateChecker { + + public static OkHttpClient sHttpClient = new OkHttpClient(); + + public GithubUpdateChecker(UpdateResultListener listener) { + super(listener); + } + + @Override + public void doUpdateCheck() { + Callback callback; + callback = new Callback() { + @Override + public void onFailure(Call call, IOException e) { + onNetErr(); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + if (response != null && response.isSuccessful()) { + String replay = response.body().string(); + response.close(); + try { + GithubLatestInfo info = new Gson().fromJson(replay, GithubLatestInfo.class); + if (info != null) { + if (info.isDataComplete()) { + if (StringUtil.isAppNewVersion(BuildConfig.VERSION_NAME, info.version)) { + String content = info.content; + Date date = info.date; + if (date != null) { + content = content + "\n\n更新日期: " + DateUtil.toString(date); + } + onHasUpdate(info.version, content, info.contentUrl, info.getDownloadUrl()); + } else { + onNoUpdate(); + } + return; + } + } + } catch (Exception e) { + L.d(e); + } + } + onNetErr(); + } + }; + + Request request = new Request.Builder() + .url(Constant.UPDATE_URL_GITHUB) + .build(); + sHttpClient.newCall(request).enqueue(callback); + } + +} diff --git a/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/github/bean/GithubAssetsInfo.java b/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/github/bean/GithubAssetsInfo.java new file mode 100644 index 0000000..e334813 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/github/bean/GithubAssetsInfo.java @@ -0,0 +1,15 @@ +package com.yyxx.wechatfp.network.updateCheck.github.bean; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Jason on 2017/9/10. + */ + +public class GithubAssetsInfo { + + public String name; + + @SerializedName("browser_download_url") + public String url; +} diff --git a/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/github/bean/GithubLatestInfo.java b/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/github/bean/GithubLatestInfo.java new file mode 100644 index 0000000..8f8f2c3 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/network/updateCheck/github/bean/GithubLatestInfo.java @@ -0,0 +1,73 @@ +package com.yyxx.wechatfp.network.updateCheck.github.bean; + +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; +import com.yyxx.wechatfp.BuildConfig; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Created by Jason on 2017/9/10. + */ + +public class GithubLatestInfo { + + + @SerializedName("html_url") + public String contentUrl; + + @SerializedName("name") + public String version; + + @SerializedName("body") + public String content; + + @SerializedName("created_at") + public Date date; + + + public List assets = new ArrayList<>(); + + @Nullable + public String getDownloadUrl() { + if (assets.size() == 0) { + return null; + } + String lowerProductName = BuildConfig.APP_PRODUCT_NAME.toLowerCase(); + for (GithubAssetsInfo asset : assets) { + if (asset == null || TextUtils.isEmpty(asset.name) || TextUtils.isEmpty(asset.url)) { + continue; + } + if (asset.name.toLowerCase().contains(lowerProductName)) { + return asset.url; + } + } + return null; + } + + public boolean isDataComplete() { + if (TextUtils.isEmpty(getDownloadUrl())) { + return false; + } + if (TextUtils.isEmpty(version)) { + return false; + } + if (TextUtils.isEmpty(contentUrl)) { + return false; + } + if (TextUtils.isEmpty(content)) { + return false; + } + return true; + } + + @Override + public String toString() { + return new Gson().toJson(this); + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/Utils/AESHelper.java b/app/src/main/java/com/yyxx/wechatfp/util/AESUtil.java similarity index 97% rename from app/src/main/java/com/yyxx/wechatfp/Utils/AESHelper.java rename to app/src/main/java/com/yyxx/wechatfp/util/AESUtil.java index c8df018..d3bb4d9 100644 --- a/app/src/main/java/com/yyxx/wechatfp/Utils/AESHelper.java +++ b/app/src/main/java/com/yyxx/wechatfp/util/AESUtil.java @@ -1,12 +1,12 @@ -package com.yyxx.wechatfp.Utils; +package com.yyxx.wechatfp.util; import java.io.UnsupportedEncodingException; -import javax.crypto.Cipher; +import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; ///* AES对称加密解密类 */ -public class AESHelper { +public class AESUtil { private static final String CipherMode = "AES/ECB/PKCS5Padding"; diff --git a/app/src/main/java/com/yyxx/wechatfp/util/Config.java b/app/src/main/java/com/yyxx/wechatfp/util/Config.java new file mode 100644 index 0000000..e484e8e --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/Config.java @@ -0,0 +1,77 @@ +package com.yyxx.wechatfp.util; + +import android.content.Context; +import android.content.SharedPreferences; +import android.provider.Settings; +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import com.yyxx.wechatfp.BuildConfig; + +import java.util.WeakHashMap; + +/** + * Created by Jason on 2017/9/9. + */ + +public class Config { + + + private static WeakHashMap sConfigCache = new WeakHashMap<>(); + + private ObjectCache mCache; + + public Config(Context context) { + if (sConfigCache.containsKey(context)) { + mCache = sConfigCache.get(context); + } + if (mCache == null) { + SharedPreferences sharedPreferences = context.getSharedPreferences(BuildConfig.APPLICATION_ID + ".settings", Context.MODE_PRIVATE); + String deviceId = Settings.System.getString(context.getContentResolver(), Settings.System.ANDROID_ID); + int passwordEncKey = deviceId.hashCode(); + mCache = new ObjectCache(sharedPreferences, passwordEncKey); + sConfigCache.put(context, mCache); + } + } + + public boolean isOn() { + return mCache.sharedPreferences.getBoolean("switch_on", false); + } + + public void setOn(boolean on) { + mCache.sharedPreferences.edit().putBoolean("switch_on", on).apply(); + } + + @Nullable + public String getPassword() { + String enc = mCache.sharedPreferences.getString("password", null); + if (TextUtils.isEmpty(enc)) { + return null; + } + return AESUtil.decrypt(enc, String.valueOf(mCache.passwordEncKey)); + } + + public void setPassword(String password) { + String enc = AESUtil.encrypt(password, String.valueOf(mCache.passwordEncKey)); + mCache.sharedPreferences.edit().putString("password", enc).apply(); + } + + public void setSkipVersion(String version) { + mCache.sharedPreferences.edit().putString("skip_version", version).apply(); + } + + @Nullable + public String getSkipVersion() { + return mCache.sharedPreferences.getString("skip_version", null); + } + + private class ObjectCache { + SharedPreferences sharedPreferences; + int passwordEncKey; + + public ObjectCache(SharedPreferences sharedPreferences, int passwordEncKey) { + this.sharedPreferences = sharedPreferences; + this.passwordEncKey = passwordEncKey; + } + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/util/DateUtil.java b/app/src/main/java/com/yyxx/wechatfp/util/DateUtil.java new file mode 100644 index 0000000..e35f7c8 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/DateUtil.java @@ -0,0 +1,17 @@ +package com.yyxx.wechatfp.util; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Created by Jason on 2017/9/10. + */ + +public class DateUtil { + + private static final SimpleDateFormat DATE_FORMAT_YYYYMMDDHHMMSS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + public static String toString(Date date) { + return DATE_FORMAT_YYYYMMDDHHMMSS.format(date); + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/util/DonateUtil.java b/app/src/main/java/com/yyxx/wechatfp/util/DonateUtil.java new file mode 100644 index 0000000..776d6c8 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/DonateUtil.java @@ -0,0 +1,57 @@ +package com.yyxx.wechatfp.util; + +import android.content.Context; +import android.content.Intent; + +import com.yyxx.wechatfp.Constant; +import com.yyxx.wechatfp.util.log.L; + +import java.net.URLEncoder; + +/** + * Created by Jason on 2017/9/10. + */ + +public class DonateUtil { + + public static boolean openAlipayPayPage(Context context) { + return openAlipayPayPage(context, Constant.DONATE_ID_ALIPAY); + } + + public static boolean openAlipayPayPage(Context context, String qrcode) { + try { + qrcode = URLEncoder.encode(qrcode, "utf-8"); + } catch (Exception e) { + } + try { + final String alipayqr = "alipayqr://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=" + qrcode; + openUri(context, alipayqr + "%3F_s%3Dweb-other&_t=" + System.currentTimeMillis()); + return true; + } catch (Exception e) { + L.e(e); + } + return false; + } + + public static void openWeChatPay(Context context) { + try { + Intent donateIntent = new Intent(); + donateIntent.setClassName(context, "com.tencent.mm.plugin.remittance.ui.RemittanceAdapterUI"); + donateIntent.putExtra("scene", 1); + donateIntent.putExtra("pay_channel", 13); + donateIntent.putExtra("receiver_name", Constant.DONATE_ID_WECHAT); + context.startActivity(donateIntent); + } catch (Exception e) { + L.e(e); + } + } + + private static void openUri(Context context, String s) { + try { + Intent intent = Intent.parseUri(s, Intent.URI_INTENT_SCHEME); + context.startActivity(intent); + } catch (Exception e) { + L.e(e); + } + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/util/DpUtil.java b/app/src/main/java/com/yyxx/wechatfp/util/DpUtil.java new file mode 100644 index 0000000..58808a3 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/DpUtil.java @@ -0,0 +1,24 @@ +package com.yyxx.wechatfp.util; + +import android.content.Context; +import android.util.DisplayMetrics; + +/** + * Created by Jason on 2017/9/9. + */ + +public class DpUtil { + + public static int dip2px(Context context, float dipValue) { + return (int) (dipValue * getDensity(context) + 0.5f); + } + + public static float dip2pxF(Context context, float dipValue) { + return dipValue * getDensity(context); + } + + private static float getDensity(Context context){ + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + return dm.density; + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/util/StringUtil.java b/app/src/main/java/com/yyxx/wechatfp/util/StringUtil.java new file mode 100644 index 0000000..4ef23dd --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/StringUtil.java @@ -0,0 +1,54 @@ +package com.yyxx.wechatfp.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by Jason on 2017/9/9. + */ + +public class StringUtil { + + public static Pattern sNumberPattern = Pattern.compile("(\\d+)"); + + /** + * 判断是否为最新版本方法 将版本号根据.切分为int数组 比较 + * + * @param localVersion 本地版本号 + * @param onlineVersion 线上版本号 + * @return + */ + public static boolean isAppNewVersion(String localVersion, String onlineVersion) { + if (localVersion.equals(onlineVersion)) { + return false; + } + String[] localArray = localVersion.split("\\."); + String[] onlineArray = onlineVersion.split("\\."); + + int length = localArray.length < onlineArray.length ? localArray.length : onlineArray.length; + + try { + for (int i = 0; i < length; i++) { + if (getNumberFromString(onlineArray[i]) > getNumberFromString((localArray[i]))) { + return true; + } else if (getNumberFromString(onlineArray[i]) < getNumberFromString(localArray[i])) { + return false; + } + // 相等 比较下一组值 + } + } catch (Exception | Error e) { + return false; + } + return true; + } + + public static int getNumberFromString(String text) { + Matcher m = sNumberPattern.matcher(text); + if (m.find()) { + return Integer.parseInt(m.group(1)); + } else { + throw new NumberFormatException(); + } + } + +} diff --git a/app/src/main/java/com/yyxx/wechatfp/util/StyleUtil.java b/app/src/main/java/com/yyxx/wechatfp/util/StyleUtil.java new file mode 100644 index 0000000..ca67580 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/StyleUtil.java @@ -0,0 +1,25 @@ +package com.yyxx.wechatfp.util; + +import android.graphics.Color; +import android.widget.TextView; + +/** + * Created by Jason on 2017/9/9. + */ + +public class StyleUtil { + + public static final float TEXT_SIZE_DEFAULT = 18.0f; + public static final float TEXT_SIZE_BIG = 20.0f; + public static final float TEXT_SIZE_SMALL = 16.0f; + + public static final int TEXT_COLOR_DEFAULT = Color.BLACK; + public static final int TEXT_COLOR_SECONDARY = 0xFF8A9899; + + public static final int LINE_COLOR_DEFAULT = 0xFFE5E5E5; + + public static void apply(TextView textView) { + textView.setTextSize(TEXT_SIZE_DEFAULT); + textView.setTextColor(TEXT_COLOR_DEFAULT); + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/util/Task.java b/app/src/main/java/com/yyxx/wechatfp/util/Task.java new file mode 100644 index 0000000..456be89 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/Task.java @@ -0,0 +1,43 @@ +package com.yyxx.wechatfp.util; + +import android.os.Handler; +import android.os.Looper; + +import com.yyxx.wechatfp.util.log.L; + +/** + * Created by Jason on 2017/9/10. + */ + +public class Task { + + private static Handler sMainHandler = new Handler(Looper.getMainLooper()); + + public static void onMain(long msec, final Runnable runnable) { + Runnable run = new Runnable() { + @Override + public void run() { + try { + runnable.run(); + } catch (Exception e) { + L.d(e); + } + } + }; + sMainHandler.postDelayed(run, msec); + } + + public static void onMain(final Runnable runnable) { + Runnable run = new Runnable() { + @Override + public void run() { + try { + runnable.run(); + } catch (Exception e) { + L.d(e); + } + } + }; + sMainHandler.post(run); + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/util/UrlUtil.java b/app/src/main/java/com/yyxx/wechatfp/util/UrlUtil.java new file mode 100644 index 0000000..b52d05e --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/UrlUtil.java @@ -0,0 +1,65 @@ +package com.yyxx.wechatfp.util; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.util.Log; + +/** + * Created by Jason on 2017/7/7. + */ + +public class UrlUtil { + + public final static String TAG = UrlUtil.class.getName(); + + private static boolean isAppInstalled(Context context, String uri) { + PackageManager pm = context.getPackageManager(); + boolean installed = false; + try { + pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES); + installed = true; + } catch (PackageManager.NameNotFoundException e) { + installed = false; + } + return installed; + } + + public static void openUrl(Context context, String url){ + String[] browser = { "com.tencent.mtt", "com.UCMobile", "com.uc.browser", "com.oupeng.browser", "com.oupeng.mini.android", "com.android.browser" }; + + Intent intent = null; + for (String br : browser) { + if (isAppInstalled(context, br)) { + String clsName = null; + try { + PackageManager pm = context.getPackageManager(); + Intent intent1 = pm.getLaunchIntentForPackage(br); + ComponentName act = intent1.resolveActivity(pm); + clsName = act.getClassName(); + } catch (Exception e) { + Log.e(TAG, "", e); + } + if (clsName == null) { + break; + } + intent = new Intent(); + intent.setAction("android.intent.action.VIEW"); + Uri content_url = Uri.parse(url); + intent.setData(content_url); + intent.setClassName(br, clsName); + break; + } + } + if (intent == null) { + intent = new Intent(); + intent.setAction("android.intent.action.VIEW"); + Uri content_url = Uri.parse(url); + intent.setData(content_url); + } + context.startActivity(intent); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/yyxx/wechatfp/util/ViewUtil.java b/app/src/main/java/com/yyxx/wechatfp/util/ViewUtil.java new file mode 100644 index 0000000..21c7e24 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/ViewUtil.java @@ -0,0 +1,46 @@ +package com.yyxx.wechatfp.util; + +import android.annotation.SuppressLint; +import android.os.Build; +import android.view.View; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by Jason on 2017/9/9. + */ + +public class ViewUtil { + + private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1); + + @SuppressLint("NewApi") + public static int generateViewId() { + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + for (; ; ) { + final int result = sNextGeneratedId.get(); + // aapt-generated IDs have the high byte nonzero; clamp to the range under that. + int newValue = result + 1; + if (newValue > 0x00FFFFFF) + newValue = 1; // Roll over to 1, not 0. + if (sNextGeneratedId.compareAndSet(result, newValue)) { + return result; + } + } + } else { + return View.generateViewId(); + } + } + + public static int initId(View view) { + int id; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + id = generateViewId(); + } else { + id = View.generateViewId(); + } + view.setId(id); + return id; + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/util/log/L.java b/app/src/main/java/com/yyxx/wechatfp/util/log/L.java new file mode 100644 index 0000000..5459873 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/log/L.java @@ -0,0 +1,92 @@ +package com.yyxx.wechatfp.util.log; + +import android.util.Log; + +import com.yyxx.wechatfp.BuildConfig; +import com.yyxx.wechatfp.util.log.handler.GenericLog; +import com.yyxx.wechatfp.util.log.handler.XposedLog; +import com.yyxx.wechatfp.util.log.inf.ILog; + +/** + * Created by Jason on 2017/9/10. + */ + +public class L { + + private static final ILog sILog; + + private static final String LOG_TAG = BuildConfig.APP_PRODUCT_NAME; + + static { + ILog iLog; + try { + Class.forName("de.robv.android.xposed.XposedBridge"); + iLog = new XposedLog(); + } catch (Exception | Error ignore) { + iLog = new GenericLog(); + } + sILog = iLog; + } + + + public static void d(Object... arg) { + final String log = arg2string(arg); + if (log != null) { + sILog.debug(LOG_TAG + getTraceTag(), log); + } + } + + public static void e(Object... arg) { + final String log = arg2string(arg); + if (log != null) { + sILog.error(LOG_TAG + getTraceTag(), log); + } + } + + private static String arg2string(Object[] arg) { + StringBuilder sb = new StringBuilder(); + try { + + if (arg != null) + for (Object o : arg) + try { + if (o instanceof Exception) { + sb.append(Log.getStackTraceString(((Exception) (o)))); + if (arg.length != 1) + sb.append("\t"); + } else if (o instanceof Error) { + sb.append(Log.getStackTraceString(((Error) (o)))); + if (arg.length != 1) + sb.append("\t"); + } else { + sb.append(String.valueOf(o)); + if (arg.length != 1) + sb.append("\t"); + } + } catch (Exception e) { + } + } catch (Exception e) { + e.printStackTrace(); + } catch (OutOfMemoryError e) { + e.printStackTrace(); + System.gc(); + } + if (sb.length() < 1) + return null; + else + return sb.toString(); + } + + private static String getTraceTag() { + if (BuildConfig.DEBUG) { + StackTraceElement stackTrace = Thread.currentThread().getStackTrace()[4]; + + String className = stackTrace.getClassName(); + className = className.replaceAll("\\$.+", ""); + return " [" + stackTrace.getMethodName() + "](" + className.substring(className.lastIndexOf('.') + 1) + ".java:" + stackTrace.getLineNumber() + ")"; + } else { + return ""; + } + } + +} diff --git a/app/src/main/java/com/yyxx/wechatfp/util/log/handler/GenericLog.java b/app/src/main/java/com/yyxx/wechatfp/util/log/handler/GenericLog.java new file mode 100644 index 0000000..791aab6 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/log/handler/GenericLog.java @@ -0,0 +1,21 @@ +package com.yyxx.wechatfp.util.log.handler; + +import android.util.Log; + +import com.yyxx.wechatfp.util.log.inf.ILog; + +/** + * Created by Jason on 2017/9/10. + */ + +public class GenericLog implements ILog { + @Override + public void debug(String tag, String msg) { + Log.d(tag, msg); + } + + @Override + public void error(String tag, String msg) { + Log.e(tag, msg); + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/util/log/handler/XposedLog.java b/app/src/main/java/com/yyxx/wechatfp/util/log/handler/XposedLog.java new file mode 100644 index 0000000..4ab9d04 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/log/handler/XposedLog.java @@ -0,0 +1,23 @@ +package com.yyxx.wechatfp.util.log.handler; + +import com.yyxx.wechatfp.util.log.inf.ILog; + +import de.robv.android.xposed.XposedBridge; + +/** + * Created by Jason on 2017/9/10. + */ + +public class XposedLog implements ILog { + + @Override + public void debug(String tag, String msg) { + XposedBridge.log(tag + " " + msg); + } + + @Override + public void error(String tag, String msg) { + XposedBridge.log(tag + " " + msg); + + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/util/log/inf/ILog.java b/app/src/main/java/com/yyxx/wechatfp/util/log/inf/ILog.java new file mode 100644 index 0000000..cb64db2 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/util/log/inf/ILog.java @@ -0,0 +1,10 @@ +package com.yyxx.wechatfp.util.log.inf; + +/** + * Created by Jason on 2017/9/10. + */ + +public interface ILog { + void debug(String tag, String msg); + void error(String tag, String msg); +} diff --git a/app/src/main/java/com/yyxx/wechatfp/view/DialogFrameLayout.java b/app/src/main/java/com/yyxx/wechatfp/view/DialogFrameLayout.java new file mode 100644 index 0000000..89f3fd6 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/view/DialogFrameLayout.java @@ -0,0 +1,76 @@ +package com.yyxx.wechatfp.view; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.WindowManager; +import android.widget.FrameLayout; + +import com.yyxx.wechatfp.listener.OnDismissListener; + +/** + * Created by Jason on 2017/9/9. + */ + +public abstract class DialogFrameLayout extends FrameLayout implements DialogInterface.OnDismissListener { + + private OnDismissListener mDismissListener; + + public DialogFrameLayout(@NonNull Context context) { + super(context); + } + + public DialogFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public DialogFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void showInDialog() { + showInDialog(false); + } + + public void showInDialog(boolean showConfirmBtn) { + String title = getDialogTitle(); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()) + .setView(this).setOnDismissListener(this); + if (!TextUtils.isEmpty(title)) { + builder.setTitle(title); + } + if (showConfirmBtn) { + builder.setPositiveButton("確定", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + + } + }); + } + AlertDialog dialog; + dialog = builder.create(); + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + dialog.show(); + } + + @Override + public void onDismiss(DialogInterface dialogInterface) { + OnDismissListener listener = mDismissListener; + if (listener != null) { + listener.onDismiss(this); + } + } + + public DialogFrameLayout withOnDismissListener(OnDismissListener listener) { + mDismissListener = listener; + return this; + } + + public String getDialogTitle() { + return null; + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/view/DonateView.java b/app/src/main/java/com/yyxx/wechatfp/view/DonateView.java new file mode 100644 index 0000000..30a5a49 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/view/DonateView.java @@ -0,0 +1,88 @@ +package com.yyxx.wechatfp.view; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.AdapterView; +import android.widget.LinearLayout; +import android.widget.ListView; + +import com.yyxx.wechatfp.adapter.PreferenceAdapter; +import com.yyxx.wechatfp.util.DonateUtil; +import com.yyxx.wechatfp.util.DpUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Jason on 2017/9/9. + */ + +public class DonateView extends DialogFrameLayout implements AdapterView.OnItemClickListener { + + private static final String SETTINGS_NAME_ALIPAY = "支付寶"; + private static final String SETTINGS_NAME_WECHAT = "微信"; + + + private List mSettingsDataList = new ArrayList<>(); + private PreferenceAdapter mListAdapter; + private ListView mListView; + + public DonateView(@NonNull Context context) { + super(context); + init(context); + } + + public DonateView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public DonateView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + private void init(Context context) { + LinearLayout rootVerticalLayout = new LinearLayout(context); + rootVerticalLayout.setOrientation(LinearLayout.VERTICAL); + + int defHPadding = DpUtil.dip2px(context, 15); + int defVPadding = DpUtil.dip2px(context, 12); + + mListView = new ListView(context); + mListView.setDividerHeight(0); + mListView.setOnItemClickListener(this); + mListView.setPadding(defHPadding, defVPadding, defHPadding, defVPadding); + mListView.setDivider(new ColorDrawable(Color.TRANSPARENT)); + + mSettingsDataList.add(new PreferenceAdapter.Data(SETTINGS_NAME_ALIPAY, "276308768@qq.com")); + mSettingsDataList.add(new PreferenceAdapter.Data(SETTINGS_NAME_WECHAT, "eritpchy")); + mListAdapter = new PreferenceAdapter(mSettingsDataList); + + rootVerticalLayout.addView(mListView); + + this.addView(rootVerticalLayout); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mListView.setAdapter(mListAdapter); + } + + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long l) { + PreferenceAdapter.Data data = mListAdapter.getItem(position); + final Context context = getContext(); + if (SETTINGS_NAME_ALIPAY.equals(data.title)) { + DonateUtil.openAlipayPayPage(context); + } else if (SETTINGS_NAME_WECHAT.equals(data.title)) { + DonateUtil.openWeChatPay(context); + } + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/view/PasswordInputView.java b/app/src/main/java/com/yyxx/wechatfp/view/PasswordInputView.java new file mode 100644 index 0000000..771af2a --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/view/PasswordInputView.java @@ -0,0 +1,88 @@ +package com.yyxx.wechatfp.view; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.Editable; +import android.text.InputType; +import android.text.method.HideReturnsTransformationMethod; +import android.util.AttributeSet; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +import com.yyxx.wechatfp.util.DpUtil; + +/** + * Created by Jason on 2017/9/9. + */ + +public class PasswordInputView extends DialogFrameLayout { + + public static final String DEFAULT_HIDDEN_PASS = "123zxc456asdxxx"; + + private EditText mInputView; + + public PasswordInputView(@NonNull Context context) { + super(context); + initView(context); + } + + public PasswordInputView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + initView(context); + } + + public PasswordInputView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initView(context); + } + + private void initView(Context context) { + mInputView = new EditText(context); + mInputView.setFocusable(true); + mInputView.setFocusableInTouchMode(true); + mInputView.setTransformationMethod(HideReturnsTransformationMethod.getInstance()); + mInputView.setInputType(InputType.TYPE_CLASS_NUMBER + | InputType.TYPE_NUMBER_VARIATION_PASSWORD + ); + + LayoutParams layoutParam = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + int defHMargin = DpUtil.dip2px(context, 15); + int defTMargin = DpUtil.dip2px(context, 30); + int defBMargin = DpUtil.dip2px(context, 4); + layoutParam.setMargins(defHMargin, defTMargin, defHMargin, defBMargin); + + this.addView(mInputView, layoutParam); + } + + @NonNull + public String getInput() { + Editable ediable = mInputView.getText(); + if (ediable == null) { + return ""; + } + return ediable.toString(); + } + + public void setDefaultText(String text) { + mInputView.setText(text); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + post(new Runnable() { + @Override + public void run() { + InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mInputView, InputMethodManager.SHOW_IMPLICIT); + } + }); + } + + @Override + public String getDialogTitle() { + return "請輸入密碼"; + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/view/SettingsView.java b/app/src/main/java/com/yyxx/wechatfp/view/SettingsView.java new file mode 100644 index 0000000..904542c --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/view/SettingsView.java @@ -0,0 +1,146 @@ +package com.yyxx.wechatfp.view; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.Typeface; +import android.graphics.drawable.ColorDrawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import com.yyxx.wechatfp.BuildConfig; +import com.yyxx.wechatfp.Constant; +import com.yyxx.wechatfp.adapter.PreferenceAdapter; +import com.yyxx.wechatfp.listener.OnDismissListener; +import com.yyxx.wechatfp.network.updateCheck.UpdateFactory; +import com.yyxx.wechatfp.util.Config; +import com.yyxx.wechatfp.util.DpUtil; +import com.yyxx.wechatfp.util.UrlUtil; + +import java.util.ArrayList; +import java.util.List; + +import static com.yyxx.wechatfp.util.StyleUtil.TEXT_SIZE_BIG; +import static com.yyxx.wechatfp.view.PasswordInputView.DEFAULT_HIDDEN_PASS; + +/** + * Created by Jason on 2017/9/9. + */ + +public class SettingsView extends DialogFrameLayout implements AdapterView.OnItemClickListener { + + private static final String SETTINGS_NAME_SWITCH = "啟用"; + private static final String SETTINGS_NAME_PASSWORD = "密碼"; + private static final String SETTINGS_NAME_DONATE = "贊助我"; + private static final String SETTINGS_NAME_CHECKUPDATE = "檢查更新"; + private static final String SETTINGS_NAME_WEBSIDE = "項目主頁"; + + + private List mSettingsDataList = new ArrayList<>(); + private PreferenceAdapter mListAdapter; + private ListView mListView; + + public SettingsView(@NonNull Context context) { + super(context); + init(context); + } + + public SettingsView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public SettingsView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + private void init(Context context) { + LinearLayout rootVerticalLayout = new LinearLayout(context); + rootVerticalLayout.setOrientation(LinearLayout.VERTICAL); + + View lineView = new View(context); + lineView.setBackgroundColor(Color.TRANSPARENT); + + TextView settingsTitle = new TextView(context); + settingsTitle.setTextSize(TEXT_SIZE_BIG); + settingsTitle.setText(BuildConfig.APP_SETTINGS_NAME); + settingsTitle.setTextColor(Color.WHITE); + settingsTitle.setTypeface(null, Typeface.BOLD); + settingsTitle.setBackgroundColor(0xFF1AAEE5); + int defHPadding = DpUtil.dip2px(context, 15); + int defVPadding = DpUtil.dip2px(context, 12); + settingsTitle.setPadding(defHPadding, defVPadding, defHPadding, defVPadding); + + mListView = new ListView(context); + mListView.setDividerHeight(0); + mListView.setOnItemClickListener(this); + mListView.setPadding(defHPadding, defVPadding, defHPadding, defVPadding); + mListView.setDivider(new ColorDrawable(Color.TRANSPARENT)); + + mSettingsDataList.add(new PreferenceAdapter.Data(SETTINGS_NAME_SWITCH, "啟用微信指紋支付", true, new Config(context).isOn())); + mSettingsDataList.add(new PreferenceAdapter.Data(SETTINGS_NAME_PASSWORD, "請輸入微信支付的密碼, 密碼會加密后保存, 請放心")); + mSettingsDataList.add(new PreferenceAdapter.Data(SETTINGS_NAME_DONATE, "如果您覺得本軟件好用, 歡迎贊助, 多少都是心意")); + mSettingsDataList.add(new PreferenceAdapter.Data(SETTINGS_NAME_CHECKUPDATE, "點擊檢查插件更新")); + mSettingsDataList.add(new PreferenceAdapter.Data(SETTINGS_NAME_WEBSIDE, "訪問項目主頁")); + mListAdapter = new PreferenceAdapter(mSettingsDataList); + + rootVerticalLayout.addView(settingsTitle); + rootVerticalLayout.addView(lineView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, DpUtil.dip2px(context, 2))); + rootVerticalLayout.addView(mListView); + + this.addView(rootVerticalLayout); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mListView.setAdapter(mListAdapter); + } + + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long l) { + PreferenceAdapter.Data data = mListAdapter.getItem(position); + final Context context = getContext(); + final Config config = new Config(context); + if (SETTINGS_NAME_SWITCH.equals(data.title)) { + data.selectionState = !data.selectionState; + config.setOn(data.selectionState); + mListAdapter.notifyDataSetChanged(); + } else if (SETTINGS_NAME_PASSWORD.equals(data.title)) { + PasswordInputView passwordInputView = new PasswordInputView(context); + if (!TextUtils.isEmpty(config.getPassword())) { + passwordInputView.setDefaultText(DEFAULT_HIDDEN_PASS); + } + passwordInputView.withOnDismissListener(new OnDismissListener() { + @Override + public void onDismiss(View v) { + PasswordInputView inputView = (PasswordInputView) v; + String inputText = inputView.getInput(); + if (TextUtils.isEmpty(inputText)) { + return; + } + if (DEFAULT_HIDDEN_PASS.equals(inputText)) { + return; + } + config.setPassword(inputText); + } + }).showInDialog(true); + } else if (SETTINGS_NAME_CHECKUPDATE.equals(data.title)) { + UpdateFactory.doUpdateCheck(context, false, true); + } else if (SETTINGS_NAME_DONATE.equals(data.title)) { + new DonateView(context).showInDialog(); + } else if (SETTINGS_NAME_WEBSIDE.equals(data.title)) { + UrlUtil.openUrl(context, Constant.PROJECT_URL); + Toast.makeText(context, "如果您擁有Github賬戶, 別忘了給我的項目+個Star噢", Toast.LENGTH_LONG).show(); + } + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/xposed/ObfuscationHelper.java b/app/src/main/java/com/yyxx/wechatfp/xposed/ObfuscationHelper.java new file mode 100644 index 0000000..bf68059 --- /dev/null +++ b/app/src/main/java/com/yyxx/wechatfp/xposed/ObfuscationHelper.java @@ -0,0 +1,143 @@ +package com.yyxx.wechatfp.xposed; + +import de.robv.android.xposed.XposedHelpers; +import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; + +public class ObfuscationHelper { + + private static int sVersionIdx = 0; + + public static class MM_Classes { + public static Class PayUI, FetchUI, Payview, WalletBaseUI, PreferenceAdapter; + + private static void init(int idx, LoadPackageParam lpparam) throws Throwable { + PayUI = XposedHelpers.findClass("com.tencent.mm.plugin.wallet.pay.ui." + new String[]{ + "WalletPayUI", //6.5.8 + "WalletPayUI", //6.5.10-1080 + "WalletPayUI", //6.5.13-1100 + }[idx], lpparam.classLoader); + Payview = XposedHelpers.findClass("com.tencent.mm.plugin.wallet_core.ui." + new String[]{ + "l", //6.5.8 + "l", //6.5.10-1080 + "l", //6.5.10-1061 + "l", //6.5.13-1100 + }[idx], lpparam.classLoader); + FetchUI = XposedHelpers.findClass("com.tencent.mm.plugin.wallet.balance.ui." + new String[]{ + "WalletBalanceFetchPwdInputUI", //6.5.8 + "WalletBalanceFetchPwdInputUI", //6.5.10-1080 + "WalletBalanceFetchPwdInputUI", //6.5.13-1100 + }[idx], lpparam.classLoader); + WalletBaseUI = XposedHelpers.findClass("com.tencent.mm.wallet_core.ui." + new String[]{ + "WalletBaseUI", //6.5.8 + "WalletBaseUI", //6.5.10-1080 + "WalletBaseUI", //6.5.13-1100 + }[idx], lpparam.classLoader); + PreferenceAdapter = XposedHelpers.findClass("com.tencent.mm.ui.base.preference." + new String[]{ + "h", //6.5.8 + "h", //6.5.10-1080 + "h", //6.5.13-1100 + }[idx], lpparam.classLoader); + } + } + + public static class MM_Fields { + public static String PaypwdEditText; + public static String PaypwdView; + public static String PayInputView; + public static String PayTitle; + public static String Passwd_Text; + public static String PreferenceAdapter_vpQ; + public static String PreferenceAdapter_vpP; + + private static void init(int idx) throws Throwable { + PaypwdView = new String[]{ + "qVO", //6.5.8 + "ryk", //6.5.10-1080 + "rLB", //6.5.13-1100 + }[idx]; + PaypwdEditText = new String[]{ + "vyO", //6.5.8 + "wjm", //6.5.10-1080 + "wDJ", //6.5.13-1100 + }[idx]; + PayInputView = new String[]{ + "mOL", //6.5.8 + "nnG", //6.5.10-1080 + "nol", //6.5.13-1100 + }[idx]; + PayTitle = new String[]{ + "qVK", //6.5.8 + "ryg", //6.5.10-1080 + "rLw", //6.5.13-1100 + }[idx]; + Passwd_Text = new String[]{ + "qVK", //6.5.8 + "ryz", //6.5.10-1080 + "rLQ", //6.5.13-1100 + }[idx]; + PreferenceAdapter_vpQ = new String[]{ + "uoo", //6.5.8 + "uYA", //6.5.10-1080 + "vpQ", //6.5.13-1100 + }[idx]; + PreferenceAdapter_vpP = new String[]{ + "uon", //6.5.8 + "uYz", //6.5.10-1080 + "vpP", //6.5.13-1100 + }[idx]; + } + } + + public static class MM_Res { + public static int Finger_icon; + public static int Finger_title; + public static int Passwd_title; + + private static void init(int idx) throws Throwable { + Finger_icon = new int[]{ + 2130838280, //6.5.8 + 2130838289, //6.5.10-1080 + 2130838298, //6.5.13-1100 + }[idx]; + Finger_title = new int[]{ + 2131236833, //6.5.8 + 2131236918, //6.5.10-1080 + 2131236964, //6.5.13-1100 + }[idx]; + Passwd_title = new int[]{ + 2131236838, //6.5.8 + 2131236923, //6.5.10-1080 + 2131236969, //6.5.13-1100 + }[idx]; + } + } + + + public static boolean init(int versioncode, String versionName, LoadPackageParam lpparam) throws Throwable { + int versionIndex = isSupportedVersion(versioncode, versionName); + if (versionIndex < 0) { + return false; + } + MM_Classes.init(versionIndex, lpparam); + MM_Fields.init(versionIndex); + MM_Res.init(versionIndex); + return true; + } + + + public static int isSupportedVersion(int versionCode, String versionName) { + if (versionName.contains("6.5.8")) { + sVersionIdx = 0; + return 0; + } + if (versionName.contains("6.5.10") && versionCode == 1080) { + sVersionIdx = 1; + return 1; + } + if (versionName.contains("6.5.13") && versionCode == 1100) { + sVersionIdx = 2; + return 2; + } + return -1; + } +} diff --git a/app/src/main/java/com/yyxx/wechatfp/WalletBaseUI.java b/app/src/main/java/com/yyxx/wechatfp/xposed/WalletBaseUI.java similarity index 68% rename from app/src/main/java/com/yyxx/wechatfp/WalletBaseUI.java rename to app/src/main/java/com/yyxx/wechatfp/xposed/WalletBaseUI.java index 5c85030..e111b05 100644 --- a/app/src/main/java/com/yyxx/wechatfp/WalletBaseUI.java +++ b/app/src/main/java/com/yyxx/wechatfp/xposed/WalletBaseUI.java @@ -1,32 +1,37 @@ -package com.yyxx.wechatfp; +package com.yyxx.wechatfp.xposed; import android.annotation.TargetApi; import android.app.Application; import android.content.Context; +import com.yyxx.wechatfp.BuildConfig; +import com.yyxx.wechatfp.util.log.L; +import com.yyxx.wechatfp.xposed.loader.XposedPluginLoader; +import com.yyxx.wechatfp.xposed.plugin.XposedPlugin; + import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.IXposedHookZygoteInit; import de.robv.android.xposed.XC_MethodHook; -import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; +import static com.yyxx.wechatfp.Constant.PACKAGE_BANE_WECHAT; + public class WalletBaseUI implements IXposedHookZygoteInit, IXposedHookLoadPackage { - public static final String WECHAT_PACKAGENAME = "com.tencent.mm"; public void initZygote(StartupParam startupParam) throws Throwable { } @Override public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable { - if (lpparam.packageName.equals(WECHAT_PACKAGENAME)) { - XposedBridge.log("loaded: [" + lpparam.packageName + "]" + " version:" + BuildConfig.VERSION_NAME); + if (lpparam.packageName.equals(PACKAGE_BANE_WECHAT)) { + L.d("loaded: [" + lpparam.packageName + "]" + " version:" + BuildConfig.VERSION_NAME); XposedHelpers.findAndHookMethod(Application.class, "onCreate", new XC_MethodHook() { @TargetApi(21) protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - XposedBridge.log("Application onCreate"); + L.d("Application onCreate"); Context context = (Context) param.thisObject; XposedPluginLoader.load(XposedPlugin.class, context, lpparam); } diff --git a/app/src/main/java/com/yyxx/wechatfp/XposedPluginLoader.java b/app/src/main/java/com/yyxx/wechatfp/xposed/loader/XposedPluginLoader.java similarity index 95% rename from app/src/main/java/com/yyxx/wechatfp/XposedPluginLoader.java rename to app/src/main/java/com/yyxx/wechatfp/xposed/loader/XposedPluginLoader.java index 6ab6f27..da844e6 100644 --- a/app/src/main/java/com/yyxx/wechatfp/XposedPluginLoader.java +++ b/app/src/main/java/com/yyxx/wechatfp/xposed/loader/XposedPluginLoader.java @@ -1,9 +1,12 @@ -package com.yyxx.wechatfp; +package com.yyxx.wechatfp.xposed.loader; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import com.yyxx.wechatfp.BuildConfig; +import com.yyxx.wechatfp.util.log.L; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Enumeration; @@ -13,7 +16,6 @@ import dalvik.system.BaseDexClassLoader; import dalvik.system.DexClassLoader; import dalvik.system.DexFile; -import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.callbacks.XC_LoadPackage; /** @@ -43,7 +45,6 @@ public static void load(Class pluginClz, Context context, XC_LoadPackage.LoadPac } private static Object loadFromDex(Context context, Class pluginClz) throws Exception { - XposedBridge.log(context.toString()); try { ApplicationInfo info = context.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0); String apkPath = info.sourceDir; @@ -85,13 +86,13 @@ private static boolean forceClassLoaderReloadClasses(ClassLoader classLoader, St try { findClzMethod.invoke(classLoader, className); } catch (Exception e) { - XposedBridge.log(e); + L.d(e); } } } return true; } catch (Exception e) { - XposedBridge.log(e); + L.e(e); } return false; } diff --git a/app/src/main/java/com/yyxx/wechatfp/XposedPlugin.java b/app/src/main/java/com/yyxx/wechatfp/xposed/plugin/XposedPlugin.java similarity index 54% rename from app/src/main/java/com/yyxx/wechatfp/XposedPlugin.java rename to app/src/main/java/com/yyxx/wechatfp/xposed/plugin/XposedPlugin.java index 9542170..8386ce1 100644 --- a/app/src/main/java/com/yyxx/wechatfp/XposedPlugin.java +++ b/app/src/main/java/com/yyxx/wechatfp/xposed/plugin/XposedPlugin.java @@ -1,14 +1,15 @@ -package com.yyxx.wechatfp; +package com.yyxx.wechatfp.xposed.plugin; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; -import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.os.Bundle; -import android.provider.Settings; import android.support.annotation.Keep; +import android.text.TextUtils; import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.ImageView; import android.widget.RelativeLayout; @@ -17,15 +18,26 @@ import com.wei.android.lib.fingerprintidentify.FingerprintIdentify; import com.wei.android.lib.fingerprintidentify.base.BaseFingerprint; -import com.yyxx.wechatfp.Utils.AESHelper; +import com.yyxx.wechatfp.BuildConfig; +import com.yyxx.wechatfp.Constant; +import com.yyxx.wechatfp.network.updateCheck.UpdateFactory; +import com.yyxx.wechatfp.util.Config; +import com.yyxx.wechatfp.util.Task; +import com.yyxx.wechatfp.util.log.L; +import com.yyxx.wechatfp.view.SettingsView; +import com.yyxx.wechatfp.xposed.ObfuscationHelper; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.LinkedList; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage; -import static com.yyxx.wechatfp.WalletBaseUI.WECHAT_PACKAGENAME; - /** * Created by Jason on 2017/9/8. */ @@ -40,59 +52,80 @@ public class XposedPlugin { private TextView mPayTitleTextView, mPasswordTextView; private static FingerprintIdentify mFingerprintIdentify; private static boolean mNeedFingerprint; + private Activity mCurrentActivity; @Keep - public void main(final Context context, XC_LoadPackage.LoadPackageParam lpparam) { - XposedBridge.log("Xposed plugin init version: " + BuildConfig.VERSION_NAME); + public void main(final Context context, final XC_LoadPackage.LoadPackageParam lpparam) { + L.d("Xposed plugin init version: " + BuildConfig.VERSION_NAME); try { - PackageInfo packageInfo = context.getPackageManager().getPackageInfo(WECHAT_PACKAGENAME, 0); + PackageInfo packageInfo = context.getPackageManager().getPackageInfo(Constant.PACKAGE_BANE_WECHAT, 0); int versionCode = packageInfo.versionCode; String versionName = packageInfo.versionName; boolean isVersionSupported = ObfuscationHelper.init(versionCode, versionName, lpparam); if (!isVersionSupported) { Toast.makeText(context, "当前版本:" + versionName + "." + versionCode + "不支持", Toast.LENGTH_LONG).show(); - XposedBridge.log("当前版本:" + versionName + "." + versionCode + "不支持"); + L.d("当前版本:" + versionName + "." + versionCode + "不支持"); return; } XposedHelpers.findAndHookMethod(ObfuscationHelper.MM_Classes.PayUI, "onCreate", Bundle.class, new XC_MethodHook() { @TargetApi(21) protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - XposedBridge.log("PayUI onCreate"); + L.d("PayUI onCreate"); mWalletPayUIActivity = (Activity) param.thisObject; - mNeedFingerprint = true; + if (new Config(context).isOn()) { + mNeedFingerprint = true; + } else { + mNeedFingerprint = false; + } + } + }); + XposedHelpers.findAndHookMethod(Activity.class, "onResume", new XC_MethodHook() { + @TargetApi(21) + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + boolean firstStartUp = mCurrentActivity == null; + mCurrentActivity = (Activity) param.thisObject; + if (firstStartUp) { + Task.onMain(6000L, new Runnable() { + @Override + public void run() { + UpdateFactory.doUpdateCheck(mCurrentActivity); + } + }); + } + L.d("Activity onResume =", mCurrentActivity); } }); XposedHelpers.findAndHookMethod(ObfuscationHelper.MM_Classes.FetchUI, "onCreate", Bundle.class, new XC_MethodHook() { @TargetApi(21) protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - XposedBridge.log("FetchUI onCreate"); + L.d("FetchUI onCreate"); mWalletPayUIActivity = (Activity) param.thisObject; - mNeedFingerprint = true; + if (new Config(context).isOn()) { + mNeedFingerprint = true; + } else { + mNeedFingerprint = false; + } } }); XposedHelpers.findAndHookConstructor(ObfuscationHelper.MM_Classes.Payview, Context.class, new XC_MethodHook() { @TargetApi(21) protected void afterHookedMethod(MethodHookParam param) throws Throwable { - XposedBridge.log("Payview Constructor"); - SharedPreferences xmodPrefs = XPreferenceProvider.getRemoteSharedPreference(context); - XposedBridge.log("设置数量" + String.valueOf(xmodPrefs.getAll().size())); - boolean fingerPrintEnabled = xmodPrefs.getBoolean("enable_fp", false); - XposedBridge.log("fingerPrintEnabled:" + fingerPrintEnabled); + L.d("Payview Constructor"); - if (fingerPrintEnabled && mNeedFingerprint && mWalletPayUIActivity != null) { + if (mNeedFingerprint && mWalletPayUIActivity != null) { initFingerPrintLock(); mPasswordLayout = (RelativeLayout) XposedHelpers.getObjectField(param.thisObject, ObfuscationHelper.MM_Fields.PaypwdView); mInputEditText = (EditText) XposedHelpers.getObjectField(mPasswordLayout, ObfuscationHelper.MM_Fields.PaypwdEditText); - XposedBridge.log("密码输入框:" + mInputEditText.getClass().getName()); + L.d("密码输入框:" + mInputEditText.getClass().getName()); mInputEditText.setVisibility(View.GONE); mPayTitleTextView = (TextView) XposedHelpers.getObjectField(param.thisObject, ObfuscationHelper.MM_Fields.PayTitle); mPayTitleTextView.setText(ObfuscationHelper.MM_Res.Finger_title); final View mKeyboard = (View) XposedHelpers.getObjectField(param.thisObject, ObfuscationHelper.MM_Fields.PayInputView); - XposedBridge.log("密码键盘:" + mKeyboard.getClass().getName()); + L.d("密码键盘:" + mKeyboard.getClass().getName()); mKeyboard.setVisibility(View.GONE); mFingerPrintLayout = new RelativeLayout(mWalletPayUIActivity); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); @@ -109,7 +142,7 @@ public void onClick(View view) { mInputEditText.setVisibility(View.VISIBLE); mKeyboard.setVisibility(View.VISIBLE); mFingerprintIdentify.cancelIdentify(); - mPayTitleTextView.setText(ObfuscationHelper.MM_Res.passwd_title); + mPayTitleTextView.setText(ObfuscationHelper.MM_Res.Passwd_title); } }); mPasswordTextView = (TextView) XposedHelpers.getObjectField(param.thisObject, ObfuscationHelper.MM_Fields.Passwd_Text); @@ -121,7 +154,7 @@ public void onClick(View view) { mInputEditText.setVisibility(View.VISIBLE); mKeyboard.setVisibility(View.VISIBLE); mFingerprintIdentify.cancelIdentify(); - mPayTitleTextView.setText(ObfuscationHelper.MM_Res.passwd_title); + mPayTitleTextView.setText(ObfuscationHelper.MM_Res.Passwd_title); } }); } else { @@ -133,7 +166,7 @@ public void onClick(View view) { XposedHelpers.findAndHookMethod(ObfuscationHelper.MM_Classes.Payview, "dismiss", new XC_MethodHook() { @TargetApi(21) protected void afterHookedMethod(MethodHookParam param) throws Throwable { - XposedBridge.log("Payview dismiss"); + L.d("Payview dismiss"); if (mWalletPayUIActivity != null) { mFingerprintIdentify.cancelIdentify(); mWalletPayUIActivity = null; @@ -141,6 +174,81 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable { } } }); + + final Class className = ObfuscationHelper.MM_Classes.PreferenceAdapter; + XposedHelpers.findAndHookMethod(className, "getView", int.class, View.class, ViewGroup.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + View view = (View) param.getResult(); + if (view == null) { + return; + } + int position = (int) param.args[0]; + BaseAdapter baseAdapter = (BaseAdapter) param.thisObject; + Object item = baseAdapter.getItem(position); + if(BuildConfig.APP_SETTINGS_NAME.equals(String.valueOf(item))) { + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(final View view) { + view.post(new Runnable() { + @Override + public void run() { + try { + Activity activity = mCurrentActivity; + if (activity == null || activity.isDestroyed()) { + return; + } + SettingsView settingsView = new SettingsView(activity); + settingsView.showInDialog(); + } catch (Exception | Error e) { + L.e(e); + } + } + }); + + } + }); + L.d(item); + } + } + }); + + XposedHelpers.findAndHookMethod(className, "notifyDataSetChanged", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + + Field vpQField = className.getDeclaredField(ObfuscationHelper.MM_Fields.PreferenceAdapter_vpQ); + vpQField.setAccessible(true); + HashMap vpQ = (HashMap) vpQField.get(param.thisObject); + + if (!vpQ.toString().contains("通用")) { + return; + } + Field vpPField = className.getDeclaredField(ObfuscationHelper.MM_Fields.PreferenceAdapter_vpP); + vpPField.setAccessible(true); + LinkedList vpP = (LinkedList) vpPField.get(param.thisObject); + + String key = BuildConfig.APPLICATION_ID; + if (vpP.contains(key)) { + return; + } + + Class preferenceClz = XposedHelpers.findClass("com.tencent.mm.ui.base.preference.Preference", lpparam.classLoader); + Constructor preferenceCon = preferenceClz.getConstructor(Context.class); + preferenceCon.setAccessible(true); + Object preference = preferenceCon.newInstance(context); + Method setTitle = preferenceClz.getDeclaredMethod("setTitle", CharSequence.class); + setTitle.setAccessible(true); + setTitle.invoke(preference, BuildConfig.APP_SETTINGS_NAME); + vpP.add(0, key); + vpQ.put(key, preference); + } + + @TargetApi(21) + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + + } + }); } catch (Throwable l) { XposedBridge.log(l); } @@ -180,21 +288,12 @@ public void onStartFailedByDeviceLocked() { } private static void onSuccessUnlock(Context context) { - String pwd; - String ANDROID_ID = Settings.System.getString(mWalletPayUIActivity.getContentResolver(), Settings.System.ANDROID_ID); - - SharedPreferences xmodPrefs = XPreferenceProvider.getRemoteSharedPreference(context); - XposedBridge.log("设置数量" + String.valueOf(xmodPrefs.getAll().size())); - if (xmodPrefs.getAll().size() > 0) { - pwd = xmodPrefs.getString("paypwd", ""); - } else { - pwd = ""; + Config config = new Config(context); + String pwd = config.getPassword(); + if (TextUtils.isEmpty(pwd)) { + Toast.makeText(mWalletPayUIActivity, "未设定支付密码,请前往設置->指紋設置中设定微信的支付密码", Toast.LENGTH_SHORT).show(); + return; } - if (pwd.length() > 0) { - mInputEditText.setText(AESHelper.decrypt(pwd, ANDROID_ID)); - } else { - Toast.makeText(mWalletPayUIActivity, "未设定支付密码,请在WechatFp中设定微信的支付密码", Toast.LENGTH_SHORT).show(); - } - + mInputEditText.setText(pwd); } } diff --git a/app/src/main/res/layout/home.xml b/app/src/main/res/layout/home.xml new file mode 100644 index 0000000..8213e5c --- /dev/null +++ b/app/src/main/res/layout/home.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 27fc3e5..17d87f1 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,6 +1,6 @@ -