424 Commits

Author SHA1 Message Date
9b4de7db9c chore: bump dependencies 2025-12-28 01:30:42 +08:00
592f8a5a0b refactor: update text and enhance link styling in MainActivity 2025-12-28 01:30:37 +08:00
653563c631 chore: bump dependencies 2025-12-13 23:45:11 +08:00
ea43f8cf68 misc: update .editorconfig 2025-12-01 10:17:05 +08:00
28e25bfb57 misc: update .editorconfig 2025-11-30 22:42:08 +08:00
e43c66e971 chore: bump dependencies 2025-11-16 01:32:30 +08:00
02b36793b5 chore: update buildscript 2025-11-12 18:30:59 +08:00
10631eedb2 chore: bump gradle to 9.2.0 2025-11-12 18:25:21 +08:00
04f0a5def1 chore: migrate to version catalog, Gropify 2025-11-12 18:25:13 +08:00
f475e93642 misc: update project files 2025-09-27 00:55:23 +08:00
0f3df97300 misc: update .gitignore 2025-09-27 00:55:01 +08:00
d004b0f02a chore: bump dependencies 2025-09-23 15:48:17 +08:00
c6dd7ad517 chore: bump dependencies 2025-09-13 19:42:04 +08:00
22a8113175 refactor: support monochrome app icon 2025-09-06 22:35:55 +08:00
940bef9f28 chore: bump dependencies 2025-09-06 22:35:20 +08:00
4b77993cf1 chore: bump dependencies 2025-08-24 03:00:30 +08:00
95649b432b chore: update target sdk to 36 2025-08-19 16:48:18 +08:00
c602eac87f chore: update jdk to 21 2025-08-19 16:42:28 +08:00
7a3ab62a45 chore: bump gradle to 8.14.3 2025-08-19 16:42:21 +08:00
8bfb5255c8 chore: bump dependencies 2025-08-19 16:42:14 +08:00
74de586340 refactor: update KavaRef usage to 1.0.1 2025-07-06 21:29:16 +08:00
b7cbd9ec6c refactor: migrate and update to YukiHookAPI 1.3.0 2025-06-25 23:20:11 +08:00
88028b05e0 docs: update README 2025-06-24 14:10:17 +08:00
2652e3e964 docs: update README 2025-06-20 12:48:37 +08:00
5d69ce3966 chore: disable type auto conversion for sweet-property 2025-05-09 23:19:38 +08:00
9f55cbf7e0 feat: test migrate to Hikage(https://github.com/BetterAndroid/Hikage) 2025-04-22 09:40:20 +08:00
9af783018e feat: add BaseActivity2 2025-04-22 09:39:30 +08:00
edbac2f5a7 chore: bump dependencies 2025-03-16 23:02:29 +08:00
fc1eac45a1 chore: bump gradle to 8.13 2025-03-16 23:02:23 +08:00
f155edf730 chore: update project files 2025-03-16 23:02:14 +08:00
1fd5a35ff8 chore: bump ci to v4 2025-02-22 01:05:12 +08:00
9125b0f8ce chore: update .gitignore 2025-02-22 01:05:00 +08:00
1e05b625d8 docs: update license 2025-01-13 11:12:54 +08:00
a47696015e refactor: support Android 15 edge-to-edge system bars 2024-11-25 18:50:55 +08:00
bb3016e4bf refactor: support TIM 4.0+ 2024-11-13 15:39:05 +08:00
1d5632d87d chore: update target sdk to 35 2024-11-10 20:40:51 +08:00
4b80a964de chore: some tweaks in build.gradle.kts 2024-11-10 20:40:40 +08:00
47fb508d2c chore: update project files 2024-11-10 20:30:10 +08:00
49a69be04f chore: bump dependencies 2024-11-10 20:09:28 +08:00
7cc9343edd chore: update project files 2024-11-10 20:09:19 +08:00
a83cd02253 chore: bump gradle to 8.10.2 2024-11-10 20:09:12 +08:00
c9a92e39a1 chore: bump dependencies 2024-09-29 00:06:34 +08:00
fb6cc4664d fix: wake lock acquire not release will cause problem in BasicHookFactory 2024-09-06 18:25:07 +08:00
6388f4ea76 chore: bump dependencies 2024-09-05 10:56:47 +08:00
50cd79b1b9 chore: bump gradle to 8.8 2024-09-01 00:09:05 +08:00
65f665559b chore: update .editorconfig 2024-09-01 00:07:56 +08:00
b18d0d9e15 chore: update .editorconfig 2024-06-21 10:09:50 +08:00
00f1000037 chore: update project files 2024-06-21 10:09:46 +08:00
d6fbfeb5bb chore: bump dependencies 2024-06-20 11:12:02 +08:00
4c036336bb chore: bump gradle to 8.7 2024-06-20 11:11:57 +08:00
ab8e8f824f chore: update .editorconfig 2024-06-20 11:07:14 +08:00
d5f2ab7b98 feat: support QQ NT (>= 9.0.0) version 2024-06-17 01:36:00 +08:00
071607174a refactor: make some change for dexkit 2024-06-17 01:22:15 +08:00
8745a6a2ab chore: bump dependencies 2024-02-20 11:29:04 +08:00
395832d1fa chore: bump gradle to 8.6 2024-02-20 11:29:01 +08:00
02cf837318 chore: update .editorconfig 2024-01-13 21:22:31 +08:00
ab6ed17752 chore: update project files 2024-01-13 21:22:25 +08:00
4496ea4737 docs: update copyright date to 2024 for all existing files 2024-01-01 01:29:39 +08:00
c1bfcf6ed4 style: merge to new ktlint version & rules 2023-12-29 21:32:04 +08:00
60425c992b chore: bump dependencies 2023-12-28 23:46:46 +08:00
b5df7c8f9d chore: bump gradle to 8.5 2023-12-28 23:42:36 +08:00
5997d6df00 chore: update .gitignore 2023-12-28 23:42:25 +08:00
a02f0a21dd docs: use relative link 2023-12-11 02:48:23 +08:00
666386564c chore: update project files 2023-12-11 02:48:19 +08:00
2fd54bef3a chore: bump dependency versions 2023-12-11 00:00:19 +08:00
0a4974e752 docs: update piracy statement 2023-12-11 00:00:14 +08:00
151c1ddc08 docs: update promotion 2023-11-18 18:15:31 +08:00
8dc2663c23 chore: bump "com.highcapable.sweetdependency" version to 1.0.4 2023-11-14 01:01:15 +08:00
e0a32be287 chore: bump "com.highcapable.sweetproperty" version to 1.0.5 2023-11-08 15:13:34 +08:00
dcecfb181a chore: update project files 2023-11-08 15:13:30 +08:00
2d8330dfcf chore: bump dependencies 2023-11-04 04:01:34 +08:00
d4459aab40 chore: bump plugin versions
- bump "com.highcapable.sweetdependency" version to 1.0.3
- bump "com.highcapable.sweetproperty" version to 1.0.4
2023-11-04 03:57:42 +08:00
5bfd34e211 docs: replace download links 2023-11-03 14:32:07 +08:00
db00a5cdb9 docs: update release channel 2023-10-26 21:15:02 +08:00
968815c0f0 Bump version to 4.4 2023-10-21 00:56:03 +08:00
ba61bcd2f5 feat(docs): update YukiHookAPI owner link 2023-10-21 00:55:59 +08:00
d73c2ddd45 refactor: remove DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION 2023-10-21 00:48:02 +08:00
57b127985a refactor: make state to unsupported when methods not found 2023-10-21 00:36:40 +08:00
7bca7c616a chore: bump "org.luckypray:dexkit" version to 2.0.0-rc7 2023-10-21 00:33:31 +08:00
737c4b6d2e chore: update target sdk to 34 2023-10-21 00:30:38 +08:00
62a7ae0181 chore: bump "org.luckypray:dexkit" version to 2.0.0-rc5 2023-10-09 01:26:44 +08:00
ed348a999e feat: simple support dexkit test 2023-10-08 03:55:25 +08:00
fd5eeb59c1 refactor: migrate to YukiHookAPI new usage 2023-10-07 20:51:34 +08:00
172550f0f0 chore: bump dependency versions 2023-10-07 19:45:35 +08:00
e23c8c6300 feat: support QQ 8.9.83 2023-10-02 17:35:15 +08:00
537b947d95 chore: bump plugin versions
- bump "com.highcapable.sweetdependency" version to 1.0.2
- bump "com.highcapable.sweetproperty" version to 1.0.3
2023-09-26 09:05:17 +08:00
270fa60ab2 refactor: add new R8 rules to fix possible problems 2023-09-19 08:18:53 +08:00
b6af8b62a0 feat: support QQ 8.9.80 2023-09-16 01:17:19 +08:00
0e17881b08 docs: optimize comments 2023-09-16 01:13:55 +08:00
580d463304 feat: lots of changes
- add BuildConfigWrapper
- merge to new project promote
- add ci version tag support
- add open parasitic from Host App
2023-09-16 01:12:03 +08:00
e5437162be docs: update README 2023-09-16 01:11:03 +08:00
36e255ae4b style: optimize code 2023-09-16 01:11:02 +08:00
70823e6d8e docs: move banner to img-src 2023-09-16 01:11:02 +08:00
1471de8f06 refactor: use new payment code 2023-09-16 01:11:02 +08:00
d7f4062c21 fix: class not found when R8 since android gradle plugin 8+ 2023-09-16 01:11:02 +08:00
6902c127e3 ci: optimize and add artifacts post to Telegram 2023-09-16 01:11:02 +08:00
1c0d33fe08 chore: add Android 14 option 2023-09-16 01:11:02 +08:00
613127080c chore: migrate build script from groovy to kts
- using SweetDependency, SweetProperty
- change support min sdk to 24
- merge singing key file configs to properties
- update gradle and dependencies
2023-09-16 01:11:01 +08:00
436729a4f9 chore: clean up build step files 2023-09-16 00:53:18 +08:00
0699e713e4 [Change Commit Specification] Use the new commit spec from here on
child commits:
chore: add .editorconfig
2023-09-16 00:53:17 +08:00
a7916f9912 Modify support QQ 8.9.78 2023-09-02 18:18:06 +08:00
4d321f04fc Modify support QQ 8.9.75~8.9.76 2023-08-19 04:28:08 +08:00
6203051d45 Modify support QQ 8.9.73 2023-08-03 21:31:52 +08:00
a6e51f4605 Modify support QQ 8.9.71 2023-08-01 18:05:30 +08:00
1772fa3d52 Update version to 4.3 | Support QQ 8.9.53~8.9.70 versions 2023-07-26 02:59:29 +08:00
256a23da89 Modify support QQ 8.9.70 2023-07-26 02:46:17 +08:00
dd969123a0 Modify support QQ 8.9.70 (QQ-NT) hook entry item 2023-07-26 02:45:56 +08:00
9c16101e24 Modify support QQ 8.9.58~8.9.68 2023-07-18 22:40:08 +08:00
86a0b12cc8 Modify optimize code in QQTIMHooker 2023-07-18 22:35:28 +08:00
a95c434624 Modify support QQ 8.9.55 2023-05-17 07:35:17 +08:00
ebafb4bcfb Update Gradle dependencies 2023-05-17 07:35:09 +08:00
e0af87f303 Modify support QQ 8.9.53 2023-04-27 01:23:06 +08:00
5ee87d3c89 Update YukiHookAPI 2023-04-25 06:57:15 +08:00
76c0a26c9b Update YukiHookAPI 2023-04-21 01:25:06 +08:00
34a237274f Update version to 4.25 | Support QQ 8.9.30~8.9.50 versions 2023-04-17 06:19:55 +08:00
7f4fb277ff Modify change need restart dialog to prompt in ConfigActivity, activity_config 2023-04-17 05:59:55 +08:00
1ce3135228 Modify merge to YukiHookAPI new usage 2023-04-17 05:50:35 +08:00
0389d3b0a7 Update YukiHookAPI 2023-04-17 05:45:05 +08:00
88da937d31 Modify merge contents of build.gradle into constant definitions 2023-04-15 22:35:36 +08:00
1798b74ccc Modify support QQ 8.9.50 2023-04-14 17:48:06 +08:00
0987ce2c38 Update Gradle & Kotlin
- Update Kotlin version to 1.8.20
- Update Gradle version to 8.0.2
- Update Gradle dependencies
2023-04-08 00:00:35 +08:00
21dfa1c138 Modify support QQ 8.9.38 2023-04-05 05:16:10 +08:00
aac7bafe7f Modify support QQ 8.9.35 2023-03-20 18:13:55 +08:00
5a79b79c06 Modify support QQ 8.9.33 2023-03-03 11:51:55 +08:00
951c068589 Modify support QQ 8.9.30 2023-02-22 06:19:35 +08:00
77596233cb Modify change package resources id cause some same host modules conflict with it in build.gradle 2023-02-22 06:17:48 +08:00
8ca874bf52 Update Gradle dependencies 2023-02-22 06:15:05 +08:00
01a53db1c5 Added star history chart in README 2023-02-19 14:35:35 +08:00
3b2ef7954b Fix "GitHub" spelling in all files 2023-02-07 05:29:35 +08:00
a1e1f9cd57 Modify change ProgressBar to CircularProgressIndicator in DialogBuilderFactory 2023-02-03 23:19:05 +08:00
2864e825f3 Modify optimize code in MainActivity 2023-02-03 01:43:05 +08:00
8bbee457a6 Update YukiHookAPI 2023-02-01 04:25:05 +08:00
565d8440a3 Update .gitignore 2023-02-01 04:23:15 +08:00
a61e74cd2c Update Gradle dependencies 2023-02-01 03:30:05 +08:00
c0ddae0b42 Added new bug report issues template 2023-01-31 19:12:35 +08:00
4ef7b89da6 Update YukiHookAPI 2023-01-21 00:59:49 +08:00
0bad5e4902 Update Android Gradle Plugin to 7.4.0 2023-01-21 00:59:32 +08:00
03d1e2bc7e Update Gradle dependencies 2023-01-19 22:27:32 +08:00
d66fd19bcf Update README.md 2023-01-18 00:49:01 +08:00
1687a6d290 Modify rename ui/view to ui/widget 2023-01-17 11:14:27 +08:00
71ee1df07d Fix the central color problem of views such as CheckBox 2023-01-16 22:39:27 +08:00
3ad32d5106 Update copyright date to 2023 for all existing file 2023-01-14 10:57:18 +08:00
6c548056d9 Update version to 4.2 | Support many QQ 8.9.x version 2023-01-14 00:37:02 +08:00
d5a173685d Modify remove localTime function time second format and change description text in GithubReleaseTool 2023-01-14 00:16:45 +08:00
55fca16bd8 Modify change promote message in YukiPromoteTool 2023-01-14 00:13:53 +08:00
e7fde8a5a4 Modify remove "endsWith" method's param name statement 2023-01-14 00:12:55 +08:00
85ad6d6e1e Modify change related description text in activity_main 2023-01-14 00:08:34 +08:00
4e6ce88e90 Modify change related description text in MainActivity 2023-01-14 00:00:33 +08:00
2bd58cacb4 Added executor info and change some description text in ConfigActivity, activity_config 2023-01-13 23:57:52 +08:00
d9fdf9dd13 Modify remove "replace" method's param name statement 2023-01-13 23:20:26 +08:00
b74b998c03 Modify remove instanceClass param for hookQQSettingsUI function in QQTIMHooker 2023-01-13 22:38:14 +08:00
77008246d7 Modify change related description text in activity_config 2023-01-13 22:24:25 +08:00
f99e80fed3 Modify change isOnFailureThrowToApp to false for onAppLifecycle event in QQTIMHooker 2023-01-13 22:19:53 +08:00
f1a09cf942 Modify make HookEntry singleton 2023-01-13 22:13:50 +08:00
dd04543ba5 Modify merge to YukiHookAPI new usage 2023-01-13 04:56:15 +08:00
4bc6b8a780 Update Gradle & Kotlin
- Update Kotlin version to 1.7.22
- Update Gradle version to 7.6
- Update Gradle dependencies
2023-01-13 04:53:45 +08:00
4fc57beb5f Update YukiHookAPI 2023-01-13 04:50:50 +08:00
a2c4b05cd9 Fix internal browser X5 kernel issues (cause QQ crashed) 2023-01-12 15:00:52 +08:00
809bbeffbf Fix WeChat SettingsUI's top right TSBattery icon lost problem when version >= 8.0.28 2023-01-12 03:17:01 +08:00
565343374a Fix not found QQSettingSettingFragment Class when not in Pad Mode (disabled error output) 2023-01-11 02:45:02 +08:00
138fbe6993 Fix TaiChi activation state lost problem for API version higher than 30 2023-01-02 17:43:24 +08:00
66b6eacb2b Modify support QQ 8.9.28 2023-01-01 01:06:11 +08:00
e958f18e6a Modify support QQ 8.9.25 2022-12-16 02:21:14 +08:00
bc7118d1de Modify support QQ 8.9.23 2022-11-30 22:37:34 +08:00
1d3bbb7da0 Modify add release channel description, release status description in README 2022-11-26 00:30:31 +08:00
cd198381cc Added automatic build workflows for Github Actions 2022-11-26 00:29:54 +08:00
0297d7a88b Modify support QQ 8.9.20 2022-11-18 01:50:26 +08:00
5545f43bbf Modify support QQ 8.9.19 2022-11-08 23:45:27 +08:00
62e8d4722d Modify support QQ 8.9.18 2022-11-03 10:35:47 +08:00
29047b5621 Modify change logo url to raw in README 2022-11-03 10:35:32 +08:00
fd1bddad26 Modify remove wrap/part at the end of file 2022-11-03 10:18:47 +08:00
b298158dfe Update Gradle & PlatformSDK
- Update Kotlin version to 1.7.20
2022-11-03 10:17:50 +08:00
2d6129fd41 Modify support QQ Pad Mode for QQ Settings UI entry item since 8.9.15 2022-10-22 02:00:57 +08:00
9fdf535066 Update Gradle dependencies 2022-10-22 01:11:11 +08:00
2faa2dd594 Modify support QQ 8.9.15 2022-10-22 01:09:32 +08:00
ef6aaa76f5 Modify change not support version log to warn level in QQTIMHooker 2022-10-21 21:24:57 +08:00
bb70227845 Update Gradle & PlatformSDK
- Update Android Gradle Plugin version to 7.3.1
- Update Kotlin version to 1.7.20
2022-10-20 00:20:13 +08:00
a7c52b1c44 Modify merge to new usage in HookEntry 2022-10-04 07:29:37 +08:00
fac7700866 Update YukiHookAPI 2022-10-04 07:28:56 +08:00
097e7b39b2 Update README.md 2022-10-02 22:57:46 +08:00
7e4987d374 Modify merge all png elements to svg elements 2022-10-02 22:55:44 +08:00
bf8fd9987b Modify change icon to svg in activity_main 2022-10-02 21:38:49 +08:00
ebfd6cc491 Fix variable name shadowed bug in QQTIMHooker 2022-10-02 21:26:17 +08:00
5347907277 Modify compatible with API 33 2022-10-01 02:16:55 +08:00
0a8a4cfe2b Modify standard code naming in FunctionFactory 2022-10-01 00:46:56 +08:00
9f9c3f9ff3 Update YukiHookAPI 2022-09-30 23:47:24 +08:00
9c26cdf896 Modify change code note in MainActivity 2022-09-30 22:21:16 +08:00
3136d6ed12 Modify optimize code style in HookEntry 2022-09-30 22:14:07 +08:00
8576b8dc89 Added Project icon 2022-09-30 22:10:48 +08:00
07261afd47 Update version to 4.1 | Support QQ 8.9.13 2022-09-30 05:18:28 +08:00
54599cccd1 Fix Non-Activity dialog destroy problem in DialogBuilderFactory 2022-09-30 05:16:31 +08:00
2cc4d0e53d Fix QQ, TIM make Activity DPI deformation problem 2022-09-30 05:06:12 +08:00
859a45ce7f Modify change Activity Proxy Class from QQSettingSettingActivity to AboutActivity in QQTIMHooker 2022-09-30 04:57:38 +08:00
a85b2ba27a Modify support QQ、TIM night mode theme 2022-09-30 04:57:17 +08:00
7b3a227398 Update YuiHookAPI 2022-09-30 03:39:29 +08:00
f284634d9c Fix change hook entry inject function try fix some Non-Root Hook Framework not work, such as TaiChi 2022-09-30 00:02:48 +08:00
34a8f4bd25 Modify support QQ 8.9.13 2022-09-29 21:38:31 +08:00
24794ccc88 Update version to 4.0 | Support QQ 8.9.3, 8.9.5, 8.9.8 and 8.9.10 2022-09-29 08:50:09 +08:00
7e09aca853 Added new module configs info in activity_main 2022-09-29 08:44:56 +08:00
1852a53b9a Added directly open module app function in ConfigActivity 2022-09-29 08:37:35 +08:00
48ef4558d4 Update proguard-rules.pro 2022-09-29 08:21:34 +08:00
b3008f6c53 Modify move main configs function to Host App and refactored 2022-09-29 08:06:08 +08:00
7694ef823f Modify change net work state judgment in GithubReleaseTool 2022-09-29 07:16:57 +08:00
ad39c71c88 Modify add some function in FunctionFactory 2022-09-29 07:16:50 +08:00
2c46b235df Modify add special Resources Id in completing 2022-09-28 20:44:26 +08:00
7c09bfb3c3 Modify make BaseActivity extends ModuleAppCompatActivity 2022-09-28 20:42:51 +08:00
60eff953dd Modify change hideOrShowLauncherIcon in a hard-code way 2022-09-28 20:29:04 +08:00
33aedaee1e Modify merge to new DialogBuilderFactory 2022-09-28 20:12:09 +08:00
209bae1b23 Modify merge YukiHookAPI new usage and remove DataConst.ENABLE_MODULE_VERSION 2022-09-28 20:10:51 +08:00
e23a3a1fea Update Gradle & PlatformSDK 2022-09-28 19:53:27 +08:00
bdcbca37e7 Update YuiHookAPI 2022-09-28 19:51:40 +08:00
6aa32c686f Support QQ 8.9.10 2022-09-17 11:12:14 +08:00
d12b243545 Support QQ 8.9.8 2022-09-01 23:46:16 +08:00
b7b10742d7 Support QQ 8.9.3 and 8.9.5 2022-08-17 21:03:36 +08:00
b949dadf2a Update Gradle dependencies 2022-08-17 20:58:14 +08:00
df801a9304 Update version to 3.99.6 | Support QQ 8.9.2 2022-07-21 19:22:03 +08:00
02e96b2bc7 Support QQ 8.9.2 2022-07-21 19:20:43 +08:00
0e6268127b Update version to 3.99.5 | Support many QQ 8.x.x version 2022-07-20 22:58:46 +08:00
ded0e16f9f Update gradle version 2022-07-20 22:54:43 +08:00
f02789e02f Update Kotlin version to 1.7.10 2022-07-20 22:50:16 +08:00
a7ebda39d2 ReFormat code style 2022-07-20 22:48:26 +08:00
Fankesyooni
c78e546a1d Merge pull request #20 from chase535/patch-1
适配大量 QQ 8.x.x
2022-07-20 12:50:43 +08:00
chase535
3e74061f4e Update MainActivity.kt 2022-07-20 12:28:24 +08:00
chase535
572140ba91 Update HookEntry.kt 2022-07-20 11:38:09 +08:00
chase535
30c4becd98 适配大量 QQ 8.x.x
适配了绝大多数(可能是全部)8.x.x版本的正式版QQ
2022-07-20 11:13:58 +08:00
4b33166142 Merge dependencies 2022-07-20 02:08:42 +08:00
b988a8d8f7 Update version to 3.99 | Support QQ 8.9.0 and 8.9.1 2022-07-19 22:11:34 +08:00
cb057eb2c8 Support QQ 8.9.0 and 8.9.1 2022-07-19 22:02:04 +08:00
76c8386209 Fix hook BaseChatPie failure bug 2022-07-19 21:57:06 +08:00
Fankesyooni
1c43fc3568 Merge pull request #19 from StarWishsama/patch-1
适配 QQ 8.9.0
2022-07-17 01:04:11 +08:00
NoRainCity
058b9eadc5 Update QQ 8.9.0 2022-07-16 20:53:33 +08:00
8234924ef6 Update version to 3.98 | Support QQ 8.8.98 2022-07-05 22:49:18 +08:00
9a70a9cb8b Added QQ version support situation 2022-07-05 22:45:17 +08:00
1f1250ff14 Modify BugHook luckily 2022-07-05 22:04:04 +08:00
1c3c59036f Support QQ 8.8.98 2022-07-05 22:01:49 +08:00
0491263ee0 Update version to 3.97 | Support QQ 8.8.95 2022-06-16 02:25:01 +08:00
b4218f0ad0 Update Gradle & Kotlin & PlatformSDK
- Update Kotlin version to 1.7.0
- Update Gradle dependencies
- Merge legacy code
2022-06-10 17:13:58 +08:00
c6c448caa4 Merge hook function 2022-06-10 17:10:51 +08:00
29b91bb7ed Make UI to Primary Theme 2022-06-08 15:07:23 +08:00
6ed6ef77f7 Merge DialogBuilderFactory with new code style 2022-06-07 16:55:29 +08:00
275e56065f Update version to 3.96 | Support QQ 8.8.93 2022-06-04 03:20:38 +08:00
6752eb4f6e Fix GithubReleaseTool to LocalTime 2022-06-04 03:04:54 +08:00
806d60fda5 Added BuildConfig.VERSION_NAME changed 2022-06-03 23:54:21 +08:00
6c53cf4b40 Update version to 3.95 2022-05-31 02:06:35 +08:00
f5d88e1e6b Update YukiHookAPI 2022-05-31 02:02:26 +08:00
f6d1ca8579 Merge code 2022-05-30 02:11:30 +08:00
319eed10db Merge code 2022-05-30 02:03:14 +08:00
d8664b5923 Merge code 2022-05-30 01:43:47 +08:00
002fe05dc3 Merge systemBar support with native 2022-05-30 00:47:47 +08:00
b7e2e78690 Repair Licenses 2022-05-30 00:16:53 +08:00
9aa09c0513 Update version to 3.9 | Support QQ 8.8.90 2022-05-30 00:10:35 +08:00
14d88636f1 Added YukiPromoteTool 2022-05-30 00:03:57 +08:00
d1c1421d39 Support QQ 8.8.90 2022-05-29 22:51:25 +08:00
380b86f9a4 Update YukiHookAPI 2022-05-29 04:12:54 +08:00
91f37ac9fe Update YukiHookAPI 2022-05-27 03:34:16 +08:00
bd5bb780f1 Update YukiHookAPI 2022-05-25 04:36:25 +08:00
93e59e303f Update YukiHookAPI 2022-05-10 01:52:07 +08:00
22f60b31ec Update YukiHookAPI 2022-05-06 15:01:27 +08:00
add924bb6b Update YukiHookAPI 2022-05-04 14:01:41 +08:00
67d4560aaa Update YukiHookAPI 2022-05-04 10:14:29 +08:00
551c1ac039 Update YukiHookAPI 2022-05-04 09:31:13 +08:00
7d96de8d2c Update Kotlin version 2022-05-04 07:01:04 +08:00
27a9f90df2 Update YukiHookAPI 2022-05-04 07:00:17 +08:00
161aa8e245 Update version to 3.8,support QQ 8.8.88 2022-05-01 12:26:13 +08:00
6ed3dc6e5d Update YukiHookAPI 2022-05-01 12:15:20 +08:00
a75ca94bf9 Merge code 2022-04-25 02:46:13 +08:00
713b956fc3 Update YukiHookAPI 2022-04-18 03:12:32 +08:00
a45a75c577 Update YukiHookAPI 2022-04-18 03:11:27 +08:00
787e65d8bc Merge code 2022-04-15 15:26:01 +08:00
cb4326377f Update YukiHookAPI 2022-04-15 05:11:06 +08:00
e60ce53ab1 Merge code 2022-04-13 04:48:52 +08:00
d994bd1040 Merge code 2022-04-13 04:46:15 +08:00
be282dd9ca Update YukiHookAPI 2022-04-13 04:42:25 +08:00
739ba49618 Update YukiHookAPI 2022-04-10 03:13:58 +08:00
e12f2a7b16 Update YukiHookAPI 2022-04-09 02:28:05 +08:00
0bc8a47ef4 Update README.md 2022-04-09 01:48:48 +08:00
1f765182b0 Update YukiHookAPI 2022-04-05 22:46:12 +08:00
fbc6a020f1 Update YukiHookAPI 2022-04-04 22:53:27 +08:00
91640a125a Update version to 3.7 2022-04-04 03:06:17 +08:00
7d25a61518 Update YukiHookAPI 2022-04-04 02:59:32 +08:00
798297a567 修复新版 QQ 设置页面的圆角问题 2022-04-01 13:25:38 +08:00
7036da48f5 修复新版 QQ 设置页面的圆角问题 2022-04-01 13:25:18 +08:00
fd2f7016b2 Update version to 3.6,support QQ 8.8.85 2022-03-31 15:04:17 +08:00
27550e821d Merge code 2022-03-31 15:00:34 +08:00
9fec60fabc 加入设置页面显示守护状态功能,加入新的省电 Hook 点 2022-03-31 14:58:54 +08:00
b6d9096475 适配 QQ 8.8.85 2022-03-31 12:38:17 +08:00
c1d851739c Remove some hook when bug 2022-03-31 12:28:15 +08:00
68dc46b1ea Update YukiHookAPI 2022-03-30 14:12:01 +08:00
213b84b564 Merge code 2022-03-29 21:06:31 +08:00
5b34cb6fcd Update YukiHookAPI 2022-03-29 21:06:11 +08:00
8a3929c6d1 Update YukiHookAPI 2022-03-29 21:04:49 +08:00
66aa51a090 Merge code 2022-03-28 14:19:13 +08:00
27abf13cfb Merge code 2022-03-28 13:58:34 +08:00
7530c91451 Update YukiHookAPI 2022-03-28 00:38:44 +08:00
2dc1c5ca69 添加本地化 LSPosed 作用域 2022-03-25 14:22:58 +08:00
f0b5f92b0e Update YukiHookAPI 2022-03-25 01:54:34 +08:00
1f1f5a4791 Update YukiHookAPI 2022-03-25 01:07:26 +08:00
ce7bf3f804 Update version to 3.5 2022-03-24 15:37:14 +08:00
a453036196 Update version to 3.5 2022-03-24 15:36:12 +08:00
c1f4c4723f Merge code 2022-03-24 15:31:20 +08:00
6ae822d337 Merge code 2022-03-24 15:23:22 +08:00
a0b274d211 Merge code 2022-03-24 15:22:47 +08:00
ba0932158c Merge code 2022-03-24 15:21:41 +08:00
8420930f15 Merge code 2022-03-23 00:07:09 +08:00
0f3971c10a Merge code 2022-03-23 00:06:48 +08:00
057bcfed3a Merge code 2022-03-22 02:58:32 +08:00
6a641e9bfd Merge code 2022-03-22 01:32:11 +08:00
b6c946bd2d 更新模块 UI 以及相关设置 2022-03-21 14:40:17 +08:00
7e13b80791 更新模块 UI 以及相关设置 2022-03-21 14:17:29 +08:00
9ce746a7b7 Update README.md 2022-03-20 22:28:36 +08:00
9ca53068e6 增加模块自动检查更新功能 2022-03-20 22:28:21 +08:00
7214cda419 增加模块自动检查更新功能 2022-03-20 22:28:06 +08:00
4fb818da3a 增加模块自动检查更新功能 2022-03-20 14:06:11 +08:00
220d7d9f10 增加模块自动检查更新功能 2022-03-20 13:55:36 +08:00
b3969c84dc 增加模块自动检查更新功能 2022-03-20 13:19:05 +08:00
ae7090223a Merge code 2022-03-20 12:11:36 +08:00
4c9a18b98f Update YuKiHookAPI 2022-03-20 03:37:16 +08:00
803ddaf987 Merge code 2022-03-20 00:54:08 +08:00
775e999cbe Merge R8 Rules 2022-03-19 20:33:30 +08:00
5cb64fc2dd 布局更换为 ViewBinding 并适配 MD3 风格对话框 2022-03-19 14:31:48 +08:00
6ea0a91ed1 Merge code 2022-03-19 01:40:28 +08:00
cfce01c82d Update YuKiHookAPI 2022-03-18 23:51:10 +08:00
a1beae9b4b Update version to 3.3,support QQ 8.8.83 2022-03-18 15:10:57 +08:00
d3514bd117 取消缓存设置实时生效 2022-03-18 15:06:48 +08:00
0658f7c3ef 增加通知栏守护状态可关闭功能 2022-03-18 15:05:40 +08:00
31ea8a094b Update YukiHookAPI 2022-03-18 14:51:20 +08:00
919d5bbd92 Merge code 2022-03-18 14:51:00 +08:00
a14b0e4868 Merge code 2022-03-18 14:25:35 +08:00
f70b4c8ec8 适配 QQ 8.8.83 2022-03-18 14:19:59 +08:00
741120cffc Update YukiHookAPI 2022-03-18 13:59:45 +08:00
6522d350ac Update YukiHookAPI 2022-03-18 05:59:29 +08:00
d0d91a4da5 Update README.md 2022-03-17 23:56:55 +08:00
32f37a4d12 Update README.md 2022-03-17 23:56:08 +08:00
463226c910 Merge code 2022-03-17 05:39:20 +08:00
4f1523e179 调整了一个按钮样式 2022-03-17 04:10:52 +08:00
04c614e4e2 Merge code 2022-03-13 01:00:05 +08:00
df5935c08a Update YukiHookAPI 2022-03-06 01:02:11 +08:00
1e01104401 修正文案 2022-03-05 00:24:09 +08:00
c1a3b8e9f1 Update version to 3.2,support QQ 8.8.80 2022-02-25 22:24:38 +08:00
516bc97c46 适配 QQ 8.8.80 2022-02-25 22:22:18 +08:00
549eb02e38 适配 QQ 8.8.8.0 2022-02-25 22:04:57 +08:00
d1ef761375 Merge code 2022-02-25 21:36:44 +08:00
a53fc9a16c Merge code 2022-02-20 03:42:59 +08:00
626688f3ec Merge code 2022-02-19 03:11:32 +08:00
2e10c02bc2 更新依赖库版本 2022-02-18 04:17:35 +08:00
36d847219d 规范资源文件命名 2022-02-16 03:35:05 +08:00
3f034011da 修改文案 2022-02-15 22:16:22 +08:00
d482976d99 Merge new version 2022-02-15 13:40:56 +08:00
928e3ff248 Merge new version 2022-02-15 13:39:41 +08:00
80086969c0 Merge new version 2022-02-15 13:39:12 +08:00
c0e903b23e Merge new version 2022-02-15 12:51:13 +08:00
9dc5b617f6 优化代码 2022-02-15 03:32:22 +08:00
f39f3e3ff5 Update README.md 2022-02-15 02:00:47 +08:00
04daf15cb4 Update README.md 2022-02-15 02:00:24 +08:00
ae2a7d015a Update README.md 2022-02-15 01:54:32 +08:00
26023d53c6 Refactor to YukiHookAPI https://github.com/fankes/YukiHookAPI 2022-02-15 01:47:07 +08:00
e5c5c4874a 更新文案 2022-02-10 19:53:11 +08:00
2d1f230b5c 更新文案 2022-02-10 19:29:42 +08:00
95ea5fa49d 更新文案 2022-02-10 19:24:49 +08:00
89d22ef41d 更新文案 2022-02-10 19:24:12 +08:00
971821656d 更新文案 2022-02-10 19:21:35 +08:00
3628c2f9a8 更新文案 2022-02-10 19:20:37 +08:00
1b9d7d0313 更新文案 2022-02-10 18:57:08 +08:00
0009ad2de3 更新文案 2022-02-10 18:54:57 +08:00
bfb4a8d1d2 更新版本协议到 A-GPL3.0 2022-02-10 18:53:30 +08:00
Fankesyooni
250478bca2 Update README.md 2022-02-10 18:40:47 +08:00
Fankesyooni
8b8bf8089b Update README.md 2022-02-10 18:38:29 +08:00
Fankesyooni
d5e4c933dd Update LICENSE 2022-02-10 18:38:07 +08:00
7bb28aede3 修正文案 2022-02-07 21:15:48 +08:00
93ca95a546 摆烂 2022-02-07 00:39:32 +08:00
6425565bd8 整理规范代码 2022-02-03 20:29:07 +08:00
43b7cfee93 整理规范代码 2022-01-30 23:07:14 +08:00
31a2bc5ba8 适配夜间模式以及 Material3 2022-01-30 19:21:21 +08:00
4c04e3f0d2 Update Version to 3.1 Support QQ 8.8.68 2022-01-25 05:51:16 +08:00
621342c164 优化代码,适配 Play 版本 2022-01-25 05:47:14 +08:00
cbed68b1ad 优化代码,适配 Play 版本 2022-01-25 05:45:23 +08:00
3ba55b8b9b 优化文案 2022-01-25 05:23:46 +08:00
cc499e3d80 优化代码 2022-01-25 05:22:13 +08:00
0e26d3b08f 优化代码 2022-01-25 05:14:44 +08:00
3d78fbbeab 优化代码 2022-01-25 04:59:39 +08:00
f8d4350805 优化代码 2022-01-25 04:58:19 +08:00
1ef0d5412e 加入快捷跳转应用信息页面操作 2022-01-25 04:36:38 +08:00
b95fdeb099 加入多项 Hook 策略 2022-01-25 04:22:58 +08:00
b69ea3196a 加入多项 Hook 策略 2022-01-25 04:17:37 +08:00
59a3b05a1e 加入多项 Hook 策略 2022-01-25 04:10:34 +08:00
217b863730 加入多项 Hook 策略 2022-01-25 04:06:27 +08:00
ebba77108a 加入多项 Hook 策略 2022-01-25 03:46:44 +08:00
f73d47d4b6 Update README.md 2022-01-24 11:30:02 +08:00
8076d97023 优化代码 2022-01-24 06:13:21 +08:00
51794cfe19 优化代码 2022-01-24 06:05:35 +08:00
4f423984a4 优化代码 2022-01-24 05:54:13 +08:00
2014fdcf85 优化代码 2022-01-24 05:52:27 +08:00
a8e14327d5 优化代码 2022-01-24 05:49:57 +08:00
8166cbebf9 优化代码 2022-01-24 05:48:35 +08:00
a71979966a 优化代码注释 2022-01-24 05:42:49 +08:00
58764ced85 优化注释 2022-01-24 05:42:40 +08:00
7be601372f 日常维护 2022-01-24 05:06:37 +08:00
8c587c1321 Update Version to 3.0 The brand new theme Material You. 2022-01-09 21:33:17 +08:00
980ec468e7 Update Version to 3.0 The brand new theme Material You. 2022-01-08 01:32:02 +08:00
d30d499378 Update Version to 3.0 The brand new theme Material You. 2022-01-08 01:29:44 +08:00
1f533eb4ba Update Version to 3.0 The brand new theme Material You. 2022-01-08 01:29:23 +08:00
5d6709028f 合并代码,加入全新 Material You 风格主题 2022-01-08 01:26:50 +08:00
56f2cd96da 合并代码,加入全新 Material You 风格主题 2022-01-08 01:12:20 +08:00
d9bf728333 重构界面,加入安装状态图标 2022-01-08 00:11:51 +08:00
9b7e6fa23b 合并优化代码,封装对话框代码 2022-01-07 23:48:49 +08:00
0ec7ad8a5b 合并优化代码,封装对话框代码 2022-01-07 23:47:49 +08:00
77bcb27daa Support QQ 8.8.55 2022-01-07 23:16:02 +08:00
6c1497aebc Update Banner Logo 2021-12-23 08:19:20 +08:00
7303171777 Update Banner Logo 2021-12-23 08:17:43 +08:00
8c89751887 Update Banner Logo 2021-12-23 08:15:21 +08:00
d17c6fbbbc Update Banner Logo 2021-12-23 08:14:28 +08:00
18c67b3fe4 Update Banner Logo 2021-12-23 08:10:08 +08:00
a8488fa4f5 Update Version to 2.5 Support WeChat(Base Save Battery) 2021-12-22 01:12:54 +08:00
0420114d9d 微信省电(新建文件夹) 2021-12-22 01:07:16 +08:00
d26187ea07 微信省电(新建文件夹) 2021-12-22 00:36:02 +08:00
e90ff7f542 合并优化代码,优化模块主界面显示效果,加入微信省电的未来计划(新建文件夹) 2021-12-22 00:28:23 +08:00
Fankesyooni
dba5d51944 Merge pull request #6 from StarWishsama/master
支持 QQ 8.8.35
2021-12-21 23:44:51 +08:00
StarWishsama
3bdb442387 feat: support QQ 8.8.35 2021-12-21 22:58:50 +08:00
409de75134 更新了一个日志外显功能,并将 API 降回 26 2021-12-07 00:30:31 +08:00
2b861d49c3 Update README 2021-11-28 13:29:45 +08:00
bfe7832305 Support QQ 8.8.50 Update version to 2.4 2021-11-27 19:18:24 +08:00
33b19ec62e Support QQ 8.8.50 Update version to 2.4 2021-11-27 19:16:50 +08:00
4612332c47 整理代码并更新依赖版本 2021-11-27 18:59:44 +08:00
Fankesyooni
a6fb895fa8 Merge pull request #4 from JiZhi-Error/master
Support QQ 8.8.50
2021-11-24 18:52:10 +08:00
103 changed files with 4844 additions and 1703 deletions

35
.editorconfig Normal file
View File

@@ -0,0 +1,35 @@
[{*.kt,*.kts}]
ktlint_standard_annotation = disabled
ktlint_standard_filename = disabled
ktlint_standard_wrapping = disabled
ktlint_standard_import-ordering = enabled
ktlint_standard_max-line-length = disabled
ktlint_standard_multiline-if-else = disabled
ktlint_standard_argument-list-wrapping = disabled
ktlint_standard_parameter-list-wrapping = disabled
ktlint_standard_trailing-comma-on-declaration-site = disabled
ktlint_function_signature_body_expression_wrapping = multiline
ktlint_standard_string-template-indent = disabled
ktlint_standard_function-signature = disabled
ktlint_standard_trailing-comma-on-call-site = disabled
ktlint_standard_multiline-expression-wrapping = disabled
ktlint_standard_no-empty-first-line-in-class-body = disabled
ktlint_standard_if-else-wrapping = disabled
ktlint_standard_if-else-bracing = disabled
ktlint_standard_statement-wrapping = disabled
ktlint_standard_blank-line-before-declaration = disabled
ktlint_standard_no-empty-file = disabled
ktlint_standard_property-naming = disabled
ktlint_standard_function-naming = disabled
ktlint_standard_chain-method-continuation = disabled
ktlint_standard_class-signature = disabled
ktlint_standard_condition-wrapping = disabled
ktlint_standard_blank-line-between-when-conditions = disabled
ktlint_standard_no-trailing-spaces = disabled
ktlint_standard_multiline-loop = disabled
ktlint_standard_when-entry-bracing = disabled
ij_continuation_indent_size = 2
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 150

105
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,105 @@
name: 问题与 BUG 反馈
description: 问题反馈必须使用此模板进行提交
labels: [ bug ]
title: "[问题与 BUG 反馈] (在这里简要描述问题原因)"
body:
- type: markdown
attributes:
value: |
### 请在下方填写问题发生的具体原因和复现步骤。
我们只接受从官方渠道或应用市场下载的 QQ、TIM、微信如果你正在第三方修改版请不要提交任何 BUG 与问题,我们无义务去解决,请自求多福。
请务必知悉:部分问题可能是 APP 自身产生的 BUG发生这种情况模块没有义务去负责解决与修复你可以尝试关闭模块以验证该问题是否来源于 APP 自身原因。
发生异常、崩溃、闪退或功能性问题,必须提交问题 Log (日志),没有 Log 的 issues 将直接被关闭。
- type: input
attributes:
label: 模块版本
description: 请填写当前使用的模块完整版本号,例如:**3.0**
validations:
required: true
- type: dropdown
attributes:
label: 作用域 APP
description: 请选择出现问题的作用域 APP。
options:
- QQ
- TIM
- 微信
- 两者或以上
validations:
required: true
- type: input
attributes:
label: 作用域 APP 版本
description: |
这里填写当前作用域 APP 的版本,例如:**QQ 8.9.28**、**微信 8.0.30**
如果存在多个有问题的作用域 APP请使用顿号分隔依次填写
validations:
required: true
- type: dropdown
attributes:
label: Android 版本
options:
- 14
- 13
- 12L/12.1
- 12
- 11
- 10
- 9
- 8.1
- 8.0.0
validations:
required: true
- type: input
attributes:
label: Xposed 框架名称与版本号
description: 请填写当前使用的 Xposed 框架,例如:**LSPosed 1.8.4(次版本号)**
validations:
required: true
- type: dropdown
attributes:
label: 系统是否已 Root
options:
-
-
validations:
required: true
- type: input
attributes:
label: 与当前作用域 APP 同作用域的 Xposed 模块
description: |
此模块的作用域为 QQ、TIM、微信为确保非其它模块冲突造成的问题请一定要填写当前你同时激活的相关模块。
若没有,请直接在下方填写“无”。
validations:
required: true
- type: textarea
attributes:
label: 详细描述问题发生的具体原因
description: 请在下方详细描述问题发生的具体场景、复现步骤和经过,以便我们能够按照你所描述的步骤复现这个问题。
validations:
required: true
- type: textarea
attributes:
label: 提供模块问题 Log 或必要 Log
description: LSPosed 可在日志管理中查看并筛选包含 `TSBattery` 的日志。
value: |
<details><summary>展开查看</summary><pre><code>
(此处粘贴问题 Log)
</code></pre></details>
<!-- 提交时请将括号内容包括括号全部删除,粘贴你复制的日志,不要破坏代码格式 -->
validations:
required: true
- type: checkboxes
attributes:
label: 确认一下你提交的信息
description: |
为了确保 issues 的质量和避免浪费不必要的时间,未勾选下方选项的 issues 将直接被关闭。
请一定确保你已经**勾选下方的选项**后再提交。
options:
- label: 我确保上述信息准确无误
required: false

88
.github/workflows/commit_ci.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: Automatic Build on Commit
on:
workflow_dispatch:
push:
branches: [ master ]
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '!.github/workflows/**'
jobs:
build:
name: Build CI
if: ${{ success() }}
runs-on: ubuntu-latest
env:
APK_OUTPUT_PATH: 'app/build/outputs/apk'
TG_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TG_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
COMMIT_MESSAGE: |+
New push to GitHub\!
```
${{ github.event.head_commit.message }}
```by `${{ github.event.head_commit.author.name }}`
See commit detail [here](${{ github.event.head_commit.url }})
COMMIT_URL: ${{ github.event.head_commit.url }}
steps:
- uses: actions/checkout@v4
- name: Prepare GitHub Env
run: |
GITHUB_SHA=${{ github.sha }}
GITHUB_CI_COMMIT_ID=${GITHUB_SHA:0:7}
echo "GITHUB_CI_COMMIT_ID=$GITHUB_CI_COMMIT_ID" >> $GITHUB_ENV
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1
with:
cmake-version: '3.22.1'
- name: Prepare Java 21
uses: actions/setup-java@v4
with:
java-version: 21
java-package: jdk
distribution: 'temurin'
cache: 'gradle'
- name: Cache Gradle Dependencies
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
!~/.gradle/caches/build-cache-*
key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }}
restore-keys: |
gradle-deps
- name: Cache Gradle Build
uses: actions/cache@v3
with:
path: |
~/.gradle/caches/build-cache-*
key: gradle-builds-core-${{ github.sha }}
restore-keys: |
gradle-builds
- name: Build with Gradle
run: |
./gradlew :app:assembleDebug
./gradlew :app:assembleRelease
echo "DEBUG_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV
echo "RELEASE_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV
- name: Upload Artifacts (Debug)
uses: actions/upload-artifact@v4
with:
path: ${{ env.DEBUG_APK_PATH }}
name: TSBattery-debug-${{ github.event.head_commit.id }}
- name: Upload Artifacts (Release)
uses: actions/upload-artifact@v4
with:
path: ${{ env.RELEASE_APK_PATH }}
name: TSBattery-release-${{ github.event.head_commit.id }}
- name: Post Artifacts to Telegram
run: |
export debug=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name "*.apk")
export release=$(find ${{ env.APK_OUTPUT_PATH }}/release -name "*.apk")
ESCAPED=`python3 -c 'import json,os,urllib.parse; msg = json.dumps(os.environ["COMMIT_MESSAGE"]); print(urllib.parse.quote(msg if len(msg) <= 1024 else json.dumps(os.environ["COMMIT_URL"])))'`
curl -v "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMediaGroup?chat_id=${TG_CHAT_ID}&media=%5B%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Fdebug%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Frelease%22%2C%22parse_mode%22%3A%22MarkdownV2%22%2C%22caption%22:${ESCAPED}%7D%5D" \
-F debug="@$debug" \
-F release="@$release"

