174 Commits
4.1 ... master

Author SHA1 Message Date
b7015f8a69 chore: bump dependencies 2025-08-24 03:00:30 +08:00
d39f50642a chore: update target sdk to 36 2025-08-19 16:48:18 +08:00
5a6a61b4be chore: update jdk to 21 2025-08-19 16:42:28 +08:00
114ec89764 chore: bump gradle to 8.14.3 2025-08-19 16:42:21 +08:00
476b6d6be6 chore: bump dependencies 2025-08-19 16:42:14 +08:00
53249108d1 refactor: update KavaRef usage to 1.0.1 2025-07-06 21:29:16 +08:00
147e4c117e refactor: migrate and update to YukiHookAPI 1.3.0 2025-06-25 23:20:11 +08:00
276820fdc5 docs: update README 2025-06-24 14:10:17 +08:00
0b7ade9076 docs: update README 2025-06-20 12:48:37 +08:00
6c26a8b0cd chore: disable type auto conversion for sweet-property 2025-05-09 23:19:38 +08:00
d925873c4c feat: test migrate to Hikage(https://github.com/BetterAndroid/Hikage) 2025-04-22 09:40:20 +08:00
9fb52332d5 feat: add BaseActivity2 2025-04-22 09:39:30 +08:00
bf92f0241c chore: bump dependencies 2025-03-16 23:02:29 +08:00
7ec8de4be0 chore: bump gradle to 8.13 2025-03-16 23:02:23 +08:00
df949cf56b chore: update project files 2025-03-16 23:02:14 +08:00
f1061b27fa chore: bump ci to v4 2025-02-22 01:05:12 +08:00
b8543130cc chore: update .gitignore 2025-02-22 01:05:00 +08:00
9ae395ecee docs: update license 2025-01-13 11:12:54 +08:00
8d2d233b93 refactor: support Android 15 edge-to-edge system bars 2024-11-25 18:50:55 +08:00
70757b1594 refactor: support TIM 4.0+ 2024-11-13 15:39:05 +08:00
a14a38e00f chore: update target sdk to 35 2024-11-10 20:40:51 +08:00
910c67a943 chore: some tweaks in build.gradle.kts 2024-11-10 20:40:40 +08:00
6a31eef23e chore: update project files 2024-11-10 20:30:10 +08:00
0517df5dc4 chore: bump dependencies 2024-11-10 20:09:28 +08:00
f25472b124 chore: update project files 2024-11-10 20:09:19 +08:00
ebb68c69b4 chore: bump gradle to 8.10.2 2024-11-10 20:09:12 +08:00
c89eb39a75 chore: bump dependencies 2024-09-29 00:06:34 +08:00
43e0dff6cb fix: wake lock acquire not release will cause problem in BasicHookFactory 2024-09-06 18:25:07 +08:00
215d680955 chore: bump dependencies 2024-09-05 10:56:47 +08:00
c25f316512 chore: bump gradle to 8.8 2024-09-01 00:09:05 +08:00
798a55a328 chore: update .editorconfig 2024-09-01 00:07:56 +08:00
3dea65c4c6 chore: update .editorconfig 2024-06-21 10:09:50 +08:00
46efaf7083 chore: update project files 2024-06-21 10:09:46 +08:00
760b7a42cd chore: bump dependencies 2024-06-20 11:12:02 +08:00
6be267f74d chore: bump gradle to 8.7 2024-06-20 11:11:57 +08:00
7d1dbbbf44 chore: update .editorconfig 2024-06-20 11:07:14 +08:00
fb0182f075 feat: support QQ NT (>= 9.0.0) version 2024-06-17 01:36:00 +08:00
80e89d087f refactor: make some change for dexkit 2024-06-17 01:22:15 +08:00
c5711338e3 chore: bump dependencies 2024-02-20 11:29:04 +08:00
2fac3c2709 chore: bump gradle to 8.6 2024-02-20 11:29:01 +08:00
7b65ca01fb chore: update .editorconfig 2024-01-13 21:22:31 +08:00
4d75fb9e8c chore: update project files 2024-01-13 21:22:25 +08:00
13d25dcafd docs: update copyright date to 2024 for all existing files 2024-01-01 01:29:39 +08:00
1298077c9f style: merge to new ktlint version & rules 2023-12-29 21:32:04 +08:00
374ffaab4f chore: bump dependencies 2023-12-28 23:46:46 +08:00
76904665cc chore: bump gradle to 8.5 2023-12-28 23:42:36 +08:00
dfdb18dcb7 chore: update .gitignore 2023-12-28 23:42:25 +08:00
d1fb794284 docs: use relative link 2023-12-11 02:48:23 +08:00
3adaad1040 chore: update project files 2023-12-11 02:48:19 +08:00
39b132cc1a chore: bump dependency versions 2023-12-11 00:00:19 +08:00
13837b3b33 docs: update piracy statement 2023-12-11 00:00:14 +08:00
4405808ff3 docs: update promotion 2023-11-18 18:15:31 +08:00
7c66519e2d chore: bump "com.highcapable.sweetdependency" version to 1.0.4 2023-11-14 01:01:15 +08:00
9b620d7688 chore: bump "com.highcapable.sweetproperty" version to 1.0.5 2023-11-08 15:13:34 +08:00
cde830b804 chore: update project files 2023-11-08 15:13:30 +08:00
884a743338 chore: bump dependencies 2023-11-04 04:01:34 +08:00
e9aa721902 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
fa967c8dfb docs: replace download links 2023-11-03 14:32:07 +08:00
7fe6bd91f0 docs: update release channel 2023-10-26 21:15:02 +08:00
8025b45b06 Bump version to 4.4 2023-10-21 00:56:03 +08:00
d8b3884cb0 feat(docs): update YukiHookAPI owner link 2023-10-21 00:55:59 +08:00
1a3c8d9bcb refactor: remove DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION 2023-10-21 00:48:02 +08:00
b6d8040bfa refactor: make state to unsupported when methods not found 2023-10-21 00:36:40 +08:00
d4f8d7d3fd chore: bump "org.luckypray:dexkit" version to 2.0.0-rc7 2023-10-21 00:33:31 +08:00
a4970747c0 chore: update target sdk to 34 2023-10-21 00:30:38 +08:00
4a8816de37 chore: bump "org.luckypray:dexkit" version to 2.0.0-rc5 2023-10-09 01:26:44 +08:00
6969d7b2c6 feat: simple support dexkit test 2023-10-08 03:55:25 +08:00
ea0b46020f refactor: migrate to YukiHookAPI new usage 2023-10-07 20:51:34 +08:00
4436815525 chore: bump dependency versions 2023-10-07 19:45:35 +08:00
e96b6a5d2a feat: support QQ 8.9.83 2023-10-02 17:35:15 +08:00
576c8574c8 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
94d425fcf7 refactor: add new R8 rules to fix possible problems 2023-09-19 08:18:53 +08:00
58510cef52 feat: support QQ 8.9.80 2023-09-16 01:17:19 +08:00
efe000d690 docs: optimize comments 2023-09-16 01:13:55 +08:00
c9d49a398f 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
bd027b37f5 docs: update README 2023-09-16 01:11:03 +08:00
2e56baa6c7 style: optimize code 2023-09-16 01:11:02 +08:00
147b7fa6f5 docs: move banner to img-src 2023-09-16 01:11:02 +08:00
6bd906a51e refactor: use new payment code 2023-09-16 01:11:02 +08:00
5c92671ae2 fix: class not found when R8 since android gradle plugin 8+ 2023-09-16 01:11:02 +08:00
b5bb7eb892 ci: optimize and add artifacts post to Telegram 2023-09-16 01:11:02 +08:00
3361284f16 chore: add Android 14 option 2023-09-16 01:11:02 +08:00
5b4da6df45 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
f520983d32 chore: clean up build step files 2023-09-16 00:53:18 +08:00
1e67116f0a [Change Commit Specification] Use the new commit spec from here on
child commits:
chore: add .editorconfig
2023-09-16 00:53:17 +08:00
7257127c0f Modify support QQ 8.9.78 2023-09-02 18:18:06 +08:00
63d5043f31 Modify support QQ 8.9.75~8.9.76 2023-08-19 04:28:08 +08:00
64c4c282b2 Modify support QQ 8.9.73 2023-08-03 21:31:52 +08:00
89cbc49287 Modify support QQ 8.9.71 2023-08-01 18:05:30 +08:00
6b0fe1014e Update version to 4.3 | Support QQ 8.9.53~8.9.70 versions 2023-07-26 02:59:29 +08:00
bdf52ba463 Modify support QQ 8.9.70 2023-07-26 02:46:17 +08:00
c2b95b9133 Modify support QQ 8.9.70 (QQ-NT) hook entry item 2023-07-26 02:45:56 +08:00
2f64fb9ea9 Modify support QQ 8.9.58~8.9.68 2023-07-18 22:40:08 +08:00
f48277f434 Modify optimize code in QQTIMHooker 2023-07-18 22:35:28 +08:00
047f746afb Modify support QQ 8.9.55 2023-05-17 07:35:17 +08:00
ac885baa64 Update Gradle dependencies 2023-05-17 07:35:09 +08:00
4332881dad Modify support QQ 8.9.53 2023-04-27 01:23:06 +08:00
49c0655412 Update YukiHookAPI 2023-04-25 06:57:15 +08:00
7eeda27937 Update YukiHookAPI 2023-04-21 01:25:06 +08:00
a8692e8a33 Update version to 4.25 | Support QQ 8.9.30~8.9.50 versions 2023-04-17 06:19:55 +08:00
39ec0e8ef7 Modify change need restart dialog to prompt in ConfigActivity, activity_config 2023-04-17 05:59:55 +08:00
1dc22b90bb Modify merge to YukiHookAPI new usage 2023-04-17 05:50:35 +08:00
da955fc157 Update YukiHookAPI 2023-04-17 05:45:05 +08:00
94752010ec Modify merge contents of build.gradle into constant definitions 2023-04-15 22:35:36 +08:00
f9bc6fc013 Modify support QQ 8.9.50 2023-04-14 17:48:06 +08:00
dcd95e4d8d 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
7f63f93165 Modify support QQ 8.9.38 2023-04-05 05:16:10 +08:00
e95af0bad9 Modify support QQ 8.9.35 2023-03-20 18:13:55 +08:00
344104903b Modify support QQ 8.9.33 2023-03-03 11:51:55 +08:00
249cec2bee Modify support QQ 8.9.30 2023-02-22 06:19:35 +08:00
42387603c6 Modify change package resources id cause some same host modules conflict with it in build.gradle 2023-02-22 06:17:48 +08:00
fb3aa96c0a Update Gradle dependencies 2023-02-22 06:15:05 +08:00
f7c31d9786 Added star history chart in README 2023-02-19 14:35:35 +08:00
8d00aca110 Fix "GitHub" spelling in all files 2023-02-07 05:29:35 +08:00
00a1aaad5b Modify change ProgressBar to CircularProgressIndicator in DialogBuilderFactory 2023-02-03 23:19:05 +08:00
a4602df9cb Modify optimize code in MainActivity 2023-02-03 01:43:05 +08:00
d5624e0e24 Update YukiHookAPI 2023-02-01 04:25:05 +08:00
60f9af38b2 Update .gitignore 2023-02-01 04:23:15 +08:00
d3bea2ec83 Update Gradle dependencies 2023-02-01 03:30:05 +08:00
d085659fbb Added new bug report issues template 2023-01-31 19:12:35 +08:00
f2ec8bf33e Update YukiHookAPI 2023-01-21 00:59:49 +08:00
3233e772bb Update Android Gradle Plugin to 7.4.0 2023-01-21 00:59:32 +08:00
e58da11553 Update Gradle dependencies 2023-01-19 22:27:32 +08:00
90d4c04593 Update README.md 2023-01-18 00:49:01 +08:00
bcb7f6b2c5 Modify rename ui/view to ui/widget 2023-01-17 11:14:27 +08:00
596726e8c7 Fix the central color problem of views such as CheckBox 2023-01-16 22:39:27 +08:00
0a52914911 Update copyright date to 2023 for all existing file 2023-01-14 10:57:18 +08:00
3c30c532a4 Update version to 4.2 | Support many QQ 8.9.x version 2023-01-14 00:37:02 +08:00
8f7772d047 Modify remove localTime function time second format and change description text in GithubReleaseTool 2023-01-14 00:16:45 +08:00
c148a535c8 Modify change promote message in YukiPromoteTool 2023-01-14 00:13:53 +08:00
a5448a2c61 Modify remove "endsWith" method's param name statement 2023-01-14 00:12:55 +08:00
a8a01e8afa Modify change related description text in activity_main 2023-01-14 00:08:34 +08:00
c322c3dc29 Modify change related description text in MainActivity 2023-01-14 00:00:33 +08:00
2e9761f01a Added executor info and change some description text in ConfigActivity, activity_config 2023-01-13 23:57:52 +08:00
0fd1a33533 Modify remove "replace" method's param name statement 2023-01-13 23:20:26 +08:00
e465739f95 Modify remove instanceClass param for hookQQSettingsUI function in QQTIMHooker 2023-01-13 22:38:14 +08:00
52f992a6cd Modify change related description text in activity_config 2023-01-13 22:24:25 +08:00
37be07f11d Modify change isOnFailureThrowToApp to false for onAppLifecycle event in QQTIMHooker 2023-01-13 22:19:53 +08:00
d8d9fc8c41 Modify make HookEntry singleton 2023-01-13 22:13:50 +08:00
f27fdb2f25 Modify merge to YukiHookAPI new usage 2023-01-13 04:56:15 +08:00
3629ca3df5 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
12a1c12f8a Update YukiHookAPI 2023-01-13 04:50:50 +08:00
bc41ac2bdc Fix internal browser X5 kernel issues (cause QQ crashed) 2023-01-12 15:00:52 +08:00
5f13e203f0 Fix WeChat SettingsUI's top right TSBattery icon lost problem when version >= 8.0.28 2023-01-12 03:17:01 +08:00
69a68999a5 Fix not found QQSettingSettingFragment Class when not in Pad Mode (disabled error output) 2023-01-11 02:45:02 +08:00
bc48d59b59 Fix TaiChi activation state lost problem for API version higher than 30 2023-01-02 17:43:24 +08:00
cadf7b8873 Modify support QQ 8.9.28 2023-01-01 01:06:11 +08:00
7f0c8dd2b5 Modify support QQ 8.9.25 2022-12-16 02:21:14 +08:00
67349c45fd Modify support QQ 8.9.23 2022-11-30 22:37:34 +08:00
4ab612d5fa Modify add release channel description, release status description in README 2022-11-26 00:30:31 +08:00
bb4b942dae Added automatic build workflows for Github Actions 2022-11-26 00:29:54 +08:00
ff0931b7bc Modify support QQ 8.9.20 2022-11-18 01:50:26 +08:00
fc42a0ddf0 Modify support QQ 8.9.19 2022-11-08 23:45:27 +08:00
abab096ae0 Modify support QQ 8.9.18 2022-11-03 10:35:47 +08:00
ce8e786b5b Modify change logo url to raw in README 2022-11-03 10:35:32 +08:00
dd6c69b337 Modify remove wrap/part at the end of file 2022-11-03 10:18:47 +08:00
69564cccf6 Update Gradle & PlatformSDK
- Update Kotlin version to 1.7.20
2022-11-03 10:17:50 +08:00
a8c16d7823 Modify support QQ Pad Mode for QQ Settings UI entry item since 8.9.15 2022-10-22 02:00:57 +08:00
3616919978 Update Gradle dependencies 2022-10-22 01:11:11 +08:00
c5dfbcbe0d Modify support QQ 8.9.15 2022-10-22 01:09:32 +08:00
f21e8769e0 Modify change not support version log to warn level in QQTIMHooker 2022-10-21 21:24:57 +08:00
eada17db44 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
67209859c4 Modify merge to new usage in HookEntry 2022-10-04 07:29:37 +08:00
a65b3c1dbe Update YukiHookAPI 2022-10-04 07:28:56 +08:00
6375aa7844 Update README.md 2022-10-02 22:57:46 +08:00
cbd6fd038d Modify merge all png elements to svg elements 2022-10-02 22:55:44 +08:00
7fcf9228f4 Modify change icon to svg in activity_main 2022-10-02 21:38:49 +08:00
d995c69b5c Fix variable name shadowed bug in QQTIMHooker 2022-10-02 21:26:17 +08:00
376a2a5890 Modify compatible with API 33 2022-10-01 02:16:55 +08:00
e2ff60e2ef Modify standard code naming in FunctionFactory 2022-10-01 00:46:56 +08:00
ad0b6d253a Update YukiHookAPI 2022-09-30 23:47:24 +08:00
76af063ed8 Modify change code note in MainActivity 2022-09-30 22:21:16 +08:00
e0fdf269a0 Modify optimize code style in HookEntry 2022-09-30 22:14:07 +08:00
b1d4cd4017 Added Project icon 2022-09-30 22:10:48 +08:00
86 changed files with 2575 additions and 1484 deletions

35
.editorconfig Normal file
View File

@@ -0,0 +1,35 @@
# noinspection EditorConfigKeyCorrectness
[{*.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_class-signature = disabled
ktlint_standard_no-trailing-spaces = disabled
ktlint_standard_multiline-loop = 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 }}

117
.gitignore vendored
View File

@@ -1,15 +1,110 @@
## 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/workspace.xml # Misc
/.idea/navEditor.xml .idea/misc.xml
/.idea/assetWizardSettings.xml
.DS_Store # CMake
/build 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
**/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
# Mac OS
.DS_Store

3
.idea/.gitignore generated vendored
View File

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

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

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>

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="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="11" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

BIN
.idea/icon.png generated Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -7,5 +7,6 @@
<option name="processLiterals" value="true" /> <option name="processLiterals" value="true" />
<option name="processComments" value="true" /> <option name="processComments" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="YAMLSchemaValidation" enabled="false" level="WARNING" enabled_by_default="false" />
</profile> </profile>
</component> </component>

View File

@@ -1,45 +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>
<remote-repository>
<option name="id" value="maven3" />
<option name="name" value="maven3" />
<option name="url" value="https://www.jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://maven.aliyun.com/nexus/content/groups/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="https://maven.aliyun.com/nexus/content/repositories/jcenter" />
</remote-repository>
</component>
</project>

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

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.1.10" />
</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>

24
.idea/misc.xml generated
View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
<map>
<entry key="app/src/main/res/drawable-v24/dark_round.xml" value="0.4482051282051282" />
<entry key="app/src/main/res/drawable/bg_orange_round.xml" value="0.229" />
<entry key="app/src/main/res/drawable/bg_permotion_round.xml" value="0.2495" />
<entry key="app/src/main/res/drawable/button_round.xml" value="0.44871794871794873" />
<entry key="app/src/main/res/drawable/dark_round.xml" value="0.4482051282051282" />
<entry key="app/src/main/res/drawable/permotion_round.xml" value="0.4482051282051282" />
<entry key="app/src/main/res/drawable/red_round.xml" value="0.4482051282051282" />
<entry key="app/src/main/res/layout/activity_main.xml" value="0.3413246647704185" />
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.2495" />
</map>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

17
.idea/runConfigurations.xml generated Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<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>
</option>
</component>
</project>

View File

@@ -1,54 +1,92 @@
# TSBattery # TSBattery
[![Blank](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/fankes/TSBattery) [![GitHub license](https://img.shields.io/github/license/fankes/TSBattery?color=blue&style=flat-square)](https://github.com/fankes/TSBattery/blob/master/LICENSE)
[![Blank](https://img.shields.io/badge/license-AGPL3.0-blue)](https://github.com/fankes/TSBattery/blob/master/LICENSE) [![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)
[![Blank](https://img.shields.io/badge/version-v4.1-green)](https://github.com/fankes/TSBattery/releases) [![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)
[![Blank](https://img.shields.io/github/downloads/fankes/TSBattery/total?label=Release)](https://github.com/fankes/TSBattery/releases) ![GitHub all releases](https://img.shields.io/github/downloads/fankes/TSBattery/total?label=downloads&style=flat-square)
[![Blank](https://img.shields.io/github/downloads/Xposed-Modules-Repo/com.fankes.tsbattery/total?label=LSPosed%20Repo&logo=Android&style=flat&labelColor=F48FB1&logoColor=ffffff)](https://github.com/Xposed-Modules-Repo/com.fankes.tsbattery/releases) ![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](https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram)](https://t.me/XiaofangInternet)
<br/><br/> [![Telegram CI](https://img.shields.io/badge/CI%20builds-Telegram-blue.svg?logo=telegram&style=flat-square)](https://t.me/TSBattery_CI)
![banner](https://github.com/fankes/TSBattery/blob/master/banner.png)<br/> [![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. A new way to save your battery avoid cancer apps hacker it.
TSBattery 是一个旨在使 QQ、TIM、微信 变得更省电的开源 Xposed 模块。 TSBattery 是一个旨在使 QQ、TIM、微信 变得更省电的开源 Xposed 模块。
## Developer ## For Non-Chinese Users
[酷安 @星夜不荟](http://www.coolapk.com/u/876977) This Xposed Module is for use by specific apps for users in mainland China, you should not need it.
## 适配说明 ## 适配说明
- 支持并建议使用 **LSPosed**(若作用域没有自动出现推荐请勾选 QQ、TIM、微信) - 解锁 BootLoader 并安装 **KernelSU**、**Magisk** 的设备建议使用 [LSPosed](https://github.com/LSPosed/LSPosed)
- 可以使用 **~~EdXposed~~**,但随时停止支持 - **太极 (无极)** 支持性不是很好,建议使用 [LSPatch](https://github.com/LSPosed/LSPatch)
- **太极无极 · 阴** 支持性不是很好,建议使用 **太极无极 · 阳****LSPatch (推荐)** - 支持一些第三方 Xposed 框架,但是不保证其稳定性
- 支持 **Pine**(梦境模块) 但是部分功能有限制 - 支持一些第三方免 Root 框架例如**应用转生**、**SandVXposed**,但是不推荐使用,可能会造成封号风险
- 请不要使用 **~~应用转生~~**,发生封号情况后果自负 - 如果在微信设置界面右上角你无法找到 **TSBattery**
的图标,请尝试同时激活 [WeXposed (微X模块)](https://github.com/Xposed-Modules-Repo/com.fkzhang.wechatxposed)
- 如果微信不能在 **LSPosed** 中生效,请尝试勾选任意包含微信作用域的模块,例如 **微X模块** ## 发行渠道
## 请勿用于非法用途 | <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 自动构建 (测试版) |
|-----------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|---------------|
- 本模块发布地址仅有 [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.tsbattery/releases) | <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) | 正式版 (稳定版) |
[Release](https://github.com/fankes/TSBattery/releases) 及 [蓝奏云](https://fankes.lanzouy.com/b02zfz3sj),从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。 |------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------|-----------|
## 开始贡献 | <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) | 正式版 (稳定版) |
|------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|-----------|
欢迎为此项目进行新版本的适配代码贡献!<br/> | <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) | 正式版 (稳定版) |
|------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|-----------|
- [CONTRIBUTING](https://github.com/fankes/TSBattery/blob/master/CONTRIBUTING.md) 本模块发布地址仅限于上述所列出的地址,从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
## 注意事项
<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) - [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
``` ```
Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as it under the terms of the GNU Affero General Public License as
@@ -61,9 +99,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero 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/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
``` ```
Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI) Powered by [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI)
版权所有 © 2019-2022 Fankes Studio(qzmmcn@163.com) 版权所有 © 2017 Fankes Studio(qzmmcn@163.com)

4
app/.gitignore vendored
View File

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

View File

@@ -1,73 +0,0 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'com.google.devtools.ksp' version '1.7.10-1.0.6'
}
android {
signingConfigs {
debug {
storeFile file('../keystore/public')
storePassword '123456'
keyAlias 'public'
keyPassword '123456'
v1SigningEnabled true
v2SigningEnabled true
}
}
compileSdkVersion 33
defaultConfig {
applicationId "com.fankes.tsbattery"
minSdk 22
targetSdk 33
versionCode rootProject.ext.appVersionCode
versionName rootProject.ext.appVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled rootProject.ext.enableR8
shrinkResources rootProject.ext.enableR8
zipAlignEnabled rootProject.ext.enableR8
signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '11'
freeCompilerArgs = [
'-Xno-param-assertions',
'-Xno-call-assertions',
'-Xno-receiver-assertions'
]
}
buildFeatures {
viewBinding true
}
lintOptions {
checkReleaseBuilds false
}
aaptOptions.additionalParameters '--allow-reserved-package-id', '--package-id', '0x64'
}
dependencies {
compileOnly 'de.robv.android.xposed:api:82'
implementation 'com.highcapable.yukihookapi:api:1.1.2'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.1.2'
implementation 'com.github.duanhong169:drawabletoolbox:1.0.7'
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.7'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

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

@@ -0,0 +1,94 @@
plugins {
autowire(libs.plugins.android.application)
autowire(libs.plugins.kotlin.android)
autowire(libs.plugins.kotlin.ksp)
}
android {
namespace = property.project.app.packageName
compileSdk = property.project.android.compileSdk
signingConfigs {
create("universal") {
keyAlias = property.project.app.signing.keyAlias
keyPassword = property.project.app.signing.keyPassword
storeFile = rootProject.file(property.project.app.signing.storeFilePath)
storePassword = property.project.app.signing.storePassword
enableV1Signing = true
enableV2Signing = true
}
}
defaultConfig {
applicationId = property.project.app.packageName
minSdk = property.project.android.minSdk
targetSdk = property.project.android.targetSdk
versionName = property.project.app.versionName
versionCode = property.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
// Workaround for GitHub Actions.
// Why? I don't know, but it works.
// Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
// public inline fun CharSequence.isNotBlank(): Boolean defined in kotlin.text.
@Suppress("UNNECESSARY_SAFE_CALL", "RemoveRedundantCallsOfConversionMethods")
val currentSuffix = property.github.ci.commit.id?.let { suffix ->
// Workaround for GitHub Actions.
// Strongly transfer type to [String].
val sSuffix = suffix.toString()
if (sSuffix.isNotBlank()) "-$sSuffix" else ""
}
val currentVersion = "${output.versionName.get()}$currentSuffix(${output.versionCode.get()})"
if (output is com.android.build.api.variant.impl.VariantOutputImpl)
output.outputFileName.set("${property.project.name}-v$currentVersion-$currentType.apk")
}
}
}
dependencies {
compileOnly(de.robv.android.xposed.api)
implementation(com.highcapable.yukihookapi.api)
ksp(com.highcapable.yukihookapi.ksp.xposed)
ksp(com.highcapable.hikage.hikage.compiler)
implementation(com.highcapable.kavaref.kavaref.core)
implementation(com.highcapable.kavaref.kavaref.extension)
implementation(com.highcapable.hikage.hikage.core)
implementation(com.highcapable.hikage.hikage.extension)
implementation(com.highcapable.hikage.hikage.widget.androidx)
implementation(com.highcapable.hikage.hikage.widget.material)
implementation(com.fankes.projectpromote.project.promote)
implementation(org.luckypray.dexkit)
implementation(com.github.duanhong169.drawabletoolbox)
implementation(com.squareup.okhttp3.okhttp)
implementation(androidx.core.core.ktx)
implementation(androidx.appcompat.appcompat)
implementation(com.google.android.material.material)
implementation(androidx.constraintlayout.constraintlayout)
testImplementation(junit.junit)
androidTestImplementation(androidx.test.ext.junit)
androidTestImplementation(androidx.test.espresso.espresso.core)
}

View File

@@ -32,16 +32,29 @@
-adaptresourcefilecontents -adaptresourcefilecontents
-renamesourcefileattribute P -renamesourcefileattribute P
-keepattributes SourceFile,LineNumberTable -keepattributes SourceFile,Signature,LineNumberTable
# 排除注入的 Activity # 排除注入的 Activity
-keep class com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity -keep class com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
# DexKit
-keep class org.luckypray.dexkit.DexKitBridge {
native <methods>;
}
# 防止某些类被 R8 混淆 (可能是 BUG)
# FIXME: 已知问题字符串类名 (即使是常量) 也会被 R8 处理为混淆后的类名
# FIXME: 所以目前只能把不允许 R8 处理的类 keep 掉,同时在当前模块中也不会被混淆就是了
-keep class kotlin.Unit
-keep class kotlin.jvm.functions.Function0
-assumenosideeffects class kotlin.jvm.internal.Intrinsics { -assumenosideeffects class kotlin.jvm.internal.Intrinsics {
public static *** throwUninitializedProperty(...); public static *** throwUninitializedProperty(...);
public static *** throwUninitializedPropertyAccessException(...); public static *** throwUninitializedPropertyAccessException(...);
} }
-keepclassmembers class * implements androidx.viewbinding.ViewBinding { -keep class * extends android.app.Activity
-keep class * implements androidx.viewbinding.ViewBinding {
<init>();
*** inflate(android.view.LayoutInflater); *** inflate(android.view.LayoutInflater);
} }

View File

@@ -1,16 +1,26 @@
<?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.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <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 --> <!-- 作用域 APP -->
<queries> <queries>
<package android:name="com.tencent.mobileqq" /> <package android:name="com.tencent.mobileqq" />
<package android:name="com.tencent.tim" /> <package android:name="com.tencent.tim" />
<package android:name="com.tencent.mm" /> <package android:name="com.tencent.mm" />
<intent>
<action android:name="android.intent.action.MAIN" />
</intent>
</queries> </queries>
<application <application

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,7 +17,7 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <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.
*/ */
package com.fankes.tsbattery.application package com.fankes.tsbattery.application

View File

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,10 +17,15 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/9/29. * This file is created by fankes on 2022/9/29.
*/ */
@file:Suppress("MemberVisibilityCanBePrivate")
package com.fankes.tsbattery.const package com.fankes.tsbattery.const
import com.fankes.tsbattery.generated.AppProperties
import com.fankes.tsbattery.wrapper.BuildConfigWrapper
/** /**
* 包名常量定义类 * 包名常量定义类
*/ */
@@ -44,3 +49,26 @@ object JumpEvent {
/** 启动模块设置 */ /** 启动模块设置 */
const val OPEN_MODULE_SETTING = "tsbattery_open_module_settings" 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

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,13 +17,16 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/9/28. * This file is created by fankes on 2022/9/28.
*/ */
@file:Suppress("StaticFieldLeak")
package com.fankes.tsbattery.data package com.fankes.tsbattery.data
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import android.widget.CompoundButton import android.widget.CompoundButton
import com.highcapable.yukihookapi.hook.factory.prefs
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookPrefsBridge
/** /**
* 全局配置存储控制类 * 全局配置存储控制类
@@ -42,30 +45,30 @@ object ConfigData {
/** 停用全部省电功能 (停用模块) */ /** 停用全部省电功能 (停用模块) */
const val DISABLE_ALL_HOOK = "disable_all_hook" const val DISABLE_ALL_HOOK = "disable_all_hook"
/** 当前的 [SharedPreferences] */ /** 当前的 [YukiHookPrefsBridge] */
private var sharePrefs: SharedPreferences? = null private var prefs: YukiHookPrefsBridge? = null
/** /**
* 读取 [SharedPreferences] * 读取 [YukiHookPrefsBridge]
* @param key 键值名称 * @param key 键值名称
* @param value 键值内容 * @param value 键值内容
* @return [Boolean] * @return [Boolean]
*/ */
private fun getBoolean(key: String, value: Boolean = false) = sharePrefs?.getBoolean(key, value) ?: value private fun getBoolean(key: String, value: Boolean = false) = prefs?.getBoolean(key, value) ?: value
/** /**
* 存入 [SharedPreferences] * 存入 [YukiHookPrefsBridge]
* @param key 键值名称 * @param key 键值名称
* @param value 键值内容 * @param value 键值内容
*/ */
private fun putBoolean(key: String, value: Boolean = false) = sharePrefs?.edit()?.putBoolean(key, value)?.apply() private fun putBoolean(key: String, value: Boolean = false) = prefs?.edit { putBoolean(key, value) }
/** /**
* 初始化 [SharedPreferences] * 初始化 [YukiHookPrefsBridge]
* @param context 实例 * @param context 实例
*/ */
fun init(context: Context) { fun init(context: Context) {
sharePrefs = context.getSharedPreferences("tsbattery_config", Context.MODE_PRIVATE) prefs = context.prefs(name = "tsbattery_config").native()
} }
/** /**

View File

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,10 +17,8 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/2/15. * This file is created by fankes on 2022/2/15.
*/ */
@file:Suppress("IMPLICIT_CAST_TO_ANY")
package com.fankes.tsbattery.hook package com.fankes.tsbattery.hook
import com.fankes.tsbattery.const.PackageName import com.fankes.tsbattery.const.PackageName
@@ -31,14 +29,11 @@ import com.highcapable.yukihookapi.hook.factory.configs
import com.highcapable.yukihookapi.hook.factory.encase import com.highcapable.yukihookapi.hook.factory.encase
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
@InjectYukiHookWithXposed(isUsingResourcesHook = false) @InjectYukiHookWithXposed
class HookEntry : IYukiHookXposedInit { object HookEntry : IYukiHookXposedInit {
companion object {
/** 是否完全支持当前版本 */ /** 是否完全支持当前版本 */
var isHookClientSupport = true var isHookClientSupport = true
}
override fun onInit() = configs { override fun onInit() = configs {
debugLog { tag = "TSBattery" } debugLog { tag = "TSBattery" }
@@ -47,8 +42,7 @@ class HookEntry : IYukiHookXposedInit {
} }
override fun onHook() = encase { override fun onHook() = encase {
loadApp(PackageName.QQ) { loadHooker(QQTIMHooker) } loadApp(PackageName.QQ, PackageName.TIM) { loadHooker(QQTIMHooker) }
loadApp(PackageName.TIM) { loadHooker(QQTIMHooker) } loadApp(PackageName.WECHAT, WeChatHooker)
loadApp(PackageName.WECHAT) { loadHooker(WeChatHooker) }
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,34 +17,49 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/9/29. * This file is created by fankes on 2022/9/29.
*/ */
@file:Suppress("ConstPropertyName")
package com.fankes.tsbattery.hook.entity package com.fankes.tsbattery.hook.entity
import android.app.Activity import android.app.Activity
import android.app.Service import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.os.Bundle
import android.os.Message
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
import com.fankes.tsbattery.BuildConfig 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.const.PackageName
import com.fankes.tsbattery.data.ConfigData import com.fankes.tsbattery.data.ConfigData
import com.fankes.tsbattery.hook.HookEntry import com.fankes.tsbattery.hook.HookEntry
import com.fankes.tsbattery.hook.factory.hookSystemWakeLock 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.jumpToModuleSettings
import com.fankes.tsbattery.hook.factory.startModuleSettings 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.fankes.tsbattery.utils.factory.dp
import com.fankes.tsbattery.utils.factory.versionName import com.highcapable.kavaref.KavaRef.Companion.asResolver
import com.highcapable.yukihookapi.hook.bean.VariousClass 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.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.* import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
import com.highcapable.yukihookapi.hook.log.loggerD import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
import com.highcapable.yukihookapi.hook.log.loggerE import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.log.loggerI import java.lang.reflect.Method
import com.highcapable.yukihookapi.hook.type.android.* import java.lang.reflect.Proxy
import com.highcapable.yukihookapi.hook.type.java.*
/** /**
* Hook QQ、TIM * Hook QQ、TIM
@@ -52,29 +67,64 @@ import com.highcapable.yukihookapi.hook.type.java.*
object QQTIMHooker : YukiBaseHooker() { object QQTIMHooker : YukiBaseHooker() {
/** QQ、TIM 存在的类 */ /** QQ、TIM 存在的类 */
const val JumpActivityClass = "${PackageName.QQ}.activity.JumpActivity" const val JumpActivityClassName = "${PackageName.QQ}.activity.JumpActivity"
/** QQ、TIM 存在的类 */ /** QQ、TIM 存在的类 */
private const val QQSettingSettingActivityClass = "${PackageName.QQ}.activity.QQSettingSettingActivity" private val JumpActivityClass by lazyClassOrNull(JumpActivityClassName)
/** QQ、TIM 存在的类 */ /** QQ、TIM 存在的类 (NT 版本不再存在) */
private const val AboutActivityClass = "${PackageName.QQ}.activity.AboutActivity" 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 新版本存在的类 */ /** QQ、TIM 新版本存在的类 */
private const val FormSimpleItemClass = "${PackageName.QQ}.widget.FormSimpleItem" private val FormSimpleItemClass by lazyClassOrNull("${PackageName.QQ}.widget.FormSimpleItem")
/** QQ、TIM 旧版本存在的类 */ /** QQ、TIM 旧版本存在的类 */
private const val FormCommonSingleLineItemClass = "${PackageName.QQ}.widget.FormCommonSingleLineItem" private val FormCommonSingleLineItemClass by lazyClassOrNull("${PackageName.QQ}.widget.FormCommonSingleLineItem")
/** QQ、TIM 存在的类 */ /** QQ、TIM 存在的类 */
private const val CoreServiceClass = "${PackageName.QQ}.app.CoreService" private val CoreServiceClass by lazyClassOrNull("${PackageName.QQ}.app.CoreService")
/** QQ、TIM 存在的类 */ /** QQ、TIM 存在的类 */
private const val CoreService_KernelServiceClass = "${PackageName.QQ}.app.CoreService\$KernelService" private val CoreService_KernelServiceClass by lazyClassOrNull("${PackageName.QQ}.app.CoreService\$KernelService")
/** 根据多个版本存的不同的类 */ /** 根据多个版本存的不同的类 */
private val BaseChatPieClass = private val BaseChatPieClass by lazyClassOrNull(
VariousClass("${PackageName.QQ}.activity.aio.core.BaseChatPie", "${PackageName.QQ}.activity.BaseChatPie") 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] */ /** 默认的 [Configuration] */
var baseConfiguration: Configuration? = null var baseConfiguration: Configuration? = null
@@ -85,8 +135,79 @@ object QQTIMHooker : YukiBaseHooker() {
*/ */
private val isQQ get() = packageName == PackageName.QQ private val isQQ get() = packageName == PackageName.QQ
/**
* 当前是否为 QQ 的 NT 版本
*
* 在 QQ NT 中 [AboutActivityClass] 已被移除 - 以此作为判断条件
* @return [Boolean]
*/
private val isQQNTVersion get() = AboutActivityClass == null
/** 当前宿主的版本 */ /** 当前宿主的版本 */
private var appVersionName = "<unknown>" 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 是控制聊天界面的 * 这个类 QQ 的 BaseChatPie 是控制聊天界面的
@@ -95,204 +216,69 @@ object QQTIMHooker : YukiBaseHooker() {
* *
* remainScreenOn、cancelRemainScreenOn * remainScreenOn、cancelRemainScreenOn
* *
* 这两个方法一个是挂起电源锁常驻亮屏 * 这两个方法一个是挂起电源锁常驻亮屏 - 一个是停止常驻亮屏
* *
* 一个是停止常驻亮屏 * - 在 QQ NT 版本中完全移除了 BaseChatPie 类 - 所以不再处理
*
* 不由分说每个版本混淆的方法名都会变
*
* 所以说每个版本重新适配 - 也可以提交分支帮我适配
*
* - ❗Hook 错了方法会造成闪退!
*/ */
private fun hookQQBaseChatPie() { private fun hookQQBaseChatPie() {
if (isQQ) when (appVersionName) { if (hasBaseChatPieClass.not()) {
"8.0.0" -> { HookEntry.isHookClientSupport = true
hookBaseChatPie(methodName = "bq") return YLog.debug("Start for QQ NT version,.")
hookBaseChatPie(methodName = "aL")
} }
"8.0.5", "8.0.7" -> {
hookBaseChatPie(methodName = "bw")
hookBaseChatPie(methodName = "aQ")
}
"8.1.0", "8.1.3" -> {
hookBaseChatPie(methodName = "bE")
hookBaseChatPie(methodName = "aT")
}
"8.1.5" -> {
hookBaseChatPie(methodName = "bF")
hookBaseChatPie(methodName = "aT")
}
"8.1.8", "8.2.0", "8.2.6" -> {
hookBaseChatPie(methodName = "bC")
hookBaseChatPie(methodName = "aT")
}
"8.2.7", "8.2.8", "8.2.11", "8.3.0" -> {
hookBaseChatPie(methodName = "bE")
hookBaseChatPie(methodName = "aV")
}
"8.3.5" -> {
hookBaseChatPie(methodName = "bR")
hookBaseChatPie(methodName = "aX")
}
"8.3.6" -> {
hookBaseChatPie(methodName = "cp")
hookBaseChatPie(methodName = "aX")
}
"8.3.9" -> {
hookBaseChatPie(methodName = "cj")
hookBaseChatPie(methodName = "aT")
}
"8.4.1", "8.4.5" -> {
hookBaseChatPie(methodName = "ck")
hookBaseChatPie(methodName = "aT")
}
"8.4.8", "8.4.10", "8.4.17", "8.4.18", "8.5.0" -> {
hookBaseChatPie(methodName = "remainScreenOn")
hookBaseChatPie(methodName = "cancelRemainScreenOn")
}
"8.5.5" -> {
hookBaseChatPie(methodName = "bT")
hookBaseChatPie(methodName = "aN")
}
"8.6.0", "8.6.5", "8.7.0", "8.7.5", "8.7.8", "8.8.0", "8.8.3", "8.8.5" -> {
hookBaseChatPie(methodName = "ag")
hookBaseChatPie(methodName = "ah")
}
"8.8.11", "8.8.12" -> {
hookBaseChatPie(methodName = "bc")
hookBaseChatPie(methodName = "bd")
}
"8.8.17", "8.8.20" -> {
hookBaseChatPie(methodName = "bd")
hookBaseChatPie(methodName = "be")
}
"8.8.23", "8.8.28" -> {
hookBaseChatPie(methodName = "bf")
hookBaseChatPie(methodName = "bg")
}
"8.8.33" -> {
hookBaseChatPie(methodName = "bg")
hookBaseChatPie(methodName = "bh")
}
"8.8.35", "8.8.38" -> {
hookBaseChatPie(methodName = "bi")
hookBaseChatPie(methodName = "bj")
}
"8.8.50" -> {
hookBaseChatPie(methodName = "bj")
hookBaseChatPie(methodName = "bk")
}
"8.8.55", "8.8.68", "8.8.80" -> {
hookBaseChatPie(methodName = "bk")
hookBaseChatPie(methodName = "bl")
}
"8.8.83", "8.8.85", "8.8.88", "8.8.90" -> {
hookBaseChatPie(methodName = "bl")
hookBaseChatPie(methodName = "bm")
}
"8.8.93", "8.8.95" -> {
hookBaseChatPie(methodName = "J3")
hookBaseChatPie(methodName = "S")
}
"8.8.98" -> {
hookBaseChatPie(methodName = "M3")
hookBaseChatPie(methodName = "S")
}
"8.9.0", "8.9.1", "8.9.2" -> {
hookBaseChatPie(methodName = "N3")
hookBaseChatPie(methodName = "S")
}
"8.9.3", "8.9.5" -> {
hookBaseChatPie(methodName = "H3")
hookBaseChatPie(methodName = "P")
}
"8.9.8", "8.9.10" -> {
hookBaseChatPie(methodName = "H3")
hookBaseChatPie(methodName = "N")
}
"8.9.13" -> {
hookBaseChatPie(methodName = "y3")
hookBaseChatPie(methodName = "H")
}
else -> {
HookEntry.isHookClientSupport = false
loggerD(msg = "$appVersionName not supported!")
}
}
}
/** /**
* 拦截 [BaseChatPieClass] 的目标方法体封装 * 打印警告信息
* @param methodName 方法名 * @param index 序号
*/ */
private fun hookBaseChatPie(methodName: String) { fun warn(index: Int) {
BaseChatPieClass.hook { HookEntry.isHookClientSupport = false
injectMember { YLog.warn("$hostVersionName [$index] not support!")
method {
name = methodName
emptyParam()
returnType = UnitType
}
intercept()
}
} }
DexKitData.BaseChatPie_RemainScreenOnMethod?.hook()?.intercept() ?: warn(index = 0)
DexKitData.BaseChatPie_CancelRemainScreenOnMethod?.hook()?.intercept() ?: warn(index = 1)
} }
/** Hook CoreService QQ、TIM */ /** Hook CoreService QQ、TIM */
private fun hookCoreService() { private fun hookCoreService() {
CoreServiceClass.hook { CoreServiceClass?.resolve()?.optional()?.apply {
if (isQQ) { if (isQQ) {
injectMember { firstMethodOrNull {
method { name = "startTempService" } name = "startTempService"
intercept() }?.hook()?.intercept()
}.ignoredNoSuchMemberFailure() firstMethodOrNull {
injectMember {
method {
name = "startCoreService" name = "startCoreService"
param(BooleanType) parameters(Boolean::class)
} }?.hook()?.intercept()
intercept() firstMethodOrNull {
}.ignoredNoSuchMemberFailure()
injectMember {
method {
name = "onStartCommand" name = "onStartCommand"
param(IntentClass, IntType, IntType) parameters(Intent::class, Int::class, Int::class)
}?.hook()?.replaceTo(any = 2)
} }
replaceTo(any = 2) firstMethodOrNull {
}.ignoredNoSuchMemberFailure() name = "onCreate"
} }?.hook()?.after {
injectMember {
method { name = "onCreate" }
afterHook {
if (ConfigData.isEnableKillQQTimCoreService) if (ConfigData.isEnableKillQQTimCoreService)
instance<Service>().apply { instance<Service>().apply {
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf() stopSelf()
loggerD(msg = "Shutdown CoreService OK!") YLog.debug("Shutdown CoreService OK!")
} }
} }
} }
} CoreService_KernelServiceClass?.resolve()?.optional()?.apply {
CoreService_KernelServiceClass.hook { firstMethodOrNull {
injectMember { name = "onCreate"
method { name = "onCreate" } }?.hook()?.after {
afterHook {
if (ConfigData.isEnableKillQQTimCoreServiceChild) if (ConfigData.isEnableKillQQTimCoreServiceChild)
instance<Service>().apply { instance<Service>().apply {
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf() stopSelf()
loggerD(msg = "Shutdown CoreService\$KernelService OK!") YLog.debug("Shutdown CoreService\$KernelService OK!")
} }
} }
} firstMethodOrNull {
injectMember {
method {
name = "onStartCommand" name = "onStartCommand"
param(IntentClass, IntType, IntType) parameters(Intent::class, Int::class, Int::class)
} }?.hook()?.replaceTo(any = 2)
replaceTo(any = 2)
}.ignoredNoSuchMemberFailure()
} }
} }
@@ -304,63 +290,61 @@ object QQTIMHooker : YukiBaseHooker() {
* 每个版本的差异暂未做排查 * 每个版本的差异暂未做排查
* 旧版本理论上没有这个类 * 旧版本理论上没有这个类
*/ */
findClass(name = "${PackageName.QQ}.msf.service.y").hook { "${PackageName.QQ}.msf.service.y".toClassOrNull()
injectMember { ?.resolve()
method { ?.optional(silent = true)
?.firstMethodOrNull {
name = "a" name = "a"
param(StringType, LongType) parameters(String::class, Long::class)
returnType = UnitType returnType = Void.TYPE
} }?.hook()?.intercept()
intercept()
}.onAllFailure { loggerE(msg = "Hook MsfService Failed $it") }
}.ignoredHookClassNotFoundFailure()
/** /**
* 干掉自动上传服务的电源锁 * 干掉自动上传服务的电源锁
* 每个版本的差异暂未做排查 * 每个版本的差异暂未做排查
*/ */
findClass(name = "com.tencent.upload.impl.UploadServiceImpl").hook { "com.tencent.upload.impl.UploadServiceImpl".toClassOrNull()
injectMember { ?.resolve()
method { name = "acquireWakeLockIfNot" } ?.optional(silent = true)
intercept() ?.firstMethodOrNull {
}.onAllFailure { loggerE(msg = "Hook UploadServiceImpl Failed $it") } name = "acquireWakeLockIfNot"
}.ignoredHookClassNotFoundFailure() }?.hook()?.intercept()
/** /**
* Hook 掉一个一像素保活 [Activity] 真的我怎么都想不到讯哥的程序员做出这种事情 * Hook 掉一个一像素保活 Activity 真的我怎么都想不到讯哥的程序员做出这种事情
* 这个东西经过测试会在锁屏的时候吊起来,解锁的时候自动 finish(),无限耍流氓耗电 * 这个东西经过测试会在锁屏的时候吊起来,解锁的时候自动 finish(),无限耍流氓耗电
* 2022/1/25 后期查证:锁屏界面消息快速回复窗口的解锁后拉起保活界面,也是毒瘤 * 2022/1/25 后期查证:锁屏界面消息快速回复窗口的解锁后拉起保活界面,也是毒瘤
*/ */
findClass(name = "${PackageName.QQ}.activity.QQLSUnlockActivity").hook { "${PackageName.QQ}.activity.QQLSUnlockActivity".toClassOrNull()
injectMember { ?.resolve()
method { ?.optional(silent = true)
?.firstMethodOrNull {
name = "onCreate" name = "onCreate"
param(BundleClass) parameters(Bundle::class)
} }?.hook {
var origDevice = "" var origDevice = ""
beforeHook { before {
/** 由于在 onCreate 里有一行判断只要型号是 xiaomi 的设备就开电源锁,所以说这里临时替换成菊花厂 */ /** 由于在 onCreate 里有一行判断只要型号是 xiaomi 的设备就开电源锁,所以说这里临时替换成菊花厂 */
origDevice = Build.MANUFACTURER origDevice = Build.MANUFACTURER
if (Build.MANUFACTURER.lowercase() == "xiaomi") if (Build.MANUFACTURER.lowercase() == "xiaomi")
BuildClass.field { name = "MANUFACTURER" }.get().set("HUAWEI") Build::class.resolve().firstField { name = "MANUFACTURER" }.set("HUAWEI")
} }
afterHook { after {
instance<Activity>().finish() instance<Activity>().finish()
/** 这里再把型号替换回去 - 不影响应用变量等 Xposed 模块修改的型号 */ /** 这里再把型号替换回去 - 不影响应用变量等 Xposed 模块修改的型号 */
BuildClass.field { name = "MANUFACTURER" }.get().set(origDevice) Build::class.resolve().firstField { name = "MANUFACTURER" }.set(origDevice)
}
} }
} }
/** /**
* 这个东西同上 * 这个东西同上
* 反正也是一个一像素保活的 [Activity] * 反正也是一个一像素保活的 Activity
* 讯哥的程序员真的有你的 * 讯哥的程序员真的有你的
* 2022/1/25 后期查证:锁屏界面消息快速回复窗口 * 2022/1/25 后期查证:锁屏界面消息快速回复窗口
*/ */
findClass("${PackageName.QQ}.activity.QQLSActivity\$14", "ktq").hook { VariousClass("${PackageName.QQ}.activity.QQLSActivity\$14", "ktq").toClassOrNull()
injectMember { ?.resolve()
method { name = "run" } ?.optional(silent = true)
intercept() ?.firstMethodOrNull {
}.ignoredAllFailure() name = "run"
}.ignoredHookClassNotFoundFailure() }?.hook()?.intercept()
/** /**
* 这个是毒瘤核心类 * 这个是毒瘤核心类
* WakeLockMonitor * WakeLockMonitor
@@ -371,199 +355,170 @@ object QQTIMHooker : YukiBaseHooker() {
* 👮🏻 经过排查 Play 版本没这个类...... Emmmm 不想说啥了 * 👮🏻 经过排查 Play 版本没这个类...... Emmmm 不想说啥了
* ✅ 备注8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类 * ✅ 备注8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类
*/ */
findClass(name = "com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor").hook { "com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor".toClassOrNull()
injectMember { ?.resolve()
method { ?.optional(silent = true)
?.apply {
firstMethodOrNull {
name = "onHook" name = "onHook"
param(StringType, AnyType, AnyArrayClass, AnyType) parameters(String::class, Any::class, ArrayClass(Any::class), Any::class)
} }?.hook()?.intercept()
intercept() firstMethodOrNull {
}
injectMember {
method {
name = "doReport" name = "doReport"
param("com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor\$WakeLockEntity", IntType) parameters("com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor\$WakeLockEntity", Int::class)
} }?.hook()?.intercept()
intercept() firstMethodOrNull {
}
injectMember {
method {
name = "afterHookedMethod" name = "afterHookedMethod"
param("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam") parameters("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam")
} }?.hook()?.intercept()
intercept() firstMethodOrNull {
}
injectMember {
method {
name = "beforeHookedMethod" name = "beforeHookedMethod"
param("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam") parameters("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam")
} }?.hook()?.intercept()
intercept() firstMethodOrNull {
} name = "onAppBackground"
injectMember { }?.hook()?.intercept()
method { name = "onAppBackground" } firstMethodOrNull {
intercept()
}
injectMember {
method {
name = "onOtherProcReport" name = "onOtherProcReport"
param(BundleClass) parameters(Bundle::class)
} }?.hook()?.intercept()
intercept() firstMethodOrNull {
} name = "onProcessRun30Min"
injectMember { }?.hook()?.intercept()
method { name = "onProcessRun30Min" } firstMethodOrNull {
intercept() name = "onProcessBG5Min"
} }?.hook()?.intercept()
injectMember { firstMethodOrNull {
method { name = "onProcessBG5Min" }
intercept()
}
injectMember {
method {
name = "writeReport" name = "writeReport"
param(BooleanType) parameters(Boolean::class)
}?.hook()?.intercept()
} }
intercept()
}
}.ignoredHookClassNotFoundFailure()
/** /**
* 这个是毒瘤核心操作类 * 这个是毒瘤核心操作类
* 功能同上、全部拦截 * 功能同上、全部拦截
* 👮🏻 经过排查 Play 版本也没这个类...... Emmmm 不想说啥了 * 👮🏻 经过排查 Play 版本也没这个类...... Emmmm 不想说啥了
* ✅ 备注8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类 * ✅ 备注8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类
*/ */
findClass(name = "com.tencent.qapmsdk.qqbattery.QQBatteryMonitor").hook { "com.tencent.qapmsdk.qqbattery.QQBatteryMonitor".toClassOrNull()
injectMember { ?.resolve()
method { name = "start" } ?.optional(silent = true)
intercept() ?.apply {
} firstMethodOrNull {
injectMember { name = "start"
method { name = "stop" } }?.hook()?.intercept()
intercept() firstMethodOrNull {
} name = "stop"
injectMember { }?.hook()?.intercept()
method { firstMethodOrNull {
name = "handleMessage" name = "handleMessage"
param(MessageClass) parameters(Message::class)
} }?.hook()?.intercept()
replaceToTrue() firstMethodOrNull {
} name = "startMonitorInner"
injectMember { }?.hook()?.intercept()
method { name = "startMonitorInner" } firstMethodOrNull {
intercept() name = "onAppBackground"
} }?.hook()?.intercept()
injectMember { firstMethodOrNull {
method { name = "onAppBackground" } name = "onAppForeground"
intercept() }?.hook()?.intercept()
} firstMethodOrNull {
injectMember {
method { name = "onAppForeground" }
intercept()
}
injectMember {
method {
name = "setLogWhite" name = "setLogWhite"
paramCount = 2 parameterCount = 2
} }?.hook()?.intercept()
intercept() firstMethodOrNull {
}
injectMember {
method {
name = "setCmdWhite" name = "setCmdWhite"
paramCount = 2 parameterCount = 2
} }?.hook()?.intercept()
intercept() firstMethodOrNull {
}
injectMember {
method {
name = "onWriteLog" name = "onWriteLog"
param(StringType, StringType) parameters(String::class, String::class)
} }?.hook()?.intercept()
intercept() firstMethodOrNull {
}
injectMember {
method {
name = "onCmdRequest" name = "onCmdRequest"
param(StringType) parameters(String::class)
} }?.hook()?.intercept()
intercept() firstMethodOrNull {
}
injectMember {
method {
name = "addData" name = "addData"
paramCount = 4 parameterCount = 4
} }?.hook()?.intercept()
intercept() firstMethodOrNull {
}
injectMember {
method {
name = "onGpsScan" name = "onGpsScan"
paramCount = 2 parameterCount = 2
}?.hook()?.intercept()
} }
intercept()
}
}.ignoredHookClassNotFoundFailure()
} }
override fun onHook() { /** Hook QQ 的设置界面添加模块设置入口 (新版) */
onAppLifecycle { private fun hookQQSettingsUi() {
attachBaseContext { baseContext, hasCalledSuper -> if (MainSettingFragmentClass == null) return YLog.error("Could not found main setting class, hook aborted")
if (hasCalledSuper.not()) baseConfiguration = baseContext.resources.configuration 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")
} }
onCreate { MainSettingConfigProviderClass?.resolve()?.optional()?.firstMethodOrNull {
appVersionName = versionName parameters(Context::class)
ConfigData.init(context = this) returnType = List::class
registerModuleAppActivities(AboutActivityClass) }?.hook()?.after {
if (ConfigData.isDisableAllHook) return@onCreate val context = args().first().cast<Context>() ?: return@after
hookSystemWakeLock() val processor = result<MutableList<Any?>>() ?: return@after
hookQQBaseChatPie() processor.add(
hookCoreService() 1,
hookQQDisgusting() processor[0]?.javaClass?.createInstanceOrNull(
loggerI(msg = "All processes are completed for \"${processName.takeIf { it != packageName } ?: packageName}\"") arrayListOf<Any>().apply {
add(createTSEntryItem(context))
}.toList(), "", ""
)
)
} }
} }
/** 仅注入主进程 */
withProcess(mainProcessName) { /**
/** Hook 跳转事件 */ * Hook QQ 的设置界面添加模块设置入口 (旧版)
JumpActivityClass.hook { * @param instance 当前设置界面实例
injectMember { */
method { private fun hookQQSettingsUiLegacy(instance: Any?) {
name = "doOnCreate"
param(BundleClass)
}
afterHook { instance<Activity>().jumpToModuleSettings() }
}
}
/** 将条目注入设置界面 */
QQSettingSettingActivityClass.hook {
injectMember {
method {
name = "doOnCreate"
param(BundleClass)
}
afterHook {
/** 当前的顶级 Item 实例 */ /** 当前的顶级 Item 实例 */
val formItemRefRoot = field { val formItemRefRoot = instance?.asResolver()?.optional()?.lastFieldOrNull {
type(FormSimpleItemClass).index(num = 1) type { it == FormSimpleItemClass || it == FormCommonSingleLineItemClass }
}.ignored().get(instance).cast() ?: field { }?.get<View>()
type(FormCommonSingleLineItemClass).index(num = 1)
}.ignored().get(instance).cast<View?>()
/** 创建一个新的 Item */ /** 创建一个新的 Item */
FormSimpleItemClass.toClassOrNull()?.buildOf<View>(instance) { param(ContextClass) }?.current { val item = FormSimpleItemClass?.createInstanceAsTypeOrNull<View>(instance?.compatToActivity())
method { item?.asResolver()?.optional()?.apply {
firstMethodOrNull {
name = "setLeftText" name = "setLeftText"
param(CharSequenceType) parameters(CharSequence::class)
}.call("TSBattery") }?.invoke("TSBattery")
method { firstMethodOrNull {
name = "setRightText" name = "setRightText"
param(CharSequenceType) parameters(CharSequence::class)
}.call(BuildConfig.VERSION_NAME) }?.invoke(ModuleVersion.toString())
method { firstMethodOrNull {
name = "setBgType" name = "setBgType"
param(IntType) parameters(Int::class)
}.call(if (isQQ) 0 else 2) }?.invoke(if (isQQ) 0 else 2)
}?.apply { setOnClickListener { instance<Activity>().startModuleSettings() } }?.also { item -> }
item ?: return
item.setOnClickListener { it.context.startModuleSettings() }
var listGroup = formItemRefRoot?.parent as? ViewGroup? var listGroup = formItemRefRoot?.parent as? ViewGroup?
val lparam = (if (listGroup?.childCount == 1) { val lparam = (if (listGroup?.childCount == 1) {
listGroup = listGroup.parent as? ViewGroup listGroup = listGroup.parent as? ViewGroup
@@ -575,8 +530,50 @@ object QQTIMHooker : YukiBaseHooker() {
/** 将 Item 添加到设置界面 */ /** 将 Item 添加到设置界面 */
listGroup?.also { if (isQQ) it.addView(item, lparam) else it.addView(item, 0, lparam) } 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

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,13 +17,17 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/9/29. * This file is created by fankes on 2022/9/29.
*/ */
@file:Suppress("ConstPropertyName")
package com.fankes.tsbattery.hook.entity package com.fankes.tsbattery.hook.entity
import android.app.Activity import android.app.Activity
import android.content.Context
import android.os.Build import android.os.Build
import android.view.Gravity import android.view.Gravity
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
@@ -35,16 +39,16 @@ import com.fankes.tsbattery.hook.factory.hookSystemWakeLock
import com.fankes.tsbattery.hook.factory.jumpToModuleSettings import com.fankes.tsbattery.hook.factory.jumpToModuleSettings
import com.fankes.tsbattery.hook.factory.startModuleSettings import com.fankes.tsbattery.hook.factory.startModuleSettings
import com.fankes.tsbattery.utils.factory.absoluteStatusBarHeight 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.fankes.tsbattery.utils.factory.dp
import com.fankes.tsbattery.utils.factory.versionCode import com.highcapable.kavaref.KavaRef.Companion.asResolver
import com.fankes.tsbattery.utils.factory.versionName import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
import com.highcapable.yukihookapi.hook.factory.processName import com.highcapable.yukihookapi.hook.factory.processName
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
import com.highcapable.yukihookapi.hook.log.loggerI import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.type.android.BundleClass
/** /**
* Hook 微信 * Hook 微信
@@ -54,62 +58,30 @@ import com.highcapable.yukihookapi.hook.type.android.BundleClass
object WeChatHooker : YukiBaseHooker() { object WeChatHooker : YukiBaseHooker() {
/** 微信存在的类 - 未测试每个版本是否都存在 */ /** 微信存在的类 - 未测试每个版本是否都存在 */
const val LauncherUIClass = "${PackageName.WECHAT}.ui.LauncherUI" const val LauncherUIClassName = "${PackageName.WECHAT}.ui.LauncherUI"
/** 微信存在的类 - 未测试每个版本是否都存在 */ /** 微信存在的类 - 未测试每个版本是否都存在 */
private const val EmptyActivityClass = "${PackageName.WECHAT}.ui.EmptyActivity" private val LauncherUIClass by lazyClassOrNull("${PackageName.WECHAT}.ui.LauncherUI")
/** 微信存在的类 - 未测试每个版本是否都存在 */ /** 微信存在的类 - 未测试每个版本是否都存在 */
private const val WelabMainUIClass = "${PackageName.WECHAT}.plugin.welab.ui.WelabMainUI" private val EmptyActivityClass by lazyClassOrNull("${PackageName.WECHAT}.ui.EmptyActivity")
/** 微信存在的类 - 未测试每个版本是否都存在 */ /** 微信存在的类 - 未测试每个版本是否都存在 */
private const val SettingsUIClass = "${PackageName.WECHAT}.plugin.setting.ui.setting.SettingsUI" private val WelabMainUIClass by lazyClassOrNull("${PackageName.WECHAT}.plugin.welab.ui.WelabMainUI")
override fun onHook() { /** 微信存在的类 - 未测试每个版本是否都存在 */
onAppLifecycle { private val SettingsUIClass by lazyClassOrNull("${PackageName.WECHAT}.plugin.setting.ui.setting.SettingsUI")
onCreate {
ConfigData.init(context = this) /** TSBattery 图标 TAG 名称 */
registerModuleAppActivities( private const val TSBARRERY_ICON_TAG = "tsbattery_icon"
when {
EmptyActivityClass.hasClass() -> EmptyActivityClass /**
WelabMainUIClass.hasClass() -> WelabMainUIClass * 创建 TSBattery 图标
else -> error("Inject WeChat Activity Proxy failed, unsupport version $versionName($versionCode)") * @param context 当前实例
} * @return [LinearLayout]
) */
if (ConfigData.isDisableAllHook) return@onCreate private fun createPreferenceIcon(context: Context) = LinearLayout(context).apply {
hookSystemWakeLock() tag = TSBARRERY_ICON_TAG
loggerI(msg = "All processes are completed for \"${processName.takeIf { it != packageName } ?: packageName}\"")
}
}
/** 仅注入主进程 */
withProcess(mainProcessName) {
/** Hook 跳转事件 */
LauncherUIClass.hook {
injectMember {
method {
name = "onResume"
emptyParam()
}
afterHook { instance<Activity>().jumpToModuleSettings(isFinish = false) }
}
}
/** 向设置界面右上角添加按钮 */
SettingsUIClass.hook {
injectMember {
method {
name = "onCreate"
param(BundleClass)
}
afterHook {
method {
name = "get_fragment"
emptyParam()
superClass(isOnlySuperClass = true)
}.get(instance).call()?.current()
?.field { name = "mController" }
?.current()?.method { name = "getContentView" }
?.invoke<ViewGroup>()?.addView(LinearLayout(instance()).apply {
context.injectModuleAppResources()
gravity = Gravity.END or Gravity.BOTTOM gravity = Gravity.END or Gravity.BOTTOM
layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
addView(ImageView(context).apply { addView(ImageView(context).apply {
@@ -122,7 +94,48 @@ object WeChatHooker : YukiBaseHooker() {
if (Build.VERSION.SDK_INT >= 26) tooltipText = "TSBattery 设置" if (Build.VERSION.SDK_INT >= 26) tooltipText = "TSBattery 设置"
setOnClickListener { context.startModuleSettings() } 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

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,24 +17,24 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/9/29. * This file is created by fankes on 2022/9/29.
*/ */
package com.fankes.tsbattery.hook.factory package com.fankes.tsbattery.hook.factory
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.PowerManager
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import com.fankes.tsbattery.const.JumpEvent import com.fankes.tsbattery.const.JumpEvent
import com.fankes.tsbattery.const.PackageName import com.fankes.tsbattery.const.PackageName
import com.fankes.tsbattery.hook.entity.QQTIMHooker.toClass import com.fankes.tsbattery.hook.entity.QQTIMHooker.toClass
import com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity 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.YukiHookAPI
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.type.android.PowerManager_WakeLockClass
import kotlin.system.exitProcess import kotlin.system.exitProcess
/** QQ、TIM 存在的类 */ /** QQ、TIM 存在的类 */
@@ -47,14 +47,27 @@ private val ThemeUtilClass = VariousClass("${PackageName.QQ}.theme.ThemeUtil", "
* QQ、TIM 主题是否为夜间模式 * QQ、TIM 主题是否为夜间模式
* @return [Boolean] * @return [Boolean]
*/ */
private fun Context.isQQNightMode() = runCatching { fun Context.isQQNightMode(): Boolean {
ThemeUtilClass.get(classLoader).method { 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" name = "getUserCurrentThemeId"
paramCount = 1 parameterCount = 1
}.get().string(MobileQQClass.toClass(classLoader) }?.invokeQuietly<String>(mAppRuntime)
.field { name = "sMobileQQ" }.ignored().get().current(ignored = true)?.field { name = "mAppRuntime" }?.any() return currentThemeId?.let { it.endsWith("1103") || it.endsWith("2920") } == true
).let { it.endsWith(suffix = "1103") || it.endsWith(suffix = "2920") } }
}.getOrNull() ?: false
/** 启动模块设置 [Activity] */ /** 启动模块设置 [Activity] */
fun Context.startModuleSettings() { fun Context.startModuleSettings() {
@@ -81,13 +94,14 @@ fun Activity.jumpToModuleSettings(isFinish: Boolean = true) {
/** Hook 系统电源锁 */ /** Hook 系统电源锁 */
fun PackageParam.hookSystemWakeLock() { fun PackageParam.hookSystemWakeLock() {
PowerManager_WakeLockClass.hook { PowerManager.WakeLock::class.resolve().apply {
injectMember { firstMethod {
method {
name = "acquireLocked" name = "acquireLocked"
emptyParam() emptyParameters()
} }.hook().intercept()
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

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,7 +17,7 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2021/9/4. * This file is created by fankes on 2021/9/4.
*/ */
@file:Suppress("SetTextI18n", "LocalVariableName", "SameParameterValue") @file:Suppress("SetTextI18n", "LocalVariableName", "SameParameterValue")
@@ -27,51 +27,37 @@ import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.view.HapticFeedbackConstants import android.view.HapticFeedbackConstants
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.fankes.tsbattery.BuildConfig import com.fankes.projectpromote.ProjectPromote
import com.fankes.tsbattery.R import com.fankes.tsbattery.R
import com.fankes.tsbattery.const.JumpEvent import com.fankes.tsbattery.const.JumpEvent
import com.fankes.tsbattery.const.ModuleVersion
import com.fankes.tsbattery.const.PackageName import com.fankes.tsbattery.const.PackageName
import com.fankes.tsbattery.databinding.ActivityMainBinding import com.fankes.tsbattery.databinding.ActivityMainBinding
import com.fankes.tsbattery.hook.entity.QQTIMHooker import com.fankes.tsbattery.hook.entity.QQTIMHooker
import com.fankes.tsbattery.hook.entity.WeChatHooker import com.fankes.tsbattery.hook.entity.WeChatHooker
import com.fankes.tsbattery.ui.activity.base.BaseActivity import com.fankes.tsbattery.ui.activity.base.BaseActivity
import com.fankes.tsbattery.utils.factory.* 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.utils.tool.GithubReleaseTool
import com.fankes.tsbattery.utils.tool.YukiPromoteTool import com.fankes.tsbattery.wrapper.BuildConfigWrapper
import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.YukiHookAPI
class MainActivity : BaseActivity<ActivityMainBinding>() { class MainActivity : BaseActivity<ActivityMainBinding>() {
companion object { companion object {
private const val QQ_SUPPORT_VERSION = "理论支持 8.0.0+ 及以上版本。"
private val qqSupportVersions = arrayOf( private const val TIM_SUPPORT_VERSION = "2+、3+ (并未完全测试每个版本)。"
"8.0.0", "8.0.5", "8.0.7", "8.1.0", "8.1.3", "8.1.5", "8.1.8", private const val WECHAT_SUPPORT_VERSION = "全版本仅支持基础省电,更多功能依然画饼。"
"8.2.0", "8.2.6", "8.2.7", "8.2.8", "8.2.11", "8.3.0", "8.3.5",
"8.3.6", "8.3.9", "8.4.1", "8.4.5", "8.4.8", "8.4.10", "8.4.17",
"8.4.18", "8.5.0", "8.5.5", "8.6.0", "8.6.5", "8.7.0", "8.7.5",
"8.7.8", "8.8.0", "8.8.3", "8.8.5", "8.8.11", "8.8.12", "8.8.17",
"8.8.20", "8.8.23", "8.8.28", "8.8.33", "8.8.35", "8.8.38", "8.8.50",
"8.8.55", "8.8.68", "8.8.80", "8.8.83", "8.8.85", "8.8.88", "8.8.90",
"8.8.93", "8.8.95", "8.8.98", "8.9.0", "8.9.1", "8.9.2", "8.9.3",
"8.9.5", "8.9.8", "8.9.10", "8.9.13"
)
private val qqSupportVersion by lazy {
if (qqSupportVersions.isNotEmpty()) {
var value = ""
qqSupportVersions.forEach { value += "$it" }
"${value.trim().let { it.substring(0, it.lastIndex) }}\n\n其余版本请自行测试是否有效。"
} else "empty"
}
private const val timSupportVersion = "2+、3+ (并未完全测试每个版本)"
private const val wechatSupportVersion = "全版本仅支持基础省电,更多功能依然画饼"
/** 预发布的版本标识 */
private const val pendingFlag = ""
} }
override fun onCreate() { override fun onCreate() {
/** 检查更新 */ /** 检查更新 */
GithubReleaseTool.checkingForUpdate(context = this, BuildConfig.VERSION_NAME) { version, function -> GithubReleaseTool.checkingForUpdate(context = this, ModuleVersion.NAME) { version, function ->
binding.mainTextReleaseVersion.apply { binding.mainTextReleaseVersion.apply {
text = "点击更新 $version" text = "点击更新 $version"
isVisible = true isVisible = true
@@ -81,12 +67,12 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
/** 判断 Hook 状态 */ /** 判断 Hook 状态 */
if (YukiHookAPI.Status.isModuleActive) { if (YukiHookAPI.Status.isModuleActive) {
binding.mainLinStatus.setBackgroundResource(R.drawable.bg_green_round) binding.mainLinStatus.setBackgroundResource(R.drawable.bg_green_round)
binding.mainImgStatus.setImageResource(R.mipmap.ic_success) binding.mainImgStatus.setImageResource(R.drawable.ic_success)
binding.mainTextStatus.text = "模块已激活" binding.mainTextStatus.text = "模块已激活"
binding.mainTextApiWay.isVisible = true binding.mainTextApiWay.isVisible = true
refreshActivateExecutor() refreshActivateExecutor()
/** 推广、恰饭 */ /** 推广、恰饭 */
YukiPromoteTool.promote(context = this) ProjectPromote.show(activity = this, ModuleVersion.toString())
} else } else
showDialog { showDialog {
title = "模块没有激活" title = "模块没有激活"
@@ -95,15 +81,35 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
noCancelable() noCancelable()
} }
/** 设置安装状态 */ /** 设置安装状态 */
binding.mainTextQqVer.text = if (PackageName.QQ.isInstall) version(PackageName.QQ) else "未安装" binding.mainTextQqVer.text = PackageName.QQ.takeIf { isInstall(it) }?.let { appVersionBrandOf(it) } ?: "未安装"
binding.mainTextTimVer.text = if (PackageName.TIM.isInstall) version(PackageName.TIM) else "未安装" binding.mainTextTimVer.text = PackageName.TIM.takeIf { isInstall(it) }?.let { appVersionBrandOf(it) } ?: "未安装"
binding.mainTextWechatVer.text = if (PackageName.WECHAT.isInstall) version(PackageName.WECHAT) else "未安装" binding.mainTextWechatVer.text = PackageName.WECHAT.takeIf { isInstall(it) }?.let { appVersionBrandOf(it) } ?: "未安装"
/** 设置文本 */ /** 设置文本 */
binding.mainTextVersion.text = "模块版本:${BuildConfig.VERSION_NAME} $pendingFlag" 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 { binding.mainQqItem.setOnClickListener {
showDialog { showDialog {
title = "兼容的 QQ 版本" title = "兼容的 QQ 版本"
msg = qqSupportVersion msg = QQ_SUPPORT_VERSION
confirmButton(text = "我知道了") confirmButton(text = "我知道了")
} }
/** 振动提醒 */ /** 振动提醒 */
@@ -112,7 +118,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.mainTimItem.setOnClickListener { binding.mainTimItem.setOnClickListener {
showDialog { showDialog {
title = "兼容的 TIM 版本" title = "兼容的 TIM 版本"
msg = timSupportVersion msg = TIM_SUPPORT_VERSION
confirmButton(text = "我知道了") confirmButton(text = "我知道了")
} }
/** 振动提醒 */ /** 振动提醒 */
@@ -121,18 +127,12 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.mainWechatItem.setOnClickListener { binding.mainWechatItem.setOnClickListener {
showDialog { showDialog {
title = "兼容的微信版本" title = "兼容的微信版本"
msg = wechatSupportVersion msg = WECHAT_SUPPORT_VERSION
confirmButton(text = "我知道了") confirmButton(text = "我知道了")
} }
/** 振动提醒 */ /** 振动提醒 */
it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
} }
/** 获取 Sp 存储的信息 */
binding.hideIconInLauncherSwitch.isChecked = isLauncherIconShowing.not()
binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
hideOrShowLauncherIcon(b)
}
/** 快捷操作 QQ */ /** 快捷操作 QQ */
binding.quickQqButton.setOnClickListener { startModuleSettings(PackageName.QQ) } binding.quickQqButton.setOnClickListener { startModuleSettings(PackageName.QQ) }
/** 快捷操作 TIM */ /** 快捷操作 TIM */
@@ -145,6 +145,17 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.linkWithFollowMe.setOnClickListener { binding.linkWithFollowMe.setOnClickListener {
openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market") 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
}
} }
/** /**
@@ -152,11 +163,11 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
* @param packageName 包名 * @param packageName 包名
*/ */
private fun startModuleSettings(packageName: String) { private fun startModuleSettings(packageName: String) {
if (packageName.isInstall) runCatching { if (isInstall(packageName)) runCatching {
startActivity(Intent().apply { startActivity(Intent().apply {
component = ComponentName( component = ComponentName(
packageName, packageName,
if (packageName != PackageName.WECHAT) QQTIMHooker.JumpActivityClass else WeChatHooker.LauncherUIClass if (packageName != PackageName.WECHAT) QQTIMHooker.JumpActivityClassName else WeChatHooker.LauncherUIClassName
) )
putExtra(JumpEvent.OPEN_MODULE_SETTING, YukiHookAPI.Status.compiledTimestamp) putExtra(JumpEvent.OPEN_MODULE_SETTING, YukiHookAPI.Status.compiledTimestamp)
flags = Intent.FLAG_ACTIVITY_NEW_TASK flags = Intent.FLAG_ACTIVITY_NEW_TASK
@@ -166,12 +177,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
/** 刷新模块激活使用的方式 */ /** 刷新模块激活使用的方式 */
private fun refreshActivateExecutor() { private fun refreshActivateExecutor() {
when { if (YukiHookAPI.Status.Executor.apiLevel > 0)
YukiHookAPI.Status.executorVersion > 0 -> binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name} API ${YukiHookAPI.Status.Executor.apiLevel}"
binding.mainTextApiWay.text = else binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name}"
"Activated by ${YukiHookAPI.Status.executorName} API ${YukiHookAPI.Status.executorVersion}"
YukiHookAPI.Status.isTaiChiModuleActive -> binding.mainTextApiWay.text = "Activated by TaiChi"
else -> binding.mainTextApiWay.text = "Activated by anonymous"
}
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,25 +17,26 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/30. * This file is created by fankes on 2022/1/30.
*/ */
@file:Suppress("UNCHECKED_CAST")
package com.fankes.tsbattery.ui.activity.base package com.fankes.tsbattery.ui.activity.base
import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.fankes.tsbattery.R import com.fankes.tsbattery.R
import com.fankes.tsbattery.utils.factory.isNotSystemInDarkMode import com.fankes.tsbattery.utils.factory.isNotSystemInDarkMode
import com.highcapable.yukihookapi.hook.factory.current import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass import com.highcapable.kavaref.extension.toClassOrNull
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base.ModuleAppCompatActivity import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.proxy.ModuleActivity
abstract class BaseActivity<VB : ViewBinding> : ModuleAppCompatActivity() { abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity(), ModuleActivity {
override val moduleTheme get() = R.style.Theme_TSBattery override val moduleTheme get() = R.style.Theme_TSBattery
@@ -43,11 +44,14 @@ abstract class BaseActivity<VB : ViewBinding> : ModuleAppCompatActivity() {
lateinit var binding: VB lateinit var binding: VB
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
delegate.onCreate(savedInstanceState)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = current().generic()?.argument()?.method { val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
name = "inflate" name = "inflate"
param(LayoutInflaterClass) parameters(LayoutInflater::class)
}?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed") }?.invoke<VB>(layoutInflater) ?: error("binding failed")
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
setContentView(binding.root) setContentView(binding.root)
/** 隐藏系统的标题栏 */ /** 隐藏系统的标题栏 */
supportActionBar?.hide() supportActionBar?.hide()
@@ -56,6 +60,7 @@ abstract class BaseActivity<VB : ViewBinding> : ModuleAppCompatActivity() {
isAppearanceLightStatusBars = isNotSystemInDarkMode isAppearanceLightStatusBars = isNotSystemInDarkMode
isAppearanceLightNavigationBars = isNotSystemInDarkMode isAppearanceLightNavigationBars = isNotSystemInDarkMode
} }
@Suppress("DEPRECATION")
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also { ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
window?.statusBarColor = it window?.statusBarColor = it
window?.navigationBarColor = it window?.navigationBarColor = it
@@ -67,4 +72,16 @@ abstract class BaseActivity<VB : ViewBinding> : ModuleAppCompatActivity() {
/** 回调 [onCreate] 方法 */ /** 回调 [onCreate] 方法 */
abstract fun 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

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,91 +17,178 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/9/28. * This file is created by fankes on 2022/9/28.
*/ */
@file:Suppress("SetTextI18n", "DEPRECATION") @file:Suppress("SetTextI18n", "DEPRECATION")
package com.fankes.tsbattery.ui.activity.parasitic package com.fankes.tsbattery.ui.activity.parasitic
import android.content.ComponentName import android.animation.LayoutTransition
import android.content.Intent
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Typeface
import android.view.Gravity
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.fankes.tsbattery.BuildConfig 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.const.PackageName
import com.fankes.tsbattery.data.ConfigData import com.fankes.tsbattery.data.ConfigData
import com.fankes.tsbattery.data.ConfigData.bind import com.fankes.tsbattery.data.ConfigData.bind
import com.fankes.tsbattery.databinding.ActivityConfigBinding
import com.fankes.tsbattery.hook.HookEntry import com.fankes.tsbattery.hook.HookEntry
import com.fankes.tsbattery.hook.entity.QQTIMHooker import com.fankes.tsbattery.hook.entity.QQTIMHooker
import com.fankes.tsbattery.ui.activity.MainActivity import com.fankes.tsbattery.ui.activity.MainActivity
import com.fankes.tsbattery.ui.activity.base.BaseActivity import com.fankes.tsbattery.ui.activity.base.BaseActivity2
import com.fankes.tsbattery.utils.factory.* 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.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 kotlin.system.exitProcess
import android.R as Android_R
class ConfigActivity : BaseActivity<ActivityConfigBinding>() { 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() { override fun onCreate() {
/** 检查更新 */ setContentView {
GithubReleaseTool.checkingForUpdate(context = this, BuildConfig.VERSION_NAME) { version, function -> LinearLayout(
binding.updateVersionText.apply { lparams = LayoutParams(matchParent = true),
text = "点击更新 $version" init = {
isVisible = true orientation = LinearLayout.VERTICAL
setOnClickListener { function() } setBackgroundResource(R.color.colorThemeBackground)
} }
) {
LinearLayout(
lparams = LayoutParams(widthMatchParent = true),
init = {
gravity = Gravity.CENTER or Gravity.START
setPadding(15.dp)
} }
binding.titleBackIcon.setOnClickListener { finish() } ) {
binding.titleModuleIcon.setOnClickListener { 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 { showDialog {
title = "打开模块主界面" title = "打开模块主界面"
msg = "点击确定后将打开模块主界面,如果未安装模块本体将会无法打开" msg = "点击确定后将打开模块主界面,如果未安装模块本体将尝试打开寄生界面"
confirmButton { confirmButton {
runCatching { runCatching {
startActivity(Intent().apply { startActivity(
component = ComponentName(BuildConfig.APPLICATION_ID, MainActivity::class.java.name) packageName = BuildConfigWrapper.APPLICATION_ID,
flags = Intent.FLAG_ACTIVITY_NEW_TASK activityClass = classOf<MainActivity>().name
}) )
}.onFailure { snake(msg = "打开失败,请确认你已安装模块 APP\n$it") } }.onFailure {
runCatching {
startActivity<MainActivity>()
}.onFailure { snake(msg = "打开失败,请确认你已安装模块 APP 或在模块更新后重启过$appName\n$it") }
}
} }
cancelButton() cancelButton()
} }
} }
binding.titleNameText.text = "TSBattery 设置 (${appName.trim()})"
binding.appIcon.setImageDrawable(findAppIcon())
binding.appName.text = appName.trim()
binding.appVersion.text = "${versionName}($versionCode)"
binding.moduleVersion.text = "${BuildConfig.VERSION_NAME}(${BuildConfig.VERSION_CODE})"
binding.activeModeIcon.isVisible = HookEntry.isHookClientSupport
binding.inactiveModeIcon.isGone = HookEntry.isHookClientSupport
binding.unsupportItem.isGone = HookEntry.isHookClientSupport
/** 刷新当前模式文本 */
fun refreshCurrentModeText() {
binding.currentModeText.text = when {
ConfigData.isDisableAllHook -> "模块已停用"
packageName == PackageName.WECHAT -> "仅限基础省电模式"
ConfigData.isEnableQQTimProtectMode -> "已启用保守模式"
else -> "已启用完全模式"
} }
} }
refreshCurrentModeText() NestedScrollView(
/** 刷新配置条目显示隐藏状态 */ lparams = LayoutParams(matchParent = true),
fun refreshConfigItems() { init = {
binding.itemQqTimConfig.isVisible = packageName != PackageName.WECHAT && ConfigData.isDisableAllHook.not() setFadingEdgeLength(10.dp)
isVerticalFadingEdgeEnabled = true
isVerticalScrollBarEnabled = false
isHorizontalScrollBarEnabled = false
} }
refreshConfigItems() ) {
binding.infoTipText.replaceToAppName() LinearLayout(
binding.qqTimProtectTipText.replaceToAppName() lparams = LayoutParams(widthMatchParent = true),
binding.disableAllHookSwitch.bind(ConfigData.DISABLE_ALL_HOOK) { refreshConfigItems(); refreshCurrentModeText(); showRestartDialog() } init = {
binding.qqTimProtectModeSwitch.bind(ConfigData.ENABLE_QQ_TIM_PROTECT_MODE) { refreshCurrentModeText(); showRestartDialog() } orientation = LinearLayout.VERTICAL
binding.qqTimCoreServiceSwitch.bind(ConfigData.ENABLE_KILL_QQ_TIM_CORESERVICE) { showRestartDialog() } layoutTransition = LayoutTransition()
binding.qqTimCoreServiceChildSwitch.bind(ConfigData.ENABLE_KILLE_QQ_TIM_CORESERVICE_CHILD) { showRestartDialog() } updatePadding(bottom = 15.dp)
} }
) {
/** 显示重新启动对话框 */ repeat(2) { index ->
private fun showRestartDialog() { 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 { showDialog {
title = "需要重新启动" title = "需要重新启动"
msg = "你必须重新启动${appName}才能使当前更改生效,现在重新启动吗?" msg = "你必须重新启动${appName}才能使当前更改生效,现在重新启动吗?"
@@ -110,13 +197,365 @@ class ConfigActivity : BaseActivity<ActivityConfigBinding>() {
finish() finish()
exitProcess(status = 0) exitProcess(status = 0)
} }
cancelButton(text = "稍后再说") 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())
}
/** 替换占位符到当前 APP 名称 */ /** 显示需要重新启动提示 */
private fun TextView.replaceToAppName() { private fun showNeedRestartTip() {
text = text.toString().replace(oldValue = "{APP_NAME}", appName) 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 修改它 */ /** 重新设置 DPI 防止 QQ、TIM 修改它 */
@@ -134,5 +573,5 @@ class ConfigActivity : BaseActivity<ActivityConfigBinding>() {
* 获取当前 APP 名称 * 获取当前 APP 名称
* @return [String] * @return [String]
*/ */
private val appName by lazy { findAppName().let { if (packageName == PackageName.WECHAT) it else " $it " } } private val appName by lazy { appNameOf().let { if (packageName == PackageName.WECHAT) it else " $it " } }
} }

View File

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,11 +17,11 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is created by fankes on 2022/1/8.
*/ */
@file:Suppress("SameParameterValue") @file:Suppress("SameParameterValue")
package com.fankes.tsbattery.ui.view package com.fankes.tsbattery.ui.widget
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
@@ -31,8 +31,10 @@ import android.util.AttributeSet
import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.SwitchCompat
import com.fankes.tsbattery.utils.factory.dp import com.fankes.tsbattery.utils.factory.dp
import com.fankes.tsbattery.utils.factory.isSystemInDarkMode import com.fankes.tsbattery.utils.factory.isSystemInDarkMode
import com.highcapable.hikage.annotation.HikageView
import top.defaults.drawabletoolbox.DrawableBuilder import top.defaults.drawabletoolbox.DrawableBuilder
@HikageView
class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(context, attrs) { class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(context, attrs) {
private fun toColors(selected: Int, pressed: Int, normal: Int): ColorStateList { private fun toColors(selected: Int, pressed: Int, normal: Int): ColorStateList {

View File

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,9 +17,9 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/7. * This file is created by fankes on 2022/1/7.
*/ */
@file:Suppress("unused", "OPT_IN_USAGE", "EXPERIMENTAL_API_USAGE") @file:Suppress("unused")
package com.fankes.tsbattery.utils.factory package com.fankes.tsbattery.utils.factory
@@ -29,12 +29,11 @@ import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.fankes.tsbattery.R import com.fankes.tsbattery.R
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.highcapable.yukihookapi.annotation.CauseProblemsApi import com.google.android.material.progressindicator.CircularProgressIndicator
import com.highcapable.yukihookapi.hook.factory.applyModuleTheme import com.highcapable.yukihookapi.hook.factory.applyModuleTheme
/** /**
@@ -89,7 +88,10 @@ class DialogBuilder(val context: Context) {
customLayoutView = LinearLayout(context).apply { customLayoutView = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER or Gravity.START gravity = Gravity.CENTER or Gravity.START
addView(ProgressBar(context)) addView(CircularProgressIndicator(context).apply {
isIndeterminate = true
trackCornerRadius = 10.dp(context)
})
addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(20.dp(context), 5) }) addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(20.dp(context), 5) })
addView(TextView(context).apply { addView(TextView(context).apply {
tag = "progressContent" tag = "progressContent"
@@ -131,7 +133,6 @@ class DialogBuilder(val context: Context) {
fun cancel() = dialogInstance?.cancel() fun cancel() = dialogInstance?.cancel()
/** 显示对话框 */ /** 显示对话框 */
@CauseProblemsApi
fun show() = runInSafe { fun show() = runInSafe {
instance?.create()?.apply { instance?.create()?.apply {
customLayoutView?.let { setView(it) } customLayoutView?.let { setView(it) }

View File

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,13 +17,13 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/3/13. * This file is created by fankes on 2022/3/13.
*/ */
@file:Suppress("unused") @file:Suppress("unused", "UnusedReceiverParameter")
package com.fankes.tsbattery.utils.factory package com.fankes.tsbattery.utils.factory
import com.highcapable.yukihookapi.hook.log.loggerE import com.highcapable.yukihookapi.hook.log.YLog
/** /**
* 忽略异常返回值 * 忽略异常返回值
@@ -78,5 +78,5 @@ inline fun <T> safeOf(default: T, result: () -> T) = try {
* @param block 正常回调 * @param block 正常回调
*/ */
inline fun <T> T.runInSafe(msg: String = "", block: () -> Unit) { inline fun <T> T.runInSafe(msg: String = "", block: () -> Unit) {
runCatching(block).onFailure { if (msg.isNotBlank()) loggerE(msg = msg, e = it) } runCatching(block).onFailure { if (msg.isNotBlank()) YLog.error(msg, it) }
} }

View File

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,9 +17,9 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/7. * This file is created by fankes on 2022/1/7.
*/ */
@file:Suppress("DEPRECATION", "unused", "DiscouragedApi", "InternalInsetResource") @file:Suppress("unused", "DiscouragedApi", "InternalInsetResource")
package com.fankes.tsbattery.utils.factory package com.fankes.tsbattery.utils.factory
@@ -29,15 +29,18 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.PackageManager.PackageInfoFlags
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.Uri import android.net.Uri
import android.os.Build
import android.provider.Settings import android.provider.Settings
import android.widget.Toast import android.widget.Toast
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import com.fankes.tsbattery.BuildConfig import androidx.core.content.pm.PackageInfoCompat
import com.fankes.tsbattery.wrapper.BuildConfigWrapper
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.Companion.appContext import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.Companion.appContext
@@ -66,76 +69,75 @@ val Context.isSystemInDarkMode get() = (resources.configuration.uiMode and Confi
inline val Context.isNotSystemInDarkMode get() = !isSystemInDarkMode inline val Context.isNotSystemInDarkMode get() = !isSystemInDarkMode
/** /**
* 得到安装包信息 * 得到 APP 安装包信息 (兼容)
* @return [PackageInfo] * @param packageName APP 包名
* @param flag [PackageInfoFlags]
* @return [PackageInfo] or null
*/ */
val Context.packageInfo get() = packageManager?.getPackageInfo(packageName, 0) ?: PackageInfo() 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 [Boolean]
*/
val String.isInstall
get() = try {
appContext.packageManager.getPackageInfo(
this,
PackageManager.GET_UNINSTALLED_PACKAGES
)
true
} catch (e: Exception) {
false
}
/**
* 得到版本信息
* @return [String]
*/
val Context.versionName get() = packageInfo.versionName ?: ""
/**
* 得到版本号
* @return [Int] * @return [Int]
*/ */
val Context.versionCode get() = packageInfo.versionCode private val PackageInfo.versionCodeCompat get() = PackageInfoCompat.getLongVersionCode(this)
/** /**
* 得到版本信息与版本号 * 判断 APP 是否安装
* @param packageName 包名 * @param packageName APP 包名
* @return [Boolean]
*/
fun Context.isInstall(packageName: String) = getPackageInfoCompat(packageName)?.let { true } ?: false
/**
* 得到 APP 版本信息
* @return [String] * @return [String]
*/ */
fun Context.version(packageName: String) = safeOfNothing { val Context.appVersionName get() = getPackageInfoCompat(packageName)?.versionName ?: ""
packageManager?.getPackageInfo(packageName, 0)?.let {
"${it.versionName}(${it.versionCode})" /**
} ?: "" * 得到 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 名称 * 得到 APP 名称
* @param name APP 包名 - 默认为当前 APP * @param packageName APP 包名 - 默认为当前 APP
* @return [String] * @return [String]
*/ */
fun Context.findAppName(name: String = packageName) = fun Context.appNameOf(packageName: String = getPackageName()) =
safeOfNothing { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadLabel(packageManager).toString() } getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: ""
/** /**
* 得到 APP 图标 * 得到 APP 图标
* @param name APP 包名 - 默认为当前 APP * @param packageName APP 包名 - 默认为当前 APP
* @return [Drawable] or null * @return [Drawable] or null
*/ */
fun Context.findAppIcon(name: String = packageName) = fun Context.appIconOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.applicationInfo?.loadIcon(packageManager)
safeOfNull { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadIcon(packageManager) }
/** /**
* 网络连接是否正常 * 网络连接是否正常
* @return [Boolean] 网络是否连接 * @return [Boolean] 网络是否连接
*/ */
val isNetWorkSuccess get() = appContext.isNetWorkSuccess val Context.isNetWorkSuccess
get() = safeOfFalse {
/** @Suppress("DEPRECATION")
* 网络连接是否正常 getSystemService<ConnectivityManager>()?.activeNetworkInfo != null
* @return [Boolean] 网络是否连接 }
*/
val Context.isNetWorkSuccess get() = safeOfFalse { getSystemService<ConnectivityManager>()?.activeNetworkInfo != null }
/** /**
* dp 转换为 pxInt * dp 转换为 pxInt
@@ -190,7 +192,7 @@ fun Context.snake(msg: String, actionText: String = "", callback: () -> Unit = {
* @param packageName 包名 * @param packageName 包名
*/ */
fun Context.openSelfSetting(packageName: String = appContext.packageName) = runCatching { fun Context.openSelfSetting(packageName: String = appContext.packageName) = runCatching {
if (packageName.isInstall) if (isInstall(packageName))
startActivity(Intent().apply { startActivity(Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
@@ -225,7 +227,7 @@ fun Context.openBrowser(url: String, packageName: String = "") = runCatching {
*/ */
fun Context.hideOrShowLauncherIcon(isShow: Boolean) { fun Context.hideOrShowLauncherIcon(isShow: Boolean) {
packageManager?.setComponentEnabledSetting( packageManager?.setComponentEnabledSetting(
ComponentName(packageName, "${BuildConfig.APPLICATION_ID}.Home"), ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home"),
if (isShow) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED, if (isShow) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP PackageManager.DONT_KILL_APP
) )
@@ -237,5 +239,5 @@ fun Context.hideOrShowLauncherIcon(isShow: Boolean) {
*/ */
val Context.isLauncherIconShowing val Context.isLauncherIconShowing
get() = packageManager?.getComponentEnabledSetting( get() = packageManager?.getComponentEnabledSetting(
ComponentName(packageName, "${BuildConfig.APPLICATION_ID}.Home") ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home")
) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED ) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED

View File

@@ -1,6 +1,6 @@
/* /*
* TSBattery - A new way to save your battery avoid cancer apps hacker it. * TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) * Copyright (C) 2017 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery * https://github.com/fankes/TSBattery
* *
* This software is non-free but opensource software: you can redistribute it * This software is non-free but opensource software: you can redistribute it
@@ -17,7 +17,7 @@
* and eula along with this software. If not, see * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/> * <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/3/20. * This file is created by fankes on 2022/3/20.
*/ */
@file:Suppress("NewApi") @file:Suppress("NewApi")
@@ -28,15 +28,23 @@ import android.content.Context
import android.icu.text.SimpleDateFormat import android.icu.text.SimpleDateFormat
import android.icu.util.Calendar import android.icu.util.Calendar
import android.icu.util.TimeZone import android.icu.util.TimeZone
import com.fankes.tsbattery.utils.factory.* import com.fankes.tsbattery.utils.factory.isNetWorkSuccess
import okhttp3.* 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 org.json.JSONObject
import java.io.IOException import java.io.IOException
import java.io.Serializable import java.io.Serializable
import java.util.* import java.util.Locale
/** /**
* 获取 Github Release 最新版本工具类 * 获取 GitHub Release 最新版本工具类
*/ */
object GithubReleaseTool { object GithubReleaseTool {
@@ -60,7 +68,6 @@ object GithubReleaseTool {
.build() .build()
).enqueue(object : Callback { ).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {} override fun onFailure(call: Call, e: IOException) {}
override fun onResponse(call: Call, response: Response) = runInSafe { override fun onResponse(call: Call, response: Response) = runInSafe {
JSONObject(response.body.string()).apply { JSONObject(response.body.string()).apply {
GithubReleaseBean( GithubReleaseBean(
@@ -103,7 +110,7 @@ object GithubReleaseTool {
(context as? Activity?)?.runOnUiThread { (context as? Activity?)?.runOnUiThread {
context.showDialog { context.showDialog {
title = "网络不可用" title = "网络不可用"
msg = "模块的联网权限可能已被禁用,请开启联网权限以定期检查更新。" msg = "应用的联网权限可能已被禁用,请开启联网权限以定期检查更新。"
confirmButton(text = "去开启") { context.openSelfSetting() } confirmButton(text = "去开启") { context.openSelfSetting() }
cancelButton() cancelButton()
noCancelable() noCancelable()
@@ -121,16 +128,16 @@ object GithubReleaseTool {
* 格式化时间为本地时区 * 格式化时间为本地时区
* @return [String] 本地时区时间 * @return [String] 本地时区时间
*/ */
private fun String.localTime() = replace(oldValue = "T", newValue = " ").replace(oldValue = "Z", newValue = "").let { private fun String.localTime() = replace("T", " ").replace("Z", "").let {
runCatching { runCatching {
val local = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT).apply { timeZone = Calendar.getInstance().timeZone } val local = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = Calendar.getInstance().timeZone }
val current = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("GMT") } val current = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("GMT") }
local.format(current.parse(it)) local.format(current.parse(it))
}.getOrNull() ?: it }.getOrNull() ?: it
} }
/** /**
* Github Release bean * GitHub Release bean
* @param name 版本名称 * @param name 版本名称
* @param htmlUrl 网页地址 * @param htmlUrl 网页地址
* @param content 更新日志 * @param content 更新日志

View File

@@ -1,59 +0,0 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 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/5/30.
*/
package com.fankes.tsbattery.utils.tool
import android.content.Context
import com.fankes.tsbattery.BuildConfig
import com.fankes.tsbattery.utils.factory.openBrowser
import com.fankes.tsbattery.utils.factory.showDialog
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
/**
* [YukiHookAPI] 的自动推广工具类
*/
object YukiPromoteTool {
/** 推广已读存储键值 */
private val YUKI_PROMOTE_READED = PrefsData("yuki_promote_readed_${BuildConfig.VERSION_NAME}", false)
/**
* 显示推广对话框
* @param context 实例
*/
fun promote(context: Context) {
fun saveReaded() = context.modulePrefs.put(YUKI_PROMOTE_READED, value = true)
if (context.modulePrefs.get(YUKI_PROMOTE_READED).not())
context.showDialog {
title = "面向开发者的推广"
msg = "你想快速拥有一个自己的 Xposed 模块吗,你只需要拥有基础的 Android 开发经验以及使用 Kotlin 编程语言即可。\n\n" +
"快来体验 YukiHookAPI这是一个使用 Kotlin 重新构建的高效 Xposed Hook API助你的开发变得更轻松。"
confirmButton(text = "去看看") {
context.openBrowser(url = "https://github.com/fankes/YukiHookAPI")
saveReaded()
}
cancelButton(text = "我不是开发者") { saveReaded() }
noCancelable()
}
}
}

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,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

@@ -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,7 +3,8 @@
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:scaleX="0.03609375"
android:scaleY="0.03609375" android:scaleY="0.03609375"
android:translateX="35.52" android:translateX="35.52"
android:translateY="35.52"> android:translateY="35.52">

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,383 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorThemeBackground"
android:orientation="vertical"
tools:context=".ui.activity.parasitic.ConfigActivity"
tools:ignore="HardcodedText,ContentDescription,UnusedAttribute,UselessParent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="0dp"
android:gravity="center|start"
android:paddingLeft="15dp"
android:paddingTop="15dp"
android:paddingRight="15dp"
android:paddingBottom="15dp">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/title_back_icon"
style="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
android:src="@mipmap/ic_back"
android:tint="@color/colorTextGray"
android:tooltipText="返回" />
<TextView
android:id="@+id/title_name_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="2.5dp"
android:layout_weight="1"
android:singleLine="true"
android:text="@string/app_name"
android:textColor="@color/colorTextGray"
android:textSize="19sp"
android:textStyle="bold" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/title_module_icon"
android:layout_width="23dp"
android:layout_height="23dp"
android:layout_marginEnd="10dp"
android:padding="1.5dp"
android:src="@drawable/ic_icon"
android:tint="@color/colorTextGray"
android:tooltipText="打开模块主界面" />
</LinearLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadingEdgeLength="10dp"
android:orientation="vertical"
android:requiresFadingEdge="vertical"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical"
android:paddingBottom="15dp">
<TextView
android:id="@+id/update_version_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="5dp"
android:background="@drawable/bg_orange_round"
android:gravity="center"
android:padding="5dp"
android:text="点击更新 %1"
android:textColor="@color/white"
android:textSize="13sp"
android:visibility="gone" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp"
android:layout_marginRight="15dp"
android:background="@drawable/bg_permotion_round"
android:elevation="0dp"
android:gravity="center"
android:orientation="horizontal"
android:padding="15dp">
<ImageView
android:id="@+id/app_icon"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginEnd="15dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:gravity="center|start">
<TextView
android:id="@+id/app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="7.5dp"
android:singleLine="true"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:id="@+id/app_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:alpha="0.85"
android:singleLine="true"
android:text="|"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:text="模块版本:"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
<TextView
android:id="@+id/module_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center|start"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_width="13dp"
android:layout_height="13dp"
android:layout_marginEnd="6dp"
app:cardBackgroundColor="@color/colorThemeBackground"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/active_mode_icon"
android:layout_width="13dp"
android:layout_height="13dp"
android:src="@mipmap/ic_success"
android:tint="#FF26A69A"
android:visibility="gone" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/inactive_mode_icon"
android:layout_width="13dp"
android:layout_height="13dp"
android:src="@mipmap/ic_error"
android:tint="#FF7043"
android:visibility="gone" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:id="@+id/unsupport_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:gravity="center|start"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:text="检测到未适配的版本"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:alpha="0.85"
android:singleLine="true"
android:text="|"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
</LinearLayout>
<TextView
android:id="@+id/current_mode_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/info_tip_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:layout_marginRight="15dp"
android:alpha="0.8"
android:background="@drawable/bg_permotion_round"
android:lineSpacingExtra="10dp"
android:padding="15dp"
android:text="模块只对挂后台锁屏情况下有省电效果,请不要将过多的群提醒,消息通知打开,这样子在使用过程时照样会极其耗电。\n持续常驻使用{APP_NAME}依然会耗电,任何软件都是如此,模块是无法帮你做到前台不耗电的。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:layout_marginRight="15dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_permotion_round"
android:elevation="0dp"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingTop="10dp"
android:paddingRight="15dp"
android:paddingBottom="15dp">
<com.fankes.tsbattery.ui.view.MaterialSwitch
android:id="@+id/disable_all_hook_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginBottom="5dp"
android:text="停用省电策略"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="选择停用后模块将关闭所有省电功能,模块停止使用。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/item_qq_tim_config"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:layout_marginRight="15dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_permotion_round"
android:elevation="0dp"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingTop="10dp"
android:paddingRight="15dp">
<com.fankes.tsbattery.ui.view.MaterialSwitch
android:id="@+id/qq_tim_protect_mode_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginBottom="5dp"
android:text="启用保守模式"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:id="@+id/qq_tim_protect_tip_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="此选项默认关闭,默认情况下模块将会干掉{APP_NAME}自身的电源锁控制类,开启后模块将只对系统电源锁生效,如果你的{APP_NAME}视频通话等设置发生了故障,可以尝试开启这个功能。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:background="@drawable/bg_permotion_round"
android:elevation="0dp"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingTop="10dp"
android:paddingRight="15dp"
android:paddingBottom="15dp">
<com.fankes.tsbattery.ui.view.MaterialSwitch
android:id="@+id/qq_tim_core_service_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginBottom="5dp"
android:text="关闭 CoreService"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="关闭后可能会影响消息接收与视频通话,但是会达到省电效果,如果你的系统拥有推送服务(HMS)或(GMS)可以尝试关闭。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<com.fankes.tsbattery.ui.view.MaterialSwitch
android:id="@+id/qq_tim_core_service_child_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginBottom="5dp"
android:text="关闭 CoreService$KernelService"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="这是一个辅助子服务,理论主服务关闭后子服务同样不会被启动,建议在保证消息接收的前提下可以尝试关闭子服务。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@@ -36,7 +36,7 @@
android:layout_height="27dp" android:layout_height="27dp"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
android:alpha="0.85" android:alpha="0.85"
android:src="@mipmap/ic_github" android:src="@drawable/ic_github"
android:tint="@color/colorTextGray" android:tint="@color/colorTextGray"
android:tooltipText="项目地址" /> android:tooltipText="项目地址" />
</LinearLayout> </LinearLayout>
@@ -59,7 +59,7 @@
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/ic_warn" android:src="@drawable/ic_warn"
android:tint="@color/white" /> android:tint="@color/white" />
<LinearLayout <LinearLayout
@@ -250,7 +250,7 @@
android:layout_height="15dp" android:layout_height="15dp"
android:layout_marginEnd="15dp" android:layout_marginEnd="15dp"
android:alpha="0.85" android:alpha="0.85"
android:src="@mipmap/ic_about" android:src="@drawable/ic_about"
android:tint="@color/colorTextDark" /> android:tint="@color/colorTextDark" />
<TextView <TextView
@@ -265,6 +265,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/quick_action_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"
@@ -282,11 +283,21 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:gravity="center|start"> android:gravity="center|start">
<ImageView <androidx.cardview.widget.CardView
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:src="@mipmap/ic_shot_icon" /> 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 <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -346,6 +357,7 @@
</LinearLayout> </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"
@@ -364,11 +376,21 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center|start"> android:gravity="center|start">
<ImageView <androidx.cardview.widget.CardView
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:src="@mipmap/ic_home" /> app:cardBackgroundColor="#FFFF9800"
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_home" />
</androidx.cardview.widget.CardView>
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -380,7 +402,7 @@
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
<com.fankes.tsbattery.ui.view.MaterialSwitch <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"
@@ -428,11 +450,21 @@
android:layout_marginBottom="15dp" android:layout_marginBottom="15dp"
android:gravity="center|start"> android:gravity="center|start">
<ImageView <androidx.cardview.widget.CardView
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:src="@mipmap/ic_help" /> app:cardBackgroundColor="#FF03A9F4"
app:cardCornerRadius="50dp"
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"
@@ -470,7 +502,7 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.8" android:alpha="0.8"
android:lineSpacingExtra="10dp" android:lineSpacingExtra="10dp"
android:text="Q.如何使用?\nA.目前模块支持 LSPosed、LSPatch 以及太极(无极)框架,在太极和 LSPosed 的作用域中,只需勾选 QQ、TIM、微信即可模块可以做到即插即用激活后无需重启手机重启 QQ、TIM 或微信就可以了。" android:text="Q.如何使用?\nA.目前模块支持 LSPosed、LSPatch 以及太极和一些常见的免 Root 框架,在太极和 LSPosed 的作用域中,只需勾选 QQ、TIM、微信即可模块可以做到即插即用激活后无需重启手机重启 QQ、TIM 或微信就可以了。"
android:textColor="@color/colorTextDark" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
@@ -560,7 +592,8 @@
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@mipmap/bg_qr_pay" /> android:adjustViewBounds="true"
android:src="@mipmap/bg_payment_code" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<TextView <TextView
@@ -598,7 +631,36 @@
android:ellipsize="end" android:ellipsize="end"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:maxLines="2" android:maxLines="2"
android:text="此模块使用 YukiHookAPI 构建。\n了解更多 https://github.com/fankes/YukiHookAPI" 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:textColor="@color/colorTextGray"
android:textSize="11sp" /> android:textSize="11sp" />
</LinearLayout> </LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 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.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -4,11 +4,11 @@
<!-- Primary brand color. --> <!-- Primary brand color. -->
<item name="colorPrimary">@color/colorPrimaryAccent</item> <item name="colorPrimary">@color/colorPrimaryAccent</item>
<item name="colorPrimaryVariant">@color/colorPrimaryAccent</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/colorPrimaryAccent</item> <item name="colorSecondary">@color/colorPrimaryAccent</item>
<item name="colorSecondaryVariant">@color/colorPrimaryAccent</item> <item name="colorSecondaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnSecondary">@color/colorPrimaryAccent</item> <item name="colorOnSecondary">@color/white</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->

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

@@ -4,11 +4,11 @@
<!-- Primary brand color. --> <!-- Primary brand color. -->
<item name="colorPrimary">@color/colorPrimaryAccent</item> <item name="colorPrimary">@color/colorPrimaryAccent</item>
<item name="colorPrimaryVariant">@color/colorPrimaryAccent</item> <item name="colorPrimaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnPrimary">@color/colorPrimaryAccent</item> <item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. --> <!-- Secondary brand color. -->
<item name="colorSecondary">@color/colorPrimaryAccent</item> <item name="colorSecondary">@color/colorPrimaryAccent</item>
<item name="colorSecondaryVariant">@color/colorPrimaryAccent</item> <item name="colorSecondaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnSecondary">@color/colorPrimaryAccent</item> <item name="colorOnSecondary">@color/white</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->

View File

@@ -1,15 +0,0 @@
plugins {
id 'com.android.application' version '7.3.0' apply false
id 'com.android.library' version '7.3.0' apply false
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
}
ext {
appVersionName = "4.1"
appVersionCode = 26
enableR8 = true
}
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 {
autowire(libs.plugins.android.application) apply false
autowire(libs.plugins.kotlin.android) apply false
autowire(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,23 +1,17 @@
# Project-wide Gradle settings. # Compiler Configuration
# IDE (e.g. Android Studio) users: org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# 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=-XX:+UseParallelGC
# 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 for this project: "official" or "obsolete":
kotlin.code.style=official kotlin.code.style=official
# Incremental # Project Configuration
kotlin.incremental.useClasspathSnapshot=true 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

View File

@@ -0,0 +1,94 @@
preferences:
autowire-on-sync-mode: UPDATE_OPTIONAL_DEPENDENCIES
repositories-mode: FAIL_ON_PROJECT_REPOS
repositories:
gradle-plugin-portal:
scope: PLUGINS
google:
maven-central:
jit-pack:
sonatype-oss-releases:
rovo89-xposed-api:
scope: LIBRARIES
url: https://api.xposed.info/
content:
include:
group:
de.robv.android.xposed
fankes-maven-releases:
url: https://raw.githubusercontent.com/fankes/maven-repository/main/repository/releases
plugins:
com.android.application:
alias: android-application
version: 8.12.1
org.jetbrains.kotlin.android:
alias: kotlin-android
version: 2.2.10
com.google.devtools.ksp:
alias: kotlin-ksp
version: 2.2.10-2.0.2
libraries:
com.fankes.projectpromote:
project-promote:
version: 1.0.0
repositories:
fankes-maven-releases
de.robv.android.xposed:
api:
version: 82
repositories:
rovo89-xposed-api
com.highcapable.yukihookapi:
api:
version: 1.3.0
ksp-xposed:
version-ref: <this>::api
com.highcapable.kavaref:
kavaref-core:
version: 1.0.1
kavaref-extension:
version: 1.0.1
com.highcapable.hikage:
hikage-core:
version: 1.0.2
hikage-compiler:
version: 1.0.1
hikage-extension:
version: 1.0.1
hikage-widget-androidx:
version: 1.0.1
hikage-widget-material:
version: 1.0.1
org.luckypray:
dexkit:
version: 2.0.6
com.github.duanhong169:
drawabletoolbox:
version: 1.0.7
com.squareup.okhttp3:
okhttp:
version: 5.1.0
androidx.core:
core-ktx:
version: 1.17.0
androidx.appcompat:
appcompat:
version: 1.7.1
com.google.android.material:
material:
version: 1.12.0
androidx.constraintlayout:
constraintlayout:
version: 2.2.1
androidx.test.ext:
junit:
version: 1.3.0
androidx.test.espresso:
espresso-core:
version: 3.7.0
junit:
junit:
version: 4.13.2

View File

@@ -1,6 +1,5 @@
#Wed May 25 04:34:58 CST 2022
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -1,19 +0,0 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
maven { url "https://api.xposed.info/" }
maven { url "https://www.jitpack.io" }
maven { url "https://s01.oss.sonatype.org/content/repositories/releases" }
mavenCentral()
}
}
rootProject.name = "TSBattery"
include ':app'

27
settings.gradle.kts Normal file
View File

@@ -0,0 +1,27 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
plugins {
id("com.highcapable.sweetdependency") version "1.0.4"
id("com.highcapable.sweetproperty") version "1.0.8"
}
sweetProperty {
global {
all {
permanentKeyValues("GITHUB_CI_COMMIT_ID" to "")
generateFrom(ROOT_PROJECT, SYSTEM_ENV)
}
sourcesCode {
includeKeys("GITHUB_CI_COMMIT_ID")
// 关闭类型自动转换功能,防止一些特殊 "COMMIT ID" 被生成为数值
isEnableTypeAutoConversion = false
}
}
rootProject { all { isEnable = false } }
}
rootProject.name = "TSBattery"
include(":app")