├── settings.gradle
├── fakebalance
├── src
│ └── main
│ │ ├── assets
│ │ └── xposed_init
│ │ ├── res
│ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable
│ │ │ ├── ic_arrow_back.xml
│ │ │ └── ic_info.xml
│ │ ├── menu
│ │ │ └── menu_main.xml
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── layout
│ │ │ ├── toolbar_common.xml
│ │ │ ├── dialog_about_content.xml
│ │ │ └── activity_main.xml
│ │ ├── java
│ │ └── com
│ │ │ └── wuxiaosu
│ │ │ └── fakebalance
│ │ │ ├── util
│ │ │ └── NumberUtils.java
│ │ │ ├── hook
│ │ │ ├── QQHook.java
│ │ │ ├── WeChatHook.java
│ │ │ ├── QQPluginHook.java
│ │ │ ├── AliPayHook.java
│ │ │ └── TimHook.java
│ │ │ ├── base
│ │ │ └── BaseActivity.java
│ │ │ ├── Main.java
│ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── screenshots
├── 00.png
├── 01.png
├── 02.png
└── 03.png
├── README.md
└── LICENSE
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':fakebalance'
2 |
--------------------------------------------------------------------------------
/fakebalance/src/main/assets/xposed_init:
--------------------------------------------------------------------------------
1 | com.wuxiaosu.fakebalance.Main
--------------------------------------------------------------------------------
/screenshots/00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxiaosu/XposedManyMoney/HEAD/screenshots/00.png
--------------------------------------------------------------------------------
/screenshots/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxiaosu/XposedManyMoney/HEAD/screenshots/01.png
--------------------------------------------------------------------------------
/screenshots/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxiaosu/XposedManyMoney/HEAD/screenshots/02.png
--------------------------------------------------------------------------------
/screenshots/03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxiaosu/XposedManyMoney/HEAD/screenshots/03.png
--------------------------------------------------------------------------------
/fakebalance/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxiaosu/XposedManyMoney/HEAD/fakebalance/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/fakebalance/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxiaosu/XposedManyMoney/HEAD/fakebalance/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/fakebalance/src/main/res/drawable/ic_arrow_back.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/fakebalance/src/main/res/drawable/ic_info.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/fakebalance/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
--------------------------------------------------------------------------------
/fakebalance/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #F44336
4 | #D32F2F
5 | #FFCDD2
6 | #FFC107
7 | #212121
8 | #757575
9 | #FFFFFF
10 | #BDBDBD
11 |
12 |
--------------------------------------------------------------------------------
/fakebalance/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 好多钱
3 | 假装自己有好多钱
4 |
5 | hide_icon
6 | fake_wechat
7 | wechat
8 | fake_tenpay
9 | tenpay
10 | fake_alipay
11 | alipay
12 | fake_alipay_tts
13 | alipay_tts
14 |
15 | 关于
16 |
17 |
18 |
--------------------------------------------------------------------------------
/fakebalance/src/main/res/layout/toolbar_common.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # XposedManyMoney
2 | Xposed 修改微信、QQ、TIM、支付宝余额,假装自己很有钱,如图所示。
3 | ## 效果图
4 | 
5 | 
6 | 
7 | 
8 | ## 下载
9 | - release from [github](https://github.com/wuxiaosu/XposedManyMoney/releases)
10 | - release from [酷安](https://www.coolapk.com/apk/178402)
11 | ## v1.05
12 | 增加支付宝收款语音;
13 | ## v1.04
14 | 更新支持支付宝 10.1.20;
15 | 更新支持 QQ 7.5.5、7.5.8;
16 | 更新支持 微信 6.6.6;
17 | ## v1.03
18 | 增加对QQ的支持
19 | ## v1.02
20 | 更新支持微信 6.6.5,支付宝 10.1.15
21 | ## v1.01
22 | 修复支付宝金额格式化错误
23 | ## v1.0
24 | 支持:
25 | 微信 "6.6.0", "6.6.1", "6.6.2", "6.6.3"
26 | TIM "2.0.0", "2.0.1", "2.0.5", "2.1.0", "2.1.5"
27 | 支付宝 "10.1.0", "10.1.2", "10.1.5", "10.1.8", "10.1.10", "10.1.12", "10.1.15"
--------------------------------------------------------------------------------
/fakebalance/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 | -keep class com.wuxiaosu.fakebalance.Main {
23 | public void handleLoadPackage(...);
24 | }
25 | -keep class com.wuxiaosu.fakebalance.MainActivity {
26 | private static boolean isModuleActive(...);
27 | }
28 |
--------------------------------------------------------------------------------
/fakebalance/src/main/java/com/wuxiaosu/fakebalance/util/NumberUtils.java:
--------------------------------------------------------------------------------
1 | package com.wuxiaosu.fakebalance.util;
2 |
3 | import java.math.BigDecimal;
4 | import java.text.DecimalFormat;
5 |
6 | /**
7 | * Created by su on 2018/2/27.
8 | */
9 |
10 | public class NumberUtils {
11 |
12 | /**
13 | * ex: 5.2 -> 5.20
14 | *
15 | * @param number
16 | * @return
17 | */
18 | public static String num2num00(String number) {
19 | BigDecimal b = new BigDecimal(number);
20 | return String.valueOf(
21 | b.divide(new BigDecimal(1), 2, BigDecimal.ROUND_HALF_UP));
22 | }
23 |
24 | /**
25 | * ex: 2018.2 -> 2,018.20
26 | *
27 | * @param number
28 | * @return
29 | */
30 | public static String num2num00WithComma(String number) {
31 | DecimalFormat df = new DecimalFormat("#,###.00");
32 | String result = df.format(Double.valueOf(num2num00(number)));
33 | if (result.startsWith(".")) {
34 | result = "0" + result;
35 | }
36 | return result;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/fakebalance/src/main/res/layout/dialog_about_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
23 |
24 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/fakebalance/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
25 |
26 |
37 |
38 |
41 |
42 |
--------------------------------------------------------------------------------
/fakebalance/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
40 |
43 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/fakebalance/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 26
5 |
6 | defaultConfig {
7 | applicationId "com.wuxiaosu.fakebalance"
8 | minSdkVersion 21
9 | targetSdkVersion 23
10 | versionCode 6
11 | versionName "1.05"
12 | }
13 |
14 | signingConfigs {
15 | release {
16 | storeFile file(FAKEBALANCE_STORE_FILE)
17 | storePassword FAKEBALANCE_STORE_PASSWORD
18 | keyAlias FAKEBALANCE_KEY_ALIAS
19 | keyPassword FAKEBALANCE_KEY_PASSWORD
20 | }
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled true
26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
27 | signingConfig signingConfigs.release
28 |
29 | applicationVariants.all { variant ->
30 | variant.outputs.all { output ->
31 | def oldFile = output.outputFile
32 | def newName = oldFile.name
33 | if (variant.buildType.name == 'release') {
34 | newName = oldFile.name.replace(".", "-v" + variant.versionName + '.')
35 | }
36 | outputFileName = newName
37 | }
38 | }
39 | }
40 | debug {
41 | minifyEnabled true
42 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
43 | }
44 | }
45 |
46 | }
47 |
48 | dependencies {
49 | implementation fileTree(include: ['*.jar'], dir: 'libs')
50 | implementation 'com.android.support:appcompat-v7:26.1.0'
51 | implementation 'com.android.support:design:26.1.0'
52 | implementation 'com.android.support:support-v4:26.1.0'
53 | compileOnly 'de.robv.android.xposed:api:82'
54 | implementation 'com.github.wuxiaosu:SettingLabelView:1.0@aar'
55 | }
56 |
--------------------------------------------------------------------------------
/fakebalance/src/main/java/com/wuxiaosu/fakebalance/hook/QQHook.java:
--------------------------------------------------------------------------------
1 | package com.wuxiaosu.fakebalance.hook;
2 |
3 | import com.wuxiaosu.fakebalance.BuildConfig;
4 | import com.wuxiaosu.fakebalance.util.NumberUtils;
5 | import com.wuxiaosu.widget.SettingLabelView;
6 |
7 | import de.robv.android.xposed.XC_MethodHook;
8 | import de.robv.android.xposed.XSharedPreferences;
9 | import de.robv.android.xposed.XposedHelpers;
10 |
11 |
12 | /**
13 | * Created by su on 2018/3/12.
14 | * QQ hook
15 | */
16 |
17 | public class QQHook {
18 | private static XSharedPreferences xsp;
19 |
20 | private static boolean fakeBalance;
21 | private static String balance;
22 |
23 | private static void reload() {
24 | xsp.reload();
25 | balance = NumberUtils.num2num00(xsp.getString("tenpay", "0.00"));
26 | fakeBalance = xsp.getBoolean("fake_tenpay", false);
27 | }
28 |
29 | public static void hook(ClassLoader classLoader) {
30 | xsp = new XSharedPreferences(BuildConfig.APPLICATION_ID, SettingLabelView.DEFAULT_PREFERENCES_NAME);
31 | xsp.makeWorldReadable();
32 | try {
33 | Class numAnimClazz = XposedHelpers.findClass("com.tencent.mobileqq.activity.qwallet.widget.NumAnim", classLoader);
34 | XposedHelpers.findAndHookMethod(numAnimClazz, "run",
35 | new XC_MethodHook() {
36 | @Override
37 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
38 | Object rulerObject = XposedHelpers.getObjectField(param.thisObject, "mRuler");
39 | XposedHelpers.findAndHookMethod(rulerObject.getClass(), "getNumber", double.class, new XC_MethodHook() {
40 | @Override
41 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
42 | reload();
43 | if (fakeBalance) {
44 | param.args[0] = Double.valueOf(balance);
45 | }
46 | super.beforeHookedMethod(param);
47 | }
48 | });
49 | super.beforeHookedMethod(param);
50 | }
51 | });
52 |
53 | } catch (Error | Exception e) {
54 | e.printStackTrace();
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/fakebalance/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
14 |
15 |
20 |
21 |
26 |
27 |
32 |
33 |
36 |
37 |
40 |
41 |
42 |
43 |
48 |
49 |
52 |
53 |
56 |
57 |
58 |
63 |
64 |
67 |
68 |
71 |
72 |
73 |
78 |
79 |
82 |
83 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/fakebalance/src/main/java/com/wuxiaosu/fakebalance/base/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.wuxiaosu.fakebalance.base;
2 |
3 | import android.app.ProgressDialog;
4 | import android.content.pm.ActivityInfo;
5 | import android.content.pm.PackageManager;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.Toolbar;
8 | import android.view.View;
9 | import android.widget.Toast;
10 |
11 | import com.wuxiaosu.fakebalance.R;
12 |
13 |
14 | /**
15 | * Created by su on 2018/1/7.
16 | */
17 |
18 | public class BaseActivity extends AppCompatActivity {
19 |
20 | private Toolbar mToolbar;
21 |
22 | @Override
23 | public void setContentView(int layoutResID) {
24 | super.setContentView(layoutResID);
25 | initToolbar();
26 | }
27 |
28 | private void initToolbar() {
29 | mToolbar = findViewById(R.id.toolbar);
30 | if (null == mToolbar) {
31 | return;
32 | }
33 | ActivityInfo mActivityInfo = null;
34 | try {
35 | mActivityInfo = getPackageManager().
36 | getActivityInfo(this.getComponentName(), PackageManager.GET_META_DATA);
37 | } catch (PackageManager.NameNotFoundException e) {
38 | e.printStackTrace();
39 | }
40 | // 在 AndroidManifest 中添加 android:label 设置默认标题栏
41 | if (mActivityInfo != null && 0 != mActivityInfo.labelRes) {
42 | mToolbar.setTitle(mActivityInfo.labelRes);
43 | } else {
44 | mToolbar.setTitle("");
45 | }
46 | setSupportActionBar(mToolbar);
47 | mToolbar.setNavigationIcon(R.drawable.ic_arrow_back);
48 | mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
49 | @Override
50 | public void onClick(View v) {
51 | onBackPressed();
52 | }
53 | });
54 | }
55 |
56 | public Toolbar getToolbar() {
57 | return mToolbar;
58 | }
59 |
60 | /**
61 | * 显示状态栏
62 | */
63 | public void showToolbar() {
64 | getToolbar().setVisibility(View.VISIBLE);
65 | }
66 |
67 | /**
68 | * 隐藏状态栏
69 | */
70 | public void hideToolbar() {
71 | getToolbar().setVisibility(View.GONE);
72 | }
73 |
74 | private ProgressDialog mProgressDialog;
75 |
76 | /**
77 | * 显示 progress dialog
78 | */
79 | public void showProgressDialog() {
80 | showProgressDialog("请稍候...", false);
81 | }
82 |
83 | /**
84 | * 显示 progress dialog
85 | *
86 | * @param cancelable 点击空白处是否可以关闭
87 | */
88 | public void showProgressDialog(boolean cancelable) {
89 | showProgressDialog("请稍候...", cancelable);
90 | }
91 |
92 | /**
93 | * 显示 progress dialog
94 | *
95 | * @param text
96 | * @param cancelable
97 | */
98 | public void showProgressDialog(String text, boolean cancelable) {
99 | if (null == mProgressDialog) {
100 | mProgressDialog = new ProgressDialog(this);
101 | }
102 | mProgressDialog.setMessage(text);
103 | mProgressDialog.setCancelable(cancelable);
104 | mProgressDialog.show();
105 | }
106 |
107 | /**
108 | * 关闭 progress dialog
109 | */
110 | public void dismissProgressDialog() {
111 | if (null != mProgressDialog && mProgressDialog.isShowing()) {
112 | mProgressDialog.dismiss();
113 | }
114 | }
115 |
116 | /***
117 | * 双击退出
118 | *
119 | * @param text
120 | */
121 | protected void exitBy2Click(String text) {
122 | long secondTime = System.currentTimeMillis();
123 | if (secondTime - firstTime > 2000) {
124 | Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
125 | firstTime = secondTime;
126 | } else {
127 | finish();
128 | }
129 | }
130 |
131 | private long firstTime = 0;
132 | }
133 |
--------------------------------------------------------------------------------
/fakebalance/src/main/java/com/wuxiaosu/fakebalance/hook/WeChatHook.java:
--------------------------------------------------------------------------------
1 | package com.wuxiaosu.fakebalance.hook;
2 |
3 | import android.text.Editable;
4 | import android.text.TextWatcher;
5 | import android.widget.TextView;
6 |
7 | import com.wuxiaosu.fakebalance.BuildConfig;
8 | import com.wuxiaosu.fakebalance.util.NumberUtils;
9 | import com.wuxiaosu.widget.SettingLabelView;
10 |
11 | import java.lang.reflect.Field;
12 |
13 | import de.robv.android.xposed.XC_MethodHook;
14 | import de.robv.android.xposed.XSharedPreferences;
15 | import de.robv.android.xposed.XposedHelpers;
16 |
17 | import static de.robv.android.xposed.XposedHelpers.getObjectField;
18 |
19 |
20 | /**
21 | * Created by su on 2018/2/05.
22 | * 零钱 hook
23 | */
24 |
25 | public class WeChatHook {
26 | private static XSharedPreferences xsp;
27 |
28 | private static boolean fakeBalance;
29 | private static String balance;
30 |
31 | public static void hook(final ClassLoader classLoader) {
32 | xsp = new XSharedPreferences(BuildConfig.APPLICATION_ID, SettingLabelView.DEFAULT_PREFERENCES_NAME);
33 | xsp.makeWorldReadable();
34 | final Class mallIndexUIClazz =
35 | XposedHelpers.findClass("com.tencent.mm.plugin.mall.ui.MallIndexUI", classLoader);
36 | final Class walletBalanceManagerUIClazz =
37 | XposedHelpers.findClass("com.tencent.mm.plugin.wallet.balance.ui.WalletBalanceManagerUI", classLoader);
38 |
39 | handleHook(mallIndexUIClazz, mallIndexUIClazz.getSuperclass().getDeclaredFields());
40 | handleHook(walletBalanceManagerUIClazz, walletBalanceManagerUIClazz.getDeclaredFields());
41 | }
42 |
43 | private static void reload() {
44 | xsp.reload();
45 | balance = NumberUtils.num2num00(xsp.getString("wechat", "0.00"));
46 | fakeBalance = xsp.getBoolean("fake_wechat", false);
47 | }
48 |
49 | private static void handleHook(Class clazz, final Field[] fields) {
50 | try {
51 | XposedHelpers.findAndHookMethod(clazz, "onResume", new XC_MethodHook() {
52 | @Override
53 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
54 | reload();
55 | if (fakeBalance) {
56 | Object object = param.thisObject;
57 | for (Field field : fields) {
58 | if (field.getType() == TextView.class) {
59 | final TextView textView = (TextView) getObjectField(object, field.getName());
60 | if (textView != null) {
61 | textView.addTextChangedListener(new TextWatcher() {
62 | @Override
63 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
64 |
65 | }
66 |
67 | @Override
68 | public void onTextChanged(CharSequence s, int start, int before, int count) {
69 |
70 | }
71 |
72 | @Override
73 | public void afterTextChanged(Editable s) {
74 | String string = s.toString();
75 | if (string.startsWith("¥") && !s.toString().equals("¥" + balance)) {
76 | textView.setText("¥" + balance);
77 | }
78 | }
79 | });
80 | }
81 | }
82 | }
83 | }
84 | super.afterHookedMethod(param);
85 | }
86 | });
87 | } catch (Error | Exception e) {
88 | e.printStackTrace();
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/fakebalance/src/main/java/com/wuxiaosu/fakebalance/hook/QQPluginHook.java:
--------------------------------------------------------------------------------
1 | package com.wuxiaosu.fakebalance.hook;
2 |
3 | import android.text.Editable;
4 | import android.text.TextWatcher;
5 | import android.widget.TextView;
6 |
7 | import com.wuxiaosu.fakebalance.BuildConfig;
8 | import com.wuxiaosu.fakebalance.util.NumberUtils;
9 | import com.wuxiaosu.widget.SettingLabelView;
10 |
11 | import java.lang.reflect.Method;
12 |
13 | import de.robv.android.xposed.XC_MethodHook;
14 | import de.robv.android.xposed.XSharedPreferences;
15 | import de.robv.android.xposed.XposedBridge;
16 | import de.robv.android.xposed.XposedHelpers;
17 |
18 |
19 | /**
20 | * Created by su on 2018/3/12.
21 | * QQ hook
22 | */
23 |
24 | public class QQPluginHook {
25 | private static XSharedPreferences xsp;
26 |
27 | private static boolean fakeBalance;
28 | private static String balance;
29 |
30 | private static void reload() {
31 | xsp.reload();
32 | balance = NumberUtils.num2num00(xsp.getString("tenpay", "0.00"));
33 | fakeBalance = xsp.getBoolean("fake_tenpay", false);
34 | }
35 |
36 | public static void hook(ClassLoader classLoader) {
37 | xsp = new XSharedPreferences(BuildConfig.APPLICATION_ID, SettingLabelView.DEFAULT_PREFERENCES_NAME);
38 | xsp.makeWorldReadable();
39 | try {
40 | final Class qvipPayAccountActivityClazz = XposedHelpers.findClass("com.qwallet.activity.QvipPayAccountActivity", classLoader);
41 | handleHook(qvipPayAccountActivityClazz);
42 | Method[] methods = qvipPayAccountActivityClazz.getMethods();
43 | for (Method method : methods) {
44 | if (method.getParameterTypes().length == 1
45 | && method.getParameterTypes()[0] == qvipPayAccountActivityClazz
46 | && method.getReturnType() == TextView.class) {
47 |
48 | XposedBridge.hookAllMethods(qvipPayAccountActivityClazz, method.getName(), new XC_MethodHook() {
49 | @Override
50 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
51 | Object object = param.getResult();
52 | if (object != null && object.getClass() == TextView.class) {
53 | handleHook((TextView) object);
54 | }
55 | super.afterHookedMethod(param);
56 | }
57 | });
58 | }
59 | }
60 | } catch (Error | Exception e) {
61 | e.printStackTrace();
62 | }
63 | }
64 |
65 | private static void handleHook(final Class clazz) {
66 | try {
67 | XposedHelpers.findAndHookMethod(clazz, "onResume", new XC_MethodHook() {
68 |
69 | @Override
70 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
71 | reload();
72 | super.beforeHookedMethod(param);
73 | }
74 | });
75 | } catch (Error | Exception e) {
76 | e.printStackTrace();
77 | }
78 | }
79 |
80 | private static void handleHook(final TextView textView) {
81 | textView.addTextChangedListener(new TextWatcher() {
82 | @Override
83 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
84 |
85 | }
86 |
87 | @Override
88 | public void onTextChanged(CharSequence s, int start, int before, int count) {
89 |
90 | }
91 |
92 | @Override
93 | public void afterTextChanged(Editable s) {
94 | if (fakeBalance) {
95 | String string = s.toString();
96 | boolean isDouble = true;
97 | try {
98 | Double.valueOf(string);
99 | } catch (Exception e) {
100 | isDouble = false;
101 | }
102 | if (isDouble && !string.equals(balance)) {
103 | textView.setText(balance);
104 | }
105 | }
106 | }
107 | });
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/fakebalance/src/main/java/com/wuxiaosu/fakebalance/Main.java:
--------------------------------------------------------------------------------
1 | package com.wuxiaosu.fakebalance;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.content.pm.ApplicationInfo;
6 | import android.content.pm.PackageInfo;
7 | import android.content.pm.PackageManager;
8 |
9 | import com.wuxiaosu.fakebalance.hook.AliPayHook;
10 | import com.wuxiaosu.fakebalance.hook.QQHook;
11 | import com.wuxiaosu.fakebalance.hook.QQPluginHook;
12 | import com.wuxiaosu.fakebalance.hook.TimHook;
13 | import com.wuxiaosu.fakebalance.hook.WeChatHook;
14 |
15 | import java.io.File;
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | import dalvik.system.BaseDexClassLoader;
20 | import de.robv.android.xposed.IXposedHookLoadPackage;
21 | import de.robv.android.xposed.XC_MethodHook;
22 | import de.robv.android.xposed.XC_MethodReplacement;
23 | import de.robv.android.xposed.XposedHelpers;
24 | import de.robv.android.xposed.callbacks.XC_LoadPackage;
25 |
26 | /**
27 | * Created by su on 2017/12/29.
28 | */
29 |
30 | public class Main implements IXposedHookLoadPackage {
31 |
32 | private static final String ALIPAY_PKG_NAME = "com.eg.android.AlipayGphone";
33 | private static final String TIM_PKG_NAME = "com.tencent.tim";
34 | private static final String QQ_PKG_NAME = "com.tencent.mobileqq";
35 | private static final String WECHAT_PKG_NAME = "com.tencent.mm";
36 |
37 | private static List pkgList = new ArrayList<>();
38 |
39 | static {
40 | pkgList.add(ALIPAY_PKG_NAME);
41 | pkgList.add(TIM_PKG_NAME);
42 | pkgList.add(WECHAT_PKG_NAME);
43 | pkgList.add(QQ_PKG_NAME);
44 | }
45 |
46 | @Override
47 | public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
48 |
49 | if (lpparam.appInfo == null || (lpparam.appInfo.flags & (ApplicationInfo.FLAG_SYSTEM |
50 | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) {
51 | return;
52 | }
53 | final String packageName = lpparam.packageName;
54 |
55 | if (packageName.equals(BuildConfig.APPLICATION_ID)) {
56 | XposedHelpers.findAndHookMethod(BuildConfig.APPLICATION_ID + ".MainActivity", lpparam.classLoader,
57 | "isModuleActive", XC_MethodReplacement.returnConstant(true));
58 | return;
59 | }
60 |
61 | if (pkgList.contains(packageName)) {
62 | XposedHelpers.findAndHookMethod(Application.class,
63 | "attach",
64 | Context.class, new XC_MethodHook() {
65 | @Override
66 | protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
67 | super.afterHookedMethod(param);
68 | Context context = (Context) param.args[0];
69 | ClassLoader appClassLoader = context.getClassLoader();
70 |
71 | switch (packageName) {
72 | case ALIPAY_PKG_NAME:
73 | new AliPayHook(getVersionName(context, ALIPAY_PKG_NAME)).hook(appClassLoader);
74 | break;
75 | case WECHAT_PKG_NAME:
76 | WeChatHook.hook(appClassLoader);
77 | break;
78 | case QQ_PKG_NAME:
79 | QQHook.hook(appClassLoader);
80 | break;
81 | default:
82 | }
83 | }
84 | });
85 |
86 | if (packageName.equals(TIM_PKG_NAME) || packageName.equals(QQ_PKG_NAME)) {
87 | XposedHelpers.findAndHookConstructor("dalvik.system.BaseDexClassLoader",
88 | lpparam.classLoader, String.class, File.class, String.class, ClassLoader.class, new XC_MethodHook() {
89 | @Override
90 | protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
91 |
92 | if (param.args[0].toString().contains("qwallet_plugin.apk")) {
93 | ClassLoader classLoader = (BaseDexClassLoader) param.thisObject;
94 | if (packageName.equals(TIM_PKG_NAME)) {
95 | TimHook.hook(classLoader);
96 | } else {
97 | QQPluginHook.hook(classLoader);
98 | }
99 | }
100 | }
101 | });
102 | }
103 | }
104 | }
105 |
106 |
107 | private String getVersionName(Context context, String pkgName) {
108 | try {
109 | PackageManager packageManager = context.getPackageManager();
110 | PackageInfo packInfo = packageManager.getPackageInfo(pkgName, 0);
111 | return packInfo.versionName;
112 | } catch (PackageManager.NameNotFoundException e) {
113 | e.printStackTrace();
114 | }
115 | return "";
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/fakebalance/src/main/java/com/wuxiaosu/fakebalance/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.wuxiaosu.fakebalance;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.AlertDialog;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.SharedPreferences;
9 | import android.content.pm.PackageManager;
10 | import android.net.Uri;
11 | import android.os.Bundle;
12 | import android.text.Editable;
13 | import android.text.Html;
14 | import android.text.TextWatcher;
15 | import android.view.LayoutInflater;
16 | import android.view.Menu;
17 | import android.view.MenuItem;
18 | import android.view.View;
19 | import android.widget.CompoundButton;
20 | import android.widget.EditText;
21 | import android.widget.TextView;
22 | import android.widget.Toast;
23 |
24 | import com.wuxiaosu.fakebalance.base.BaseActivity;
25 | import com.wuxiaosu.widget.SettingLabelView;
26 |
27 |
28 | public class MainActivity extends BaseActivity {
29 |
30 | @Override
31 | protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_main);
34 | getToolbar().setNavigationIcon(null);
35 |
36 | if (!isModuleActive()) {
37 | Toast.makeText(this, "模块未激活", Toast.LENGTH_SHORT).show();
38 | }
39 | initView();
40 | }
41 |
42 | private void initView() {
43 | SharedPreferences sharedPreferences =
44 | getSharedPreferences(SettingLabelView.DEFAULT_PREFERENCES_NAME, Context.MODE_WORLD_READABLE);
45 |
46 | bindPreferences(R.id.et_wechat, sharedPreferences, R.string.pre_key_wechat, "0.00");
47 | bindPreferences(R.id.et_tenpay, sharedPreferences, R.string.pre_key_tenpay, "0.00");
48 | bindPreferences(R.id.et_alipay, sharedPreferences, R.string.pre_key_alipay, "0.00");
49 | bindPreferences(R.id.et_alipay_tts, sharedPreferences, R.string.pre_key_alipay_tts, "5000000.00");
50 |
51 | ((SettingLabelView) findViewById(R.id.slv_hide_icon)).
52 | setCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
53 | @Override
54 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
55 | hideLauncherIcon(isChecked);
56 | }
57 | });
58 | }
59 |
60 | /**
61 | * 绑定设置
62 | *
63 | * @param viewId
64 | * @param sharedPreferences
65 | * @param preStrResId
66 | * @param defaultValue
67 | */
68 | private void bindPreferences(int viewId, final SharedPreferences sharedPreferences,
69 | final int preStrResId, Object defaultValue) {
70 | View view = findViewById(viewId);
71 | if (view instanceof EditText) {
72 | String temp = sharedPreferences.getString(getString(preStrResId), (String) defaultValue);
73 | ((EditText) view).setText(temp);
74 | ((EditText) view).addTextChangedListener(new TextWatcher() {
75 | @Override
76 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
77 |
78 | }
79 |
80 | @Override
81 | public void onTextChanged(CharSequence s, int start, int before, int count) {
82 |
83 | }
84 |
85 | @Override
86 | public void afterTextChanged(Editable s) {
87 | sharedPreferences.edit().putString(getString(preStrResId), s.toString()).apply();
88 | }
89 | });
90 | }
91 |
92 | }
93 |
94 | public void hideLauncherIcon(boolean isHide) {
95 | PackageManager packageManager = this.getPackageManager();
96 | int hide = isHide ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
97 | : PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
98 | packageManager.setComponentEnabledSetting(getAliasComponentName(),
99 | hide, PackageManager.DONT_KILL_APP);
100 | }
101 |
102 |
103 | @Override
104 | public boolean onCreateOptionsMenu(Menu menu) {
105 | getMenuInflater().inflate(R.menu.menu_main, menu);
106 | return true;
107 | }
108 |
109 | @Override
110 | public boolean onOptionsItemSelected(MenuItem item) {
111 | int id = item.getItemId();
112 | if (id == R.id.action_about) {
113 | showInfo();
114 | }
115 | return super.onOptionsItemSelected(item);
116 | }
117 |
118 |
119 | @SuppressLint("SetTextI18n")
120 | private void showInfo() {
121 | View view = LayoutInflater.from(this).inflate(R.layout.dialog_about_content, null);
122 | TextView mTvVersionName = view.findViewById(R.id.tv_version_name);
123 | TextView mTvInfo = view.findViewById(R.id.tv_info);
124 | final TextView mTvUrl = view.findViewById(R.id.tv_url);
125 | mTvUrl.setText(Html.fromHtml("https://github.com/wuxiaosu/XposedManyMoney"));
126 | mTvUrl.setOnClickListener(new View.OnClickListener() {
127 | @Override
128 | public void onClick(View v) {
129 | sendURLIntent(((TextView) v).getText().toString());
130 | }
131 | });
132 | mTvVersionName.setText(getString(R.string.app_name) + " v" + BuildConfig.VERSION_NAME);
133 | mTvInfo.setText(getString(R.string.app_description)
134 | + "\n作者没什么想说的,就祝你们多多发财吧"
135 | + "\n更多详情:");
136 | AlertDialog alertDialog = new AlertDialog.Builder(this)
137 | .setTitle("关于")
138 | .setView(view)
139 | .create();
140 | alertDialog.show();
141 | }
142 |
143 | private void sendURLIntent(String url) {
144 | Intent intent = new Intent();
145 | intent.setAction("android.intent.action.VIEW");
146 | Uri contentUrl = Uri.parse(url);
147 | intent.setData(contentUrl);
148 | startActivity(intent);
149 | }
150 |
151 | private ComponentName getAliasComponentName() {
152 | return new ComponentName(MainActivity.this, "com.wuxiaosu.fakebalance.MainActivity_Alias");
153 | }
154 |
155 | /**
156 | * 模块是否启用
157 | *
158 | * @return
159 | */
160 | private static boolean isModuleActive() {
161 | return false;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/fakebalance/src/main/java/com/wuxiaosu/fakebalance/hook/AliPayHook.java:
--------------------------------------------------------------------------------
1 | package com.wuxiaosu.fakebalance.hook;
2 |
3 |
4 | import com.wuxiaosu.fakebalance.BuildConfig;
5 | import com.wuxiaosu.fakebalance.util.NumberUtils;
6 | import com.wuxiaosu.widget.SettingLabelView;
7 |
8 | import java.util.regex.Matcher;
9 | import java.util.regex.Pattern;
10 |
11 | import de.robv.android.xposed.XC_MethodHook;
12 | import de.robv.android.xposed.XC_MethodReplacement;
13 | import de.robv.android.xposed.XSharedPreferences;
14 | import de.robv.android.xposed.XposedBridge;
15 | import de.robv.android.xposed.XposedHelpers;
16 |
17 |
18 | /**
19 | * Created by su on 2018/2/05.
20 | * alipay hook
21 | */
22 |
23 | public class AliPayHook {
24 | private static XSharedPreferences xsp;
25 |
26 | private static boolean fakeBalance;
27 | private static String balance;
28 |
29 | private static boolean fakeTts;
30 | private static String tts;
31 |
32 | private String ttsClassName;
33 |
34 | public AliPayHook(String versionName) {
35 | xsp = new XSharedPreferences(BuildConfig.APPLICATION_ID, SettingLabelView.DEFAULT_PREFERENCES_NAME);
36 | xsp.makeWorldReadable();
37 | switch (versionName) {
38 | default:
39 | ttsClassName = "com.alipay.mobile.rome.pushservice.tts.e";
40 | break;
41 | }
42 | }
43 |
44 | private void reload() {
45 | xsp.reload();
46 | balance = NumberUtils.num2num00WithComma(xsp.getString("alipay", "0.00"));
47 | fakeBalance = xsp.getBoolean("fake_alipay", false);
48 | tts = NumberUtils.num2num00(xsp.getString("alipay_tts", "0.00"));
49 | fakeTts = xsp.getBoolean("fake_alipay_tts", false);
50 | }
51 |
52 | public void hook(ClassLoader classLoader) {
53 | securityCheckHook(classLoader);
54 | try {
55 | Class clazz = XposedHelpers.findClass("com.flybird.FBDocument", classLoader);
56 | XposedHelpers.findAndHookMethod(clazz, "updateLayout", String.class,
57 | new XC_MethodHook() {
58 | @Override
59 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
60 | reload();
61 | if (fakeBalance) {
62 | String strings = (String) param.args[0];
63 | String b = new String(new byte[]{(byte) 1});
64 | String c = new String(new byte[]{2});
65 |
66 | String pattern = "余额账户\\(元\\)[\\s\\S]*?text(.+?)up_css";
67 |
68 | Pattern r = Pattern.compile(pattern);
69 | Matcher m = r.matcher(strings);
70 | if (m.find()) {
71 | strings = strings.replace(m.group(1), c + balance + c + b);
72 | }
73 | param.args[0] = strings;
74 | }
75 | super.beforeHookedMethod(param);
76 | }
77 | });
78 | Class viewClazz = XposedHelpers.findClass("com.alipay.asset.common.view.BaseWealthWidgetView", classLoader);
79 | XposedBridge.hookAllMethods(viewClazz, "setWidgetModule", new XC_MethodHook() {
80 | @Override
81 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
82 | reload();
83 | if (fakeBalance) {
84 | Object object = param.args[0];
85 | String title = (String) XposedHelpers.callMethod(object, "getTitle");
86 | if (title.equals("余额")) {
87 | XposedHelpers.callMethod(object, "setMainInfo", balance + " 元");
88 | }
89 | param.args[0] = object;
90 | }
91 | super.beforeHookedMethod(param);
92 | }
93 | });
94 |
95 | Class ttsClass = XposedHelpers.findClass(ttsClassName, classLoader);
96 | XposedBridge.hookAllConstructors(ttsClass, new XC_MethodHook() {
97 | @Override
98 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
99 | reload();
100 | if (fakeTts) {
101 | param.args[1] = tts;
102 | XposedHelpers.callMethod(param.args[2], "setContent", tts);
103 | }
104 | super.beforeHookedMethod(param);
105 | }
106 | });
107 | } catch (Error | Exception e) {
108 | e.printStackTrace();
109 | }
110 | }
111 |
112 | private void securityCheckHook(ClassLoader classLoader) {
113 | try {
114 | Class securityCheckClazz = XposedHelpers.findClass("com.alipay.mobile.base.security.CI", classLoader);
115 | XposedHelpers.findAndHookMethod(securityCheckClazz, "a", String.class, String.class, String.class, new XC_MethodHook() {
116 | @Override
117 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
118 | Object object = param.getResult();
119 | XposedHelpers.setBooleanField(object, "a", false);
120 | param.setResult(object);
121 | super.afterHookedMethod(param);
122 | }
123 | });
124 |
125 | XposedHelpers.findAndHookMethod(securityCheckClazz, "a", Class.class, String.class, String.class, new XC_MethodReplacement() {
126 | @Override
127 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
128 | return (byte) 1;
129 | }
130 | });
131 | XposedHelpers.findAndHookMethod(securityCheckClazz, "a", ClassLoader.class, String.class, new XC_MethodReplacement() {
132 | @Override
133 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
134 | return (byte) 1;
135 | }
136 | });
137 | XposedHelpers.findAndHookMethod(securityCheckClazz, "a", new XC_MethodReplacement() {
138 | @Override
139 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
140 | return false;
141 | }
142 | });
143 |
144 | } catch (Error | Exception e) {
145 | e.printStackTrace();
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/fakebalance/src/main/java/com/wuxiaosu/fakebalance/hook/TimHook.java:
--------------------------------------------------------------------------------
1 | package com.wuxiaosu.fakebalance.hook;
2 |
3 | import android.text.Editable;
4 | import android.text.TextWatcher;
5 | import android.widget.TextView;
6 |
7 | import com.wuxiaosu.fakebalance.BuildConfig;
8 | import com.wuxiaosu.fakebalance.util.NumberUtils;
9 | import com.wuxiaosu.widget.SettingLabelView;
10 |
11 | import java.lang.reflect.Field;
12 | import java.lang.reflect.Method;
13 |
14 | import de.robv.android.xposed.XC_MethodHook;
15 | import de.robv.android.xposed.XSharedPreferences;
16 | import de.robv.android.xposed.XposedBridge;
17 | import de.robv.android.xposed.XposedHelpers;
18 |
19 |
20 | /**
21 | * Created by su on 2018/2/05.
22 | * tim hook
23 | */
24 |
25 | public class TimHook {
26 | private static XSharedPreferences xsp;
27 |
28 | private static boolean fakeBalance;
29 | private static String balance;
30 |
31 |
32 | private static void reload() {
33 | xsp.reload();
34 | balance = NumberUtils.num2num00(xsp.getString("tenpay", "0.00"));
35 | fakeBalance = xsp.getBoolean("fake_tenpay", false);
36 | }
37 |
38 | public static void hook(ClassLoader classLoader) {
39 | xsp = new XSharedPreferences(BuildConfig.APPLICATION_ID, SettingLabelView.DEFAULT_PREFERENCES_NAME);
40 | xsp.makeWorldReadable();
41 | try {
42 | final Class qvipPayWalletActivityClazz = XposedHelpers.findClass("com.qwallet.activity.QvipPayWalletActivity", classLoader);
43 | final Class qvipPayAccountActivityClazz = XposedHelpers.findClass("com.qwallet.activity.QvipPayAccountActivity", classLoader);
44 | final Class enClazz = XposedHelpers.findClass("com.tenpay.android.qqplugin.activity.en", classLoader);
45 |
46 | handleHook(qvipPayWalletActivityClazz);
47 | handleHook(qvipPayAccountActivityClazz);
48 |
49 | XposedBridge.hookAllMethods(qvipPayWalletActivityClazz, "a", new XC_MethodHook() {
50 | @Override
51 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
52 | Object object = param.getResult();
53 | if (object != null && object.getClass() == TextView.class) {
54 | handleHook((TextView) object);
55 | }
56 | super.afterHookedMethod(param);
57 | }
58 | });
59 |
60 | Method[] methods = qvipPayAccountActivityClazz.getMethods();
61 | for (Method method : methods) {
62 | if (method.getParameterTypes().length == 1
63 | && method.getParameterTypes()[0] == qvipPayAccountActivityClazz
64 | && method.getReturnType() == TextView.class) {
65 |
66 | XposedBridge.hookAllMethods(qvipPayAccountActivityClazz, method.getName(), new XC_MethodHook() {
67 | @Override
68 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
69 | Object object = param.getResult();
70 | if (object != null && object.getClass() == TextView.class) {
71 | handleHook((TextView) object);
72 | }
73 | super.afterHookedMethod(param);
74 | }
75 | });
76 | }
77 | }
78 |
79 | XposedHelpers.findAndHookMethod(enClazz, "onResume", new XC_MethodHook() {
80 |
81 | @Override
82 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
83 | reload();
84 | Field[] fields = enClazz.getDeclaredFields();
85 | for (Field field : fields) {
86 | if (field.getType() == TextView.class) {
87 | final TextView textView = (TextView) XposedHelpers.getObjectField(param.thisObject, field.getName());
88 | textView.addTextChangedListener(new TextWatcher() {
89 | @Override
90 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
91 |
92 | }
93 |
94 | @Override
95 | public void onTextChanged(CharSequence s, int start, int before, int count) {
96 |
97 | }
98 |
99 | @Override
100 | public void afterTextChanged(Editable s) {
101 | if (fakeBalance) {
102 | String string = s.toString();
103 | if (!string.equals("记录")) {
104 | boolean isDouble = true;
105 | try {
106 | Double.valueOf(string);
107 | } catch (Exception e) {
108 | isDouble = false;
109 | }
110 | if (isDouble && !string.equals(balance)) {
111 | textView.setText(balance);
112 | }
113 | }
114 | }
115 | }
116 | });
117 | }
118 | }
119 | super.beforeHookedMethod(param);
120 | }
121 | });
122 | } catch (Error | Exception e) {
123 | e.printStackTrace();
124 | }
125 | }
126 |
127 | private static void handleHook(final Class clazz) {
128 | try {
129 | XposedHelpers.findAndHookMethod(clazz, "onResume", new XC_MethodHook() {
130 |
131 | @Override
132 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
133 | reload();
134 | super.beforeHookedMethod(param);
135 | }
136 | });
137 | } catch (Error | Exception e) {
138 | e.printStackTrace();
139 | }
140 | }
141 |
142 | private static void handleHook(final TextView textView) {
143 | textView.addTextChangedListener(new TextWatcher() {
144 | @Override
145 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
146 |
147 | }
148 |
149 | @Override
150 | public void onTextChanged(CharSequence s, int start, int before, int count) {
151 |
152 | }
153 |
154 | @Override
155 | public void afterTextChanged(Editable s) {
156 | if (fakeBalance) {
157 | String string = s.toString();
158 | if (string.startsWith("¥") && !s.toString().equals("¥" + balance)) {
159 | textView.setText("¥" + balance);
160 | return;
161 | }
162 |
163 | if (string.endsWith(" 元")) {
164 | if (!s.toString().equals(balance + " 元")) {
165 | textView.setText(balance + " 元");
166 | }
167 | } else {
168 | if (string.endsWith("元")) {
169 | if (!s.toString().equals(balance + "元")) {
170 | textView.setText(balance + "元");
171 | }
172 | }
173 | }
174 | }
175 | }
176 | });
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | , 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------