70
.github/workflows/pull_request_ci.yml vendored Normal file
View File

@@ -0,0 +1,70 @@
name: Pull Request Checker
on:
pull_request:
branches: [ master ]
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '!.github/workflows/**'
jobs:
build:
name: Pull Request Check
if: ${{ success() }}
runs-on: ubuntu-latest
env:
APK_OUTPUT_PATH: 'app/build/outputs/apk'
steps:
- uses: actions/checkout@v4
- name: Prepare GitHub Env
run: |
GITHUB_SHA=${{ github.sha }}
GITHUB_CI_COMMIT_ID=${GITHUB_SHA:0:7}
echo "GITHUB_CI_COMMIT_ID=$GITHUB_CI_COMMIT_ID" >> $GITHUB_ENV
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1
with:
cmake-version: '3.22.1'
- name: Prepare Java 21
uses: actions/setup-java@v4
with:
java-version: 21
java-package: jdk
distribution: 'temurin'
cache: 'gradle'
- name: Cache Gradle Dependencies
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
!~/.gradle/caches/build-cache-*
key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }}
restore-keys: |
gradle-deps
- name: Cache Gradle Build
uses: actions/cache@v3
with:
path: |
~/.gradle/caches/build-cache-*
key: gradle-builds-core-${{ github.sha }}
restore-keys: |
gradle-builds
- name: Build with Gradle
run: |
./gradlew :app:assembleDebug
./gradlew :app:assembleRelease
echo "DEBUG_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV
echo "RELEASE_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV
- name: Upload Artifacts (Debug)
uses: actions/upload-artifact@v4
with:
path: ${{ env.DEBUG_APK_PATH }}
name: TSBattery-debug-${{ github.event.head_commit.id }}
- name: Upload Artifacts (Release)
uses: actions/upload-artifact@v4
with:
path: ${{ env.RELEASE_APK_PATH }}
name: TSBattery-release-${{ github.event.head_commit.id }}

127
.gitignore vendored
View File

@@ -1,15 +1,120 @@
## Fully .gtignore for IntelliJ, Android Studio and Gradle based Java projects
## References:
## - https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
## - https://github.com/android/platform-samples/blob/main/.gitignore
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
.idea/.name
.idea/artifacts
.idea/compiler.xml
.idea/jarRepositories.xml
.idea/modules.xml
.idea/*.iml
.idea/modules
.idea/caches
.idea/material_theme**
.idea/other.xml
*.iml *.iml
.gradle *.ipr
/local.properties
/.idea/caches # Kotlin
/.idea/libraries .kotlin
/.idea/modules.xml .idea/kotlinc.xml
/.idea/workspace.xml
/.idea/navEditor.xml # Misc
/.idea/assetWizardSettings.xml .idea/misc.xml
.DS_Store .idea/markdown.xml
/build
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# Android studio 3.1+ additional
.idea/deployment*.xml
.idea/assetWizardSettings.xml
.idea/androidTestResultsUserPreferences.xml
# Android projects
.idea/AndroidProjectSystem.xml
.idea/deviceManager.xml
**/local.properties
/captures /captures
.externalNativeBuild .externalNativeBuild
.cxx .cxx
local.properties
# Gradle projects
.gradle
build/
# Mkdocs temporary serving folder
docs-gen
site
*.bak
.idea/appInsightsSettings.xml
# Discord plugin for IntelliJ
.idea/discord.xml
# Copilot for IntelliJ
.idea/copilot**
# Mac OS
.DS_Store

3
.idea/.gitignore generated vendored
View File

@@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

117
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,117 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

6
.idea/compiler.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>

20
.idea/gradle.xml generated
View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

BIN
.idea/icon.png generated Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -2,10 +2,7 @@
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="DifferentStdlibGradleVersion" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="DifferentStdlibGradleVersion" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false"> <inspection_tool class="UnstableApiUsage" enabled="false" level="WARNING" enabled_by_default="false" />
<option name="processCode" value="true" /> <inspection_tool class="YAMLSchemaValidation" enabled="false" level="WARNING" enabled_by_default="false" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile> </profile>
</component> </component>

View File

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

7
.idea/ktlint-plugin.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KtLint plugin">
<ktlintMode>MANUAL</ktlintMode>
<formatOnSave>false</formatOnSave>
</component>
</project>

6
.idea/ktlint.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KtlintProjectConfiguration">
<treatAsErrors>false</treatAsErrors>
</component>
</project>

10
.idea/migrations.xml generated Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

9
.idea/misc.xml generated
View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@@ -3,7 +3,14 @@
<component name="RunConfigurationProducerService"> <component name="RunConfigurationProducerService">
<option name="ignoredProducers"> <option name="ignoredProducers">
<set> <set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" /> <option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set> </set>
</option> </option>
</component> </component>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

