├── .gitignore
├── .idea
├── .gitignore
├── compiler.xml
├── deploymentTargetDropDown.xml
├── gradle.xml
├── jarRepositories.xml
├── misc.xml
└── vcs.xml
├── app
├── .gitignore
├── build.gradle
├── libs
│ └── XposedBridgeApi-82.jar
├── proguard-rules.pro
├── release
│ ├── app-release.apk
│ └── output-metadata.json
└── src
│ ├── androidTest
│ └── java
│ │ └── indi
│ │ └── conastin
│ │ └── textmodify
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── xposed_init
│ ├── ic_launcher-playstore.png
│ ├── java
│ │ └── indi
│ │ │ └── conastin
│ │ │ └── textmodify
│ │ │ ├── HookEntrance.java
│ │ │ ├── MainActivity.kt
│ │ │ ├── activity
│ │ │ ├── AboutActivity.kt
│ │ │ ├── AddRule.kt
│ │ │ ├── AppListActivity.kt
│ │ │ ├── AppMethodActivity.kt
│ │ │ └── GlobalMethodActivity.kt
│ │ │ ├── adapter
│ │ │ ├── AppListAdapter.kt
│ │ │ └── AppMethodAdapter.kt
│ │ │ ├── databse
│ │ │ ├── AppInfo.kt
│ │ │ ├── HookMethodDao.kt
│ │ │ ├── HookMethodDatabase.kt
│ │ │ ├── HookMethodEntity.kt
│ │ │ ├── MethodInfo.kt
│ │ │ └── RuleInfo.kt
│ │ │ ├── log
│ │ │ └── NewLog.kt
│ │ │ └── viewmodel
│ │ │ ├── InventoryViewModel.kt
│ │ │ └── RulesViewModel.kt
│ └── res
│ │ ├── drawable
│ │ ├── bg_gray_bottom_solid.xml
│ │ ├── bg_gray_solid.xml
│ │ ├── bg_green_solid.xml
│ │ ├── bg_red_solid.xml
│ │ ├── bg_white_solid.xml
│ │ ├── bt_gray_solid.xml
│ │ ├── donation.png
│ │ ├── ic_about.xml
│ │ ├── ic_add.xml
│ │ ├── ic_app_lst.xml
│ │ ├── ic_global_method.xml
│ │ ├── ic_is_active.xml
│ │ ├── ic_is_not_active.xml
│ │ ├── ic_launcher_foreground.xml
│ │ ├── ic_log.xml
│ │ ├── ic_refresh.xml
│ │ ├── ic_setting.xml
│ │ └── ic_textmodify.xml
│ │ ├── layout
│ │ ├── activity_about.xml
│ │ ├── activity_add_rule.xml
│ │ ├── activity_app_method.xml
│ │ ├── activity_app_method_info.xml
│ │ ├── activity_main.xml
│ │ ├── app_list_listview_item.xml
│ │ ├── app_method_listview_item.xml
│ │ └── global_method.xml
│ │ ├── menu
│ │ └── app_method_topbar_menu.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── values-night
│ │ └── themes.xml
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ └── data_extraction_rules.xml
│ └── test
│ └── java
│ └── indi
│ └── conastin
│ └── textmodify
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # 默认忽略的文件
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetDropDown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'org.jetbrains.kotlin.android'
4 | id 'kotlin-kapt'
5 | }
6 |
7 | android {
8 | compileSdkVersion 31
9 | buildToolsVersion "30.0.3"
10 |
11 | defaultConfig {
12 | applicationId "indi.conastin.textmodify"
13 | minSdkVersion 26
14 | targetSdkVersion 31
15 | versionCode 1
16 | versionName "1.0"
17 |
18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19 | javaCompileOptions {
20 | annotationProcessorOptions {
21 | arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]//指定数据库schema导出的位置
22 | }
23 | }
24 | }
25 |
26 | buildTypes {
27 | release {
28 | // 开启会导致XposedBridgeApi无法加载
29 | // minifyEnabled true
30 | // shrinkResources true
31 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
32 | }
33 | }
34 | compileOptions {
35 | sourceCompatibility JavaVersion.VERSION_1_8
36 | targetCompatibility JavaVersion.VERSION_1_8
37 | }
38 | kotlinOptions {
39 | jvmTarget = '1.8'
40 | }
41 |
42 | buildFeatures {
43 | viewBinding = true
44 | }
45 | }
46 |
47 | dependencies {
48 |
49 | implementation "androidx.core:core-ktx:$core_ktx_version"
50 | implementation "androidx.appcompat:appcompat:$appcompat_version"
51 | implementation "com.google.android.material:material:$material_version"
52 | implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
53 |
54 | // Fragment libraries
55 | implementation "androidx.fragment:fragment-ktx:$fragment_version"
56 |
57 | // Room libraries
58 | implementation "androidx.room:room-runtime:$room_version"
59 | // annotationProcessor "androidx.room:room-compiler:$room_version"
60 | kapt "androidx.room:room-compiler:$room_version"
61 | implementation "androidx.room:room-ktx:$room_version"
62 |
63 | // Lifecycle libraries
64 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
65 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
66 |
67 | // Navigation libraries
68 | implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
69 | implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
70 |
71 | // Xposed libraries
72 | compileOnly files('libs/XposedBridgeApi-82.jar')
73 |
74 | // AndroidTest libraries
75 | testImplementation "junit:junit:4.13.2"
76 | androidTestImplementation "androidx.test.ext:junit:1.1.3"
77 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
78 | }
--------------------------------------------------------------------------------
/app/libs/XposedBridgeApi-82.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/libs/XposedBridgeApi-82.jar
--------------------------------------------------------------------------------
/app/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
--------------------------------------------------------------------------------
/app/release/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/release/app-release.apk
--------------------------------------------------------------------------------
/app/release/output-metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "artifactType": {
4 | "type": "APK",
5 | "kind": "Directory"
6 | },
7 | "applicationId": "indi.conastin.textmodify",
8 | "variantName": "release",
9 | "elements": [
10 | {
11 | "type": "SINGLE",
12 | "filters": [],
13 | "attributes": [],
14 | "versionCode": 1,
15 | "versionName": "1.0",
16 | "outputFile": "app-release.apk"
17 | }
18 | ],
19 | "elementType": "File"
20 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/indi/conastin/textmodify/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("indi.conastin.textmodify", appContext.packageName)
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
11 |
14 |
15 |
25 |
28 |
31 |
34 |
37 |
40 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
52 |
55 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/assets/xposed_init:
--------------------------------------------------------------------------------
1 | indi.conastin.textmodify.HookEntrance
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/HookEntrance.java:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify;
2 |
3 | import android.text.SpannableString;
4 | import android.text.SpannableStringBuilder;
5 | import android.widget.TextView;
6 |
7 | import java.util.ArrayList;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | import de.robv.android.xposed.IXposedHookLoadPackage;
12 | import de.robv.android.xposed.XC_MethodHook;
13 | import de.robv.android.xposed.XC_MethodReplacement;
14 | import de.robv.android.xposed.XSharedPreferences;
15 | import de.robv.android.xposed.XposedHelpers;
16 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
17 | import indi.conastin.textmodify.databse.RuleInfo;
18 | import indi.conastin.textmodify.log.NewLog;
19 |
20 | public class HookEntrance implements IXposedHookLoadPackage {
21 |
22 | // 存放全部规则
23 | private final ArrayList allRules = new ArrayList<>();
24 |
25 | @Override
26 | public void handleLoadPackage(final LoadPackageParam lpparam) {
27 | if (lpparam.packageName.equals("indi.conastin.textmodify")) {
28 | // hook checkModelActive
29 | XposedHelpers.findAndHookMethod("indi.conastin.textmodify.MainActivity", lpparam.classLoader, "checkModelActive", XC_MethodReplacement.returnConstant(true));
30 | } else {
31 | // 消除附加进程干扰(可能导致异常)
32 | if (lpparam.isFirstApplication) {
33 | NewLog log = new NewLog();
34 | log.xposedLog("【HookEntrance】 | packageName: " + lpparam.packageName + " | try to init file");
35 | // 初始化配置文件
36 | // XSharedPreferences globalSps = new XSharedPreferences(lpparam.packageName, "global_TextModify");
37 | XSharedPreferences globalSps = new XSharedPreferences("indi.conastin.textmodify", "global_TextModify");
38 | globalSps.makeWorldReadable();
39 | log.xposedLog("【HookEntrance】 | globalFilePath: " + globalSps.getFile() + " | globalFileValues: " + globalSps.getAll());
40 | XSharedPreferences packageSps = new XSharedPreferences(lpparam.packageName, lpparam.packageName + "_TextModify");
41 | log.xposedLog("【HookEntrance】 | packageFilePath: " + packageSps.getFile() + " | packageFileValues: " + packageSps.getAll());
42 | // 配置文件读取到globalRules和packageRules
43 | allRules.addAll(loadRuleList(globalSps));
44 | allRules.addAll(loadRuleList(packageSps));
45 | log.xposedLog("【HookEntrance】 | allRules: " + allRules.toString());
46 | // hook text TextView.class method
47 | XC_MethodHook textviewHook = new XC_MethodHook() {
48 | @Override
49 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
50 | super.beforeHookedMethod(param);
51 | if (param.args[0] != null) {
52 | if (param.args[0] instanceof String) {
53 | String finalText = param.args[0].toString();
54 | for (int i = 0; i < allRules.size(); i++) {
55 | if (param.args[0].toString().contains(allRules.get(i).getOriginText())) {
56 | log.xposedLog("【HookEntrance】 | String finalText init: " + finalText);
57 | finalText = finalText.replace(allRules.get(i).getOriginText(), allRules.get(i).getNewText());
58 | log.xposedLog("【HookEntrance】 | origin: " + allRules.get(i).getOriginText() + " | new: " + allRules.get(i).getNewText() + " | finalText: " + finalText);
59 | }
60 | }
61 | param.args[0] = finalText;
62 | } else if (param.args[0] instanceof SpannableString) {
63 | String finalText = param.args[0].toString();
64 | for (int i = 0; i < allRules.size(); i++) {
65 | if (param.args[0].toString().contains(allRules.get(i).getOriginText())) {
66 | log.xposedLog("【HookEntrance】 | SpannableString finalText init: " + finalText);
67 | finalText = finalText.replace(allRules.get(i).getOriginText(), allRules.get(i).getNewText());
68 | log.xposedLog("【HookEntrance】 | origin: " + allRules.get(i).getOriginText() + " | new: " + allRules.get(i).getNewText() + " | finalText: " + finalText);
69 | }
70 | }
71 | param.args[0] = new SpannableString(finalText);
72 | } else if (param.args[0] instanceof SpannableStringBuilder) {
73 | SpannableStringBuilder finalText = (SpannableStringBuilder) param.args[0];
74 | for (int i = 0; i < allRules.size(); i++) {
75 | if (param.args[0].toString().contains(allRules.get(i).getOriginText())) {
76 | log.xposedLog("【HookEntrance】 | SpannableStringBuilder finalText init: " + finalText.toString());
77 | // 获取起始位置
78 | int start = param.args[0].toString().indexOf(allRules.get(i).getOriginText());
79 | // 计算替换文字大小
80 | if (allRules.get(i).getOriginText().length() == allRules.get(i).getNewText().length()) {
81 | // 相等
82 | finalText.replace(start, allRules.get(i).getOriginText().length(), allRules.get(i).getNewText());
83 | } else if (allRules.get(i).getOriginText().length() > allRules.get(i).getNewText().length()) {
84 | // 删减了
85 | finalText.replace(start, allRules.get(i).getNewText().length() + 1, allRules.get(i).getNewText());
86 | finalText.delete(start + allRules.get(i).getNewText().length(), start + allRules.get(i).getOriginText().length() - allRules.get(i).getNewText().length() + 1);
87 | } else {
88 | // 增加了
89 | finalText.replace(start, allRules.get(i).getOriginText().length() + 1, allRules.get(i).getNewText().subSequence(0, allRules.get(i).getOriginText().length()));
90 | finalText.insert(start + allRules.get(i).getOriginText().length(), allRules.get(i).getNewText().subSequence(allRules.get(i).getOriginText().length(), allRules.get(i).getNewText().length()));
91 | }
92 | log.xposedLog("【HookEntrance】 | origin: " + allRules.get(i).getOriginText() + " | new: " + allRules.get(i).getNewText() + " | finalText: " + finalText);
93 | }
94 | }
95 | param.args[0] = finalText;
96 | } else if (param.args[0] instanceof StringBuffer) {
97 | String finalText = param.args[0].toString();
98 | for (int i = 0; i < allRules.size(); i++) {
99 | if (param.args[0].toString().contains(allRules.get(i).getOriginText())) {
100 | log.xposedLog("【HookEntrance】 | SpannableString finalText init: " + finalText);
101 | finalText = finalText.replace(allRules.get(i).getOriginText(), allRules.get(i).getNewText());
102 | log.xposedLog("【HookEntrance】 | origin: " + allRules.get(i).getOriginText() + " | new: " + allRules.get(i).getNewText() + " | finalText: " + finalText);
103 | }
104 | }
105 | param.args[0] = new StringBuffer(finalText);
106 | } else {
107 | String origin = param.args[0].toString();
108 | log.xposedLog("【HookEntrance】 | 未知的字符串类型 | 文字: " + origin + " | 类: " + param.args[0].getClass());
109 | }
110 | }
111 | }
112 | };
113 | XposedHelpers.findAndHookMethod(TextView.class, "setText", CharSequence.class, TextView.BufferType.class, textviewHook);
114 | }
115 | }
116 | }
117 |
118 | private ArrayList loadRuleList(XSharedPreferences sps) {
119 | // temp中介 ruleList存放成对的列表
120 | ArrayList ruleList = new ArrayList<>();
121 | Map temp = new HashMap<>();
122 | Map map = sps.getAll();
123 | // map按配对存至temp
124 | for (String key : map.keySet()) {
125 | // 排除num的干扰 在HookEntrance里面没什么用
126 | if (!key.equals("num")) {
127 | // log.xposedLog("【HookEntrance】 | key: " + key + " | key.startsWith('o'): " + key.startsWith("o"));
128 | if (key.startsWith("o")) {
129 | // o
130 | // log.xposedLog("【HookEntrance】 | key.substring(1): " + key.substring(1));
131 | // log.xposedLog("【HookEntrance】 | map.get(key): " + map.get(key).toString());
132 | // log.xposedLog("【HookEntrance】 | temp.get(key.substring(1)): " + temp.get(key.substring(1)));
133 | if (temp.get(key.substring(1)) == null) {
134 | temp.put(key.substring(1), new RuleInfo(map.get(key).toString(), ""));
135 | } else {
136 | temp.put(key.substring(1), new RuleInfo(map.get(key).toString(), temp.get(key.substring(1)).getNewText()));
137 | }
138 | // log.xposedLog("【HookEntrance】 | temp: " + temp.toString());
139 | } else {
140 | // log.xposedLog("【HookEntrance】 | key.substring(1): " + key.substring(1));
141 | // log.xposedLog("【HookEntrance】 | map.get(key): " + map.get(key).toString());
142 | // log.xposedLog("【HookEntrance】 | temp.get(key.substring(1)): " + temp.get(key.substring(1)));
143 | // n
144 | if (temp.get(key.substring(1)) == null) {
145 | temp.put(key.substring(1), new RuleInfo("", map.get(key).toString()));
146 | } else {
147 | temp.put(key.substring(1), new RuleInfo(temp.get(key.substring(1)).getOriginText(), map.get(key).toString()));
148 | }
149 | // log.xposedLog("【HookEntrance】 | temp: " + temp.toString());
150 | }
151 | }
152 | }
153 | // log.xposedLog("【HookEntrance】 | temp: " + temp.toString());
154 | // temp键值对转List至ruleList
155 | for (String key : temp.keySet()) {
156 | ruleList.add(temp.get(key));
157 | }
158 | // log.xposedLog("【HookEntrance】 | ruleList: " + ruleList.toString());
159 | return ruleList;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import android.os.Process
7 | import android.widget.Toast
8 | import androidx.appcompat.app.AppCompatActivity
9 | import indi.conastin.textmodify.activity.AboutActivity
10 | import indi.conastin.textmodify.activity.AppListActivity
11 | import indi.conastin.textmodify.activity.GlobalMethodActivity
12 | import indi.conastin.textmodify.databinding.ActivityMainBinding
13 |
14 |
15 | class MainActivity : AppCompatActivity() {
16 |
17 | private lateinit var binding: ActivityMainBinding
18 |
19 | @SuppressLint("UseCompatLoadingForDrawables")
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | // binding创建layout
23 | binding = ActivityMainBinding.inflate(layoutInflater)
24 | val view = binding.root
25 | setContentView(view)
26 | // 检查root权限
27 | Runtime.getRuntime().exec("su")
28 | // 检查xposed激活状态
29 | if (checkModelActive()) {
30 | binding.mainCheckXposedActiveBackground.setBackgroundResource(R.drawable.bg_green_solid)
31 | binding.mainCheckXposedActiveText.setText(R.string.main_xposed_is_active)
32 | binding.mainCheckXposedActiveIcon.setImageDrawable(
33 | resources.getDrawable(R.drawable.ic_is_active, null)
34 | )
35 | }
36 | // 重启按钮监听
37 | binding.mainCheckXposedActiveBackground.setOnClickListener {
38 | // 重启
39 | val intent = Intent(this, MainActivity::class.java)
40 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
41 | this.startActivity(intent)
42 | Process.killProcess(Process.myPid())
43 | }
44 | // 应用列表按钮监听
45 | binding.appMethod.setOnClickListener {
46 | startActivity(Intent(view.context, AppListActivity::class.java))
47 | }
48 | // 全局替换按钮监听
49 | binding.globalMethod.setOnClickListener {
50 | startActivity(Intent(view.context, GlobalMethodActivity::class.java))
51 | }
52 |
53 | binding.setting.setOnClickListener {
54 | Toast.makeText(this, "憋催了,没写呢!", Toast.LENGTH_SHORT).show()
55 | // 隐藏图标
56 | // val componentName = ComponentName(this, MainActivity::class.java)
57 | // val res = packageManager.getComponentEnabledSetting(componentName);
58 | // Log.d("TextModify", (res == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).toString() + (res == PackageManager.COMPONENT_ENABLED_STATE_ENABLED));
59 | // if (res == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
60 | // || res == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
61 | // ) {
62 | // // 隐藏应用图标
63 | // packageManager.setComponentEnabledSetting(
64 | // componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
65 | // PackageManager.DONT_KILL_APP
66 | // );
67 | // } else {
68 | // // 显示应用图标
69 | // packageManager.setComponentEnabledSetting(
70 | // componentName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
71 | // PackageManager.DONT_KILL_APP
72 | // );
73 | // }
74 | }
75 | // 关于按钮监听
76 | binding.info.setOnClickListener {
77 | startActivity(Intent(view.context, AboutActivity::class.java))
78 | }
79 | }
80 |
81 | private fun checkModelActive(): Boolean {
82 | return false
83 | }
84 |
85 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/activity/AboutActivity.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.activity
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import indi.conastin.textmodify.databinding.ActivityAboutBinding
6 |
7 | class AboutActivity : AppCompatActivity() {
8 | override fun onCreate(savedInstanceState: Bundle?) {
9 | super.onCreate(savedInstanceState)
10 | // binding创建layout
11 | val binding = ActivityAboutBinding.inflate(layoutInflater)
12 | setContentView(binding.root)
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/activity/AddRule.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.activity
2 |
3 | import android.content.SharedPreferences
4 | import android.os.Bundle
5 | import android.widget.Toast
6 | import androidx.appcompat.app.AppCompatActivity
7 | import indi.conastin.textmodify.databinding.ActivityAddRuleBinding
8 | import indi.conastin.textmodify.databse.HookMethodDatabase
9 | import indi.conastin.textmodify.databse.RuleInfo
10 | import indi.conastin.textmodify.viewmodel.InventoryViewModel
11 | import java.io.BufferedReader
12 | import java.io.InputStreamReader
13 |
14 | class AddRule : AppCompatActivity() {
15 |
16 | private lateinit var binding: ActivityAddRuleBinding
17 | private lateinit var viewModel: InventoryViewModel
18 |
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | super.onCreate(savedInstanceState)
21 | // binding创建layout
22 | binding = ActivityAddRuleBinding.inflate(layoutInflater)
23 | setContentView(binding.root)
24 | // 接收Intent参数
25 | val bundle = this.intent.extras
26 | val id = bundle?.get("id")
27 | val packageName = bundle?.get("packageName")
28 | val originText = bundle?.get("originText")
29 | val newText = bundle?.get("newText")
30 | // Log.d("TextModify", "【AddRule】 | id: $id | packageName: $packageName | originText: $originText | newText: $newText")
31 | // 配置SharedPreference对象
32 | val sps = getSharedPreferences("${packageName}_TextModify", MODE_PRIVATE)
33 | // 初始化存储数量变量
34 | var num = 0
35 | // 获取SharedPreference的所有键值对
36 | val all = readSharedPreference(sps)
37 | // Log.d("TextModify", "【AddRule】 | all: $all")
38 | // map按id存储originText和newText
39 | val map = mutableMapOf()
40 | // map赋值
41 | if (all != null) {
42 | for ((k, v) in all) {
43 | if (k == "num") {
44 | // 获取总共的originText和newText数量
45 | num = v as Int
46 | // Log.d("TextModify", "【AddRule】 | num: $num")
47 | } else {
48 | // 获取originText和newText给map
49 | // Log.d("TextModify", "【AddRule】 | k.drop(1): ${k.drop(1)}")
50 | if ("o" in k) {
51 | // k是原文字
52 | map[k.drop(1)] = RuleInfo(v as String, map[k.drop(1)]?.newText ?: "")
53 | } else {
54 | // k是新文字
55 | map[k.drop(1)] = RuleInfo(map[k.drop(1)]?.originText ?: "", v as String)
56 | }
57 | }
58 | }
59 | }
60 | // Log.d("TextModify", "【AddRule】 | map: $map")
61 | // room对象
62 | val database = HookMethodDatabase.getDatabase(this)
63 | // 初始化id对象
64 | var maxId = 0
65 | // viewModel对象
66 | viewModel = InventoryViewModel(database.HookMethodDao())
67 | // 获取最大id
68 | viewModel.allMethod.observe(this) {
69 | it.let {
70 | maxId = it.count()
71 | }
72 | }
73 | // Log.d("TextModify", "【AddRule】 | maxId: $maxId | packageName: $packageName | originText: $originText | newText: $newText")
74 | // 更改时将原值显示
75 | if (id != null) {
76 | if (originText != null) {
77 | binding.originText.setText(originText.toString())
78 | }
79 | if (newText != null) {
80 | binding.newText.setText(newText.toString())
81 | }
82 | }
83 |
84 | // 保存按钮监听
85 | binding.save.setOnClickListener {
86 | if (id == null) {
87 | // 新建
88 | viewModel.addPackageMethod(
89 | maxId,
90 | packageName.toString(),
91 | binding.originText.text.toString(),
92 | binding.newText.text.toString()
93 | )
94 | addRuleToSharedPreference(
95 | sps,
96 | num,
97 | maxId,
98 | binding.originText.text.toString(),
99 | binding.newText.text.toString()
100 | )
101 | Toast.makeText(
102 | this,
103 | "添加规则成功\n原文字:" + binding.originText.text.toString() + "\n替换文字:" + binding.newText.text.toString(),
104 | Toast.LENGTH_SHORT
105 | ).show()
106 | this.finish()
107 | } else {
108 | // 更新
109 | viewModel.updatePackageMethod(
110 | id as Int,
111 | packageName.toString(),
112 | binding.originText.text.toString(),
113 | binding.newText.text.toString()
114 | )
115 | updateRuleToSharedPreference(
116 | sps,
117 | id.toInt(),
118 | binding.originText.text.toString(),
119 | binding.newText.text.toString()
120 | )
121 | Toast.makeText(
122 | this,
123 | "更新规则成功\n原文字:" + binding.originText.text.toString() + "\n替换文字:" + binding.newText.text.toString(),
124 | Toast.LENGTH_SHORT
125 | ).show()
126 | this.finish()
127 | }
128 | }
129 |
130 | // 删除按钮监听
131 | binding.delete.setOnClickListener {
132 | if (id == null) {
133 | // Log.d("TextModify", "【AddRule】 | maxId: $maxId")
134 | Toast.makeText(this, "添加规则你都还没添加\n删个屁的", Toast.LENGTH_SHORT).show()
135 | } else {
136 | // 删除
137 | viewModel.deletePackageMethod(
138 | id as Int,
139 | packageName.toString(),
140 | binding.originText.text.toString(),
141 | binding.newText.text.toString()
142 | )
143 | deleteRuleToSharedPreference(sps, num, id.toInt())
144 | Toast.makeText(
145 | this,
146 | "删了就删了,世上没有后悔药\n但我可以给你瞅一眼让你记下来\n原文字:" + binding.originText.text.toString() + "\n替换文字:" + binding.newText.text.toString(),
147 | Toast.LENGTH_SHORT
148 | ).show()
149 | this.finish()
150 | }
151 | }
152 | }
153 |
154 | private fun addRuleToSharedPreference(
155 | sps: SharedPreferences,
156 | num: Int,
157 | id: Int,
158 | originText: String,
159 | newText: String
160 | ) {
161 | val edit = sps.edit()
162 | edit.putInt("num", num + 1)
163 | edit.putString("o$id", originText)
164 | edit.putString("n$id", newText)
165 | edit.apply()
166 | }
167 |
168 | private fun updateRuleToSharedPreference(
169 | sps: SharedPreferences,
170 | id: Int,
171 | originText: String,
172 | newText: String
173 | ) {
174 | val edit = sps.edit()
175 | edit.putString("o$id", originText)
176 | edit.putString("n$id", newText)
177 | edit.apply()
178 | }
179 |
180 | private fun deleteRuleToSharedPreference(sps: SharedPreferences, num: Int, id: Int) {
181 | val edit = sps.edit()
182 | edit.remove("o$id")
183 | edit.remove("n$id")
184 | edit.putInt("num", num - 1)
185 | edit.apply()
186 | }
187 |
188 | private fun getCountShared(sps: SharedPreferences): Int {
189 | return sps.getInt("num", 0)
190 | }
191 |
192 | private fun readSharedPreference(sps: SharedPreferences): MutableMap? {
193 | return sps.all
194 | }
195 |
196 | private fun cpSharedPreference(packageName: String) {
197 | if (packageName != "global") {
198 | // 获取root
199 | val process = Runtime.getRuntime().exec("su")
200 | val out = process.outputStream
201 | var cmd =
202 | "cp -f /data/data/indi.conastin.textmodify/shared_prefs/global_TextModify.xml /data/data/indi.conastin.textmodify/shared_prefs/${packageName}_TextModify.xml /data/data/$packageName/shared_prefs/ \n"
203 | out.write(cmd.toByteArray())
204 | out.flush()
205 | cmd =
206 | "chmod 777 /data/data/$packageName/shared_prefs/global_TextModify.xml /data/data/$packageName/shared_prefs/${packageName}_TextModify.xml \n"
207 | out.write(cmd.toByteArray())
208 | out.flush()
209 | out.write("exit \n".toByteArray())
210 | out.close()
211 | val fis = process.inputStream
212 | val isr = InputStreamReader(fis)
213 | val br = BufferedReader(isr)
214 | var line: String? = br.readLine()
215 | while (line != null) {
216 | line = br.readLine()
217 | }
218 | }
219 | }
220 |
221 | // private fun rubbish() {
222 | //private fun initFile(file: File) {
223 | // if (!file.parentFile.exists()) {
224 | // file.parentFile.mkdir()
225 | // }
226 | // if (!file.exists()) {
227 | // file.createNewFile()
228 | // val writer = JsonWriter(OutputStreamWriter(FileOutputStream(file), "utf-8"))
229 | // writer.setIndent(" ")
230 | // writer.beginArray()
231 | // writer.endArray()
232 | // writer.close()
233 | // }
234 | //}
235 | //// private fun readJson(file: File): MutableMap {
236 | //// var reader = JsonReader(InputStreamReader(FileInputStream(file), "utf-8"))
237 | //// val appsMethod: MutableMap = mutableMapOf()
238 | //// reader.beginArray()
239 | //// while (reader.hasNext()) {
240 | //// var id = ""
241 | //// var packageName = ""
242 | //// var originText = ""
243 | //// var newText = ""
244 | //// reader.beginObject()
245 | //// while (reader.hasNext()) {
246 | //// var field = reader.nextName()
247 | //// if (field.equals("id")) {
248 | //// id = reader.nextString()
249 | //// } else if (field.equals("packageName")) {
250 | //// packageName = reader.nextString()
251 | //// } else if (field.equals("originText")) {
252 | //// originText = reader.nextString()
253 | //// } else if (field.equals("newText")) {
254 | //// newText = reader.nextString()
255 | //// } else {
256 | //// reader.skipValue()
257 | //// }
258 | //// }
259 | //// reader.endObject()
260 | //// appsMethod.put(id.toInt(), MethodInfo(packageName, originText, newText))
261 | //// }
262 | //// reader.endArray()
263 | //// reader.close()
264 | //// return appsMethod
265 | //// }
266 | ////
267 | //// private fun writeJson(file: File, map: MutableMap) {
268 | //// val writer = JsonWriter(OutputStreamWriter(FileOutputStream(file), "utf-8"))
269 | //// writer.setIndent(" ")
270 | //// writer.beginArray()
271 | //// for ((m, k) in map) {
272 | //// writer.beginObject()
273 | //// writer.name("id").value(m)
274 | //// writer.name("packageName").value(k.packageName)
275 | //// writer.name("originText").value(k.originText)
276 | //// writer.name("newText").value(k.newText)
277 | //// writer.endObject()
278 | //// }
279 | //// writer.endArray()
280 | //// writer.close()
281 | //// }
282 | //
283 | //
284 | // }
285 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/activity/AppListActivity.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.activity
2 |
3 | import android.content.Intent
4 | import android.content.pm.ApplicationInfo
5 | import android.graphics.drawable.Drawable
6 | import android.os.Bundle
7 | import android.view.inputmethod.EditorInfo
8 | import android.widget.SearchView
9 | import androidx.appcompat.app.AppCompatActivity
10 | import androidx.recyclerview.widget.LinearLayoutManager
11 | import indi.conastin.textmodify.R
12 | import indi.conastin.textmodify.adapter.AppListAdapter
13 | import indi.conastin.textmodify.databinding.ActivityAppMethodBinding
14 | import indi.conastin.textmodify.databse.AppInfo
15 |
16 | class AppListActivity : AppCompatActivity() {
17 |
18 | private lateinit var binding: ActivityAppMethodBinding
19 | lateinit var appsInfo: List
20 | lateinit var searchInfo: List
21 |
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 | val adapter = AppListAdapter {
25 | var intent = Intent(binding.root.context, AppMethodActivity::class.java)
26 | intent.putExtra("appName", it.appName)
27 | intent.putExtra("packageName", it.packageName)
28 | startActivity(intent)
29 | }
30 | binding = ActivityAppMethodBinding.inflate(layoutInflater)
31 | setContentView(binding.root)
32 | appsInfo = getPackageList()
33 | binding.recyclerView.layoutManager = LinearLayoutManager(this)
34 | binding.recyclerView.adapter = adapter
35 | adapter.submitList(appsInfo)
36 | binding.searchView.imeOptions = EditorInfo.IME_ACTION_DONE
37 | binding.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
38 | override fun onQueryTextSubmit(p0: String?): Boolean {
39 | return false
40 | }
41 |
42 | override fun onQueryTextChange(p0: String?): Boolean {
43 | searchInfo = arrayListOf()
44 | for (appInfo in appsInfo) {
45 | if (p0.toString() in appInfo.appName || p0.toString() in appInfo.packageName) {
46 | searchInfo += appInfo
47 | }
48 | }
49 | adapter.submitList(searchInfo)
50 | return false
51 | }
52 |
53 | })
54 |
55 | // 刷新功能
56 | binding.topBar.setOnMenuItemClickListener {
57 | when (it.itemId) {
58 | R.id.menu_refresh -> {
59 | appsInfo = getPackageList()
60 | }
61 | }
62 | true
63 | }
64 |
65 | }
66 |
67 | private fun getAppInfo(
68 | packageName: String,
69 | name: String,
70 | icon: Drawable,
71 | isSystem: Boolean
72 | ): AppInfo {
73 | return AppInfo(
74 | packageName = packageName,
75 | appName = name,
76 | icon = icon,
77 | isSystem = isSystem
78 | )
79 | }
80 |
81 | private fun getPackageList(): List {
82 | var list: MutableList = mutableListOf()
83 | var packagesInfo = packageManager.getInstalledPackages(0)
84 | for (packageInfo in packagesInfo) {
85 | var isSystem: Boolean = false
86 | var packageName: String = packageInfo.packageName
87 | var name: String = packageInfo.applicationInfo.loadLabel(packageManager).toString()
88 | var icon: Drawable = packageInfo.applicationInfo.loadIcon(packageManager)
89 | // check if system app
90 | if ((packageInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0) {
91 | // if system app
92 | isSystem = true
93 | }
94 | list.add(getAppInfo(packageName, name, icon, isSystem))
95 | }
96 | return list
97 | }
98 |
99 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/activity/AppMethodActivity.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.activity
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AppCompatActivity
6 | import androidx.recyclerview.widget.LinearLayoutManager
7 | import indi.conastin.textmodify.adapter.AppMethodAdapter
8 | import indi.conastin.textmodify.databinding.ActivityAppMethodInfoBinding
9 | import indi.conastin.textmodify.databse.HookMethodDatabase
10 | import indi.conastin.textmodify.viewmodel.InventoryViewModel
11 | import java.io.BufferedReader
12 | import java.io.InputStreamReader
13 |
14 |
15 | class AppMethodActivity : AppCompatActivity() {
16 |
17 | // private lateinit var application: InventoryApplication
18 | private lateinit var viewModel: InventoryViewModel
19 | private lateinit var binding: ActivityAppMethodInfoBinding
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 | binding = ActivityAppMethodInfoBinding.inflate(layoutInflater)
24 | setContentView(binding.root)
25 | val bundle = this.intent.extras
26 | binding.topBar.title = bundle?.get("appName").toString()
27 | val database = HookMethodDatabase.getDatabase(this)
28 | // application = InventoryApplication()
29 | // viewModel = InventoryViewModel((application as InventoryApplication).database.HookMethodDao())
30 | viewModel = InventoryViewModel(database.HookMethodDao())
31 | viewModel.getPackageMethod(bundle?.get("packageName").toString())
32 | val adapter = AppMethodAdapter {
33 | val intent = Intent(binding.root.context, AddRule::class.java)
34 | intent.putExtra("id", it.id)
35 | intent.putExtra("packageName", it.packageName)
36 | intent.putExtra("originText", it.originText)
37 | intent.putExtra("newText", it.newText)
38 | startActivity(intent)
39 | }
40 | binding.recyclerView.layoutManager = LinearLayoutManager(this)
41 | binding.recyclerView.adapter = adapter
42 | viewModel.packageMethod.observe(this) {
43 | it.let {
44 | adapter.submitList(it)
45 | }
46 | }
47 |
48 | // 添加规则
49 | binding.addRule.setOnClickListener {
50 | val intent = Intent(binding.root.context, AddRule::class.java)
51 | intent.putExtra("packageName", bundle?.get("packageName").toString())
52 | startActivity(intent)
53 | }
54 |
55 | // 重启应用
56 | binding.rebootApp.setOnClickListener {
57 | // 关闭应用
58 | // 获取root权限
59 | val process = Runtime.getRuntime().exec("su")
60 | val out = process.outputStream
61 | // 移动配置文件至目标应用私有目录
62 | var cmd =
63 | "cp -f /data/data/indi.conastin.textmodify/shared_prefs/global_TextModify.xml /data/data/indi.conastin.textmodify/shared_prefs/${
64 | bundle?.get("packageName").toString()
65 | }_TextModify.xml /data/data/${
66 | bundle?.get("packageName").toString()
67 | }/shared_prefs/\n"
68 | out.write(cmd.toByteArray())
69 | out.flush()
70 | // 因为移动过去的文件归属用户还是归TextModify 目标应用无法读取 所以需要提权使目标应用可以正常访问
71 | cmd = "chmod 777 /data/data/${
72 | bundle?.get("packageName").toString()
73 | }/shared_prefs/global_TextModify.xml /data/data/${
74 | bundle?.get("packageName").toString()
75 | }/shared_prefs/${bundle?.get("packageName").toString()}_TextModify.xml\n"
76 | out.write(cmd.toByteArray())
77 | out.flush()
78 | // 强行关闭目标应用
79 | cmd = "am force-stop ${bundle?.get("packageName").toString()}\n"
80 | out.write(cmd.toByteArray())
81 | out.flush()
82 | out.close()
83 | // 等待完成
84 | val fis = process.inputStream
85 | val isr = InputStreamReader(fis)
86 | val br = BufferedReader(isr)
87 | var line: String? = br.readLine()
88 | while (line != null) {
89 | line = br.readLine()
90 | }
91 | // 开启应用
92 | startActivity(
93 | Intent(
94 | packageManager.getLaunchIntentForPackage(
95 | bundle?.get("packageName").toString()
96 | )
97 | )
98 | )
99 | }
100 | }
101 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/activity/GlobalMethodActivity.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.activity
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AppCompatActivity
6 | import androidx.recyclerview.widget.LinearLayoutManager
7 | import indi.conastin.textmodify.adapter.AppMethodAdapter
8 | import indi.conastin.textmodify.databinding.GlobalMethodBinding
9 | import indi.conastin.textmodify.databse.HookMethodDatabase
10 | import indi.conastin.textmodify.viewmodel.InventoryViewModel
11 |
12 | class GlobalMethodActivity : AppCompatActivity() {
13 |
14 | private lateinit var binding: GlobalMethodBinding
15 | private lateinit var viewModel: InventoryViewModel
16 |
17 | override fun onCreate(savedInstanceState: Bundle?) {
18 | super.onCreate(savedInstanceState)
19 | binding = GlobalMethodBinding.inflate(layoutInflater)
20 | setContentView(binding.root)
21 | val database = HookMethodDatabase.getDatabase(this)
22 | viewModel = InventoryViewModel(database.HookMethodDao())
23 | val adapter = AppMethodAdapter {
24 | var intent = Intent(binding.root.context, AddRule::class.java)
25 | intent.putExtra("id", it.id)
26 | intent.putExtra("packageName", "global")
27 | intent.putExtra("originText", it.originText)
28 | intent.putExtra("newText", it.newText)
29 | startActivity(intent)
30 | }
31 | binding.recyclerView.layoutManager = LinearLayoutManager(this)
32 | binding.recyclerView.adapter = adapter
33 | viewModel.globalMethod.observe(this) {
34 | it.let {
35 | adapter.submitList(it)
36 | }
37 | }
38 |
39 | binding.addRule.setOnClickListener {
40 | var intent = Intent(binding.root.context, AddRule::class.java)
41 | intent.putExtra("packageName", "global")
42 | startActivity(intent)
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/adapter/AppListAdapter.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.adapter
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.DiffUtil
6 | import androidx.recyclerview.widget.ListAdapter
7 | import androidx.recyclerview.widget.RecyclerView
8 | import indi.conastin.textmodify.databinding.AppListListviewItemBinding
9 | import indi.conastin.textmodify.databse.AppInfo
10 |
11 | class AppListAdapter(private val onPackageClicked: (AppInfo) -> Unit) :
12 | ListAdapter(DiffCallback) {
13 |
14 | override fun onCreateViewHolder(
15 | parent: ViewGroup,
16 | viewType: Int
17 | ): PackageViewHolder {
18 | return PackageViewHolder(
19 | AppListListviewItemBinding.inflate(
20 | LayoutInflater.from(
21 | parent.context
22 | ),
23 | parent,
24 | false
25 | )
26 | )
27 | }
28 |
29 | override fun onBindViewHolder(holder: PackageViewHolder, position: Int) {
30 | val current = getItem(position)
31 | holder.itemView.setOnClickListener {
32 | onPackageClicked(current)
33 | }
34 | holder.bind(current)
35 | }
36 |
37 | class PackageViewHolder(private var binding: AppListListviewItemBinding) :
38 | RecyclerView.ViewHolder(binding.root) {
39 |
40 | fun bind(appInfo: AppInfo) {
41 | binding.appListItemName.text = appInfo.appName
42 | binding.appListItemPackageName.text = appInfo.packageName
43 | binding.appListItemIcon.setImageDrawable(appInfo.icon)
44 | }
45 |
46 | }
47 |
48 | companion object {
49 | private val DiffCallback = object : DiffUtil.ItemCallback() {
50 | override fun areItemsTheSame(oldItem: AppInfo, newItem: AppInfo): Boolean {
51 | return oldItem === newItem
52 | }
53 |
54 | override fun areContentsTheSame(oldItem: AppInfo, newItem: AppInfo): Boolean {
55 | return oldItem.packageName == newItem.packageName
56 | }
57 | }
58 | }
59 |
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/adapter/AppMethodAdapter.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.adapter
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.DiffUtil
6 | import androidx.recyclerview.widget.ListAdapter
7 | import androidx.recyclerview.widget.RecyclerView
8 | import indi.conastin.textmodify.databinding.AppMethodListviewItemBinding
9 | import indi.conastin.textmodify.databse.HookMethodEntity
10 |
11 | class AppMethodAdapter(private val onRuleLongClicked: (HookMethodEntity) -> Unit) :
12 | ListAdapter(DiffCallback) {
13 |
14 | override fun onCreateViewHolder(
15 | parent: ViewGroup,
16 | viewType: Int
17 | ): PackageViewHolder {
18 | return PackageViewHolder(
19 | AppMethodListviewItemBinding.inflate(
20 | LayoutInflater.from(
21 | parent.context
22 | ),
23 | parent,
24 | false
25 | )
26 | )
27 | }
28 |
29 | override fun onBindViewHolder(holder: PackageViewHolder, position: Int) {
30 | val current = getItem(position)
31 | holder.itemView.setOnClickListener {
32 | onRuleLongClicked(current)
33 | }
34 | holder.bind(current)
35 | }
36 |
37 | class PackageViewHolder(private var binding: AppMethodListviewItemBinding) :
38 | RecyclerView.ViewHolder(binding.root) {
39 |
40 | fun bind(packageInfoEntity: HookMethodEntity) {
41 | binding.originText.text = packageInfoEntity.originText
42 | binding.newText.text = packageInfoEntity.newText
43 | }
44 | }
45 |
46 | companion object {
47 | private val DiffCallback = object : DiffUtil.ItemCallback() {
48 | override fun areItemsTheSame(
49 | oldItem: HookMethodEntity,
50 | newItem: HookMethodEntity
51 | ): Boolean {
52 | return oldItem === newItem
53 | }
54 |
55 | override fun areContentsTheSame(
56 | oldItem: HookMethodEntity,
57 | newItem: HookMethodEntity
58 | ): Boolean {
59 | return oldItem.originText == newItem.originText
60 | }
61 | }
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/databse/AppInfo.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.databse
2 |
3 | import android.graphics.drawable.Drawable
4 |
5 | data class AppInfo(
6 | // 数据类用以生成应用列表
7 | val appName: String,
8 | val packageName: String,
9 | val icon: Drawable,
10 | val isSystem: Boolean
11 | )
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/databse/HookMethodDao.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.databse
2 |
3 |
4 | import androidx.room.*
5 | import kotlinx.coroutines.flow.Flow
6 |
7 | @Dao
8 | interface HookMethodDao {
9 |
10 | // 获取所有方法
11 | @Query("select * from app_method order by id desc")
12 | fun getAllMethod(): Flow>
13 |
14 | // 获取全局方法
15 | @Query("select * from app_method where packageName = 'global' order by id desc")
16 | fun getGlobalMethod(): Flow>
17 |
18 | // 获取包名的所有方法
19 | @Query("select * from app_method where packageName = :packageName order by id desc")
20 | fun getPackageMethod(packageName: String): Flow>
21 |
22 | // 新增方法
23 | @Insert
24 | suspend fun addMethod(method: HookMethodEntity)
25 |
26 | // 删除方法
27 | @Delete
28 | suspend fun deleteMethod(method: HookMethodEntity)
29 |
30 | // 更新方法
31 | @Update
32 | suspend fun updateMethod(method: HookMethodEntity)
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/databse/HookMethodDatabase.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.databse
2 |
3 | import android.content.Context
4 | import androidx.room.Database
5 | import androidx.room.Room
6 | import androidx.room.RoomDatabase
7 |
8 |
9 | @Database(entities = [HookMethodEntity::class], version = 1, exportSchema = false)
10 | abstract class HookMethodDatabase : RoomDatabase() {
11 |
12 | abstract fun HookMethodDao(): HookMethodDao
13 |
14 | companion object {
15 | @Volatile
16 | private var INSTANCE: HookMethodDatabase? = null
17 |
18 | fun getDatabase(context: Context): HookMethodDatabase {
19 | return INSTANCE ?: synchronized(this) {
20 | val instance = Room.databaseBuilder(
21 | context.applicationContext,
22 | HookMethodDatabase::class.java,
23 | "database.db"
24 | ).fallbackToDestructiveMigration().build()
25 | INSTANCE = instance
26 | instance
27 | }
28 | }
29 |
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/databse/HookMethodEntity.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.databse
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity(tableName = "app_method")
8 | data class HookMethodEntity(
9 | @PrimaryKey
10 | val id: Int,
11 |
12 | @ColumnInfo(name = "packageName")
13 | val packageName: String,
14 |
15 | @ColumnInfo(name = "originText")
16 | val originText: String,
17 |
18 | @ColumnInfo(name = "newText")
19 | val newText: String
20 | )
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/databse/MethodInfo.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.databse
2 |
3 | data class MethodInfo(
4 | val packageName: String,
5 | val originText: String,
6 | val newText: String
7 | )
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/databse/RuleInfo.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.databse
2 |
3 | data class RuleInfo(
4 | val originText: String,
5 | val newText: String
6 | )
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/log/NewLog.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.log
2 |
3 | import android.util.Log
4 | import de.robv.android.xposed.XposedBridge
5 |
6 | class NewLog {
7 |
8 | fun systemLog(string: String) {
9 | Log.d("TextModify", string)
10 | }
11 |
12 | fun xposedLog(string: String) {
13 | XposedBridge.log(string.replace("【", "【TextModify|"))
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/viewmodel/InventoryViewModel.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.viewmodel
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.ViewModel
5 | import androidx.lifecycle.asLiveData
6 | import androidx.lifecycle.viewModelScope
7 | import indi.conastin.textmodify.databse.HookMethodDao
8 | import indi.conastin.textmodify.databse.HookMethodEntity
9 | import kotlinx.coroutines.launch
10 |
11 | class InventoryViewModel(private val hookMethodDao: HookMethodDao) : ViewModel() {
12 |
13 | var allMethod: LiveData> = hookMethodDao.getAllMethod().asLiveData()
14 | val globalMethod: LiveData> =
15 | hookMethodDao.getGlobalMethod().asLiveData()
16 | lateinit var packageMethod: LiveData>
17 |
18 | fun getPackageMethod(packageName: String) {
19 | viewModelScope.launch {
20 | packageMethod = hookMethodDao.getPackageMethod(packageName).asLiveData()
21 | }
22 | }
23 |
24 | // private fun getMethodEntry(
25 | // packageName: String,
26 | // originText: String,
27 | // newText: String,
28 | // ): HookMethodEntity {
29 | // return HookMethodEntity(
30 | // packageName = packageName,
31 | // originText = originText,
32 | // newText = newText
33 | // )
34 | // }
35 |
36 | // 新增规则
37 | fun addPackageMethod(
38 | id: Int,
39 | packageName: String,
40 | originText: String,
41 | newText: String
42 | ) {
43 | // val packageMethod = getMethodEntry(packageName, originText, newText)
44 | viewModelScope.launch {
45 | hookMethodDao.addMethod(HookMethodEntity(id, packageName, originText, newText))
46 | }
47 | }
48 |
49 | // 删除规则
50 | fun deletePackageMethod(
51 | id: Int,
52 | packageName: String,
53 | originText: String,
54 | newText: String
55 | ) {
56 | // val packageMethod = getMethodEntry(packageName, originText, newText)
57 | viewModelScope.launch {
58 | hookMethodDao.deleteMethod(HookMethodEntity(id, packageName, originText, newText))
59 | }
60 | }
61 |
62 | // 更新规则
63 | fun updatePackageMethod(
64 | id: Int,
65 | packageName: String,
66 | originText: String,
67 | newText: String
68 | ) {
69 | viewModelScope.launch {
70 | hookMethodDao.updateMethod(HookMethodEntity(id, packageName, originText, newText))
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/app/src/main/java/indi/conastin/textmodify/viewmodel/RulesViewModel.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify.viewmodel
2 |
3 | import android.annotation.SuppressLint
4 | import android.util.JsonReader
5 | import android.util.JsonWriter
6 | import androidx.lifecycle.ViewModel
7 | import androidx.lifecycle.viewModelScope
8 | import indi.conastin.textmodify.databse.RuleInfo
9 | import kotlinx.coroutines.launch
10 | import java.io.*
11 |
12 | class RulesViewModel : ViewModel() {
13 | @SuppressLint("SdCardPath")
14 | val file = File("/data/user/0/indi.conastin.textmodify/files", "rules.json")
15 | lateinit var methods: HashMap>
16 |
17 | fun getMethod() {
18 | viewModelScope.launch {
19 | initFIle()
20 | methods = readJson()
21 | }
22 | }
23 |
24 | private fun initFIle() {
25 | if (!file.exists()) {
26 | // 初始化
27 | file.createNewFile()
28 | val writer = JsonWriter(OutputStreamWriter(FileOutputStream(file), "utf-8"))
29 | writer.setIndent(" ")
30 | writer.beginArray()
31 | writer.endArray()
32 | writer.close()
33 | }
34 | }
35 |
36 | private fun readJson(): HashMap> {
37 | var reader = JsonReader(InputStreamReader(FileInputStream(file), "utf-8"))
38 | val appsMethod: HashMap> = hashMapOf()
39 | reader.beginArray()
40 | while (reader.hasNext()) {
41 | var id = ""
42 | var packageName = ""
43 | var originText = ""
44 | var newText = ""
45 | reader.beginObject()
46 | while (reader.hasNext()) {
47 | var field = reader.nextName()
48 | if (field.equals("packageName")) {
49 | packageName = reader.nextString()
50 | } else if (field.equals("originText")) {
51 | originText = reader.nextString()
52 | } else if (field.equals("newText")) {
53 | newText = reader.nextString()
54 | } else {
55 | reader.skipValue()
56 | }
57 | }
58 | reader.endObject()
59 | if (appsMethod[packageName] == null) {
60 | appsMethod[packageName] = arrayListOf(RuleInfo(originText, newText))
61 | } else {
62 | appsMethod[packageName] = arrayListOf(
63 | appsMethod[packageName],
64 | RuleInfo(originText, newText)
65 | ) as java.util.ArrayList
66 | }
67 | }
68 | reader.endArray()
69 | reader.close()
70 | return appsMethod
71 | }
72 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_gray_bottom_solid.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
7 |
8 |
12 |
13 |
14 | -
19 |
20 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_gray_solid.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
9 |
10 |
11 |
12 | -
17 |
18 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_green_solid.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | -
12 |
13 |
17 |
18 |
19 |
20 | -
25 |
26 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_red_solid.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
9 |
10 |
11 |
12 | -
17 |
18 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_white_solid.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
9 |
10 |
11 |
12 | -
17 |
18 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bt_gray_solid.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
9 |
10 |
11 |
12 | -
17 |
18 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/donation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/res/drawable/donation.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_about.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_app_lst.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_global_method.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_is_active.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_is_not_active.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
14 |
17 |
20 |
23 |
28 |
31 |
34 |
37 |
40 |
43 |
46 |
47 |
48 |
51 |
52 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_log.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_refresh.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_setting.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_textmodify.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
18 |
23 |
26 |
29 |
32 |
35 |
38 |
41 |
42 |
43 |
46 |
47 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
23 |
24 |
25 |
26 |
31 |
32 |
37 |
38 |
48 |
49 |
53 |
54 |
61 |
62 |
70 |
71 |
79 |
80 |
81 |
82 |
83 |
84 |
94 |
95 |
99 |
100 |
107 |
108 |
116 |
117 |
125 |
126 |
134 |
135 |
143 |
144 |
145 |
146 |
147 |
148 |
158 |
159 |
163 |
164 |
171 |
172 |
180 |
181 |
189 |
190 |
191 |
192 |
193 |
194 |
204 |
205 |
209 |
210 |
217 |
218 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_add_rule.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
25 |
26 |
37 |
38 |
43 |
44 |
54 |
55 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_app_method.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
24 |
25 |
26 |
27 |
28 |
35 |
36 |
45 |
46 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_app_method_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
23 |
24 |
25 |
26 |
27 |
28 |
35 |
36 |
39 |
40 |
50 |
51 |
61 |
62 |
63 |
64 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
23 |
24 |
25 |
26 |
31 |
32 |
37 |
38 |
49 |
50 |
58 |
59 |
63 |
64 |
71 |
72 |
79 |
80 |
81 |
82 |
83 |
84 |
96 |
97 |
104 |
105 |
109 |
110 |
116 |
117 |
118 |
119 |
120 |
121 |
133 |
134 |
141 |
142 |
146 |
147 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
209 |
210 |
217 |
218 |
222 |
223 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
247 |
248 |
255 |
256 |
260 |
261 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
282 |
283 |
287 |
288 |
295 |
296 |
304 |
305 |
313 |
314 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_list_listview_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
22 |
23 |
28 |
29 |
35 |
36 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_method_listview_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
21 |
22 |
28 |
29 |
36 |
37 |
38 |
39 |
40 |
46 |
47 |
53 |
54 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/global_method.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
22 |
23 |
24 |
25 |
30 |
31 |
39 |
40 |
47 |
48 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/app_method_topbar_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF247BA0
4 |
5 | #FF70C1B3
6 | #FF64AC9F
7 | #FFB2DBBF
8 |
9 | #FFF3FFBD
10 | #FFBEAD63
11 |
12 | #FFFF1654
13 | #FFCD6E7C
14 | #FFB66573
15 |
16 | #FFE6E5E5
17 | #FFD5D5D5
18 | #FFEEEEEE
19 | #FFCED1DA
20 |
21 | #FFBB86FC
22 | #FF6200EE
23 | #FF3700B3
24 |
25 | #FF03DAC5
26 | #FF018786
27 |
28 | #FF000000
29 |
30 | #FFFFFF
31 |
32 | #FF81D8D1
33 |
34 | #9F645C
35 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | TextModify
3 |
4 | 设置
5 | 关于
6 |
7 | 模块已激活
8 | 模块未激活
9 | 全局替换
10 | 应用列表
11 | 日志
12 | 设置
13 | 隐藏应用图标
14 | 显示应用图标
15 | 关于
16 |
17 | 全局替换
18 | 添加规则
19 |
20 | 应用列表
21 |
22 | 添加规则
23 | 重启应用
24 |
25 | 支付宝
26 | 微信
27 | 请作者吃好吃的
28 | 关于这个软件
29 | \t这是一个修改目标应用文字的Xposed模块,致敬原模块CustomText(作者liubaoyua),原模块由于安卓版本的升级已经无法稳定使用。
30 | \t由于安卓10以上版本限制,应用只能在私有目录下存储文件,导致Xposed无法读取应用规则的配置文件,我所能想到的解决方法就是在重启目标应用时注入配置文件,这就导致了更新配置文件后依赖此软件的重启目标应用功能。
31 | 待开发
32 | \t1.正则表达式匹配
33 | \t2.Class及控件选择器
34 | \t3.日志功能
35 | \t4.应用列表筛选及已有配置文件的优先排序
36 | 点击以刷新激活状态
37 | 刷新
38 | 原文字:
39 | 替换文字:
40 | 原文字
41 | 新文字
42 | 保存
43 | 删除
44 | 关于此项目
45 | \t作者酷安:Conastin
46 | \tGithub:https://github.com/Conastin/TextModify
47 | 版权声明
48 | 本软件为开源免费软件
49 | 仅供学习交流使用
50 | 切勿倒卖或进行违法犯罪活动
51 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
13 |
14 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/test/java/indi/conastin/textmodify/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package indi.conastin.textmodify
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | activity_version = '1.1.0'
4 | appcompat_version = '1.3.1'
5 | constraintlayout_version = '2.1.1'
6 | core_ktx_version = '1.6.0'
7 | coroutines_version = '1.4.2'
8 | kotlin_version = '1.5.31'
9 | lifecycle_version = '2.3.1'
10 | material_version = '1.4.0'
11 | nav_version = '2.3.5'
12 | room_version = '2.3.0'
13 | fragment_version = '1.3.6'
14 | }
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | dependencies {
20 | classpath "com.android.tools.build:gradle:7.0.3"
21 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
22 | classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
23 | }
24 | }
25 |
26 | allprojects {
27 | repositories {
28 | google()
29 | mavenCentral()
30 | }
31 | }
32 |
33 | task clean(type: Delete) {
34 | delete rootProject.buildDir
35 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 | #org.gradle.warning.mode=all
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Conastin/TextModify/e0238e43bd89d8eb750fa96418dd42b1b15b961d/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Nov 02 22:20:12 CST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-rc-3-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name = "TextModify"
--------------------------------------------------------------------------------