47
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,47 @@
# 开始贡献
欢迎为此项目进行新版本的适配代码贡献!<br/>
## 分支规定
不管是直接 Push 代码还是提交 Pull Request都必须使 commit 指向 master 分支。
## 代码格式规范
- 1.全部提交代码必须使用 IDE(Android Studio 或 IDEA) 进行格式化,未经格式化的代码将拒绝合并提交请求
- 2.代码必须使用 4 spaces 缩进格式化
## 代码注释规范
- 1.第一种注释方式:可使用在方法名或顶级变量名上
```kotlin
/** 注释内容 */
fun a() {
}
/**
* 注释名称
* @param test 方法名称
* @return 返回值名称
*/
fun a(test: String) {
}
```
- 2.第二种注释方式:仅可使用在变量后方
```kotlin
val a = "" // 变量注释
```
- ⚠️ 注意:只允许两个 // 后方要有空格
## 项目要求
- 1.调试性质或大批量注释代码,禁止提交
- 2.类名和方法名仅能由开发者进行修改和提交,禁止随意修改项目名称、方法名称以及类名
- 3.禁止随意更新项目依赖以及增加新的依赖,有问题请提前提交到 issues 进行说明
- 4.禁止更新项目版本号,版本号交由开发者合并代码并发布 release 版本
- 5.代码语言要求,请统一使用 Kotlin除特殊情况外不接受其他语言的提交
- 6.以上

722
LICENSE

File diff suppressed because it is too large Load Diff

112
README.md
View File

@@ -1,7 +1,107 @@
# TSBattery # TSBattery
TSBattery a new way to save your battery avoid cancer apps hacker it.<br/>
TSBattery 是一个旨在使 QQ、TIM 变得更省电的开源 Xposed 模块 [![GitHub license](https://img.shields.io/github/license/fankes/TSBattery?color=blue&style=flat-square)](https://github.com/fankes/TSBattery/blob/master/LICENSE)
# Get startted [![GitHub CI](https://img.shields.io/github/actions/workflow/status/fankes/TSBattery/commit_ci.yml?label=CI%20builds&style=flat-square)](https://github.com/fankes/TSBattery/actions/workflows/commit_ci.yml)
此模块支持原生 Xposed、Lsposed(作用域 QQ、TIM 如果不起作用勾选系统框架)、EdXposed(不推荐)、太极无极(阴和阳)、Pine(梦境模块) [![GitHub release](https://img.shields.io/github/v/release/fankes/TSBattery?display_name=release&logo=github&color=green&style=flat-square)](https://github.com/fankes/TSBattery/releases)
# 禁止任何商业用途 ![GitHub all releases](https://img.shields.io/github/downloads/fankes/TSBattery/total?label=downloads&style=flat-square)
本模块完全开源免费,如果好用你可以打赏支持开发,严禁未经许可进行二改贩卖,违者必惩必究。 ![GitHub all releases](https://img.shields.io/github/downloads/Xposed-Modules-Repo/com.fankes.tsbattery/total?label=LSPosed%20downloads&labelColor=F48FB1&style=flat-square)
[![Telegram CI](https://img.shields.io/badge/CI%20builds-Telegram-blue.svg?logo=telegram&style=flat-square)](https://t.me/TSBattery_CI)
[![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram&style=flat-square)](https://t.me/XiaofangInternet)
[![QQ](https://img.shields.io/badge/discussion-QQ-blue.svg?logo=tencent-qq&logoColor=red&style=flat-square)](https://qm.qq.com/cgi-bin/qm/qr?k=dp2h5YhWiga9WWb_Oh7kSHmx01X8I8ii&jump_from=webapi&authKey=Za5CaFP0lk7+Zgsk2KpoBD7sSaYbeXbsDgFjiWelOeH4VSionpxFJ7V0qQBSqvFM)
[![QQ 频道](https://img.shields.io/badge/discussion-QQ%20频道-blue.svg?logo=tencent-qq&logoColor=red&style=flat-square)](https://pd.qq.com/s/44gcy28h)
<img src="app/src/main/ic_launcher-playstore.png" width = "100" height = "100" alt="LOGO"/>
A new way to save your battery avoid cancer apps hacker it.
TSBattery 是一个旨在使 QQ、TIM、微信 变得更省电的开源 Xposed 模块。
## For Non-Chinese Users
This Xposed Module is for use by specific apps for users in mainland China, you should not need it.
## 适配说明
- 解锁 BootLoader 并安装 **KernelSU**、**Magisk** 的设备建议使用 [LSPosed](https://github.com/LSPosed/LSPosed)
- **太极 (无极)** 支持性不是很好,建议使用 [LSPatch](https://github.com/LSPosed/LSPatch)
- 支持一些第三方 Xposed 框架,但是不保证其稳定性
- 支持一些第三方免 Root 框架例如**应用转生**、**SandVXposed**,但是不推荐使用,可能会造成封号风险
- 如果在微信设置界面右上角你无法找到 **TSBattery**
的图标,请尝试同时激活 [WeXposed (微X模块)](https://github.com/Xposed-Modules-Repo/com.fkzhang.wechatxposed)
## 发行渠道
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub CI](https://github.com/fankes/TSBattery/actions/workflows/commit_ci.yml) | CI 自动构建 (测试版) |
|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|---------------|
| <img src="https://github.com/peter-iakovlev/Telegram/blob/public/Icon.png?raw=true" width = "30" height = "30" alt="LOGO"/> | [Telegram CI 频道](https://t.me/TSBattery_CI) | CI 自动构建 (测试版) |
|-----------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|---------------|
| <img src="https://avatars.githubusercontent.com/in/15368?s=64&v=4" width = "30" height = "30" alt="LOGO"/> | [GitHub Releases](https://github.com/fankes/TSBattery/releases) | 正式版 (稳定版) |
|------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------|-----------|
| <img src="https://avatars.githubusercontent.com/u/78217009?s=200&v=4?raw=true" width = "30" height = "30" alt="LOGO"/> | [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.tsbattery/releases) | 正式版 (稳定版) |
|------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|-----------|
| <img src="https://github.com/fankes/fankes/assets/37344460/82113d3c-aa7b-4dd1-95c7-cda650065c12" width = "30" height = "30" alt="LOGO"/> | [123 云盘 **(密码tsbt)**](https://www.123pan.com/s/5SlUVv-N8DBh.html) | 正式版 (稳定版) |
|------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|-----------|
本模块发布地址仅限于上述所列出的地址,从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
## 注意事项
<h3>1.&nbsp;本软件免费、由兴趣驱动开发,仅供学习交流使用。如果你是从其他非官方渠道付费获得本软件,可能已遭遇欺诈,欢迎向我们举报可疑行为。</h3>
<h3>2.&nbsp;本软件采用 <strong>GNU Affero General Public License (AGPL 3.0)</strong> 许可证。根据该许可证的要求:</h3>
- 任何衍生作品必须采用相同的 AGPL 许可证
- 分发本软件或其修改版本时,必须提供完整的源代码
- 必须保留原始的版权声明及许可证信息
- 不得额外施加限制来限制他人对本软件的自由使用
<h3>3.&nbsp;我们鼓励在遵守 AGPL 3.0 条款的前提下进行自由传播和改进,但请尊重作者署名权,勿冒用原作者名义。</h3>
## 项目推广
<!--suppress HtmlDeprecatedAttribute -->
<div align="center">
<h2>嘿,还请君留步!👋</h2>
<h3>这里有 Android 开发工具、UI 设计、Gradle 插件、Xposed 模块和实用软件等相关项目。</h3>
<h3>如果下方的项目能为你提供帮助,不妨为我点个 star 吧!</h3>
<h3>所有项目免费、开源,遵循对应开源许可协议。</h3>
<h1><a href="https://github.com/fankes/fankes/blob/main/project-promote/README-zh-CN.md">→ 查看更多关于我的项目,请点击这里 ←</a></h1>
</div>
## Star History
![Star History Chart](https://api.star-history.com/svg?repos=fankes/TSBattery&type=Date)
## 许可证
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
```
Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
```
Powered by [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI)
版权所有 © 2017 Fankes Studio(qzmmcn@163.com)

3
app/.gitignore vendored
View File

@@ -1 +1,2 @@
/build /src/main/assets/xposed_init
/src/main/resources/META-INF/yukihookapi_init

View File

@@ -1,70 +0,0 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
signingConfigs {
debug {
storeFile file('../keystore/public')
storePassword '123456'
keyAlias 'public'
keyPassword '123456'
v1SigningEnabled true
v2SigningEnabled true
}
}
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.fankes.tsbattery"
minSdkVersion 22
//noinspection ExpiredTargetSdkVersion,OldTargetApi
targetSdkVersion 26
versionCode 6
versionName "2.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true
signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
compileOnly 'de.robv.android.xposed:api:82'
// 基础依赖包,必须要依赖
implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
// fragment快速实现可选
implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
// kotlin扩展可选
implementation 'com.gyf.immersionbar:immersionbar-ktx:3.0.0'
//noinspection GradleDependency,DifferentStdlibGradleVersion
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
//noinspection GradleDependency
implementation 'androidx.core:core-ktx:1.5.0'
//noinspection GradleDependency
implementation 'androidx.appcompat:appcompat:1.3.0'
//noinspection GradleDependency
implementation 'com.google.android.material:material:1.3.0'
//noinspection GradleDependency
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.13.2'
//noinspection GradleDependency
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
//noinspection GradleDependency
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

87
app/build.gradle.kts Normal file
View File

@@ -0,0 +1,87 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.ksp)
}
android {
namespace = gropify.project.app.packageName
compileSdk = gropify.project.android.compileSdk
signingConfigs {
create("universal") {
keyAlias = gropify.project.app.signing.keyAlias
keyPassword = gropify.project.app.signing.keyPassword
storeFile = rootProject.file(gropify.project.app.signing.storeFilePath)
storePassword = gropify.project.app.signing.storePassword
enableV1Signing = true
enableV2Signing = true
}
}
defaultConfig {
applicationId = gropify.project.app.packageName
minSdk = gropify.project.android.minSdk
targetSdk = gropify.project.android.targetSdk
versionName = gropify.project.app.versionName
versionCode = gropify.project.app.versionCode
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
all { signingConfig = signingConfigs.getByName("universal") }
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
buildFeatures {
buildConfig = true
viewBinding = true
}
lint { checkReleaseBuilds = false }
androidResources.additionalParameters += listOf("--allow-reserved-package-id", "--package-id", "0x37")
}
androidComponents {
onVariants(selector().all()) {
it.outputs.forEach { output ->
val currentType = it.buildType
val currentSuffix = gropify.github.ci.commit.id.let { suffix ->
if (suffix.isNotBlank()) "-$suffix" else ""
}
val currentVersion = "${output.versionName.get()}$currentSuffix(${output.versionCode.get()})"
if (output is com.android.build.api.variant.impl.VariantOutputImpl)
output.outputFileName.set("${gropify.project.name}-v$currentVersion-$currentType.apk")
}
}
}
dependencies {
compileOnly(libs.rovo89.xposed.api)
implementation(libs.yukihookapi)
ksp(libs.yukihookapi.ksp.xposed)
ksp(libs.hikage.compiler)
implementation(libs.kavaref.core)
implementation(libs.kavaref.extension)
implementation(libs.hikage.core)
implementation(libs.hikage.extension)
implementation(libs.hikage.widget.androidx)
implementation(libs.hikage.widget.material)
implementation(libs.project.promote)
implementation(libs.dexkit)
implementation(libs.drawabletoolbox)
implementation(libs.okhttp)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.androidx.test.espresso.core)
}

View File

@@ -20,29 +20,41 @@
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
-dontwarn
-ignorewarnings -ignorewarnings
-optimizationpasses 10 -optimizationpasses 10
-dontusemixedcaseclassnames -dontusemixedcaseclassnames
-dontoptimize -dontoptimize
-verbose -verbose
-overloadaggressively -overloadaggressively
-repackageclasses o
-allowaccessmodification -allowaccessmodification
-adaptclassstrings -adaptclassstrings
-adaptresourcefilenames -adaptresourcefilenames
-adaptresourcefilecontents -adaptresourcefilecontents
#-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* -renamesourcefileattribute P
-renamesourcefileattribute H -keepattributes SourceFile,Signature,LineNumberTable
-keepattributes SourceFile,LineNumberTable
-keep class android.support** # 排除注入的 Activity
-keep class androidx** -keep class com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
-keep public class * extends android.app.Activity # DexKit
-keep public class * extends android.app.Application -keep class org.luckypray.dexkit.DexKitBridge {
-keep public class * extends android.app.Service native <methods>;
-keep public class * extends android.content.ContentProvider }
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference # 防止某些类被 R8 混淆 (可能是 BUG)
# FIXME: 已知问题字符串类名 (即使是常量) 也会被 R8 处理为混淆后的类名
# FIXME: 所以目前只能把不允许 R8 处理的类 keep 掉,同时在当前模块中也不会被混淆就是了
-keep class kotlin.Unit
-keep class kotlin.jvm.functions.Function0
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
public static *** throwUninitializedProperty(...);
public static *** throwUninitializedPropertyAccessException(...);
}
-keep class * extends android.app.Activity
-keep class * implements androidx.viewbinding.ViewBinding {
<init>();
*** inflate(android.view.LayoutInflater);
}

Binary file not shown.

View File

@@ -1,18 +0,0 @@
{
"version": 2,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.fankes.tsbattery",
"variantName": "processReleaseResources",
"elements": [
{
"type": "SINGLE",
"filters": [],
"versionCode": 5,
"versionName": "2.2",
"outputFile": "app-release.apk"
}
]
}

View File

@@ -1,7 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools">
package="com.fankes.tsbattery">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
tools:node="remove" />
<permission
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
tools:node="remove" />
<!-- 作用域 APP -->
<queries>
<package android:name="com.tencent.mobileqq" />
<package android:name="com.tencent.tim" />
<package android:name="com.tencent.mm" />
<intent>
<action android:name="android.intent.action.MAIN" />
</intent>
</queries>
<application <application
android:name=".application.TSApplication" android:name=".application.TSApplication"
@@ -13,24 +33,24 @@
android:theme="@style/Theme.TSBattery" android:theme="@style/Theme.TSBattery"
tools:ignore="AllowBackup"> tools:ignore="AllowBackup">
<!-- 是否是xposed模块 -->
<meta-data <meta-data
android:name="xposedmodule" android:name="xposedmodule"
android:value="true" /> android:value="true" />
<!-- 模块描述 -->
<meta-data <meta-data
android:name="xposeddescription" android:name="xposeddescription"
android:value="抵制毒瘤,拒绝疯狂耗电,Tencent 社交毒瘤一键省电模块(目前支持 QQ、TIM)通过干掉电源锁常驻减少电量消耗理论支持最新版本。by 酷安 @星夜不荟" /> android:value="Tencent 社交毒瘤一键省电模块。\n目前支持 QQ、TIM、微信\n开发者酷安 @星夜不荟" />
<!-- 最低xposed版本号 -->
<meta-data <meta-data
android:name="xposedminversion" android:name="xposedminversion"
android:value="82" /> android:value="93" />
<meta-data
android:name="xposedscope"
android:resource="@array/module_scope" />
<activity <activity
android:name="com.fankes.tsbattery.MainActivity" android:name="com.fankes.tsbattery.ui.activity.MainActivity"
android:label="@string/app_name"> android:exported="true"
android:screenOrientation="behind">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="de.robv.android.xposed.category.MODULE_SETTINGS" /> <category android:name="de.robv.android.xposed.category.MODULE_SETTINGS" />
@@ -40,8 +60,11 @@
<activity-alias <activity-alias
android:name="com.fankes.tsbattery.Home" android:name="com.fankes.tsbattery.Home"
android:enabled="true" android:enabled="true"
android:exported="true"
android:label="@string/app_name" android:label="@string/app_name"
android:targetActivity="com.fankes.tsbattery.MainActivity"> android:screenOrientation="behind"
android:targetActivity="com.fankes.tsbattery.ui.activity.MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />

View File

@@ -1 +0,0 @@
com.fankes.tsbattery.hook.HookMain

View File

@@ -1,226 +0,0 @@
/**
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
*
* This file is part of TSBattery.
*
* TSBattery is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TSBattery is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by fankes on 2021/9/4.
*/
@file:Suppress(
"DEPRECATION", "SetTextI18n", "SetWorldReadable", "WorldReadableFiles",
"LocalVariableName", "SameParameterValue"
)
package com.fankes.tsbattery
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SwitchCompat
import androidx.constraintlayout.utils.widget.ImageFilterView
import com.fankes.tsbattery.hook.HookMedium
import com.fankes.tsbattery.utils.FileUtils
import com.gyf.immersionbar.ImmersionBar
import java.io.File
class MainActivity : AppCompatActivity() {
companion object {
private const val moduleVersion = BuildConfig.VERSION_NAME
private const val moduleSupport = "QQ 8.5.5~8.8.50、TIM 2+"
/** 声明当前实例 */
var instance: MainActivity? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/** 设置自身实例 */
instance = this
setContentView(R.layout.activity_main)
/** 隐藏系统的标题栏 */
supportActionBar?.hide()
/** 初始化沉浸状态栏 */
ImmersionBar.with(this)
.statusBarColor(R.color.white)
.autoDarkModeEnable(false)
.statusBarDarkFont(true)
.navigationBarColor(R.color.white)
.navigationBarDarkIcon(true)
.fitsSystemWindows(true)
.init()
/** 判断 Hook 状态 */
if (isHooked()) {
findViewById<LinearLayout>(R.id.main_lin_status).setBackgroundResource(R.drawable.green_round)
findViewById<ImageFilterView>(R.id.main_img_status).setImageResource(R.mipmap.succcess)
findViewById<TextView>(R.id.main_text_status).text = "模块已激活"
/** 写入激活的模块版本 */
putString(HookMedium.ENABLE_MODULE_VERSION, moduleVersion)
} else
AlertDialog.Builder(this)
.setTitle("模块没有激活")
.setMessage(
"检测到模块没有激活,模块需要 Xposed 环境依赖,同时需要系统拥有 Root 权限(太极阴可以免 Root),请自行查看本页面使用帮助与说明第三条。\n" +
"太极、应用转生、梦境(Pine)和第三方 Xposed 激活后可能不会提示激活,若想验证是否激活请打开“提示模块运行信息”自行检查,如果生效就代表模块运行正常,这里的激活状态只是一个显示意义上的存在。\n" +
"太极(无极)在 MIUI 设备上会提示打开授权,请进行允许,然后再次打开本应用查看激活状态。"
)
.setPositiveButton("我知道了", null)
.setCancelable(false)
.show()
/** 设置文本 */
findViewById<TextView>(R.id.main_text_version).text = "当前版本:$moduleVersion"
findViewById<TextView>(R.id.main_text_support).text = "支持 $moduleSupport"
/** 初始化 View */
val protectModeSwitch = findViewById<SwitchCompat>(R.id.protect_mode_switch)
val hideIconInLauncherSwitch = findViewById<SwitchCompat>(R.id.hide_icon_in_launcher_switch)
val notifyModuleInfoSwitch = findViewById<SwitchCompat>(R.id.notify_module_info_switch)
/** 获取 Sp 存储的信息 */
protectModeSwitch.isChecked = getBoolean("_white_mode")
hideIconInLauncherSwitch.isChecked = getBoolean("_hide_icon")
notifyModuleInfoSwitch.isChecked = getBoolean("_tip_run_info")
protectModeSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
putBoolean(HookMedium.ENABLE_WHITE_MODE, b)
}
hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
putBoolean(HookMedium.ENABLE_HIDE_ICON, b)
packageManager.setComponentEnabledSetting(
ComponentName(this@MainActivity, "com.fankes.tsbattery.Home"),
if (b) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP
)
}
notifyModuleInfoSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
putBoolean(HookMedium.ENABLE_RUN_INFO, b)
}
/** 项目地址点击事件 */
findViewById<View>(R.id.link_with_project_address).setOnClickListener {
try {
startActivity(Intent().apply {
action = "android.intent.action.VIEW"
data = Uri.parse("https://github.com/fankes/TSBattery")
})
} catch (e: Exception) {
Toast.makeText(this, "无法启动系统默认浏览器", Toast.LENGTH_SHORT).show()
}
}
}
/**
* 判断模块是否激活
* @return 激活状态
*/
private fun isHooked() = HookMedium.isHooked()
override fun onResume() {
super.onResume()
setWorldReadable()
}
override fun onRestart() {
super.onRestart()
setWorldReadable()
}
override fun onPause() {
super.onPause()
setWorldReadable()
}
/**
* 获取保存的值
* @param key 名称
* @return 保存的值
*/
private fun getBoolean(key: String) =
getSharedPreferences(
packageName + "_preferences",
Context.MODE_PRIVATE
).getBoolean(key, false)
/**
* 保存值
* @param key 名称
* @param bool 值
*/
private fun putBoolean(key: String, bool: Boolean) {
getSharedPreferences(
packageName + "_preferences",
Context.MODE_PRIVATE
).edit().putBoolean(key, bool).apply()
setWorldReadable()
/** 延迟继续设置强制允许 SP 可读可写 */
Handler().postDelayed({ setWorldReadable() }, 500)
Handler().postDelayed({ setWorldReadable() }, 1000)
Handler().postDelayed({ setWorldReadable() }, 1500)
}
/**
* 保存值
* @param key 名称
* @param value 值
*/
private fun putString(key: String, value: String) {
getSharedPreferences(
packageName + "_preferences",
Context.MODE_PRIVATE
).edit().putString(key, value).apply()
setWorldReadable()
/** 延迟继续设置强制允许 SP 可读可写 */
Handler().postDelayed({ setWorldReadable() }, 500)
Handler().postDelayed({ setWorldReadable() }, 1000)
Handler().postDelayed({ setWorldReadable() }, 1500)
}
/**
* 强制设置 Sp 存储为全局可读可写
* 以供模块使用
*/
private fun setWorldReadable() {
try {
if (FileUtils.getDefaultPrefFile(this).exists()) {
for (file in arrayOf<File>(
FileUtils.getDataDir(this),
FileUtils.getPrefDir(this),
FileUtils.getDefaultPrefFile(this)
)) {
file.setReadable(true, false)
file.setExecutable(true, false)
}
}
} catch (e: Exception) {
Toast.makeText(this, "无法写入模块设置,请检查权限\n如果此提示一直显示,请不要双开模块", Toast.LENGTH_SHORT).show()
}
}
override fun onDestroy() {
super.onDestroy()
/** 销毁实例防止内存泄漏 */
instance = null
}
}

View File

@@ -1,35 +1,34 @@
/* /*
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com) * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
* *
* This file is part of TSBattery. * This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
* *
* TSBattery is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TSBattery is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2021/11/9. * This file is created by fankes on 2021/11/9.
*/ */
@file:Suppress("unused")
package com.fankes.tsbattery.application package com.fankes.tsbattery.application
import android.app.Application
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
class TSApplication : Application() { class TSApplication : ModuleApplication() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
/** 禁止系统夜间模式对自己造成干扰 - 模块要什么夜间模式?😅 (其实是我懒) */ /** 跟随系统夜间模式 */
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
} }
} }

View File

@@ -0,0 +1,74 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/9/29.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.fankes.tsbattery.const
import com.fankes.tsbattery.generated.AppProperties
import com.fankes.tsbattery.wrapper.BuildConfigWrapper
/**
* 包名常量定义类
*/
object PackageName {
/** QQ */
const val QQ = "com.tencent.mobileqq"
/** TIM */
const val TIM = "com.tencent.tim"
/** 微信 */
const val WECHAT = "com.tencent.mm"
}
/**
* 跳转常量定义类
*/
object JumpEvent {
/** 启动模块设置 */
const val OPEN_MODULE_SETTING = "tsbattery_open_module_settings"
}
/**
* 模块版本常量定义类
*/
object ModuleVersion {
/** 当前 GitHub 提交的 ID (CI 自动构建) */
const val GITHUB_COMMIT_ID = AppProperties.GITHUB_CI_COMMIT_ID
/** 版本名称 */
const val NAME = BuildConfigWrapper.VERSION_NAME
/** 版本号 */
const val CODE = BuildConfigWrapper.VERSION_CODE
/** 是否为 CI 自动构建版本 */
val isCiMode = GITHUB_COMMIT_ID.isNotBlank()
/** 当前版本名称后缀 */
val suffix = GITHUB_COMMIT_ID.let { if (it.isNotBlank()) "-$it" else "" }
override fun toString() = "$NAME$suffix($CODE)"
}

View File

@@ -0,0 +1,128 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/9/28.
*/
@file:Suppress("StaticFieldLeak")
package com.fankes.tsbattery.data
import android.content.Context
import android.widget.CompoundButton
import com.highcapable.yukihookapi.hook.factory.prefs
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookPrefsBridge
/**
* 全局配置存储控制类
*/
object ConfigData {
/** QQ、TIM 保守模式*/
const val ENABLE_QQ_TIM_PROTECT_MODE = "enable_qq_tim_protect_mode"
/** 自动关闭 QQ、TIM 的 CoreService */
const val ENABLE_KILL_QQ_TIM_CORESERVICE = "enable_kill_qq_tim_core_service"
/** 自动关闭 QQ、TIM 的 CoreService$KernelService */
const val ENABLE_KILLE_QQ_TIM_CORESERVICE_CHILD = "enable_kill_qq_tim_core_service_child"
/** 停用全部省电功能 (停用模块) */
const val DISABLE_ALL_HOOK = "disable_all_hook"
/** 当前的 [YukiHookPrefsBridge] */
private var prefs: YukiHookPrefsBridge? = null
/**
* 读取 [YukiHookPrefsBridge]
* @param key 键值名称
* @param value 键值内容
* @return [Boolean]
*/
private fun getBoolean(key: String, value: Boolean = false) = prefs?.getBoolean(key, value) ?: value
/**
* 存入 [YukiHookPrefsBridge]
* @param key 键值名称
* @param value 键值内容
*/
private fun putBoolean(key: String, value: Boolean = false) = prefs?.edit { putBoolean(key, value) }
/**
* 初始化 [YukiHookPrefsBridge]
* @param context 实例
*/
fun init(context: Context) {
prefs = context.prefs(name = "tsbattery_config").native()
}
/**
* 绑定到 [CompoundButton] 自动设置选中状态
* @param key 键值名称
* @param onChange 当改变时回调
*/
fun CompoundButton.bind(key: String, onChange: (Boolean) -> Unit = {}) {
isChecked = getBoolean(key)
setOnCheckedChangeListener { button, isChecked ->
if (button.isPressed) {
putBoolean(key, isChecked)
onChange(isChecked)
}
}
}
/**
* 是否启用 QQ、TIM 保守模式
* @return [Boolean]
*/
var isEnableQQTimProtectMode
get() = getBoolean(ENABLE_QQ_TIM_PROTECT_MODE)
set(value) {
putBoolean(ENABLE_QQ_TIM_PROTECT_MODE, value)
}
/**
* 是否启用自动关闭 QQ、TIM 的 CoreService
* @return [Boolean]
*/
var isEnableKillQQTimCoreService
get() = getBoolean(ENABLE_KILL_QQ_TIM_CORESERVICE)
set(value) {
putBoolean(ENABLE_KILL_QQ_TIM_CORESERVICE, value)
}
/**
* 是否启用自动关闭 QQ、TIM 的 CoreService$KernelService
* @return [Boolean]
*/
var isEnableKillQQTimCoreServiceChild
get() = getBoolean(ENABLE_KILLE_QQ_TIM_CORESERVICE_CHILD)
set(value) {
putBoolean(ENABLE_KILLE_QQ_TIM_CORESERVICE_CHILD, value)
}
/**
* 是否停用全部省电功能 (停用模块)
* @return [Boolean]
*/
var isDisableAllHook
get() = getBoolean(DISABLE_ALL_HOOK)
set(value) {
putBoolean(DISABLE_ALL_HOOK, value)
}
}

View File

@@ -0,0 +1,48 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/2/15.
*/
package com.fankes.tsbattery.hook
import com.fankes.tsbattery.const.PackageName
import com.fankes.tsbattery.hook.entity.QQTIMHooker
import com.fankes.tsbattery.hook.entity.WeChatHooker
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
import com.highcapable.yukihookapi.hook.factory.configs
import com.highcapable.yukihookapi.hook.factory.encase
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
@InjectYukiHookWithXposed
object HookEntry : IYukiHookXposedInit {
/** 是否完全支持当前版本 */
var isHookClientSupport = true
override fun onInit() = configs {
debugLog { tag = "TSBattery" }
isDebug = false
isEnableDataChannel = false
}
override fun onHook() = encase {
loadApp(PackageName.QQ, PackageName.TIM) { loadHooker(QQTIMHooker) }
loadApp(PackageName.WECHAT, WeChatHooker)
}
}

View File

@@ -1,405 +0,0 @@
/**
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
*
* This file is part of TSBattery.
*
* TSBattery is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TSBattery is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by fankes on 2021/9/4.
*/
@file:Suppress("DEPRECATION", "SameParameterValue")
package com.fankes.tsbattery.hook
import android.app.Activity
import android.app.AlertDialog
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.annotation.Keep
import com.fankes.tsbattery.utils.XPrefUtils
import de.robv.android.xposed.*
import de.robv.android.xposed.callbacks.XC_LoadPackage
import java.util.*
@Keep
class HookMain : IXposedHookLoadPackage {
/** 仅作用于替换的 Hook 方法体 */
private val replaceToNull = object : XC_MethodReplacement() {
override fun replaceHookedMethod(param: MethodHookParam?): Any? {
return null
}
}
/** 仅作用于替换的 Hook 方法体 */
private val replaceToTrue = object : XC_MethodReplacement() {
override fun replaceHookedMethod(param: MethodHookParam?): Any {
return true
}
}
/**
* 干掉目标方法体封装
* @param clazz 类名缩写
* @param name 方法名
*/
private fun XC_LoadPackage.LoadPackageParam.replaceToNull(clazz: String, name: String) {
XposedHelpers.findAndHookMethod(
"com.tencent.mobileqq.$clazz",
classLoader,
name,
replaceToNull
)
}
/**
* 这个类 BaseChatPie 是控制聊天界面的
* 里面有两个随机混淆的方法
* 这两个方法一个是挂起电源锁常驻亮屏
* 一个是停止常驻亮屏
* 不由分说每个版本混淆的方法名都会变
* 所以说每个版本重新适配 - 也可以提交分支帮我适配
* 8.8.17 版本是 bd be
* 8.8.23 版本是 bf bg
* 8.8.38 版本是 bi bj
* ⚠️ Hook 错了方法会造成闪退!
* @param version QQ 版本
*/
private fun XC_LoadPackage.LoadPackageParam.hookBaseChatPie(version: String) {
when (version) {
"8.8.17" -> {
replaceToNull("activity.aio.core.BaseChatPie", "bd")
replaceToNull("activity.aio.core.BaseChatPie", "be")
}
"8.8.23" -> {
replaceToNull("activity.aio.core.BaseChatPie", "bf")
replaceToNull("activity.aio.core.BaseChatPie", "bg")
}
"8.8.38" -> {
replaceToNull("activity.aio.core.BaseChatPie", "bi")
replaceToNull("activity.aio.core.BaseChatPie", "bj")
}
// JiZhi适配
"8.8.50" -> {
replaceToNull("activity.aio.core.BaseChatPie", "bj")//remainScreenOn
replaceToNull("activity.aio.core.BaseChatPie", "bk")//cancelRemainScreenOn
}
else -> logD("$version not supported!")
}
}
/**
* Print the log
* @param content
*/
private fun logD(content: String) {
XposedBridge.log(content)
Log.d("TSBattery", content)
}
/**
* Print the log
* @param content
*/
private fun logE(content: String, e: Throwable? = null) {
XposedBridge.log(content)
Log.e("TSBattery", content, e)
}
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
if (lpparam == null) return
when (lpparam.packageName) {
/** Hook 自身 */
"com.fankes.tsbattery" ->
XposedHelpers.findAndHookMethod(
"com.fankes.tsbattery.hook.HookMedium",
lpparam.classLoader,
"isHooked",
replaceToTrue
)
/** 经过测试 QQ 与 TIM 这两个是一个模子里面的东西,所以他们的类名也基本上是一样的 */
"com.tencent.mobileqq", "com.tencent.tim" -> {
try {
XposedHelpers.findAndHookMethod(
"android.os.PowerManager\$WakeLock",
lpparam.classLoader,
"acquire",
replaceToNull
)
} catch (e: Throwable) {
logE("handleLoadPackage: hook wakeLock acquire() Failed", e)
}
try {
XposedHelpers.findAndHookMethod(
"android.os.PowerManager\$WakeLock",
lpparam.classLoader,
"acquire",
Long::class.java,
replaceToNull
)
} catch (e: Throwable) {
logE("handleLoadPackage: hook wakeLock acquire(time) Failed", e)
}
/** 增加通知栏文本显示守护状态 */
try {
XposedHelpers.findAndHookMethod(
"android.app.Notification\$Builder",
lpparam.classLoader,
"setContentText",
CharSequence::class.java,
object : XC_MethodHook() {
override fun beforeHookedMethod(param: MethodHookParam?) {
when (param?.args?.get(0) as? CharSequence?) {
"QQ正在后台运行" ->
param.args?.set(0, "QQ正在后台运行 - TSBattery 守护中")
"TIM正在后台运行" ->
param.args?.set(0, "TIM正在后台运行 - TSBattery 守护中")
}
}
})
} catch (e: Throwable) {
logE("handleLoadPackage: hook Notification Failed", e)
}
/** 判断是否开启提示模块运行信息 */
if (XPrefUtils.getBoolean(HookMedium.ENABLE_RUN_INFO))
try {
/**
* Hook 启动界面的第一个 [Activity]
* QQ 和 TIM 都是一样的类
* 在里面加入提示运行信息的对话框测试模块是否激活
*/
XposedHelpers.findAndHookMethod(
"com.tencent.mobileqq.activity.SplashActivity",
lpparam.classLoader,
"doOnCreate",
Bundle::class.java,
object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam?) {
val self = param?.thisObject as? Activity ?: return
try {
AlertDialog.Builder(
self,
android.R.style.Theme_Material_Light_Dialog
).setCancelable(false)
.setTitle("TSBattery 已激活")
.setMessage(
"[提示模块运行信息功能已打开]\n" +
"模块工作看起来一切正常,请自行测试是否能达到省电效果。\n\n" +
"已生效模块版本:${XPrefUtils.getString(HookMedium.ENABLE_MODULE_VERSION)}\n" +
"当前模式:${if (XPrefUtils.getBoolean(HookMedium.ENABLE_WHITE_MODE)) "保守模式" else "完全模式"}" +
"\n\n包名:${self.packageName}\n版本:${
self.packageManager.getPackageInfo(
self.packageName,
0
).versionName
}(${
self.packageManager.getPackageInfo(
self.packageName,
0
).versionCode
})" + "\n\nPS模块只对挂后台锁屏情况下有省电效果请不要将过多的群提醒消息通知打开这样子在使用过程时照样会极其耗电\n" +
"如果你不想看到此提示。请在模块设置中关闭“提示模块运行信息”,此设置默认关闭。\n" +
"开发者 酷安 @星夜不荟\n未经允许禁止转载、修改或复制我的劳动成果。"
)
.setPositiveButton("我知道了", null)
.show()
} catch (e: Exception) {
Toast.makeText(
self,
"模块已激活,但显示信息弹窗失败了\n$e",
Toast.LENGTH_SHORT
).show()
}
}
})
} catch (e: Exception) {
logE("handleLoadPackage: hook SplashActivity Failed", e)
}
/** 关闭保守模式后不再仅仅作用于系统电源锁 */
if (!XPrefUtils.getBoolean(HookMedium.ENABLE_WHITE_MODE)) {
try {
/** 通过在 SplashActivity 里取到应用的版本号 */
XposedHelpers.findAndHookMethod(
"com.tencent.mobileqq.activity.SplashActivity",
lpparam.classLoader,
"doOnCreate",
Bundle::class.java,
object : XC_MethodHook() {
override fun beforeHookedMethod(param: MethodHookParam?) {
val self = param?.thisObject as? Activity ?: return
val name = self.packageName
val version =
self.packageManager.getPackageInfo(name, 0).versionName
/** 这个地方我们只处理 QQ */
try {
if (name == "com.tencent.mobileqq")
lpparam.hookBaseChatPie(version)
} catch (e: Exception) {
logE("handleLoadPackage: hook BaseChatPie Failed", e)
}
}
})
} catch (e: Exception) {
logE("handleLoadPackage: hook BaseChatPie(first time) Failed", e)
}
try {
/**
* 一个不知道是什么作用的电源锁
* 同样直接干掉
*/
XposedHelpers.findAndHookMethod(
"com.tencent.mars.ilink.comm.WakerLock",
lpparam.classLoader,
"lock", Long::class.java,
replaceToNull
)
} catch (e: Exception) {
logE("handleLoadPackage: hook WakerLock Failed", e)
}
try {
/**
* Hook 掉一个一像素保活 [Activity] 真的我怎么都想不到讯哥的程序员做出这种事情
* 这个东西经过测试会在锁屏的时候吊起来,解锁的时候自动 finish(),无限耍流氓耗电
*/
XposedHelpers.findAndHookMethod(
"com.tencent.mobileqq.activity.QQLSUnlockActivity",
lpparam.classLoader,
"onCreate", Bundle::class.java,
object : XC_MethodHook() {
private var origDevice = ""
override fun beforeHookedMethod(param: MethodHookParam?) {
/** 由于在 onCreate 里有一行判断只要型号是 xiaomi 的设备就开电源锁,所以说这里临时替换成菊花厂 */
origDevice = Build.MANUFACTURER
if (Build.MANUFACTURER.toLowerCase(Locale.ROOT) == "xiaomi")
XposedHelpers.setStaticObjectField(
Build::class.java,
"MANUFACTURER",
"HUAWEI"
)
}
override fun afterHookedMethod(param: MethodHookParam?) {
(param?.thisObject as? Activity)?.finish()
/** 这里再把型号替换回去 - 不影响应用变量等 Xposed 模块修改的型号 */
XposedHelpers.setStaticObjectField(
Build::class.java,
"MANUFACTURER",
origDevice
)
}
}
)
/**
* 这个东西同上,不知道是啥时候调用
* 反正也是一个一像素保活的 [Activity]
* 讯哥的程序员真的有你的
*/
XposedHelpers.findAndHookMethod(
"com.tencent.mobileqq.activity.QQLSActivity\$14",
lpparam.classLoader,
"run",
replaceToNull
)
} catch (e: Exception) {
logE("handleLoadPackage: hook QQLSActivity Failed", e)
}
try {
/**
* 这个是毒瘤核心类
* WakeLockMonitor
* 这个名字真的起的特别诗情画意
* 带给用户的却是 shit 一样的体验
* 里面有各种使用 Handler 和 Timer 的各种耗时常驻后台耗电办法持续接收消息
* 直接循环全部方法全部干掉
*/
lpparam.classLoader.loadClass("com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor")
.apply {
val lockClazz =
lpparam.classLoader.loadClass("com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor\$WakeLockEntity")
val hookClazz =
lpparam.classLoader.loadClass("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam")
val onHook = getDeclaredMethod(
"onHook",
String::class.java,
Any::class.java,
java.lang.reflect.Array.newInstance(
Any::class.java,
0
).javaClass,
Any::class.java
).apply { isAccessible = true }
val doReport =
getDeclaredMethod(
"doReport",
lockClazz,
Int::class.java
).apply {
isAccessible = true
}
val afterHookedMethod =
getDeclaredMethod(
"afterHookedMethod",
hookClazz
).apply { isAccessible = true }
val beforeHookedMethod =
getDeclaredMethod("beforeHookedMethod", hookClazz).apply {
isAccessible = true
}
val onAppBackground =
getDeclaredMethod("onAppBackground").apply {
isAccessible = true
}
val onOtherProcReport =
getDeclaredMethod(
"onOtherProcReport",
Bundle::class.java
).apply { isAccessible = true }
val onProcessRun30Min =
getDeclaredMethod("onProcessRun30Min").apply {
isAccessible = true
}
val onProcessBG5Min =
getDeclaredMethod("onProcessBG5Min").apply {
isAccessible = true
}
val writeReport =
getDeclaredMethod(
"writeReport",
Boolean::class.java
).apply { isAccessible = true }
XposedBridge.hookMethod(onHook, replaceToNull)
XposedBridge.hookMethod(doReport, replaceToNull)
XposedBridge.hookMethod(afterHookedMethod, replaceToNull)
XposedBridge.hookMethod(beforeHookedMethod, replaceToNull)
XposedBridge.hookMethod(onAppBackground, replaceToNull)
XposedBridge.hookMethod(onOtherProcReport, replaceToNull)
XposedBridge.hookMethod(onProcessRun30Min, replaceToNull)
XposedBridge.hookMethod(onProcessBG5Min, replaceToNull)
XposedBridge.hookMethod(writeReport, replaceToNull)
}
} catch (e: Throwable) {
logE("handleLoadPackage: hook WakerLockMonitor Failed", e)
}
logD("handleLoadPackage: hook Complete!")
}
}
}
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
*
* This file is part of TSBattery.
*
* TSBattery is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TSBattery is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by fankes on 2021/11/9.
*/
package com.fankes.tsbattery.hook
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.annotation.Keep
import com.fankes.tsbattery.MainActivity
@Keep
object HookMedium {
const val ENABLE_HIDE_ICON = "_hide_icon"
const val ENABLE_RUN_INFO = "_tip_run_info"
const val ENABLE_WHITE_MODE = "_white_mode"
const val ENABLE_MODULE_VERSION = "_module_version"
/**
* 判断模块是否激活
* 在 [HookMain] 中 Hook 掉此方法
* @return 激活状态
*/
fun isHooked(): Boolean {
Log.d("TSBattery", "isHooked: true")
return isExpModuleActive()
}
/**
* 新增太极判断方式
* @return 是否激活
*/
private fun isExpModuleActive(): Boolean {
var isExp = false
MainActivity.instance?.also {
try {
val uri = Uri.parse("content://me.weishu.exposed.CP/")
var result: Bundle? = null
try {
result = it.contentResolver.call(uri, "active", null, null)
} catch (e: RuntimeException) {
// TaiChi is killed, try invoke
try {
val intent = Intent("me.weishu.exp.ACTION_ACTIVE")
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
it.startActivity(intent)
} catch (e1: Throwable) {
return false
}
}
if (result == null) result = it.contentResolver.call(uri, "active", null, null)
if (result == null) return false
isExp = result.getBoolean("active", false)
} catch (ignored: Throwable) {
}
}
return isExp
}
}

View File

@@ -0,0 +1,580 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/9/29.
*/
@file:Suppress("ConstPropertyName")
package com.fankes.tsbattery.hook.entity
import android.app.Activity
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.os.Message
import android.view.View
import android.view.ViewGroup
import androidx.core.app.ServiceCompat
import androidx.fragment.app.Fragment
import com.fankes.tsbattery.R
import com.fankes.tsbattery.const.ModuleVersion
import com.fankes.tsbattery.const.PackageName
import com.fankes.tsbattery.data.ConfigData
import com.fankes.tsbattery.hook.HookEntry
import com.fankes.tsbattery.hook.factory.hookSystemWakeLock
import com.fankes.tsbattery.hook.factory.isQQNightMode
import com.fankes.tsbattery.hook.factory.jumpToModuleSettings
import com.fankes.tsbattery.hook.factory.startModuleSettings
import com.fankes.tsbattery.hook.helper.DexKitHelper
import com.fankes.tsbattery.utils.factory.appVersionName
import com.fankes.tsbattery.utils.factory.dp
import com.highcapable.kavaref.KavaRef.Companion.asResolver
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.kavaref.extension.ArrayClass
import com.highcapable.kavaref.extension.VariousClass
import com.highcapable.kavaref.extension.classOf
import com.highcapable.kavaref.extension.createInstanceAsTypeOrNull
import com.highcapable.kavaref.extension.createInstanceOrNull
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
import com.highcapable.yukihookapi.hook.log.YLog
import java.lang.reflect.Method
import java.lang.reflect.Proxy
/**
* Hook QQ、TIM
*/
object QQTIMHooker : YukiBaseHooker() {
/** QQ、TIM 存在的类 */
const val JumpActivityClassName = "${PackageName.QQ}.activity.JumpActivity"
/** QQ、TIM 存在的类 */
private val JumpActivityClass by lazyClassOrNull(JumpActivityClassName)
/** QQ、TIM 存在的类 (NT 版本不再存在) */
private val QQSettingSettingActivityClass by lazyClassOrNull("${PackageName.QQ}.activity.QQSettingSettingActivity")
/** QQ 新版存在的类 (Pad 模式 - NT 版本不再存在) */
private val QQSettingSettingFragmentClass by lazyClassOrNull("${PackageName.QQ}.fragment.QQSettingSettingFragment")
/** QQ、TIM 存在的类 (NT 版本不再存在) */
private val AboutActivityClass by lazyClassOrNull("${PackageName.QQ}.activity.AboutActivity")
/** QQ 新版本存在的类 */
private val GeneralSettingActivityClass by lazyClassOrNull("${PackageName.QQ}.activity.GeneralSettingActivity")
/** QQ 新版本 (NT) 存在的类 */
private val MainSettingFragmentClass by lazyClassOrNull("${PackageName.QQ}.setting.main.MainSettingFragment")
/** QQ 新版本 (NT) 存在的类 */
private val MainSettingConfigProviderClass by lazyClassOrNull("${PackageName.QQ}.setting.main.MainSettingConfigProvider")
/** QQ、TIM 新版本存在的类 */
private val FormSimpleItemClass by lazyClassOrNull("${PackageName.QQ}.widget.FormSimpleItem")
/** QQ、TIM 旧版本存在的类 */
private val FormCommonSingleLineItemClass by lazyClassOrNull("${PackageName.QQ}.widget.FormCommonSingleLineItem")
/** QQ、TIM 存在的类 */
private val CoreServiceClass by lazyClassOrNull("${PackageName.QQ}.app.CoreService")
/** QQ、TIM 存在的类 */
private val CoreService_KernelServiceClass by lazyClassOrNull("${PackageName.QQ}.app.CoreService\$KernelService")
/** 根据多个版本存的不同的类 */
private val BaseChatPieClass by lazyClassOrNull(
VariousClass(
"${PackageName.QQ}.activity.aio.core.BaseChatPie",
"${PackageName.QQ}.activity.BaseChatPie"
)
)
/** 是否存在 [BaseChatPieClass] */
private val hasBaseChatPieClass by lazy { BaseChatPieClass != null }
/**
* DexKit 搜索结果数据实现类
*/
private object DexKitData {
var BaseChatPie_RemainScreenOnMethod: Method? = null
var BaseChatPie_CancelRemainScreenOnMethod: Method? = null
var SimpleItemProcessorClass: Class<*>? = null
var SimpleItemProcessorClass_OnClickMethod: Method? = null
}
/** 一个内部进程的名称 (与 X5 浏览器内核有关) */
private val privilegedProcessName = "$packageName:privileged_process"
/** 默认的 [Configuration] */
var baseConfiguration: Configuration? = null
/**
* 当前是否为 QQ
* @return [Boolean]
*/
private val isQQ get() = packageName == PackageName.QQ
/**
* 当前是否为 QQ 的 NT 版本
*
* 在 QQ NT 中 [AboutActivityClass] 已被移除 - 以此作为判断条件
* @return [Boolean]
*/
private val isQQNTVersion get() = AboutActivityClass == null
/** 当前宿主的版本 */
private var hostVersionName = "<unknown>"
/**
* 通过 [Activity] or [Fragment] 实例得到上下文
* @return [Activity] or null
*/
private fun Any.compatToActivity() = if (this !is Activity)
asResolver().optional().firstMethodOrNull { name = "getActivity"; superclass() }?.invoke()
else this
/** 使用 DexKit 进行搜索 */
private fun searchUsingDexKit() {
val classLoader = appClassLoader ?: return
DexKitHelper.create(this) {
BaseChatPieClass?.name?.also { baseChatPieClassName ->
DexKitData.BaseChatPie_RemainScreenOnMethod =
findMethod {
matcher {
declaredClass(baseChatPieClassName)
usingStrings("remainScreenOn")
paramCount = 0
returnType = Void.TYPE.name
}
}.singleOrNull()?.getMethodInstance(classLoader)
DexKitData.BaseChatPie_CancelRemainScreenOnMethod =
findMethod {
matcher {
declaredClass(baseChatPieClassName)
usingStrings("cancelRemainScreenOn")
paramCount = 0
returnType = Void.TYPE.name
}
}.singleOrNull()?.getMethodInstance(classLoader)
}
val kotlinFunction0 = "kotlin.jvm.functions.Function0"
findClass {
searchPackages("${PackageName.QQ}.setting.processor")
matcher {
methods {
add {
name = "<init>"
paramTypes(classOf<Context>().name, classOf<Int>().name, classOf<CharSequence>().name, classOf<Int>().name)
}
add {
paramTypes(kotlinFunction0)
returnType = Void.TYPE.name
}
}
fields { count(6..Int.MAX_VALUE) }
}
}.singleOrNull()?.name?.also { className ->
DexKitData.SimpleItemProcessorClass = className.toClass()
DexKitData.SimpleItemProcessorClass_OnClickMethod =
findMethod {
matcher {
declaredClass = className
paramTypes(kotlinFunction0)
returnType = Void.TYPE.name
usingNumbers(2)
}
}.singleOrNull()?.getMethodInstance(classLoader)
}
}
}
/**
* 这个类 QQ 的 BaseChatPie 是控制聊天界面的
*
* 里面有两个随机混淆的方法 ⬇
*
* remainScreenOn、cancelRemainScreenOn
*
* 这两个方法一个是挂起电源锁常驻亮屏 - 一个是停止常驻亮屏
*
* - 在 QQ NT 版本中完全移除了 BaseChatPie 类 - 所以不再处理
*/
private fun hookQQBaseChatPie() {
if (hasBaseChatPieClass.not()) {
HookEntry.isHookClientSupport = true
return YLog.debug("Start for QQ NT version,.")
}
/**
* 打印警告信息
* @param index 序号
*/
fun warn(index: Int) {
HookEntry.isHookClientSupport = false
YLog.warn("$hostVersionName [$index] not support!")
}
DexKitData.BaseChatPie_RemainScreenOnMethod?.hook()?.intercept() ?: warn(index = 0)
DexKitData.BaseChatPie_CancelRemainScreenOnMethod?.hook()?.intercept() ?: warn(index = 1)
}
/** Hook CoreService QQ、TIM */
private fun hookCoreService() {
CoreServiceClass?.resolve()?.optional()?.apply {
if (isQQ) {
firstMethodOrNull {
name = "startTempService"
}?.hook()?.intercept()
firstMethodOrNull {
name = "startCoreService"
parameters(Boolean::class)
}?.hook()?.intercept()
firstMethodOrNull {
name = "onStartCommand"
parameters(Intent::class, Int::class, Int::class)
}?.hook()?.replaceTo(any = 2)
}
firstMethodOrNull {
name = "onCreate"
}?.hook()?.after {
if (ConfigData.isEnableKillQQTimCoreService)
instance<Service>().apply {
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf()
YLog.debug("Shutdown CoreService OK!")
}
}
}
CoreService_KernelServiceClass?.resolve()?.optional()?.apply {
firstMethodOrNull {
name = "onCreate"
}?.hook()?.after {
if (ConfigData.isEnableKillQQTimCoreServiceChild)
instance<Service>().apply {
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf()
YLog.debug("Shutdown CoreService\$KernelService OK!")
}
}
firstMethodOrNull {
name = "onStartCommand"
parameters(Intent::class, Int::class, Int::class)
}?.hook()?.replaceTo(any = 2)
}
}
/** Hook QQ 不省电的功能 */
private fun hookQQDisgusting() {
if (isQQ.not()) return
/**
* 干掉消息收发功能的电源锁
* 每个版本的差异暂未做排查
* 旧版本理论上没有这个类
*/
"${PackageName.QQ}.msf.service.y".toClassOrNull()
?.resolve()
?.optional(silent = true)
?.firstMethodOrNull {
name = "a"
parameters(String::class, Long::class)
returnType = Void.TYPE
}?.hook()?.intercept()
/**
* 干掉自动上传服务的电源锁
* 每个版本的差异暂未做排查
*/
"com.tencent.upload.impl.UploadServiceImpl".toClassOrNull()
?.resolve()
?.optional(silent = true)
?.firstMethodOrNull {
name = "acquireWakeLockIfNot"
}?.hook()?.intercept()
/**
* Hook 掉一个一像素保活 Activity 真的我怎么都想不到讯哥的程序员做出这种事情
* 这个东西经过测试会在锁屏的时候吊起来,解锁的时候自动 finish(),无限耍流氓耗电
* 2022/1/25 后期查证:锁屏界面消息快速回复窗口的解锁后拉起保活界面,也是毒瘤
*/
"${PackageName.QQ}.activity.QQLSUnlockActivity".toClassOrNull()
?.resolve()
?.optional(silent = true)
?.firstMethodOrNull {
name = "onCreate"
parameters(Bundle::class)
}?.hook {
var origDevice = ""
before {
/** 由于在 onCreate 里有一行判断只要型号是 xiaomi 的设备就开电源锁,所以说这里临时替换成菊花厂 */
origDevice = Build.MANUFACTURER
if (Build.MANUFACTURER.lowercase() == "xiaomi")
Build::class.resolve().firstField { name = "MANUFACTURER" }.set("HUAWEI")
}
after {
instance<Activity>().finish()
/** 这里再把型号替换回去 - 不影响应用变量等 Xposed 模块修改的型号 */
Build::class.resolve().firstField { name = "MANUFACTURER" }.set(origDevice)
}
}
/**
* 这个东西同上
* 反正也是一个一像素保活的 Activity
* 讯哥的程序员真的有你的
* 2022/1/25 后期查证:锁屏界面消息快速回复窗口
*/
VariousClass("${PackageName.QQ}.activity.QQLSActivity\$14", "ktq").toClassOrNull()
?.resolve()
?.optional(silent = true)
?.firstMethodOrNull {
name = "run"
}?.hook()?.intercept()
/**
* 这个是毒瘤核心类
* WakeLockMonitor
* 这个名字真的起的特别诗情画意
* 带给用户的却是 shit 一样的体验
* 里面有各种使用 Handler 和 Timer 的各种耗时常驻后台耗电办法持续接收消息
* 直接循环全部方法全部干掉
* 👮🏻 经过排查 Play 版本没这个类...... Emmmm 不想说啥了
* ✅ 备注8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类
*/
"com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor".toClassOrNull()
?.resolve()
?.optional(silent = true)
?.apply {
firstMethodOrNull {
name = "onHook"
parameters(String::class, Any::class, ArrayClass(Any::class), Any::class)
}?.hook()?.intercept()
firstMethodOrNull {
name = "doReport"
parameters("com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor\$WakeLockEntity", Int::class)
}?.hook()?.intercept()
firstMethodOrNull {
name = "afterHookedMethod"
parameters("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam")
}?.hook()?.intercept()
firstMethodOrNull {
name = "beforeHookedMethod"
parameters("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam")
}?.hook()?.intercept()
firstMethodOrNull {
name = "onAppBackground"
}?.hook()?.intercept()
firstMethodOrNull {
name = "onOtherProcReport"
parameters(Bundle::class)
}?.hook()?.intercept()
firstMethodOrNull {
name = "onProcessRun30Min"
}?.hook()?.intercept()
firstMethodOrNull {
name = "onProcessBG5Min"
}?.hook()?.intercept()
firstMethodOrNull {
name = "writeReport"
parameters(Boolean::class)
}?.hook()?.intercept()
}
/**
* 这个是毒瘤核心操作类
* 功能同上、全部拦截
* 👮🏻 经过排查 Play 版本也没这个类...... Emmmm 不想说啥了
* ✅ 备注8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类
*/
"com.tencent.qapmsdk.qqbattery.QQBatteryMonitor".toClassOrNull()
?.resolve()
?.optional(silent = true)
?.apply {
firstMethodOrNull {
name = "start"
}?.hook()?.intercept()
firstMethodOrNull {
name = "stop"
}?.hook()?.intercept()
firstMethodOrNull {
name = "handleMessage"
parameters(Message::class)
}?.hook()?.intercept()
firstMethodOrNull {
name = "startMonitorInner"
}?.hook()?.intercept()
firstMethodOrNull {
name = "onAppBackground"
}?.hook()?.intercept()
firstMethodOrNull {
name = "onAppForeground"
}?.hook()?.intercept()
firstMethodOrNull {
name = "setLogWhite"
parameterCount = 2
}?.hook()?.intercept()
firstMethodOrNull {
name = "setCmdWhite"
parameterCount = 2
}?.hook()?.intercept()
firstMethodOrNull {
name = "onWriteLog"
parameters(String::class, String::class)
}?.hook()?.intercept()
firstMethodOrNull {
name = "onCmdRequest"
parameters(String::class)
}?.hook()?.intercept()
firstMethodOrNull {
name = "addData"
parameterCount = 4
}?.hook()?.intercept()
firstMethodOrNull {
name = "onGpsScan"
parameterCount = 2
}?.hook()?.intercept()
}
}
/** Hook QQ 的设置界面添加模块设置入口 (新版) */
private fun hookQQSettingsUi() {
if (MainSettingFragmentClass == null) return YLog.error("Could not found main setting class, hook aborted")
val kotlinUnit = "kotlin.Unit"
val simpleItemProcessorClass = DexKitData.SimpleItemProcessorClass ?: return YLog.error("Could not found processor class, hook aborted")
/**
* 创建入口点条目
* @param context 当前实例
* @return [Any]
*/
fun createTSEntryItem(context: Context): Any {
/** 为了使用图标资源 ID - 这里需要重新注入模块资源防止不生效 */
context.injectModuleAppResources()
val iconResId = if (context.isQQNightMode()) R.mipmap.ic_tsbattery_entry_night else R.mipmap.ic_tsbattery_entry_day
return simpleItemProcessorClass.createInstanceOrNull(context, R.id.tsbattery_qq_entry_item_id, "TSBattery", iconResId)?.also { item ->
val onClickMethod = DexKitData.SimpleItemProcessorClass_OnClickMethod ?: error("Could not found processor method")
val proxyOnClick = Proxy.newProxyInstance(appClassLoader, arrayOf(onClickMethod.parameterTypes[0])) { any, method, args ->
if (method.name == "invoke") {
context.startModuleSettings()
kotlinUnit.toClass().resolve().firstField { name = "INSTANCE" }.get()
} else method.invoke(any, args)
}; onClickMethod.invoke(item, proxyOnClick)
} ?: error("Could not create TSBattery entry item")
}
MainSettingConfigProviderClass?.resolve()?.optional()?.firstMethodOrNull {
parameters(Context::class)
returnType = List::class
}?.hook()?.after {
val context = args().first().cast<Context>() ?: return@after
val processor = result<MutableList<Any?>>() ?: return@after
processor.add(
1,
processor[0]?.javaClass?.createInstanceOrNull(
arrayListOf<Any>().apply {
add(createTSEntryItem(context))
}.toList(), "", ""
)
)
}
}
/**
* Hook QQ 的设置界面添加模块设置入口 (旧版)
* @param instance 当前设置界面实例
*/
private fun hookQQSettingsUiLegacy(instance: Any?) {
/** 当前的顶级 Item 实例 */
val formItemRefRoot = instance?.asResolver()?.optional()?.lastFieldOrNull {
type { it == FormSimpleItemClass || it == FormCommonSingleLineItemClass }
}?.get<View>()
/** 创建一个新的 Item */
val item = FormSimpleItemClass?.createInstanceAsTypeOrNull<View>(instance?.compatToActivity())
item?.asResolver()?.optional()?.apply {
firstMethodOrNull {
name = "setLeftText"
parameters(CharSequence::class)
}?.invoke("TSBattery")
firstMethodOrNull {
name = "setRightText"
parameters(CharSequence::class)
}?.invoke(ModuleVersion.toString())
firstMethodOrNull {
name = "setBgType"
parameters(Int::class)
}?.invoke(if (isQQ) 0 else 2)
}
item ?: return
item.setOnClickListener { it.context.startModuleSettings() }
var listGroup = formItemRefRoot?.parent as? ViewGroup?
val lparam = (if (listGroup?.childCount == 1) {
listGroup = listGroup.parent as? ViewGroup
(formItemRefRoot?.parent as? View?)?.layoutParams
} else formItemRefRoot?.layoutParams)
?: ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
/** 设置圆角和间距 */
if (isQQ) (lparam as? ViewGroup.MarginLayoutParams?)?.setMargins(0, 15.dp(item.context), 0, 0)
/** 将 Item 添加到设置界面 */
listGroup?.also { if (isQQ) it.addView(item, lparam) else it.addView(item, 0, lparam) }
}
override fun onHook() {
searchUsingDexKit()
onAppLifecycle(isOnFailureThrowToApp = false) {
attachBaseContext { baseContext, hasCalledSuper ->
if (hasCalledSuper.not()) baseConfiguration = baseContext.resources.configuration
}
onCreate {
hostVersionName = appVersionName
/** 不注入此进程防止部分系统发生 X5 浏览器内核崩溃问题 */
if (processName.startsWith(privilegedProcessName)) return@onCreate
ConfigData.init(context = this)
registerModuleAppActivities(when {
isQQNTVersion -> GeneralSettingActivityClass
else -> AboutActivityClass
})
if (ConfigData.isDisableAllHook) return@onCreate
hookSystemWakeLock()
hookQQBaseChatPie()
hookCoreService()
hookQQDisgusting()
YLog.info("All processes are completed for \"${processName.takeIf { it != packageName } ?: packageName}\"")
}
}
/** 仅注入主进程 */
withProcess(mainProcessName) {
/** Hook 跳转事件 */
JumpActivityClass?.resolve()?.optional()?.firstMethodOrNull {
name = "doOnCreate"
parameters(Bundle::class)
}?.hook()?.after { instance<Activity>().jumpToModuleSettings() }
/** Hook 设置界面入口点 */
if (isQQNTVersion) hookQQSettingsUi()
else {
/** 将条目注入设置界面 (Activity) */
QQSettingSettingActivityClass?.resolve()?.optional()?.firstMethodOrNull {
name = "doOnCreate"
parameters(Bundle::class)
}?.hook()?.after { hookQQSettingsUiLegacy(instance) }
/** 将条目注入设置界面 (Fragment) */
QQSettingSettingFragmentClass?.resolve()?.optional()?.firstMethodOrNull {
name = "doOnCreateView"
parameterCount = 3
}?.hook()?.after { hookQQSettingsUiLegacy(instance) }
}
}
}
}

View File

@@ -0,0 +1,144 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/9/29.
*/
@file:Suppress("ConstPropertyName")
package com.fankes.tsbattery.hook.entity
import android.app.Activity
import android.content.Context
import android.os.Build
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.core.content.res.ResourcesCompat
import com.fankes.tsbattery.R
import com.fankes.tsbattery.const.PackageName
import com.fankes.tsbattery.data.ConfigData
import com.fankes.tsbattery.hook.factory.hookSystemWakeLock
import com.fankes.tsbattery.hook.factory.jumpToModuleSettings
import com.fankes.tsbattery.hook.factory.startModuleSettings
import com.fankes.tsbattery.utils.factory.absoluteStatusBarHeight
import com.fankes.tsbattery.utils.factory.appVersionCode
import com.fankes.tsbattery.utils.factory.appVersionName
import com.fankes.tsbattery.utils.factory.dp
import com.highcapable.kavaref.KavaRef.Companion.asResolver
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
import com.highcapable.yukihookapi.hook.factory.processName
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
import com.highcapable.yukihookapi.hook.log.YLog
/**
* Hook 微信
*
* 具体功能还在画饼
*/
object WeChatHooker : YukiBaseHooker() {
/** 微信存在的类 - 未测试每个版本是否都存在 */
const val LauncherUIClassName = "${PackageName.WECHAT}.ui.LauncherUI"
/** 微信存在的类 - 未测试每个版本是否都存在 */
private val LauncherUIClass by lazyClassOrNull("${PackageName.WECHAT}.ui.LauncherUI")
/** 微信存在的类 - 未测试每个版本是否都存在 */
private val EmptyActivityClass by lazyClassOrNull("${PackageName.WECHAT}.ui.EmptyActivity")
/** 微信存在的类 - 未测试每个版本是否都存在 */
private val WelabMainUIClass by lazyClassOrNull("${PackageName.WECHAT}.plugin.welab.ui.WelabMainUI")
/** 微信存在的类 - 未测试每个版本是否都存在 */
private val SettingsUIClass by lazyClassOrNull("${PackageName.WECHAT}.plugin.setting.ui.setting.SettingsUI")
/** TSBattery 图标 TAG 名称 */
private const val TSBARRERY_ICON_TAG = "tsbattery_icon"
/**
* 创建 TSBattery 图标
* @param context 当前实例
* @return [LinearLayout]
*/
private fun createPreferenceIcon(context: Context) = LinearLayout(context).apply {
tag = TSBARRERY_ICON_TAG
gravity = Gravity.END or Gravity.BOTTOM
layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
addView(ImageView(context).apply {
layoutParams = ViewGroup.MarginLayoutParams(20.dp(context), 20.dp(context)).apply {
topMargin = context.absoluteStatusBarHeight + 15.dp(context)
rightMargin = 20.dp(context)
}
setColorFilter(ResourcesCompat.getColor(resources, R.color.colorTextGray, null))
setImageResource(R.drawable.ic_icon)
if (Build.VERSION.SDK_INT >= 26) tooltipText = "TSBattery 设置"
setOnClickListener { context.startModuleSettings() }
})
}
override fun onHook() {
onAppLifecycle {
onCreate {
ConfigData.init(context = this)
registerModuleAppActivities(when {
EmptyActivityClass != null -> EmptyActivityClass
WelabMainUIClass != null -> WelabMainUIClass
else -> error("Inject WeChat Activity Proxy failed, unsupport version $appVersionName($appVersionCode)")
})
if (ConfigData.isDisableAllHook) return@onCreate
hookSystemWakeLock()
YLog.info("All processes are completed for \"${processName.takeIf { it != packageName } ?: packageName}\"")
}
}
/** 仅注入主进程 */
withProcess(mainProcessName) {
/** Hook 跳转事件 */
LauncherUIClass?.resolve()?.optional()?.firstMethodOrNull {
name = "onResume"
emptyParameters()
}?.hook()?.after { instance<Activity>().jumpToModuleSettings(isFinish = false) }
/** 向设置界面右上角添加按钮 */
SettingsUIClass?.resolve()?.optional()?.firstMethodOrNull {
name = "onResume"
emptyParameters()
}?.hook()?.after {
SettingsUIClass?.resolve()?.optional()?.firstMethodOrNull {
name = "get_fragment"
emptyParameters()
superclass()
}?.of(instance)?.invoke()?.asResolver()?.optional()?.firstMethodOrNull {
name = "getView"
emptyParameters()
returnType = View::class
superclass()
}?.invoke<ViewGroup?>()?.also {
it.context?.injectModuleAppResources()
runCatching { it.getChildAt(0) as? ViewGroup? }.getOrNull()?.also { rootView ->
if (rootView.findViewWithTag<View>(TSBARRERY_ICON_TAG) == null)
rootView.addView(createPreferenceIcon(it.context))
}
}
}
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/9/29.
*/
package com.fankes.tsbattery.hook.factory
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.PowerManager
import androidx.appcompat.app.AppCompatDelegate
import com.fankes.tsbattery.const.JumpEvent
import com.fankes.tsbattery.const.PackageName
import com.fankes.tsbattery.hook.entity.QQTIMHooker.toClass
import com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
import com.highcapable.kavaref.KavaRef.Companion.asResolver
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.kavaref.extension.VariousClass
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.param.PackageParam
import kotlin.system.exitProcess
/** QQ、TIM 存在的类 */
private const val MobileQQClass = "mqq.app.MobileQQ"
/** QQ、TIM 存在的类 */
private val ThemeUtilClass = VariousClass("${PackageName.QQ}.theme.ThemeUtil", "${PackageName.QQ}.vas.theme.api.ThemeUtil")
/**
* QQ、TIM 主题是否为夜间模式
* @return [Boolean]
*/
fun Context.isQQNightMode(): Boolean {
val sMobileQQ = MobileQQClass.toClass(classLoader).resolve()
.optional()
.firstFieldOrNull {
name = "sMobileQQ"
superclass()
}?.get()
val mAppRuntime = sMobileQQ?.asResolver()
?.optional()
?.firstFieldOrNull {
name = "mAppRuntime"
superclass()
}?.get()
val currentThemeId = ThemeUtilClass.load(classLoader).resolve()
.optional()
.firstMethodOrNull {
name = "getUserCurrentThemeId"
parameterCount = 1
}?.invokeQuietly<String>(mAppRuntime)
return currentThemeId?.let { it.endsWith("1103") || it.endsWith("2920") } == true
}
/** 启动模块设置 [Activity] */
fun Context.startModuleSettings() {
/** 为 QQ、TIM 适配夜间模式 */
if (packageName == PackageName.QQ || packageName == PackageName.TIM)
AppCompatDelegate.setDefaultNightMode(if (isQQNightMode()) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO)
startActivity(Intent(this, ConfigActivity::class.java))
}
/**
* 跳转模块设置 [Activity]
* @param isFinish 执行完成是否自动关闭当前活动
*/
fun Activity.jumpToModuleSettings(isFinish: Boolean = true) {
if (intent.hasExtra(JumpEvent.OPEN_MODULE_SETTING)) {
/** 宿主版本不匹配的时候自动结束宿主进程 */
if (intent.getLongExtra(JumpEvent.OPEN_MODULE_SETTING, 0) != YukiHookAPI.Status.compiledTimestamp)
exitProcess(status = 0)
intent.removeExtra(JumpEvent.OPEN_MODULE_SETTING)
startModuleSettings()
if (isFinish) finish()
}
}
/** Hook 系统电源锁 */
fun PackageParam.hookSystemWakeLock() {
PowerManager.WakeLock::class.resolve().apply {
firstMethod {
name = "acquireLocked"
emptyParameters()
}.hook().intercept()
firstMethod {
name = "release"
parameters(Int::class)
}.hook().intercept()
}
}

View File

@@ -0,0 +1,56 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2023/10/8.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.fankes.tsbattery.hook.helper
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.param.PackageParam
import org.luckypray.dexkit.DexKitBridge
/**
* DexKit 工具类
*/
object DexKitHelper {
/** 是否已装载 */
private var isLoaded = false
/** 装载 */
fun load() {
if (isLoaded) return
runCatching {
System.loadLibrary("dexkit")
isLoaded = true
}.onFailure { YLog.error("Load DexKit failed!", it) }
}
/**
* 创建 [DexKitBridge]
* @param param 当前实例
* @param initiate 方法体
*/
fun create(param: PackageParam, initiate: DexKitBridge.() -> Unit) {
load()
runCatching { DexKitBridge.create(param.appInfo.sourceDir).use { initiate(it) } }
}
}

View File

@@ -0,0 +1,186 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2021/9/4.
*/
@file:Suppress("SetTextI18n", "LocalVariableName", "SameParameterValue")
package com.fankes.tsbattery.ui.activity
import android.content.ComponentName
import android.content.Intent
import android.view.HapticFeedbackConstants
import androidx.core.view.isVisible
import com.fankes.projectpromote.ProjectPromote
import com.fankes.tsbattery.R
import com.fankes.tsbattery.const.JumpEvent
import com.fankes.tsbattery.const.ModuleVersion
import com.fankes.tsbattery.const.PackageName
import com.fankes.tsbattery.databinding.ActivityMainBinding
import com.fankes.tsbattery.hook.entity.QQTIMHooker
import com.fankes.tsbattery.hook.entity.WeChatHooker
import com.fankes.tsbattery.ui.activity.base.BaseActivity
import com.fankes.tsbattery.utils.factory.appVersionBrandOf
import com.fankes.tsbattery.utils.factory.hideOrShowLauncherIcon
import com.fankes.tsbattery.utils.factory.isInstall
import com.fankes.tsbattery.utils.factory.isLauncherIconShowing
import com.fankes.tsbattery.utils.factory.openBrowser
import com.fankes.tsbattery.utils.factory.showDialog
import com.fankes.tsbattery.utils.factory.snake
import com.fankes.tsbattery.utils.tool.GithubReleaseTool
import com.fankes.tsbattery.wrapper.BuildConfigWrapper
import com.highcapable.betterandroid.ui.extension.view.isUnderline
import com.highcapable.yukihookapi.YukiHookAPI
class MainActivity : BaseActivity<ActivityMainBinding>() {
companion object {
private const val QQ_SUPPORT_VERSION = "理论支持 8.0.0+ 及以上版本。"
private const val TIM_SUPPORT_VERSION = "2+、3+ (并未完全测试每个版本)。"
private const val WECHAT_SUPPORT_VERSION = "全版本仅支持基础省电,更多功能依然画饼。"
}
override fun onCreate() {
/** 检查更新 */
GithubReleaseTool.checkingForUpdate(context = this, ModuleVersion.NAME) { version, function ->
binding.mainTextReleaseVersion.apply {
text = "点击更新 $version"
isVisible = true
setOnClickListener { function() }
}
}
/** 判断 Hook 状态 */
if (YukiHookAPI.Status.isModuleActive) {
binding.mainLinStatus.setBackgroundResource(R.drawable.bg_green_round)
binding.mainImgStatus.setImageResource(R.drawable.ic_success)
binding.mainTextStatus.text = "模块已激活"
binding.mainTextApiWay.isVisible = true
refreshActivateExecutor()
/** 推广、恰饭 */
ProjectPromote.show(activity = this, ModuleVersion.toString())
} else
showDialog {
title = "模块没有激活"
msg = "检测到模块没有激活,若你正在使用免 Root 框架例如 LSPatch、太极或无极你可以忽略此提示。"
confirmButton(text = "我知道了")
noCancelable()
}
/** 设置安装状态 */
binding.mainTextQqVer.text = PackageName.QQ.takeIf { isInstall(it) }?.let { appVersionBrandOf(it) } ?: "未安装"
binding.mainTextTimVer.text = PackageName.TIM.takeIf { isInstall(it) }?.let { appVersionBrandOf(it) } ?: "未安装"
binding.mainTextWechatVer.text = PackageName.WECHAT.takeIf { isInstall(it) }?.let { appVersionBrandOf(it) } ?: "未安装"
/** 设置文本 */
binding.mainTextVersion.text = "模块版本:${ModuleVersion.NAME}"
/** 设置 CI 自动构建标识 */
if (ModuleVersion.isCiMode)
binding.mainTextReleaseVersion.apply {
text = "CI ${ModuleVersion.GITHUB_COMMIT_ID}"
isVisible = true
setOnClickListener {
showDialog {
title = "CI 自动构建说明"
msg = """
你正在使用的是 CI 自动构建版本Commit ID 为 ${ModuleVersion.GITHUB_COMMIT_ID}
它是由代码提交后自动触发并构建、自动编译发布的,并未经任何稳定性测试,使用风险自负。
CI 构建的版本不支持太极 (也请不要提交 CI 版本的适配,因为它们是不稳定的),你可以使用 LSPosed / LSPatch。
""".trimIndent()
confirmButton(text = "我知道了")
noCancelable()
}
}
}
binding.mainQqItem.setOnClickListener {
showDialog {
title = "兼容的 QQ 版本"
msg = QQ_SUPPORT_VERSION
confirmButton(text = "我知道了")
}
/** 振动提醒 */
it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
}
binding.mainTimItem.setOnClickListener {
showDialog {
title = "兼容的 TIM 版本"
msg = TIM_SUPPORT_VERSION
confirmButton(text = "我知道了")
}
/** 振动提醒 */
it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
}
binding.mainWechatItem.setOnClickListener {
showDialog {
title = "兼容的微信版本"
msg = WECHAT_SUPPORT_VERSION
confirmButton(text = "我知道了")
}
/** 振动提醒 */
it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
}
/** 快捷操作 QQ */
binding.quickQqButton.setOnClickListener { startModuleSettings(PackageName.QQ) }
/** 快捷操作 TIM */
binding.quickTimButton.setOnClickListener { startModuleSettings(PackageName.TIM) }
/** 快捷操作微信 */
binding.quickWechatButton.setOnClickListener { startModuleSettings(PackageName.WECHAT) }
/** 项目地址按钮点击事件 */
binding.titleGithubIcon.setOnClickListener { openBrowser(url = "https://github.com/fankes/TSBattery") }
/** 恰饭! */
binding.linkWithFollowMe.isUnderline = true
binding.linkWithFollowMe.setOnClickListener {
openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market")
}
/** 设置桌面图标显示隐藏 */
binding.hideIconInLauncherSwitch.isChecked = isLauncherIconShowing.not()
binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
hideOrShowLauncherIcon(b)
}
/** 判断当前启动模式 */
if (packageName != BuildConfigWrapper.APPLICATION_ID) {
binding.quickActionItem.isVisible = false
binding.displaySettingItem.isVisible = false
}
}
/**
* 启动模块设置界面
* @param packageName 包名
*/
private fun startModuleSettings(packageName: String) {
if (isInstall(packageName)) runCatching {
startActivity(Intent().apply {
component = ComponentName(
packageName,
if (packageName != PackageName.WECHAT) QQTIMHooker.JumpActivityClassName else WeChatHooker.LauncherUIClassName
)
putExtra(JumpEvent.OPEN_MODULE_SETTING, YukiHookAPI.Status.compiledTimestamp)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.onFailure { snake(msg = "启动模块设置失败\n$it") } else snake(msg = "你没有安装此应用")
}
/** 刷新模块激活使用的方式 */
private fun refreshActivateExecutor() {
if (YukiHookAPI.Status.Executor.apiLevel > 0)
binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name} API ${YukiHookAPI.Status.Executor.apiLevel}"
else binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name}"
}
}

View File

@@ -0,0 +1,87 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/1/30.
*/
package com.fankes.tsbattery.ui.activity.base
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat
import androidx.viewbinding.ViewBinding
import com.fankes.tsbattery.R
import com.fankes.tsbattery.utils.factory.isNotSystemInDarkMode
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
import com.highcapable.kavaref.extension.toClassOrNull
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.proxy.ModuleActivity
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity(), ModuleActivity {
override val moduleTheme get() = R.style.Theme_TSBattery
/** 获取绑定布局对象 */
lateinit var binding: VB
override fun onCreate(savedInstanceState: Bundle?) {
delegate.onCreate(savedInstanceState)
super.onCreate(savedInstanceState)
val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
name = "inflate"
parameters(LayoutInflater::class)
}?.invoke<VB>(layoutInflater) ?: error("binding failed")
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
setContentView(binding.root)
/** 隐藏系统的标题栏 */
supportActionBar?.hide()
/** 初始化沉浸状态栏 */
WindowCompat.getInsetsController(window, window.decorView).apply {
isAppearanceLightStatusBars = isNotSystemInDarkMode
isAppearanceLightNavigationBars = isNotSystemInDarkMode
}
@Suppress("DEPRECATION")
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
window?.statusBarColor = it
window?.navigationBarColor = it
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) window?.navigationBarDividerColor = it
}
/** 装载子类 */
onCreate()
}
/** 回调 [onCreate] 方法 */
abstract fun onCreate()
override fun getClassLoader() = delegate.getClassLoader()
override fun onConfigurationChanged(newConfig: Configuration) {
delegate.onConfigurationChanged(newConfig)
super.onConfigurationChanged(newConfig)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
delegate.onRestoreInstanceState(savedInstanceState)
super.onRestoreInstanceState(savedInstanceState)
}
}

View File

@@ -0,0 +1,78 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2025/4/21.
*/
@file:Suppress("DEPRECATION")
package com.fankes.tsbattery.ui.activity.base
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.widget.FrameLayout
import androidx.annotation.CallSuper
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat
import com.fankes.tsbattery.R
import com.fankes.tsbattery.utils.factory.isNotSystemInDarkMode
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.proxy.ModuleActivity
import android.R as Android_R
abstract class BaseActivity2 : AppCompatActivity(), ModuleActivity {
override val moduleTheme get() = R.style.Theme_TSBattery
@CallSuper
override fun onCreate(savedInstanceState: Bundle?) {
delegate.onCreate(savedInstanceState)
super.onCreate(savedInstanceState)
/** 隐藏系统的标题栏 */
supportActionBar?.hide()
/** 初始化沉浸状态栏 */
WindowCompat.getInsetsController(window, window.decorView).apply {
isAppearanceLightStatusBars = isNotSystemInDarkMode
isAppearanceLightNavigationBars = isNotSystemInDarkMode
}
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
window?.statusBarColor = it
window?.navigationBarColor = it
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) window?.navigationBarDividerColor = it
}
/** 装载子类 */
onCreate()
if (Build.VERSION.SDK_INT >= 35) findViewById<FrameLayout>(Android_R.id.content).getChildAt(0)?.fitsSystemWindows = true
}
/** 回调 [onCreate] 方法 */
abstract fun onCreate()
override fun getClassLoader() = delegate.getClassLoader()
override fun onConfigurationChanged(newConfig: Configuration) {
delegate.onConfigurationChanged(newConfig)
super.onConfigurationChanged(newConfig)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
delegate.onRestoreInstanceState(savedInstanceState)
super.onRestoreInstanceState(savedInstanceState)
}
}

View File

@@ -0,0 +1,577 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/9/28.
*/
@file:Suppress("SetTextI18n", "DEPRECATION")
package com.fankes.tsbattery.ui.activity.parasitic
import android.animation.LayoutTransition
import android.content.res.Resources
import android.graphics.Typeface
import android.view.Gravity
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.view.isVisible
import androidx.core.view.setPadding
import androidx.core.view.updateMargins
import androidx.core.view.updateMarginsRelative
import androidx.core.view.updatePadding
import com.fankes.projectpromote.ProjectPromote
import com.fankes.tsbattery.R
import com.fankes.tsbattery.const.ModuleVersion
import com.fankes.tsbattery.const.PackageName
import com.fankes.tsbattery.data.ConfigData
import com.fankes.tsbattery.data.ConfigData.bind
import com.fankes.tsbattery.hook.HookEntry
import com.fankes.tsbattery.hook.entity.QQTIMHooker
import com.fankes.tsbattery.ui.activity.MainActivity
import com.fankes.tsbattery.ui.activity.base.BaseActivity2
import com.fankes.tsbattery.utils.factory.appIconOf
import com.fankes.tsbattery.utils.factory.appNameOf
import com.fankes.tsbattery.utils.factory.appVersionCode
import com.fankes.tsbattery.utils.factory.appVersionName
import com.fankes.tsbattery.utils.factory.showDialog
import com.fankes.tsbattery.utils.factory.snake
import com.fankes.tsbattery.utils.tool.GithubReleaseTool
import com.fankes.tsbattery.wrapper.BuildConfigWrapper
import com.highcapable.betterandroid.ui.extension.component.base.getThemeAttrsDrawable
import com.highcapable.betterandroid.ui.extension.component.startActivity
import com.highcapable.betterandroid.ui.extension.graphics.toNormalColorStateList
import com.highcapable.betterandroid.ui.extension.view.textColor
import com.highcapable.betterandroid.ui.extension.view.tooltipTextCompat
import com.highcapable.betterandroid.ui.extension.view.updateMargins
import com.highcapable.betterandroid.ui.extension.view.updatePadding
import com.highcapable.betterandroid.ui.extension.view.updateTypeface
import com.highcapable.hikage.extension.setContentView
import com.highcapable.hikage.widget.android.widget.ImageView
import com.highcapable.hikage.widget.android.widget.LinearLayout
import com.highcapable.hikage.widget.android.widget.TextView
import com.highcapable.hikage.widget.androidx.cardview.widget.CardView
import com.highcapable.hikage.widget.androidx.core.widget.NestedScrollView
import com.highcapable.hikage.widget.com.fankes.tsbattery.ui.widget.MaterialSwitch
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.factory.classOf
import kotlin.system.exitProcess
import android.R as Android_R
class ConfigActivity : BaseActivity2() {
private lateinit var updateVersionText: TextView
private lateinit var needRestartTipText: TextView
private lateinit var currentModeText: TextView
private lateinit var itemQQTimConfig: LinearLayout
override fun onCreate() {
setContentView {
LinearLayout(
lparams = LayoutParams(matchParent = true),
init = {
orientation = LinearLayout.VERTICAL
setBackgroundResource(R.color.colorThemeBackground)
}
) {
LinearLayout(
lparams = LayoutParams(widthMatchParent = true),
init = {
gravity = Gravity.CENTER or Gravity.START
setPadding(15.dp)
}
) {
ImageView(
lparams = LayoutParams(20.dp, 20.dp) {
marginStart = 10.dp
marginEnd = 20.dp
}
) {
imageTintList = stateColorResource(R.color.colorTextGray)
tooltipTextCompat = "返回"
setImageResource(R.drawable.ic_back)
background = getThemeAttrsDrawable(Android_R.attr.selectableItemBackgroundBorderless)
setOnClickListener { finish() }
}
TextView(
lparams = LayoutParams(width = 0) {
marginEnd = 2.dp
weight = 1f
}
) {
isSingleLine = true
textSize = 19f
textColor = colorResource(R.color.colorTextGray)
text = "TSBattery 设置 (${appName.trim()})"
updateTypeface(Typeface.BOLD)
}
ImageView(
lparams = LayoutParams(23.dp, 23.dp) {
marginEnd = 10.dp
}
) {
setPadding(1.dp)
imageTintList = stateColorResource(R.color.colorTextGray)
tooltipTextCompat = "打开模块主界面"
setImageResource(R.drawable.ic_icon)
background = getThemeAttrsDrawable(Android_R.attr.selectableItemBackgroundBorderless)
setOnClickListener {
showDialog {
title = "打开模块主界面"
msg = "点击确定后将打开模块主界面,如果未安装模块本体将尝试打开寄生界面。"
confirmButton {
runCatching {
startActivity(
packageName = BuildConfigWrapper.APPLICATION_ID,
activityClass = classOf<MainActivity>().name
)
}.onFailure {
runCatching {
startActivity<MainActivity>()
}.onFailure { snake(msg = "打开失败,请确认你已安装模块 APP 或在模块更新后重启过$appName\n$it") }
}
}
cancelButton()
}
}
}
}
NestedScrollView(
lparams = LayoutParams(matchParent = true),
init = {
setFadingEdgeLength(10.dp)
isVerticalFadingEdgeEnabled = true
isVerticalScrollBarEnabled = false
isHorizontalScrollBarEnabled = false
}
) {
LinearLayout(
lparams = LayoutParams(widthMatchParent = true),
init = {
orientation = LinearLayout.VERTICAL
layoutTransition = LayoutTransition()
updatePadding(bottom = 15.dp)
}
) {
repeat(2) { index ->
TextView(
lparams = LayoutParams(widthMatchParent = true) {
updateMargins(horizontal = 15.dp)
updateMargins(bottom = 5.dp)
}
) {
setBackgroundResource(R.drawable.bg_orange_round)
setPadding(5.dp)
gravity = Gravity.CENTER
textSize = 13f
textColor = colorResource(R.color.white)
text = when (index) {
0 -> "点击更新 %1"
else -> "新的设置需要重新启动${appName}才能生效"
}
isVisible = false
when (index) {
0 -> updateVersionText = this
else -> {
needRestartTipText = this
setOnClickListener {
showDialog {
title = "需要重新启动"
msg = "你必须重新启动${appName}才能使当前更改生效,现在重新启动吗?"
confirmButton {
cancel()
finish()
exitProcess(status = 0)
}
cancelButton(text = "稍后再说") {
cancel()
it.isVisible = false
}
}
}
}
}
}
}
LinearLayout(
lparams = LayoutParams(widthMatchParent = true) {
updateMargins(horizontal = 15.dp)
updateMargins(top = 5.dp)
},
init = {
setBackgroundResource(R.drawable.bg_permotion_round)
setPadding(15.dp)
gravity = Gravity.CENTER
}
) {
ImageView(
lparams = LayoutParams(45.dp, 45.dp) {
marginEnd = 15.dp
}
) {
setImageDrawable(appIconOf())
}
LinearLayout(
lparams = LayoutParams(widthMatchParent = true),
init = {
orientation = LinearLayout.VERTICAL
}
) {
LinearLayout(
lparams = LayoutParams(widthMatchParent = true) {
bottomMargin = 5.dp
gravity = Gravity.CENTER or Gravity.START
}
) {
TextView(
lparams = LayoutParams {
marginEnd = 7.dp
}
) {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 15f
text = appName.trim()
}
TextView {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
text = "$appVersionName($appVersionCode)"
alpha = 0.85f
}
TextView(
lparams = LayoutParams {
updateMargins(horizontal = 6.dp)
}
) {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
text = "|"
alpha = 0.85f
}
TextView {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
text = "模块版本:$ModuleVersion"
alpha = 0.85f
}
}
LinearLayout(
lparams = LayoutParams(widthMatchParent = true),
init = {
gravity = Gravity.CENTER or Gravity.START
}
) {
CardView(
lparams = LayoutParams(13.dp, 13.dp) {
marginEnd = 6.dp
},
init = {
setCardBackgroundColor(colorResource(R.color.colorThemeBackground))
radius = 50f.dp
elevation = 0f
}
) {
ImageView(
lparams = LayoutParams(13.dp, 13.dp)
) {
setImageResource(if (HookEntry.isHookClientSupport)
R.drawable.ic_success
else R.drawable.ic_error)
imageTintList = (if (HookEntry.isHookClientSupport)
0xFF26A69A
else 0xFFFF7043).toInt().toNormalColorStateList()
}
}
if (!HookEntry.isHookClientSupport)
LinearLayout(
lparams = LayoutParams {
marginEnd = 6.dp
},
init = {
gravity = Gravity.CENTER or Gravity.START
}
) {
TextView {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
alpha = 0.85f
text = "未适配"
}
TextView(
lparams = LayoutParams {
updateMarginsRelative(start = 6.dp)
}
) {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
text = "|"
alpha = 0.85f
}
}
TextView {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
alpha = 0.85f
currentModeText = this
}
TextView(
lparams = LayoutParams {
updateMarginsRelative(start = 6.dp)
}
) {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
text = "|"
alpha = 0.85f
}
TextView(
lparams = LayoutParams {
updateMarginsRelative(start = 6.dp)
}
) {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
text = "${YukiHookAPI.Status.Executor.name} API ${YukiHookAPI.Status.Executor.apiLevel}"
alpha = 0.85f
}
}
}
}
TextView(
lparams = LayoutParams(widthMatchParent = true) {
updateMargins(horizontal = 15.dp)
updateMargins(top = 15.dp)
}
) {
setBackgroundResource(R.drawable.bg_permotion_round)
setPadding(15.dp)
textSize = 12f
textColor = colorResource(R.color.colorTextDark)
setLineSpacing(10f.dp, 1f)
text = "模块只对挂后台锁屏情况下有省电效果,请不要将过多的群提醒,消息通知打开,这样子在使用过程时照样会极其耗电。\n" +
"持续常驻使用${appName}依然会耗电,任何软件都是如此,模块是无法帮你做到前台不耗电的。"
}
LinearLayout(
lparams = LayoutParams(widthMatchParent = true) {
updateMargins(horizontal = 15.dp)
updateMargins(top = 15.dp)
},
init = {
orientation = LinearLayout.VERTICAL
}
) {
LinearLayout(
lparams = LayoutParams(widthMatchParent = true),
init = {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
setBackgroundResource(R.drawable.bg_permotion_round)
updatePadding(horizontal = 15.dp)
updatePadding(top = 10.dp, bottom = 15.dp)
}
) {
MaterialSwitch(
lparams = LayoutParams(widthMatchParent = true, height = 35.dp) {
bottomMargin = 5.dp
}
) {
text = "停用省电策略"
textColor = colorResource(R.color.colorTextGray)
textSize = 15f
bind(ConfigData.DISABLE_ALL_HOOK) {
refreshConfigItems()
refreshCurrentModeText()
showNeedRestartTip()
}
}
TextView(
lparams = LayoutParams(widthMatchParent = true)
) {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
alpha = 0.6f
setLineSpacing(6f.dp, 1f)
text = "选择停用后模块将关闭所有省电功能,模块停止使用。"
}
}
}
LinearLayout(
lparams = LayoutParams(widthMatchParent = true) {
updateMargins(horizontal = 15.dp)
updateMargins(top = 15.dp)
},
init = {
orientation = LinearLayout.VERTICAL
itemQQTimConfig = this
}
) {
LinearLayout(
lparams = LayoutParams(widthMatchParent = true),
init = {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
setBackgroundResource(R.drawable.bg_permotion_round)
updatePadding(horizontal = 15.dp)
updatePadding(top = 10.dp, bottom = 15.dp)
}
) {
MaterialSwitch(
lparams = LayoutParams(widthMatchParent = true, height = 35.dp) {
bottomMargin = 5.dp
}
) {
text = "启用保守模式"
textColor = colorResource(R.color.colorTextGray)
textSize = 15f
bind(ConfigData.ENABLE_QQ_TIM_PROTECT_MODE) {
refreshCurrentModeText()
showNeedRestartTip()
}
}
TextView(
lparams = LayoutParams(widthMatchParent = true)
) {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
alpha = 0.6f
setLineSpacing(6f.dp, 1f)
text = "此选项默认关闭,默认情况下模块将会干掉${appName}自身的电源锁控制类,开启后模块将只对系统电源锁生效," +
"如果你的${appName}视频通话等设置发生了故障,可以尝试开启这个功能。"
}
}
LinearLayout(
lparams = LayoutParams(widthMatchParent = true) {
topMargin = 15.dp
},
init = {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
setBackgroundResource(R.drawable.bg_permotion_round)
updatePadding(horizontal = 15.dp)
updatePadding(top = 10.dp, bottom = 15.dp)
}
) {
MaterialSwitch(
lparams = LayoutParams(widthMatchParent = true, height = 35.dp) {
bottomMargin = 5.dp
}
) {
text = "关闭 CoreService"
textColor = colorResource(R.color.colorTextGray)
textSize = 15f
bind(ConfigData.ENABLE_KILL_QQ_TIM_CORESERVICE) { showNeedRestartTip() }
}
TextView(
lparams = LayoutParams(widthMatchParent = true) {
bottomMargin = 10.dp
}
) {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
alpha = 0.6f
setLineSpacing(6f.dp, 1f)
text = "关闭后可能会影响消息接收与视频通话,但是会达到省电效果,如果你的系统拥有推送服务 (HMS) 或 (MIPUSH) 可以尝试关闭。"
}
MaterialSwitch(
lparams = LayoutParams(widthMatchParent = true, height = 35.dp) {
bottomMargin = 5.dp
}
) {
text = "关闭 CoreService\$KernelService"
textColor = colorResource(R.color.colorTextGray)
textSize = 15f
bind(ConfigData.ENABLE_KILLE_QQ_TIM_CORESERVICE_CHILD) { showNeedRestartTip() }
}
TextView(
lparams = LayoutParams(widthMatchParent = true)
) {
isSingleLine = true
textColor = colorResource(R.color.colorTextGray)
textSize = 12f
alpha = 0.6f
setLineSpacing(6f.dp, 1f)
text = "这是一个辅助子服务,理论主服务关闭后子服务同样不会被启动,建议在保证消息接收的前提下可以尝试关闭子服务。"
}
}
}
}
}
}
}
/** 检查更新 */
GithubReleaseTool.checkingForUpdate(context = this, ModuleVersion.NAME) { version, function ->
updateVersionText.apply {
text = "点击更新 $version"
isVisible = true
setOnClickListener { function() }
}
}
refreshCurrentModeText()
refreshConfigItems()
/** 推广、恰饭 */
ProjectPromote.show(activity = this, ModuleVersion.toString())
}
/** 显示需要重新启动提示 */
private fun showNeedRestartTip() {
needRestartTipText.isVisible = true
}
/** 刷新配置条目显示隐藏状态 */
private fun refreshConfigItems() {
itemQQTimConfig.isVisible = packageName != PackageName.WECHAT && ConfigData.isDisableAllHook.not()
}
/** 刷新当前模式文本 */
private fun refreshCurrentModeText() {
currentModeText.text = when {
ConfigData.isDisableAllHook -> "模块已停用"
packageName == PackageName.WECHAT -> "基础省电模式"
ConfigData.isEnableQQTimProtectMode -> "保守模式"
else -> "完全模式"
}
}
/** 重新设置 DPI 防止 QQ、TIM 修改它 */
override fun getResources(): Resources? = super.getResources().apply {
if (packageName == PackageName.QQ || packageName == PackageName.TIM)
QQTIMHooker.baseConfiguration?.also {
updateConfiguration(configuration.apply {
fontScale = it.fontScale
densityDpi = it.densityDpi
}, displayMetrics)
}
}
/**
* 获取当前 APP 名称
* @return [String]
*/
private val appName by lazy { appNameOf().let { if (packageName == PackageName.WECHAT) it else " $it " } }
}

View File

@@ -0,0 +1,76 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/1/8.
*/
@file:Suppress("SameParameterValue")
package com.fankes.tsbattery.ui.widget
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.text.TextUtils
import android.util.AttributeSet
import androidx.appcompat.widget.SwitchCompat
import com.fankes.tsbattery.utils.factory.dp
import com.fankes.tsbattery.utils.factory.isSystemInDarkMode
import com.highcapable.hikage.annotation.HikageView
import top.defaults.drawabletoolbox.DrawableBuilder
@HikageView
class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(context, attrs) {
private fun toColors(selected: Int, pressed: Int, normal: Int): ColorStateList {
val colors = intArrayOf(selected, pressed, normal)
val states = arrayOfNulls<IntArray>(3)
states[0] = intArrayOf(android.R.attr.state_checked)
states[1] = intArrayOf(android.R.attr.state_pressed)
states[2] = intArrayOf()
return ColorStateList(states, colors)
}
private val thumbColor get() = if (context.isSystemInDarkMode) 0xFF7C7C7C else 0xFFCCCCCC
init {
trackDrawable = DrawableBuilder()
.rectangle()
.rounded()
.solidColor(0xFF656565.toInt())
.height(20.dp(context))
.cornerRadius(15.dp(context))
.build()
thumbDrawable = DrawableBuilder()
.rectangle()
.rounded()
.solidColor(Color.WHITE)
.size(20.dp(context), 20.dp(context))
.cornerRadius(20.dp(context))
.strokeWidth(8.dp(context))
.strokeColor(Color.TRANSPARENT)
.build()
trackTintList = toColors(
0xFF656565.toInt(),
thumbColor.toInt(),
thumbColor.toInt()
)
isSingleLine = true
ellipsize = TextUtils.TruncateAt.END
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
*
* This file is part of TSBattery.
*
* TSBattery is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TSBattery is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by zpp0196 on 2019/2/9.
*/
package com.fankes.tsbattery.utils;
import android.content.Context;
import android.os.Environment;
import com.fankes.tsbattery.BuildConfig;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@SuppressWarnings("ALL")
public class FileUtils {
private static final String FILE_PREF_NAME = BuildConfig.APPLICATION_ID + "_preferences.xml";
public static boolean copyFile(File srcFile, File targetFile) {
FileInputStream ins = null;
FileOutputStream out = null;
try {
if (targetFile.exists()) {
targetFile.delete();
}
File targetParent = targetFile.getParentFile();
if (!targetParent.exists()) {
targetParent.mkdirs();
}
targetFile.createNewFile();
ins = new FileInputStream(srcFile);
out = new FileOutputStream(targetFile);
byte[] b = new byte[1024];
int n;
while ((n = ins.read(b)) != -1) {
out.write(b, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
try {
if (ins != null) {
ins.close();
}
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
public static File getDataDir(Context context) {
return new File(context.getApplicationInfo().dataDir);
}
public static File getPrefDir(Context context) {
return new File(getDataDir(context), "shared_prefs");
}
public static File getDefaultPrefFile(Context context) {
return new File(getPrefDir(context), FILE_PREF_NAME);
}
public static File getBackupPrefsFile() {
return new File(getBackupDir(), FILE_PREF_NAME);
}
private static File getBackupDir() {
return new File(Environment.getExternalStorageDirectory(), "QQPurify");
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
*
* This file is part of TSBattery.
*
* TSBattery is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TSBattery is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by zpp0196 on 2018/4/11.
*/
package com.fankes.tsbattery.utils
import de.robv.android.xposed.XSharedPreferences
object XPrefUtils {
fun getBoolean(key: String) = pref.getBoolean(key, false)
fun getString(key: String) = pref.getString(key, "unknown")
private val pref: XSharedPreferences
get() {
val preferences = XSharedPreferences("com.fankes.tsbattery")
preferences.makeWorldReadable()
preferences.reload()
return preferences
}
}

View File

@@ -0,0 +1,142 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/1/7.
*/
@file:Suppress("unused")
package com.fankes.tsbattery.utils.factory
import android.app.Dialog
import android.content.Context
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import com.fankes.tsbattery.R
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.progressindicator.CircularProgressIndicator
import com.highcapable.yukihookapi.hook.factory.applyModuleTheme
/**
* 构造对话框
* @param initiate 对话框方法体
*/
inline fun Context.showDialog(initiate: DialogBuilder.() -> Unit) = DialogBuilder(context = this).apply(initiate).show()
/**
* 对话框构造器
* @param context 实例
*/
class DialogBuilder(val context: Context) {
/** 实例对象 */
private var instance: AlertDialog.Builder? = null
/** 对话框实例 */
private var dialogInstance: Dialog? = null
/** 自定义布局 */
private var customLayoutView: View? = null
init {
instance = MaterialAlertDialogBuilder(context.applyModuleTheme(R.style.Theme_TSBattery))
}
/** 设置对话框不可关闭 */
fun noCancelable() {
instance?.setCancelable(false)
}
/** 设置对话框标题 */
var title
get() = ""
set(value) {
instance?.setTitle(value)
}
/** 设置对话框消息内容 */
var msg
get() = ""
set(value) {
instance?.setMessage(value)
}
/** 设置进度条对话框消息内容 */
var progressContent
get() = ""
set(value) {
if (customLayoutView == null)
customLayoutView = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER or Gravity.START
addView(CircularProgressIndicator(context).apply {
isIndeterminate = true
trackCornerRadius = 10.dp(context)
})
addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(20.dp(context), 5) })
addView(TextView(context).apply {
tag = "progressContent"
text = value
})
setPadding(20.dp(context), 20.dp(context), 20.dp(context), 20.dp(context))
}
else customLayoutView?.findViewWithTag<TextView>("progressContent")?.text = value
}
/**
* 设置对话框确定按钮
* @param text 按钮文本内容
* @param callback 点击事件
*/
fun confirmButton(text: String = "确定", callback: () -> Unit = {}) {
instance?.setPositiveButton(text) { _, _ -> callback() }
}
/**
* 设置对话框取消按钮
* @param text 按钮文本内容
* @param callback 点击事件
*/
fun cancelButton(text: String = "取消", callback: () -> Unit = {}) {
instance?.setNegativeButton(text) { _, _ -> callback() }
}
/**
* 设置对话框第三个按钮
* @param text 按钮文本内容
* @param callback 点击事件
*/
fun neutralButton(text: String = "更多", callback: () -> Unit = {}) {
instance?.setNeutralButton(text) { _, _ -> callback() }
}
/** 取消对话框 */
fun cancel() = dialogInstance?.cancel()
/** 显示对话框 */
fun show() = runInSafe {
instance?.create()?.apply {
customLayoutView?.let { setView(it) }
dialogInstance = this
}?.show()
}
}

View File

@@ -0,0 +1,82 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/3/13.
*/
@file:Suppress("unused", "UnusedReceiverParameter")
package com.fankes.tsbattery.utils.factory
import com.highcapable.yukihookapi.hook.log.YLog
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为空
* @return [T] 发生异常时返回设定值否则返回正常值
*/
inline fun <T> safeOfNull(result: () -> T): T? = safeOf(default = null, result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 false
* @return [Boolean] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfFalse(result: () -> Boolean) = safeOf(default = false, result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 true
* @return [Boolean] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfTrue(result: () -> Boolean) = safeOf(default = true, result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 false
* @return [String] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfNothing(result: () -> String) = safeOf(default = "", result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 false
* @return [Int] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfNan(result: () -> Int) = safeOf(default = 0, result)
/**
* 忽略异常返回值
* @param default 异常返回值
* @param result 正常回调值
* @return [T] 发生异常时返回设定值否则返回正常值
*/
inline fun <T> safeOf(default: T, result: () -> T) = try {
result()
} catch (_: Throwable) {
default
}
/**
* 忽略异常运行
* @param msg 出错输出的消息 - 默认为空
* @param block 正常回调
*/
inline fun <T> T.runInSafe(msg: String = "", block: () -> Unit) {
runCatching(block).onFailure { if (msg.isNotBlank()) YLog.error(msg, it) }
}

View File

@@ -0,0 +1,243 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/1/7.
*/
@file:Suppress("unused", "DiscouragedApi", "InternalInsetResource")
package com.fankes.tsbattery.utils.factory
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PackageInfoFlags
import android.content.res.Configuration
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.net.ConnectivityManager
import android.net.Uri
import android.os.Build
import android.provider.Settings
import android.widget.Toast
import androidx.core.content.getSystemService
import androidx.core.content.pm.PackageInfoCompat
import com.fankes.tsbattery.wrapper.BuildConfigWrapper
import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.Companion.appContext
/**
* 系统深色模式是否开启
* @return [Boolean] 是否开启
*/
val isSystemInDarkMode get() = appContext.isSystemInDarkMode
/**
* 系统深色模式是否没开启
* @return [Boolean] 是否开启
*/
inline val isNotSystemInDarkMode get() = !isSystemInDarkMode
/**
* 系统深色模式是否开启
* @return [Boolean] 是否开启
*/
val Context.isSystemInDarkMode get() = (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
/**
* 系统深色模式是否没开启
* @return [Boolean] 是否开启
*/
inline val Context.isNotSystemInDarkMode get() = !isSystemInDarkMode
/**
* 得到 APP 安装包信息 (兼容)
* @param packageName APP 包名
* @param flag [PackageInfoFlags]
* @return [PackageInfo] or null
*/
private fun Context.getPackageInfoCompat(packageName: String, flag: Number = 0) = runCatching {
@Suppress("DEPRECATION", "KotlinRedundantDiagnosticSuppress")
if (Build.VERSION.SDK_INT >= 33)
packageManager?.getPackageInfo(packageName, PackageInfoFlags.of(flag.toLong()))
else packageManager?.getPackageInfo(packageName, flag.toInt())
}.getOrNull()
/**
* 得到 APP 版本号 (兼容 [PackageInfo.getLongVersionCode])
* @return [Int]
*/
private val PackageInfo.versionCodeCompat get() = PackageInfoCompat.getLongVersionCode(this)
/**
* 判断 APP 是否安装
* @param packageName APP 包名
* @return [Boolean]
*/
fun Context.isInstall(packageName: String) = getPackageInfoCompat(packageName)?.let { true } ?: false
/**
* 得到 APP 版本信息
* @return [String]
*/
val Context.appVersionName get() = getPackageInfoCompat(packageName)?.versionName ?: ""
/**
* 得到 APP 版本号
* @return [Int]
*/
val Context.appVersionCode get() = getPackageInfoCompat(packageName)?.versionCodeCompat
/**
* 得到 APP 版本信息与版本号
* @param packageName APP 包名 - 默认为当前 APP
* @return [String]
*/
fun Context.appVersionBrandOf(packageName: String = getPackageName()) =
getPackageInfoCompat(packageName)?.let { "${it.versionName}(${it.versionCodeCompat})" } ?: ""
/**
* 得到 APP 名称
* @param packageName APP 包名 - 默认为当前 APP
* @return [String]
*/
fun Context.appNameOf(packageName: String = getPackageName()) =
getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: ""
/**
* 得到 APP 图标
* @param packageName APP 包名 - 默认为当前 APP
* @return [Drawable] or null
*/
fun Context.appIconOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.applicationInfo?.loadIcon(packageManager)
/**
* 网络连接是否正常
* @return [Boolean] 网络是否连接
*/
val Context.isNetWorkSuccess
get() = safeOfFalse {
@Suppress("DEPRECATION")
getSystemService<ConnectivityManager>()?.activeNetworkInfo != null
}
/**
* dp 转换为 pxInt
* @param context 使用的实例
* @return [Int]
*/
fun Number.dp(context: Context) = dpFloat(context).toInt()
/**
* dp 转换为 pxFloat
* @param context 使用的实例
* @return [Float]
*/
fun Number.dpFloat(context: Context) = toFloat() * context.resources.displayMetrics.density
/**
* 获取绝对状态栏高度
* @return [Int]
*/
val Context.absoluteStatusBarHeight
get() = safeOfNan {
resources.getDimensionPixelSize(resources.getIdentifier("status_bar_height", "dimen", "android"))
}
/**
* 弹出 [Toast]
* @param msg 提示内容
*/
fun toast(msg: String) = appContext.toast(msg)
/**
* 弹出 [Toast]
* @param msg 提示内容
*/
fun Context.toast(msg: String) = Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
/**
* 弹出 [Snackbar]
* @param msg 提示内容
* @param actionText 按钮文本 - 不写默认取消按钮
* @param callback 按钮事件回调
*/
fun Context.snake(msg: String, actionText: String = "", callback: () -> Unit = {}) =
Snackbar.make((this as Activity).findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG).apply {
if (actionText.isBlank()) return@apply
setActionTextColor(if (isSystemInDarkMode) Color.BLACK else Color.WHITE)
setAction(actionText) { callback() }
}.show()
/**
* 跳转 APP 自身设置界面
* @param packageName 包名
*/
fun Context.openSelfSetting(packageName: String = appContext.packageName) = runCatching {
if (isInstall(packageName))
startActivity(Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", packageName, null)
})
else snake(msg = "你没有安装此应用")
}.onFailure { toast(msg = "启动 $packageName 应用信息失败") }
/**
* 启动系统浏览器
* @param url 网址
* @param packageName 指定包名 - 可不填
*/
fun Context.openBrowser(url: String, packageName: String = "") = runCatching {
startActivity(Intent().apply {
if (packageName.isNotBlank()) setPackage(packageName)
action = Intent.ACTION_VIEW
data = Uri.parse(url)
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.onFailure {
if (packageName.isNotBlank()) snake(msg = "启动 $packageName 失败")
else snake(msg = "启动系统浏览器失败")
}
/**
* 隐藏或显示启动器图标
*
* - 你可能需要 LSPosed 的最新版本以开启高版本系统中隐藏 APP 桌面图标功能
* @param isShow 是否显示
*/
fun Context.hideOrShowLauncherIcon(isShow: Boolean) {
packageManager?.setComponentEnabledSetting(
ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home"),
if (isShow) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP
)
}
/**
* 获取启动器图标状态
* @return [Boolean] 是否显示
*/
val Context.isLauncherIconShowing
get() = packageManager?.getComponentEnabledSetting(
ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home")
) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED

View File

@@ -0,0 +1,152 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2022/3/20.
*/
@file:Suppress("NewApi")
package com.fankes.tsbattery.utils.tool
import android.app.Activity
import android.content.Context
import android.icu.text.SimpleDateFormat
import android.icu.util.Calendar
import android.icu.util.TimeZone
import com.fankes.tsbattery.utils.factory.isNetWorkSuccess
import com.fankes.tsbattery.utils.factory.openBrowser
import com.fankes.tsbattery.utils.factory.openSelfSetting
import com.fankes.tsbattery.utils.factory.runInSafe
import com.fankes.tsbattery.utils.factory.showDialog
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.json.JSONObject
import java.io.IOException
import java.io.Serializable
import java.util.Locale
/**
* 获取 GitHub Release 最新版本工具类
*/
object GithubReleaseTool {
/** 仓库作者 */
private const val REPO_AUTHOR = "fankes"
/** 仓库名称 */
private const val REPO_NAME = "TSBattery"
/**
* 获取最新版本信息
* @param context 实例
* @param version 当前版本
* @param result 成功后回调 - ([String] 最新版本,[Function] 更新对话框方法体)
*/
fun checkingForUpdate(context: Context, version: String, result: (String, () -> Unit) -> Unit) = checkingInternetConnect(context) {
OkHttpClient().newBuilder().build().newCall(
Request.Builder()
.url("https://api.github.com/repos/$REPO_AUTHOR/$REPO_NAME/releases/latest")
.get()
.build()
).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {}
override fun onResponse(call: Call, response: Response) = runInSafe {
JSONObject(response.body.string()).apply {
GithubReleaseBean(
name = getString("name"),
htmlUrl = getString("html_url"),
content = getString("body"),
date = getString("published_at").localTime()
).apply {
fun showUpdate() = context.showDialog {
title = "最新版本 $name"
msg = "发布于 $date\n\n" +
"更新日志\n\n" + content
confirmButton(text = "更新") { context.openBrowser(htmlUrl) }
cancelButton()
}
if (name != version) (context as? Activity?)?.runOnUiThread {
showUpdate()
result(name) { showUpdate() }
}
}
}
}
})
}
/**
* 检查网络连接情况
* @param context 实例
* @param callback 已连接回调
*/
private fun checkingInternetConnect(context: Context, callback: () -> Unit) = runInSafe {
if (context.isNetWorkSuccess)
OkHttpClient().newBuilder().build().newCall(
Request.Builder()
.url("https://www.baidu.com")
.get()
.build()
).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
(context as? Activity?)?.runOnUiThread {
context.showDialog {
title = "网络不可用"
msg = "应用的联网权限可能已被禁用,请开启联网权限以定期检查更新。"
confirmButton(text = "去开启") { context.openSelfSetting() }
cancelButton()
noCancelable()
}
}
}
override fun onResponse(call: Call, response: Response) = runInSafe {
(context as? Activity?)?.runOnUiThread { runInSafe { callback() } }
}
})
}
/**
* 格式化时间为本地时区
* @return [String] 本地时区时间
*/
private fun String.localTime() = replace("T", " ").replace("Z", "").let {
runCatching {
val local = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = Calendar.getInstance().timeZone }
val current = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("GMT") }
local.format(current.parse(it))
}.getOrNull() ?: it
}
/**
* GitHub Release bean
* @param name 版本名称
* @param htmlUrl 网页地址
* @param content 更新日志
* @param date 发布时间
*/
private data class GithubReleaseBean(
var name: String,
var htmlUrl: String,
var content: String,
var date: String
) : Serializable
}

View File

@@ -0,0 +1,36 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is created by fankes on 2023/9/16.
*/
@file:Suppress("unused")
package com.fankes.tsbattery.wrapper
import com.fankes.tsbattery.BuildConfig
/**
* 对 [BuildConfig] 的包装
*/
object BuildConfigWrapper {
const val APPLICATION_ID = BuildConfig.APPLICATION_ID
const val VERSION_NAME = BuildConfig.VERSION_NAME
const val VERSION_CODE = BuildConfig.VERSION_CODE
val isDebug = BuildConfig.DEBUG
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#66A6A6A6" />
<corners android:radius="15dp" />
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#666E6E6E" />
<corners android:radius="15dp" />
</shape>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#777777">
<item>
<shape android:shape="rectangle">
<solid android:color="#66DAD9D9" />
<corners android:radius="15dp" />
</shape>
</item>
</ripple>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#661B1B1B" />
<corners android:radius="15dp" />
</shape>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="#6A6A6A" /> <solid android:color="#FF7043" />
<corners android:radius="15dp" /> <corners android:radius="15dp" />
</shape> </shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#66E4E4E4" />
<corners android:radius="15dp" />
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FF786F" />
<corners android:radius="30dp" />
</shape>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150.1dp"
android:height="150dp"
android:viewportWidth="1025"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="M983.8,314.4c-25.7,-60.8 -62.5,-115.4 -109.4,-162.3 -46.9,-46.9 -101.5,-83.7 -162.3,-109.4 -62.9,-26.7 -129.9,-40.2 -198.8,-40.2S377.5,16 314.5,42.7C253.8,68.4 199.1,105.2 152.3,152.2c-46.9,46.9 -83.7,101.5 -109.4,162.3 -26.7,62.9 -40.2,129.9 -40.2,198.8s13.5,135.8 40.2,198.8c25.7,60.8 62.5,115.4 109.4,162.3 46.9,46.9 101.5,83.7 162.3,109.4 62.9,26.7 129.9,40.2 198.8,40.2s135.8,-13.5 198.8,-40.2c60.8,-25.7 115.4,-62.5 162.3,-109.4 46.9,-46.9 83.7,-101.5 109.4,-162.3 26.7,-62.9 40.2,-129.9 40.2,-198.8s-13.6,-135.9 -40.3,-198.9zM550.5,768.2c0,21 -17,38 -38,38s-38,-17 -38,-38L474.5,395.6c0,-21 17,-38 38,-38s38,17 38,38v372.6zM510.8,305.5c-29.2,0 -52.7,-23.7 -52.7,-52.7 0,-29.2 23.7,-52.7 52.7,-52.7 29.2,0 52.7,23.7 52.7,52.7 0.1,29.2 -23.6,52.7 -52.7,52.7z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="192dp"
android:height="150.39165dp"
android:viewportWidth="1307"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="M268.7,566.5h929.6c36.3,0 72.6,-29 72.6,-72.6 0,-36.3 -29,-72.6 -72.6,-72.6H305l297.8,-297.8c29,-29 29,-72.6 0,-101.7 -29,-29 -72.6,-29 -101.7,0L72.6,450.3c-14.5,14.5 -21.8,36.3 -21.8,58.1 0,21.8 0,43.6 21.8,58.1l428.5,428.5c29,29 72.6,29 101.7,0 29,-29 29,-72.6 0,-101.7l-334.1,-326.8z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportWidth="896"
android:viewportHeight="896">
<path
android:fillColor="#ffffff"
android:pathData="M448,0C200.58,0 0,200.58 0,448 0,695.42 200.58,896 448,896 695.42,896 896,695.42 896,448 896,200.58 695.42,0 448,0ZM663,612.05a36.11,36.11 0,0 1,0 50.95,36.11 36.11,0 0,1 -50.91,0L448,498.91 284,663a36.11,36.11 0,0 1,-51 0,36.11 36.11,0 0,1 0,-50.91L397,448.09 233,284a36.11,36.11 0,0 1,0 -51,36.11 36.11,0 0,1 51,0l164,164.09 164,-164a36.11,36.11 0,0 1,51 -0.09,36.11 36.11,0 0,1 0,51L498.91,448Z" />
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#00000000"
android:pathData="M19,4H37L26,18H41L17,44L22,25H8L19,4Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="M512,12.6c-282.8,0 -512,229.2 -512,512 0,226.2 146.7,418.1 350.1,485.8 25.6,4.7 35,-11.1 35,-24.6 0,-12.2 -0.5,-52.5 -0.7,-95.3 -142.5,31 -172.5,-60.4 -172.5,-60.4 -23.3,-59.2 -56.8,-74.9 -56.8,-74.9 -46.5,-31.8 3.5,-31.1 3.5,-31.1 51.4,3.6 78.5,52.8 78.5,52.8 45.7,78.3 119.8,55.6 149,42.6 4.6,-33.1 17.9,-55.7 32.5,-68.5 -113.7,-12.9 -233.3,-56.9 -233.3,-253 0,-55.9 20,-101.6 52.8,-137.4 -5.3,-12.9 -22.8,-65 5,-135.5 0,0 43,-13.8 140.8,52.5 40.8,-11.4 84.6,-17 128.2,-17.2 43.5,0.2 87.3,5.9 128.3,17.2 97.7,-66.2 140.6,-52.5 140.6,-52.5 27.9,70.5 10.3,122.6 5,135.5 32.8,35.8 52.7,81.5 52.7,137.4 0,196.6 -119.8,239.9 -233.8,252.6 18.4,15.9 34.7,47 34.7,94.8 0,68.5 -0.6,123.6 -0.6,140.5 0,13.6 9.2,29.6 35.2,24.6 203.3,-67.8 349.9,-259.6 349.9,-485.8 0,-282.8 -229.2,-512 -512,-512z" />
</vector>

View File

@@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#00000000"
android:pathData="M9,18V42H39V18L24,6L9,18Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M19,29V42H29V29H19Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M9,42H39"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round" />
</vector>

View File

@@ -3,7 +3,7 @@
android:height="150dp" android:height="150dp"
android:viewportWidth="1024" android:viewportWidth="1024"
android:viewportHeight="1024"> android:viewportHeight="1024">
<path <path
android:fillColor="#FFffff" android:fillColor="#FFffff"
android:pathData="M717.48,354.1a33.64,33.64 0,0 0,-17.41 -21.56,37.17 37.17,0 0,0 -28.42,-3.38c-65.54,17.77 -123.8,43.16 -174.69,76.19 -50.89,33.02 -93.9,70.76 -129.02,113.15 -35.12,42.5 -69.48,94.52 -102.96,156.11 -5.22,9.57 -6.66,18.69 -4.35,27.44a33.64,33.64 0,0 0,17.36 21.5,37.17 37.17,0 0,0 28.47,3.43 40.4,40.4 0,0 0,22.94 -17.36c7.99,-11.47 18.69,-28.16 32.1,-50.07 13.41,-21.91 23.04,-37.38 28.77,-46.39 40.29,-59.14 83.05,-104.96 128.15,-137.42 45.16,-32.46 102.4,-58.06 171.78,-76.85a37.58,37.58 0,0 0,22.99 -17.31,34 34,0 0,0 4.35,-27.44l-0.05,-0.05zM928.53,194c9.47,35.58 15.36,72.76 17.82,111.51 5.38,88.52 -9.37,169.47 -44.34,242.84 -35.02,73.37 -87.24,141.31 -156.67,203.93 -67.74,61.75 -142.64,103.73 -224.77,125.95a490.8,490.8 0,0 1,-164.25 16.13c-5.99,-0.36 -24.22,-3.89 -54.68,-10.5 -30.41,-6.66 -49.87,-8.81 -58.27,-6.55 -5.89,1.64 -11.52,9.57 -16.95,23.91 -5.43,14.34 -10.24,29.7 -14.34,46.08 -4.1,16.33 -10.24,32.05 -18.43,47.1 -8.19,15.1 -17.61,24.06 -28.26,26.93a60.47,60.47 0,0 1,-29.7 1.43,52.99 52.99,0 0,1 -20.58,-8.91 245.91,245.91 0,0 1,-26.06 -24.93,30.31 30.31,0 0,1 -7.53,-9.73 34.66,34.66 0,0 1,-2.87 -7.37c-3.48,-13.11 -1.43,-28.42 6.14,-45.93a209.92,209.92 0,0 1,27.6 -46.95c10.85,-13.82 20.58,-27.65 29.03,-41.63 8.5,-13.93 11.78,-24.47 9.93,-31.64 -0.41,-1.48 -4.86,-7.94 -13.36,-19.25a197.63,197.63 0,0 1,-15.36 -22.37,355.48 355.48,0 0,1 -20.48,-57.14 338.43,338.43 0,0 1,-8.86 -130.15,349.9 349.9,0 0,1 37.89,-121.5A504.68,504.68 0,0 1,204.3 351.74a531.35,531.35 0,0 1,97.95 -84.07c18.43,-12.24 43.72,-24.22 75.93,-35.94 32.1,-11.72 64.56,-22.32 97.38,-31.74 32.56,-9.42 65.02,-19.46 97.23,-30a455.37,455.37 0,0 0,86.32 -37.89c25.4,-14.64 43.42,-30.87 54.02,-48.64l11.78,-20.99 12.08,-20.17c7.99,-13.41 11.98,-18.48 11.83,-15.26 -0.1,3.28 5.79,-1.54 17.66,-14.44 11.93,-12.9 19.66,-15.87 23.24,-8.96 14.34,-3.89 29.49,1.23 45.62,15.36 16.13,14.08 30.41,32.72 42.8,55.86 12.44,23.19 23.04,45.21 31.74,66.15 8.7,20.94 14.9,38.55 18.64,52.74l0.05,0.26z"/> android:pathData="M717.48,354.1a33.64,33.64 0,0 0,-17.41 -21.56,37.17 37.17,0 0,0 -28.42,-3.38c-65.54,17.77 -123.8,43.16 -174.69,76.19 -50.89,33.02 -93.9,70.76 -129.02,113.15 -35.12,42.5 -69.48,94.52 -102.96,156.11 -5.22,9.57 -6.66,18.69 -4.35,27.44a33.64,33.64 0,0 0,17.36 21.5,37.17 37.17,0 0,0 28.47,3.43 40.4,40.4 0,0 0,22.94 -17.36c7.99,-11.47 18.69,-28.16 32.1,-50.07 13.41,-21.91 23.04,-37.38 28.77,-46.39 40.29,-59.14 83.05,-104.96 128.15,-137.42 45.16,-32.46 102.4,-58.06 171.78,-76.85a37.58,37.58 0,0 0,22.99 -17.31,34 34,0 0,0 4.35,-27.44l-0.05,-0.05zM928.53,194c9.47,35.58 15.36,72.76 17.82,111.51 5.38,88.52 -9.37,169.47 -44.34,242.84 -35.02,73.37 -87.24,141.31 -156.67,203.93 -67.74,61.75 -142.64,103.73 -224.77,125.95a490.8,490.8 0,0 1,-164.25 16.13c-5.99,-0.36 -24.22,-3.89 -54.68,-10.5 -30.41,-6.66 -49.87,-8.81 -58.27,-6.55 -5.89,1.64 -11.52,9.57 -16.95,23.91 -5.43,14.34 -10.24,29.7 -14.34,46.08 -4.1,16.33 -10.24,32.05 -18.43,47.1 -8.19,15.1 -17.61,24.06 -28.26,26.93a60.47,60.47 0,0 1,-29.7 1.43,52.99 52.99,0 0,1 -20.58,-8.91 245.91,245.91 0,0 1,-26.06 -24.93,30.31 30.31,0 0,1 -7.53,-9.73 34.66,34.66 0,0 1,-2.87 -7.37c-3.48,-13.11 -1.43,-28.42 6.14,-45.93a209.92,209.92 0,0 1,27.6 -46.95c10.85,-13.82 20.58,-27.65 29.03,-41.63 8.5,-13.93 11.78,-24.47 9.93,-31.64 -0.41,-1.48 -4.86,-7.94 -13.36,-19.25a197.63,197.63 0,0 1,-15.36 -22.37,355.48 355.48,0 0,1 -20.48,-57.14 338.43,338.43 0,0 1,-8.86 -130.15,349.9 349.9,0 0,1 37.89,-121.5A504.68,504.68 0,0 1,204.3 351.74a531.35,531.35 0,0 1,97.95 -84.07c18.43,-12.24 43.72,-24.22 75.93,-35.94 32.1,-11.72 64.56,-22.32 97.38,-31.74 32.56,-9.42 65.02,-19.46 97.23,-30a455.37,455.37 0,0 0,86.32 -37.89c25.4,-14.64 43.42,-30.87 54.02,-48.64l11.78,-20.99 12.08,-20.17c7.99,-13.41 11.98,-18.48 11.83,-15.26 -0.1,3.28 5.79,-1.54 17.66,-14.44 11.93,-12.9 19.66,-15.87 23.24,-8.96 14.34,-3.89 29.49,1.23 45.62,15.36 16.13,14.08 30.41,32.72 42.8,55.86 12.44,23.19 23.04,45.21 31.74,66.15 8.7,20.94 14.9,38.55 18.64,52.74l0.05,0.26z" />
</vector> </vector>

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#00000000"
android:pathData="M24,44C29.523,44 34.523,41.761 38.142,38.142C41.761,34.523 44,29.523 44,24C44,18.477 41.761,13.477 38.142,9.858C34.523,6.239 29.523,4 24,4C18.477,4 13.477,6.239 9.858,9.858C6.239,13.477 4,18.477 4,24C4,29.523 6.239,34.523 9.858,38.142C13.477,41.761 18.477,44 24,44Z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineJoin="round" />
<path
android:fillColor="#ffffff"
android:fillType="evenOdd"
android:pathData="M24,11C25.381,11 26.5,12.119 26.5,13.5C26.5,14.881 25.381,16 24,16C22.619,16 21.5,14.881 21.5,13.5C21.5,12.119 22.619,11 24,11Z" />
<path
android:fillColor="#00000000"
android:pathData="M24.5,34V20H23.5H22.5"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M21,34H28"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</vector>

View File

@@ -3,12 +3,13 @@
android:height="108dp" android:height="108dp"
android:viewportWidth="108" android:viewportWidth="108"
android:viewportHeight="108"> android:viewportHeight="108">
<group android:scaleX="0.03609375" <group
android:scaleY="0.03609375" android:scaleX="0.03609375"
android:translateX="35.52" android:scaleY="0.03609375"
android:translateY="35.52"> android:translateX="35.52"
<path android:translateY="35.52">
android:fillColor="#FFffff" <path
android:pathData="M717.48,354.1a33.64,33.64 0,0 0,-17.41 -21.56,37.17 37.17,0 0,0 -28.42,-3.38c-65.54,17.77 -123.8,43.16 -174.69,76.19 -50.89,33.02 -93.9,70.76 -129.02,113.15 -35.12,42.5 -69.48,94.52 -102.96,156.11 -5.22,9.57 -6.66,18.69 -4.35,27.44a33.64,33.64 0,0 0,17.36 21.5,37.17 37.17,0 0,0 28.47,3.43 40.4,40.4 0,0 0,22.94 -17.36c7.99,-11.47 18.69,-28.16 32.1,-50.07 13.41,-21.91 23.04,-37.38 28.77,-46.39 40.29,-59.14 83.05,-104.96 128.15,-137.42 45.16,-32.46 102.4,-58.06 171.78,-76.85a37.58,37.58 0,0 0,22.99 -17.31,34 34,0 0,0 4.35,-27.44l-0.05,-0.05zM928.53,194c9.47,35.58 15.36,72.76 17.82,111.51 5.38,88.52 -9.37,169.47 -44.34,242.84 -35.02,73.37 -87.24,141.31 -156.67,203.93 -67.74,61.75 -142.64,103.73 -224.77,125.95a490.8,490.8 0,0 1,-164.25 16.13c-5.99,-0.36 -24.22,-3.89 -54.68,-10.5 -30.41,-6.66 -49.87,-8.81 -58.27,-6.55 -5.89,1.64 -11.52,9.57 -16.95,23.91 -5.43,14.34 -10.24,29.7 -14.34,46.08 -4.1,16.33 -10.24,32.05 -18.43,47.1 -8.19,15.1 -17.61,24.06 -28.26,26.93a60.47,60.47 0,0 1,-29.7 1.43,52.99 52.99,0 0,1 -20.58,-8.91 245.91,245.91 0,0 1,-26.06 -24.93,30.31 30.31,0 0,1 -7.53,-9.73 34.66,34.66 0,0 1,-2.87 -7.37c-3.48,-13.11 -1.43,-28.42 6.14,-45.93a209.92,209.92 0,0 1,27.6 -46.95c10.85,-13.82 20.58,-27.65 29.03,-41.63 8.5,-13.93 11.78,-24.47 9.93,-31.64 -0.41,-1.48 -4.86,-7.94 -13.36,-19.25a197.63,197.63 0,0 1,-15.36 -22.37,355.48 355.48,0 0,1 -20.48,-57.14 338.43,338.43 0,0 1,-8.86 -130.15,349.9 349.9,0 0,1 37.89,-121.5A504.68,504.68 0,0 1,204.3 351.74a531.35,531.35 0,0 1,97.95 -84.07c18.43,-12.24 43.72,-24.22 75.93,-35.94 32.1,-11.72 64.56,-22.32 97.38,-31.74 32.56,-9.42 65.02,-19.46 97.23,-30a455.37,455.37 0,0 0,86.32 -37.89c25.4,-14.64 43.42,-30.87 54.02,-48.64l11.78,-20.99 12.08,-20.17c7.99,-13.41 11.98,-18.48 11.83,-15.26 -0.1,3.28 5.79,-1.54 17.66,-14.44 11.93,-12.9 19.66,-15.87 23.24,-8.96 14.34,-3.89 29.49,1.23 45.62,15.36 16.13,14.08 30.41,32.72 42.8,55.86 12.44,23.19 23.04,45.21 31.74,66.15 8.7,20.94 14.9,38.55 18.64,52.74l0.05,0.26z"/> android:fillColor="#FFffff"
</group> android:pathData="M717.48,354.1a33.64,33.64 0,0 0,-17.41 -21.56,37.17 37.17,0 0,0 -28.42,-3.38c-65.54,17.77 -123.8,43.16 -174.69,76.19 -50.89,33.02 -93.9,70.76 -129.02,113.15 -35.12,42.5 -69.48,94.52 -102.96,156.11 -5.22,9.57 -6.66,18.69 -4.35,27.44a33.64,33.64 0,0 0,17.36 21.5,37.17 37.17,0 0,0 28.47,3.43 40.4,40.4 0,0 0,22.94 -17.36c7.99,-11.47 18.69,-28.16 32.1,-50.07 13.41,-21.91 23.04,-37.38 28.77,-46.39 40.29,-59.14 83.05,-104.96 128.15,-137.42 45.16,-32.46 102.4,-58.06 171.78,-76.85a37.58,37.58 0,0 0,22.99 -17.31,34 34,0 0,0 4.35,-27.44l-0.05,-0.05zM928.53,194c9.47,35.58 15.36,72.76 17.82,111.51 5.38,88.52 -9.37,169.47 -44.34,242.84 -35.02,73.37 -87.24,141.31 -156.67,203.93 -67.74,61.75 -142.64,103.73 -224.77,125.95a490.8,490.8 0,0 1,-164.25 16.13c-5.99,-0.36 -24.22,-3.89 -54.68,-10.5 -30.41,-6.66 -49.87,-8.81 -58.27,-6.55 -5.89,1.64 -11.52,9.57 -16.95,23.91 -5.43,14.34 -10.24,29.7 -14.34,46.08 -4.1,16.33 -10.24,32.05 -18.43,47.1 -8.19,15.1 -17.61,24.06 -28.26,26.93a60.47,60.47 0,0 1,-29.7 1.43,52.99 52.99,0 0,1 -20.58,-8.91 245.91,245.91 0,0 1,-26.06 -24.93,30.31 30.31,0 0,1 -7.53,-9.73 34.66,34.66 0,0 1,-2.87 -7.37c-3.48,-13.11 -1.43,-28.42 6.14,-45.93a209.92,209.92 0,0 1,27.6 -46.95c10.85,-13.82 20.58,-27.65 29.03,-41.63 8.5,-13.93 11.78,-24.47 9.93,-31.64 -0.41,-1.48 -4.86,-7.94 -13.36,-19.25a197.63,197.63 0,0 1,-15.36 -22.37,355.48 355.48,0 0,1 -20.48,-57.14 338.43,338.43 0,0 1,-8.86 -130.15,349.9 349.9,0 0,1 37.89,-121.5A504.68,504.68 0,0 1,204.3 351.74a531.35,531.35 0,0 1,97.95 -84.07c18.43,-12.24 43.72,-24.22 75.93,-35.94 32.1,-11.72 64.56,-22.32 97.38,-31.74 32.56,-9.42 65.02,-19.46 97.23,-30a455.37,455.37 0,0 0,86.32 -37.89c25.4,-14.64 43.42,-30.87 54.02,-48.64l11.78,-20.99 12.08,-20.17c7.99,-13.41 11.98,-18.48 11.83,-15.26 -0.1,3.28 5.79,-1.54 17.66,-14.44 11.93,-12.9 19.66,-15.87 23.24,-8.96 14.34,-3.89 29.49,1.23 45.62,15.36 16.13,14.08 30.41,32.72 42.8,55.86 12.44,23.19 23.04,45.21 31.74,66.15 8.7,20.94 14.9,38.55 18.64,52.74l0.05,0.26z" />
</vector> </group>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportWidth="1008.7"
android:viewportHeight="1008.7">
<path
android:fillColor="#ffffff"
android:pathData="M504.4,0C226.3,0 0,226.3 0,504.4 0,782.5 226.3,1008.7 504.4,1008.7c278.1,0 504.4,-226.3 504.4,-504.4C1008.7,226.3 782.5,0 504.4,0ZM786.6,407.7 L458.6,743.9c-7.8,8 -18.6,12.6 -29.8,12.6h-0.2c-11.1,0 -21.8,-4.4 -29.7,-12.3L222.5,567.9c-16.4,-16.4 -16.4,-43 0,-59.4 16.4,-16.4 43,-16.4 59.4,0L428.2,654.8 726.5,348.9c16.3,-16.6 42.9,-16.9 59.4,-0.7 16.6,16.2 16.9,42.8 0.7,59.4z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="m512,794a44.8,44.8 0,1 1,44.8 -44.8,44.8 44.8,0 0,1 -44.8,44.8zM471.9,230.8a40.1,40.1 0,0 1,80.2 0v369.1a40.1,40.1 0,0 1,-79.8 0zM512,0A512,512 0,1 0,1024 512,512 512,0 0,0 512,0Z" />
</vector>

View File

@@ -1,36 +1,56 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/colorThemeBackground"
android:orientation="vertical" android:orientation="vertical"
tools:context=".MainActivity" tools:context=".ui.activity.MainActivity"
tools:ignore="HardcodedText"> tools:ignore="HardcodedText,UseCompoundDrawables,ContentDescription,UnusedAttribute">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/white" android:elevation="0dp"
android:elevation="5dp" android:gravity="center|start"
android:padding="15dp"> android:paddingLeft="15dp"
android:paddingTop="13dp"
android:paddingRight="15dp"
android:paddingBottom="5dp">
<TextView <TextView
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true" android:singleLine="true"
android:text="TSBattery" android:text="TSBattery"
android:textColor="#FF323B42" android:textColor="@color/colorTextGray"
android:textSize="18sp" android:textSize="25sp"
android:textStyle="bold" /> android:textStyle="bold" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/title_github_icon"
style="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="27dp"
android:layout_height="27dp"
android:layout_marginEnd="5dp"
android:alpha="0.85"
android:src="@drawable/ic_github"
android:tint="@color/colorTextGray"
android:tooltipText="项目地址" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/main_lin_status" android:id="@+id/main_lin_status"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="15dp" android:layout_marginLeft="15dp"
android:background="@drawable/dark_round" android:layout_marginTop="10dp"
android:elevation="3dp" android:layout_marginRight="15dp"
android:layout_marginBottom="5dp"
android:background="@drawable/bg_dark_round"
android:elevation="0dp"
android:gravity="center"> android:gravity="center">
<androidx.constraintlayout.utils.widget.ImageFilterView <androidx.constraintlayout.utils.widget.ImageFilterView
@@ -39,14 +59,17 @@
android:layout_height="25dp" android:layout_height="25dp"
android:layout_marginStart="25dp" android:layout_marginStart="25dp"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
android:src="@mipmap/warn" android:src="@drawable/ic_warn"
android:tint="@color/white" /> android:tint="@color/white" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="20dp"> android:paddingLeft="20dp"
android:paddingTop="10dp"
android:paddingRight="20dp"
android:paddingBottom="10dp">
<TextView <TextView
android:id="@+id/main_text_status" android:id="@+id/main_text_status"
@@ -57,34 +80,143 @@
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="18sp" /> android:textSize="18sp" />
<TextView <LinearLayout
android:id="@+id/main_text_version"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="3dp" android:layout_marginBottom="10dp"
android:alpha="0.8" android:gravity="center|start"
android:text="当前版本:%1" android:orientation="horizontal">
android:textColor="@color/white"
android:textSize="13sp" /> <TextView
android:id="@+id/main_text_version"
<TextView android:layout_width="wrap_content"
android:id="@+id/main_text_support" android:layout_height="wrap_content"
android:layout_width="wrap_content" android:alpha="0.8"
android:layout_height="wrap_content" android:text="模块版本:%1"
android:layout_marginBottom="3dp" android:textColor="@color/white"
android:alpha="0.8" android:textSize="13sp" />
android:text="支持 %1"
android:textColor="@color/white" <TextView
android:textSize="12sp" /> android:id="@+id/main_text_release_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:background="@drawable/bg_orange_round"
android:paddingLeft="5dp"
android:paddingTop="2dp"
android:paddingRight="5dp"
android:paddingBottom="2dp"
android:text="点击更新 %1"
android:textColor="@color/white"
android:textSize="11sp"
android:visibility="gone" />
</LinearLayout>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fadingEdgeLength="10dp"
android:fillViewport="true"
android:requiresFadingEdge="horizontal"
android:scrollbars="none">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center|start"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/main_qq_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:gravity="center|start"
android:orientation="horizontal">
<ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_marginEnd="5dp"
android:src="@mipmap/ic_qq_icon" />
<TextView
android:id="@+id/main_text_qq_ver"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.8"
android:ellipsize="end"
android:singleLine="true"
android:text="%1"
android:textColor="@color/white"
android:textSize="11sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/main_tim_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:gravity="center|start"
android:orientation="horizontal">
<ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_marginEnd="5dp"
android:src="@mipmap/ic_tim_icon" />
<TextView
android:id="@+id/main_text_tim_ver"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.8"
android:ellipsize="end"
android:singleLine="true"
android:text="%1"
android:textColor="@color/white"
android:textSize="11sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/main_wechat_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center|start"
android:orientation="horizontal">
<ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_marginEnd="5dp"
android:src="@mipmap/ic_wechat_icon" />
<TextView
android:id="@+id/main_text_wechat_ver"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.8"
android:ellipsize="end"
android:singleLine="true"
android:text="%1"
android:textColor="@color/white"
android:textSize="11sp" />
</LinearLayout>
</LinearLayout>
</HorizontalScrollView>
<TextView <TextView
android:id="@+id/main_text_api_way"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:alpha="0.6" android:alpha="0.6"
android:text="理论在小更新内还会生效,如果失效请看下方的联系方式" android:ellipsize="end"
android:singleLine="true"
android:text="%1"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="10sp" android:textSize="11sp"
tools:ignore="SmallSp" /> android:visibility="gone" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@@ -94,7 +226,6 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:fadingEdgeLength="10dp" android:fadingEdgeLength="10dp"
android:fillViewport="true" android:fillViewport="true"
android:overScrollMode="never"
android:requiresFadingEdge="vertical"> android:requiresFadingEdge="vertical">
<LinearLayout <LinearLayout
@@ -108,83 +239,175 @@
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:background="@drawable/white_round" android:background="@drawable/bg_permotion_round"
android:elevation="2dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="horizontal"
android:paddingLeft="15dp" android:padding="15dp">
android:paddingRight="15dp">
<androidx.appcompat.widget.SwitchCompat <androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/protect_mode_switch" android:layout_width="15dp"
android:layout_width="match_parent" android:layout_height="15dp"
android:layout_height="wrap_content" android:layout_marginEnd="15dp"
android:text="启用保守模式" android:alpha="0.85"
android:textColor="#FF323B42" android:src="@drawable/ic_about"
android:textSize="15sp" /> android:tint="@color/colorTextDark" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:alpha="0.6" android:alpha="0.6"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="5dp"
android:text="此选项默认关闭,默认情况下模块将会干掉 QQ 和 TIM 自身的电源锁控制类,开启后模块将只对系统电源锁生效,如果你的 QQ 或 TIM 视频通话等设置发生了故障,可以尝试开启这个功能,开启后请重启 QQ 或 TIM。" android:text="你可以点击上述每个图标查看最佳兼容的 APP 版本。\n没有标注的版本在适配范围内的 APP 适用性都将有效,但可能不能达到最佳使用效果,建议保持使用适配内的版本。"
android:textColor="#777777" android:textColor="@color/colorTextGray"
android:textSize="12sp" /> android:textSize="10sp"
tools:ignore="SmallSp" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/quick_action_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
android:layout_marginRight="15dp"
android:background="@drawable/bg_permotion_round"
android:elevation="0dp"
android:gravity="center"
android:orientation="vertical"
android:padding="15dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:gravity="center|start">
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
app:cardBackgroundColor="#FF00BCD4"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2.5dp"
android:src="@drawable/ic_fast_op" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:text="快捷操作"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/quick_qq_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="1"
android:background="@drawable/bg_button_round"
android:gravity="center"
android:padding="10dp"
android:singleLine="true"
android:text="QQ"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:id="@+id/quick_tim_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="1"
android:background="@drawable/bg_button_round"
android:gravity="center"
android:padding="10dp"
android:singleLine="true"
android:text="TIM"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:id="@+id/quick_wechat_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_button_round"
android:gravity="center"
android:padding="10dp"
android:singleLine="true"
android:text="微信"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/display_setting_item"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:background="@drawable/white_round" android:background="@drawable/bg_permotion_round"
android:elevation="2dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="15dp" android:paddingLeft="15dp"
android:paddingTop="15dp"
android:paddingRight="15dp"> android:paddingRight="15dp">
<androidx.appcompat.widget.SwitchCompat <LinearLayout
android:id="@+id/notify_module_info_switch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="提示模块运行信息" android:gravity="center|start">
android:textColor="#FF323B42"
android:textSize="15sp" />
<TextView <androidx.cardview.widget.CardView
android:layout_width="match_parent" android:layout_width="15dp"
android:layout_height="wrap_content" android:layout_height="15dp"
android:layout_marginBottom="10dp" android:layout_marginEnd="10dp"
android:alpha="0.6" app:cardBackgroundColor="#FFFF9800"
android:lineSpacingExtra="6dp" app:cardCornerRadius="50dp"
android:text="模块工作正常情况下不要开启,如果你想测试模块是否正常激活,可以打开此提示,开启后将会在启动 QQ 或 TIM 的时候提示运行信息。" app:cardElevation="0dp">
android:textColor="#777777"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout <ImageView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginLeft="15dp" android:layout_gravity="center"
android:layout_marginTop="15dp" android:padding="2.5dp"
android:layout_marginRight="15dp" android:src="@drawable/ic_home" />
android:background="@drawable/white_round" </androidx.cardview.widget.CardView>
android:elevation="2dp"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingRight="15dp">
<androidx.appcompat.widget.SwitchCompat <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:text="显示设置"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
</LinearLayout>
<com.fankes.tsbattery.ui.widget.MaterialSwitch
android:id="@+id/hide_icon_in_launcher_switch" android:id="@+id/hide_icon_in_launcher_switch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="在桌面隐藏模块图标" android:text="在桌面隐藏模块图标"
android:textColor="#FF323B42" android:textColor="@color/colorTextGray"
android:textSize="15sp" /> android:textSize="15sp" />
<TextView <TextView
@@ -193,8 +416,18 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.6" android:alpha="0.6"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="隐藏模块图标后,模块不会再在桌面显示,你可以在 EdXposed、太极、LsPosed 中找到模块设置并打开。" android:text="隐藏模块图标后界面可能会被关闭,将不会再在桌面显示,你可以在 EdXposed、LSPosed 中找到模块设置并打开。"
android:textColor="#777777" android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="注意:请务必在 LSPosed 中关闭“强制显示桌面图标”功能"
android:textColor="#FF5722"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
@@ -204,35 +437,42 @@
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:background="@drawable/white_round" android:background="@drawable/bg_permotion_round"
android:elevation="2dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="15dp" android:padding="15dp"
android:paddingRight="15dp"> android:paddingTop="15dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginBottom="15dp"
android:layout_marginBottom="10dp"
android:gravity="center|start"> android:gravity="center|start">
<androidx.constraintlayout.utils.widget.ImageFilterView <androidx.cardview.widget.CardView
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_marginEnd="5dp" android:layout_marginEnd="10dp"
android:alpha="0.85" app:cardBackgroundColor="#FF03A9F4"
android:src="@mipmap/about" app:cardCornerRadius="50dp"
android:tint="#FF323B42" /> app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2dp"
android:src="@drawable/ic_info" />
</androidx.cardview.widget.CardView>
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:alpha="0.85" android:alpha="0.85"
android:singleLine="true" android:singleLine="true"
android:text="使用帮助说明" android:text="使用帮助&amp;说明"
android:textColor="#FF323B42" android:textColor="@color/colorTextGray"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
@@ -241,9 +481,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.8" android:alpha="0.8"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="10dp"
android:text="Q.这个模块是做什么的?\nA.此模块的诞生来源于国内厂商毒瘤 APP 强行霸占后台耗电QQ 在 8.6.0 版本以后也只是接入了 HMS 推送,但是可笑的是开发组却并没有删除之前疯狂耗电的接收消息方法,于是这个模块就诞生了。" android:text="Q.这个模块是做什么的?\nA.此模块的诞生来源于国内厂商毒瘤 APP 强行霸占后台耗电QQ 在 8.6.0 版本以后也只是接入了 HMS 推送,但是可笑的是开发组却并没有删除之前疯狂耗电的接收消息方法,于是这个模块就因此而诞生了。"
android:textColor="#777777" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
<TextView <TextView
@@ -251,9 +491,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.8" android:alpha="0.8"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="10dp"
android:text="Q.原理是什么?\nA.模块有两套工作方式,一种是针对 QQ、TIM Hook 掉系统自身的电源锁“WakeLock”使其不能影响系统休眠这样子在锁屏的时候 QQ、TIM 就可以进入睡眠状态。第二种就是针对 QQ、TIM 删除其自身的无用耗电疯狂循环检测后台强行保活服务。" android:text="Q.原理是什么?\nA.模块有两套工作方式,一种是针对 QQ、TIM Hook 掉系统自身的电源锁“WakeLock”使其不能影响系统休眠这样子在锁屏的时候 QQ、TIM 就可以进入睡眠状态。第二种就是针对 QQ、TIM 删除其自身的无用耗电疯狂循环检测后台强行保活服务。"
android:textColor="#777777" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
<TextView <TextView
@@ -261,9 +501,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.8" android:alpha="0.8"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="10dp"
android:text="Q.如何使用?\nA.目前模块支持 EdXposed、LsPosed 以及太极(无极)框架,在太极和 LsPosed 的作用域中,只需勾选 QQ 和 TIM 即可,模块可以做到即插即用,激活后无需重启手机,重启 QQTIM 就可以了。" android:text="Q.如何使用?\nA.目前模块支持 LSPosed、LSPatch 以及太极和一些常见的免 Root 框架,在太极和 LSPosed 的作用域中,只需勾选 QQ、TIM、微信即可,模块可以做到即插即用,激活后无需重启手机,重启 QQTIM 或微信就可以了。"
android:textColor="#777777" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
<TextView <TextView
@@ -271,9 +511,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.8" android:alpha="0.8"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="10dp"
android:text="Q.激活后一定可以非常省电吗?\nA.并不,模块只能减少 QQ、TIM 的耗电,但是请务必记住这一点,省电只是一个理论上的东西,实际水平由你使用的系统和硬件决定,如果你在前台疯狂使用 QQ、TIM那么照样会耗电模块只能保证后台运行和锁屏时毒瘤不会消耗过多的无用的电量仅此而已。" android:text="Q.配置界面在哪里?\nA.从 4.0 版本开始模块已将配置界面转移到目标 APP 中,你可以从以下途径找到模块配置界面:\nQQ: 设置 > TSBattery\nTIM: 设置 > TSBattery\n微信: 设置 > 右上角 TSBattery 图标\n你还可以从此界面上方的“快捷操作”中快速进入指定配置界面。"
android:textColor="#777777" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
<TextView <TextView
@@ -281,59 +521,38 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.8" android:alpha="0.8"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="10dp"
android:text="Q.激活后一定可以非常省电吗?\nA.并不,模块只能减少 QQ、TIM、微信的耗电但是请务必记住这一点省电只是一个理论上的东西实际水平由你使用的系统和硬件决定如果你在前台疯狂使用 QQ、TIM那么照样会耗电模块只能保证后台运行和锁屏时毒瘤不会消耗过多的无用的电量仅此而已。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:alpha="0.8"
android:lineSpacingExtra="10dp"
android:text="Q.模块是否需要挂后台?\nA.模块完全不需要挂后台,模块只是一个控制和显示的工具,真正的任务交由 Hook 处理,若出现失效的情况请发送模块运行日志给我们而不是将模块挂后台。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:alpha="0.8"
android:lineSpacingExtra="10dp"
android:text="Q.关于目前微信的适配情况?\nA.微信适配尚在实验阶段,敬请期待。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.8"
android:lineSpacingExtra="10dp"
android:text="Q.如何反馈问题?\nA.酷安关注 @星夜不荟" android:text="Q.如何反馈问题?\nA.酷安关注 @星夜不荟"
android:textColor="#777777" android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/link_with_project_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:layout_marginRight="15dp"
android:background="@drawable/white_round"
android:elevation="2dp"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingRight="15dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:gravity="center|start">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="5dp"
android:alpha="0.85"
android:src="@mipmap/about"
android:tint="#FF323B42" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:text="项目地址"
android:textColor="#FF323B42"
android:textSize="12sp" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:alpha="0.8"
android:lineSpacingExtra="6dp"
android:text="本软件是免费开源项目,遵循 GPL 协议,你可以点击这里前往 Github 查看源码以及获取模块更新。\n严禁以任何形式贩卖、商用本软件否则开发者有权追究其法律责任。"
android:textColor="#777777"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
@@ -344,31 +563,25 @@
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:background="@drawable/white_round" android:background="@drawable/bg_permotion_round"
android:elevation="2dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="15dp" android:paddingLeft="15dp"
android:paddingRight="15dp"> android:paddingRight="15dp">
<TextView <TextView
android:id="@+id/link_with_follow_me"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:gravity="center" android:gravity="center"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="恰饭时间\n酷安关注我,获取我的更多应用" android:text="点击这里前往酷安关注我,获取我的更多应用"
android:textColor="#FF323B42" android:textColor="@color/colorTextGray"
android:textSize="16sp" /> android:textSize="16sp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:src="@mipmap/qr_pay"
tools:ignore="ContentDescription" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -376,9 +589,67 @@
android:gravity="center" android:gravity="center"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="开发者 酷安 @星夜不荟\n未经允许不得转载、修改复制我的劳动成果" android:text="开发者 酷安 @星夜不荟\n未经允许不得转载、修改复制我的劳动成果"
android:textColor="#FF323B42" android:textColor="@color/colorTextGray"
android:textSize="16sp" /> android:textSize="16sp" />
</LinearLayout> </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp"
android:background="@drawable/bg_permotion_round"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_yukihookapi" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:ellipsize="end"
android:lineSpacingExtra="6dp"
android:maxLines="2"
android:text="此模块使用 YukiHookAPI 构建。\n了解更多 https://github.com/HighCapable/YukiHookAPI"
android:textColor="@color/colorTextGray"
android:textSize="11sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp"
android:background="@drawable/bg_permotion_round"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_kavaref" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:ellipsize="end"
android:lineSpacingExtra="6dp"
android:maxLines="2"
android:text="此模块使用 KavaRef 强力驱动。\n了解更多 https://github.com/HighCapable/KavaRef"
android:textColor="@color/colorTextGray"
android:textSize="11sp" />
</LinearLayout>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</LinearLayout> </LinearLayout>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorThemeBackground">#FF2D2D2D</color>
<color name="colorTextDark">#FFCFCFCF</color>
<color name="colorTextGray">#FFD3D3D3</color>
</resources>

View File

@@ -1,16 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.TSBattery" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <style name="Theme.TSBattery" parent="Theme.Material3.DayNight">
<!-- Primary brand color. --> <!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item> <item name="colorPrimary">@color/colorPrimaryAccent</item>
<item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorPrimaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnPrimary">@color/black</item> <item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. --> <!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item> <item name="colorSecondary">@color/colorPrimaryAccent</item>
<item name="colorSecondaryVariant">@color/teal_200</item> <item name="colorSecondaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnSecondary">@color/black</item> <item name="colorOnSecondary">@color/white</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
</style> </style>
</resources> </resources>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="module_scope">
<item>com.tencent.mobileqq</item>
<item>com.tencent.tim</item>
<item>com.tencent.mm</item>
</string-array>
</resources>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorThemeBackground">#FFFFFFFF</color>
<color name="colorTextDark">#FF777777</color>
<color name="colorTextGray">#FF323B42</color>
</resources>

View File

@@ -1,10 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="purple_200">#656565</color> <color name="colorPrimaryAccent">#656565</color>
<color name="purple_500">#656565</color>
<color name="purple_700">#656565</color>
<color name="teal_200">#656565</color>
<color name="teal_700">#656565</color>
<color name="black">#FF000000</color> <color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color> <color name="white">#FFFFFFFF</color>
</resources> </resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="tsbattery_qq_entry_item_id" type="id" />
</resources>

View File

@@ -1,16 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.TSBattery" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <style name="Theme.TSBattery" parent="Theme.Material3.DayNight">
<!-- Primary brand color. --> <!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item> <item name="colorPrimary">@color/colorPrimaryAccent</item>
<item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorPrimaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnPrimary">@color/teal_700</item> <item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. --> <!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item> <item name="colorSecondary">@color/colorPrimaryAccent</item>
<item name="colorSecondaryVariant">@color/teal_700</item> <item name="colorSecondaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnSecondary">@color/black</item> <item name="colorOnSecondary">@color/white</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
</style> </style>
</resources> </resources>

View File

@@ -1,30 +0,0 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.4.20"
repositories {
google()
//noinspection JcenterRepositoryObsolete
jcenter()
}
dependencies {
//noinspection AndroidGradlePluginVersion
classpath "com.android.tools.build:gradle:4.1.1"
//noinspection DifferentKotlinGradleVersion
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
//noinspection JcenterRepositoryObsolete
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

21
build.gradle.kts Normal file
View File

@@ -0,0 +1,21 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.ksp) apply false
}
allprojects {
tasks.withType<KotlinJvmCompile>().configureEach {
compilerOptions {
jvmTarget = JvmTarget.JVM_17
freeCompilerArgs.addAll(
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions"
)
}
}
}

View File

@@ -1,21 +1,17 @@
# Project-wide Gradle settings. # Compiler Configuration
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX android.nonTransitiveRClass=true
android.enableJetifier=true kotlin.code.style=official
# Kotlin code style for this project: "official" or "obsolete": # Project Configuration
kotlin.code.style=official project.name=TSBattery
project.android.compileSdk=36
project.android.minSdk=24
project.android.targetSdk=36
project.app.packageName=com.fankes.tsbattery
project.app.versionName="4.4"
project.app.versionCode=30
project.app.signing.keyAlias=public
project.app.signing.keyPassword="123456"
project.app.signing.storePassword="123456"
project.app.signing.storeFilePath=.secret/universal.p12

52
gradle/libs.versions.toml Normal file
View File

@@ -0,0 +1,52 @@
[versions]
agp = "8.13.2"
kotlin = "2.2.21"
ksp = "2.2.21-2.0.4"
project-promote = "1.0.1"
rovo89-xposed-api = "82"
yukihookapi = "1.3.1"
kavaref-core = "1.0.2"
kavaref-extension = "1.0.2"
hikage-core = "1.0.4"
hikage-compiler = "1.0.4"
hikage-extension = "1.0.3"
hikage-widget-androidx = "1.0.1"
hikage-widget-material = "1.0.1"
dexkit = "2.0.7"
drawabletoolbox = "1.0.7"
okhttp = "5.3.2"
androidx-core-ktx = "1.17.0"
androidx-appcompat = "1.7.1"
material = "1.13.0"
androidx-constraintlayout = "2.2.1"
junit = "4.13.2"
androidx-test-junit = "1.3.0"
androidx-test-espresso-core = "3.7.0"
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
[libraries]
project-promote = { group = "com.fankes.projectpromote", name = "project-promote", version.ref = "project-promote" }
rovo89-xposed-api = { group = "de.robv.android.xposed", name = "api", version.ref = "rovo89-xposed-api" }
yukihookapi = { group = "com.highcapable.yukihookapi", name = "api", version.ref = "yukihookapi" }
yukihookapi-ksp-xposed = { group = "com.highcapable.yukihookapi", name = "ksp-xposed", version.ref = "yukihookapi" }
kavaref-core = { group = "com.highcapable.kavaref", name = "kavaref-core", version.ref = "kavaref-core" }
kavaref-extension = { group = "com.highcapable.kavaref", name = "kavaref-extension", version.ref = "kavaref-extension" }
hikage-core = { group = "com.highcapable.hikage", name = "hikage-core", version.ref = "hikage-core" }
hikage-compiler = { group = "com.highcapable.hikage", name = "hikage-compiler", version.ref = "hikage-compiler" }
hikage-extension = { group = "com.highcapable.hikage", name = "hikage-extension", version.ref = "hikage-extension" }
hikage-widget-androidx = { group = "com.highcapable.hikage", name = "hikage-widget-androidx", version.ref = "hikage-widget-androidx" }
hikage-widget-material = { group = "com.highcapable.hikage", name = "hikage-widget-material", version.ref = "hikage-widget-material" }
dexkit = { group = "org.luckypray", name = "dexkit", version.ref = "dexkit" }
drawabletoolbox = { group = "com.github.duanhong169", name = "drawabletoolbox", version.ref = "drawabletoolbox" }
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" }
androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-test-espresso-core" }

View File

@@ -1,6 +1,5 @@
#Sat Sep 04 04:05:23 CST 2021
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

Some files were not shown because too many files have changed in this diff Show More