Compare commits
352 Commits
Author | SHA1 | Date | |
---|---|---|---|
8025b45b06
|
|||
d8b3884cb0
|
|||
1a3c8d9bcb
|
|||
b6d8040bfa
|
|||
d4f8d7d3fd
|
|||
a4970747c0
|
|||
4a8816de37
|
|||
6969d7b2c6
|
|||
ea0b46020f
|
|||
4436815525
|
|||
e96b6a5d2a
|
|||
576c8574c8
|
|||
94d425fcf7
|
|||
58510cef52
|
|||
efe000d690
|
|||
c9d49a398f
|
|||
bd027b37f5
|
|||
2e56baa6c7
|
|||
147b7fa6f5
|
|||
6bd906a51e
|
|||
5c92671ae2
|
|||
b5bb7eb892
|
|||
3361284f16
|
|||
5b4da6df45
|
|||
f520983d32
|
|||
1e67116f0a
|
|||
7257127c0f
|
|||
63d5043f31
|
|||
64c4c282b2
|
|||
89cbc49287
|
|||
6b0fe1014e
|
|||
bdf52ba463
|
|||
c2b95b9133
|
|||
2f64fb9ea9
|
|||
f48277f434
|
|||
047f746afb
|
|||
ac885baa64
|
|||
4332881dad
|
|||
49c0655412
|
|||
7eeda27937
|
|||
a8692e8a33
|
|||
39ec0e8ef7
|
|||
1dc22b90bb
|
|||
da955fc157
|
|||
94752010ec
|
|||
f9bc6fc013
|
|||
dcd95e4d8d
|
|||
7f63f93165
|
|||
e95af0bad9
|
|||
344104903b
|
|||
249cec2bee
|
|||
42387603c6
|
|||
fb3aa96c0a
|
|||
f7c31d9786
|
|||
8d00aca110
|
|||
00a1aaad5b
|
|||
a4602df9cb
|
|||
d5624e0e24
|
|||
60f9af38b2
|
|||
d3bea2ec83
|
|||
d085659fbb
|
|||
f2ec8bf33e
|
|||
3233e772bb
|
|||
e58da11553
|
|||
90d4c04593
|
|||
bcb7f6b2c5
|
|||
596726e8c7
|
|||
0a52914911
|
|||
3c30c532a4
|
|||
8f7772d047
|
|||
c148a535c8
|
|||
a5448a2c61
|
|||
a8a01e8afa
|
|||
c322c3dc29
|
|||
2e9761f01a
|
|||
0fd1a33533
|
|||
e465739f95
|
|||
52f992a6cd
|
|||
37be07f11d
|
|||
d8d9fc8c41
|
|||
f27fdb2f25
|
|||
3629ca3df5
|
|||
12a1c12f8a
|
|||
bc41ac2bdc
|
|||
5f13e203f0
|
|||
69a68999a5
|
|||
bc48d59b59
|
|||
cadf7b8873
|
|||
7f0c8dd2b5
|
|||
67349c45fd
|
|||
4ab612d5fa
|
|||
bb4b942dae
|
|||
ff0931b7bc
|
|||
fc42a0ddf0
|
|||
abab096ae0
|
|||
ce8e786b5b
|
|||
dd6c69b337
|
|||
69564cccf6
|
|||
a8c16d7823
|
|||
3616919978
|
|||
c5dfbcbe0d
|
|||
f21e8769e0
|
|||
eada17db44
|
|||
67209859c4
|
|||
a65b3c1dbe
|
|||
6375aa7844
|
|||
cbd6fd038d
|
|||
7fcf9228f4
|
|||
d995c69b5c
|
|||
376a2a5890
|
|||
e2ff60e2ef
|
|||
ad0b6d253a
|
|||
76af063ed8
|
|||
e0fdf269a0
|
|||
b1d4cd4017
|
|||
1337d7cf31
|
|||
8855595b80
|
|||
4bf53efa7a
|
|||
7f2e97a94e
|
|||
16e0278369
|
|||
20f6d5529f
|
|||
08d7676478
|
|||
a67faed81c
|
|||
5f67b54f36
|
|||
d607b5900e
|
|||
2831506340
|
|||
bdcfdda874
|
|||
2cdc8258cd
|
|||
8b0e37e5b1
|
|||
9a8b99f1a8
|
|||
dc8b137f18
|
|||
085120811d
|
|||
2e4bc3e802
|
|||
06ef371f82
|
|||
f7f4427c61
|
|||
5542cdef64
|
|||
4c55ff93f1
|
|||
fef1bf9225
|
|||
5f59ee226d
|
|||
e187e637f9
|
|||
c01cc2532e
|
|||
8b657a8aab
|
|||
f0ab310620
|
|||
711e0ac730
|
|||
b1e83a1073
|
|||
b562337995
|
|||
6b990c50f8
|
|||
|
bae91688b1 | ||
|
a59d260321 | ||
|
e850a73c49 | ||
|
1dc725c294 | ||
ce89da3ee8
|
|||
81331dfe81
|
|||
20f1ceca4d
|
|||
fc8589f1ee
|
|||
|
67378b2ebf | ||
|
bf4ecd8345 | ||
6e8d900dc5
|
|||
3d12d3f4a5
|
|||
4b36437dac
|
|||
93789f163b
|
|||
32d8937ddb
|
|||
38c9d78f1e
|
|||
0ee03340da
|
|||
54ee2358cf
|
|||
55708eb96a
|
|||
c4de8ae448
|
|||
9e092742da
|
|||
fb27c107f4
|
|||
2be9a3d934
|
|||
d7dbecb65c
|
|||
6c2b3f12b3
|
|||
f1c520586a
|
|||
e9c99343c3
|
|||
20de713ffd
|
|||
3b78e8a515
|
|||
cf898092bd
|
|||
251a5151df
|
|||
59e93bc040
|
|||
d440e85be4
|
|||
e1dc99de01
|
|||
4eab1f7046
|
|||
3fc294dd8d
|
|||
a2c06cc201 | |||
6f39d5fdab | |||
5ada7585e3 | |||
31b8f157e5 | |||
e71f137f74 | |||
9e9bbcc8ca | |||
8043457a6e | |||
d4a1536d70 | |||
5c784f65f8 | |||
1c6c6e6480 | |||
4712d0063b | |||
583c72470b | |||
8cf945b670 | |||
6fba93e1fa | |||
288563ef68 | |||
d1e4c17817 | |||
c8ec50cb29 | |||
b6e042b9d0 | |||
3c4789d9a0 | |||
46ff5f4bcf | |||
09eeeeab5f | |||
f285e6f96d | |||
fb90d4051b | |||
911eb042a6 | |||
9d48732140 | |||
2d31d46cd2 | |||
3a1a20e688 | |||
24346c056f | |||
e05c8fce30 | |||
e1b15c4c0a | |||
5b99288ea8 | |||
da958920b3 | |||
035e07db89 | |||
90de1612f3 | |||
9d2a87449d | |||
7a8bc6c8f8 | |||
b1e1e15412 | |||
569d4e278a | |||
2729724e93 | |||
64f87bd69f | |||
8d80d0b11f | |||
c045529914 | |||
6799e1e52d | |||
b11bd6dc46 | |||
99a203bde6 | |||
99b736ef36 | |||
415ab22bfc | |||
5bfa7c74d3 | |||
2ba2ff0b35 | |||
3ac6647a25 | |||
18b6b907bf | |||
73762291f1 | |||
49563d8bc5 | |||
0678357048 | |||
b49b4b9b9e | |||
af927d3f19 | |||
73be5df304 | |||
e01102a928 | |||
a21d27b65c | |||
45dc67c609 | |||
c3c2c07b00 | |||
1b573ec296 | |||
95601466ef | |||
07c52eb10a | |||
81aa8a7ef9 | |||
48eb942167 | |||
fe11d0e67b | |||
5fffb0b154 | |||
316db6f887 | |||
17b3d8ac9a | |||
6d58f0330b | |||
1c42bc4def | |||
a3772e3673 | |||
af20fad070 | |||
e510e5d043 | |||
2ebc0bc2dc | |||
3e23c67ad2 | |||
6ff9a08366 | |||
8a90303228 | |||
5b411227d9 | |||
88db36a848 | |||
9b3585c309 | |||
8c38183869 | |||
a1bc86cb81 | |||
3fb12ae743 | |||
f23034639e | |||
ccff6c04fd | |||
48fbadb692 | |||
0594352859 | |||
9aceec38b6 | |||
b925296870 | |||
28b03d2205 | |||
aeb6654550 | |||
82ff6ac311 | |||
74ba88f201 | |||
3144a160d4 | |||
34bfb4660e | |||
8d10a17914 | |||
1edd92ca79 | |||
4b583df6ae | |||
f6d51a7633 | |||
a1791f327b | |||
3d78411738 | |||
72597510f2 | |||
5b371ba3da | |||
751b0c4ce5 | |||
73677d1c33 | |||
ed658c83e6 | |||
|
0b4c58293a | ||
|
5a51ef571a | ||
|
4bd738b206 | ||
4a0473892b | |||
14354ec06f | |||
7eca11ed18 | |||
4c13768500 | |||
e30afc4011 | |||
7a1c53ed17 | |||
a4d62137a2 | |||
beb8da210d | |||
60aecc67bf | |||
69faade0e4 | |||
4ecc694dd4 | |||
674f0a9cd7 | |||
b14b9e02e2 | |||
bacff6b275 | |||
5eb2729eb6 | |||
d93b4bdc7f | |||
f1e64c293c | |||
8b2663c3cb | |||
a5b1a57a93 | |||
d2dd6c9f88 | |||
e4d1ea8519 | |||
d293e4656d | |||
731cfc13d4 | |||
342128a692 | |||
c08facd9f6 | |||
3dfd69dbc7 | |||
d13aee534e | |||
9f8f21dc9a | |||
90258a07c1 | |||
c6bed6549e | |||
b26b78e268 | |||
ef97306151 | |||
26a4a11149 | |||
dc14585805 | |||
8841ad7f59 | |||
190f59451f | |||
7ce09ac4ef | |||
a182e0e4d5 | |||
bb69b762d0 | |||
e276ee4172 | |||
692acd3358 | |||
6360859255 | |||
22f8e14afb | |||
07a6fb6cf7 | |||
163eecc0b8 | |||
5707a8b394 | |||
11aa064b90 | |||
15b1bfa498 | |||
|
673dc8127b | ||
|
7d9c360c9d | ||
d5e885db6c | |||
7481bbd7ef | |||
23c94e4e26 | |||
ead73eb23d | |||
5d82e9a104 | |||
|
14d1b9c5ac | ||
|
44ea28edda | ||
b075fd8424 |
17
.editorconfig
Normal file
@@ -0,0 +1,17 @@
|
||||
# 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
|
||||
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
@@ -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
@@ -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@v3
|
||||
- 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 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 17
|
||||
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@v3
|
||||
with:
|
||||
path: ${{ env.DEBUG_APK_PATH }}
|
||||
name: TSBattery-debug-${{ github.event.head_commit.id }}
|
||||
- name: Upload Artifacts (Release)
|
||||
uses: actions/upload-artifact@v3
|
||||
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
@@ -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@v3
|
||||
- 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 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 17
|
||||
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@v3
|
||||
with:
|
||||
path: ${{ env.DEBUG_APK_PATH }}
|
||||
name: TSBattery-debug-${{ github.event.head_commit.id }}
|
||||
- name: Upload Artifacts (Release)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ env.RELEASE_APK_PATH }}
|
||||
name: TSBattery-release-${{ github.event.head_commit.id }}
|
2
.idea/.gitignore
generated
vendored
@@ -1,3 +1,5 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
/gradle.xml
|
||||
/misc.xml
|
26
.idea/appInsightsSettings.xml
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AppInsightsSettings">
|
||||
<option name="tabSettings">
|
||||
<map>
|
||||
<entry key="Firebase Crashlytics">
|
||||
<value>
|
||||
<InsightsFilterSettings>
|
||||
<option name="connection">
|
||||
<ConnectionSetting>
|
||||
<option name="appId" value="PLACEHOLDER" />
|
||||
<option name="mobileSdkAppId" value="" />
|
||||
<option name="projectId" value="" />
|
||||
<option name="projectNumber" value="" />
|
||||
</ConnectionSetting>
|
||||
</option>
|
||||
<option name="signal" value="SIGNAL_UNSPECIFIED" />
|
||||
<option name="timeIntervalDays" value="THIRTY_DAYS" />
|
||||
<option name="visibilityType" value="ALL" />
|
||||
</InsightsFilterSettings>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
117
.idea/codeStyles/Project.xml
generated
Normal 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
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
2
.idea/compiler.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
10
.idea/deploymentTargetDropDown.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<value>
|
||||
<entry key="app">
|
||||
<State />
|
||||
</entry>
|
||||
</value>
|
||||
</component>
|
||||
</project>
|
20
.idea/gradle.xml
generated
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="PLATFORM" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
BIN
.idea/icon.png
generated
Normal file
After Width: | Height: | Size: 12 KiB |
1
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -7,5 +7,6 @@
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="YAMLSchemaValidation" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
15
.idea/jarRepositories.xml
generated
@@ -26,5 +26,20 @@
|
||||
<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
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.9.10" />
|
||||
</component>
|
||||
</project>
|
6
.idea/ktlint.xml
generated
Normal 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
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
9
.idea/misc.xml
generated
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
10
.idea/runConfigurations.xml
generated
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
47
CONTRIBUTING.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 开始贡献
|
||||
|
||||
欢迎为此项目进行新版本的适配代码贡献!<br/>
|
||||
|
||||
## 分支规定
|
||||
|
||||
不管是直接 Push 代码还是提交 Pull Request,都必须使 commit 指向 master 分支。
|
||||
|
||||
## 代码格式规范
|
||||
|
||||
- 1.全部提交代码必须使用 IDE(Android Studio 或 IDEA) 进行格式化,未经格式化的代码将拒绝合并提交请求
|
||||
- 2.代码必须使用 4 spaces 缩进格式化
|
||||
|
||||
## 代码注释规范
|
||||
|
||||
- 1.第一种注释方式:可使用在方法名或顶级变量名上
|
||||
|
||||
```kotlin
|
||||
/** 注释内容 */
|
||||
fun a() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 注释名称
|
||||
* @param test 方法名称
|
||||
* @return 返回值名称
|
||||
*/
|
||||
fun a(test: String) {
|
||||
}
|
||||
```
|
||||
|
||||
- 2.第二种注释方式:仅可使用在变量后方
|
||||
|
||||
```kotlin
|
||||
val a = "" // 变量注释
|
||||
```
|
||||
|
||||
- ⚠️ 注意:只允许两个 // 后方要有空格
|
||||
|
||||
## 项目要求
|
||||
|
||||
- 1.调试性质或大批量注释代码,禁止提交
|
||||
- 2.类名和方法名仅能由开发者进行修改和提交,禁止随意修改项目名称、方法名称以及类名
|
||||
- 3.禁止随意更新项目依赖以及增加新的依赖,有问题请提前提交到 issues 进行说明
|
||||
- 4.禁止更新项目版本号,版本号交由开发者合并代码并发布 release 版本
|
||||
- 5.代码语言要求,请统一使用 Kotlin,除特殊情况外,不接受其他语言的提交
|
||||
- 6.以上
|
104
README.md
@@ -1,7 +1,99 @@
|
||||
# TSBattery
|
||||
TSBattery a new way to save your battery avoid cancer apps hacker it.<br/>
|
||||
TSBattery 是一个旨在使 QQ、TIM 变得更省电的开源 Xposed 模块
|
||||
# Get startted
|
||||
此模块支持原生 Xposed、Lsposed(作用域 QQ、TIM 如果不起作用勾选系统框架)、EdXposed(不推荐)、太极无极(阴和阳)、Pine(梦境模块)
|
||||
# 禁止任何商业用途
|
||||
本模块完全开源免费,如果好用你可以打赏支持开发,严禁未经许可进行二改贩卖,违者必惩必究。
|
||||
|
||||
[](https://github.com/fankes/TSBattery/blob/master/LICENSE)
|
||||
[](https://github.com/fankes/TSBattery/actions/workflows/commit_ci.yml)
|
||||
[](https://github.com/fankes/TSBattery/releases)
|
||||

|
||||

|
||||
|
||||
[](https://t.me/TSBATTERY_CI)
|
||||
[](https://t.me/XiaofangInternet)
|
||||
[](https://qm.qq.com/cgi-bin/qm/qr?k=dp2h5YhWiga9WWb_Oh7kSHmx01X8I8ii&jump_from=webapi&authKey=Za5CaFP0lk7+Zgsk2KpoBD7sSaYbeXbsDgFjiWelOeH4VSionpxFJ7V0qQBSqvFM)
|
||||
[](https://pd.qq.com/s/44gcy28h)
|
||||
|
||||

|
||||
|
||||
A new way to save your battery avoid cancer apps hacker it.
|
||||
|
||||
TSBattery 是一个旨在使 QQ、TIM、微信 变得更省电的开源 Xposed 模块。
|
||||
|
||||
## For Non-Chinese Users
|
||||
|
||||
This Xposed Module is for use by specific apps for users in mainland China, you should not need it.
|
||||
|
||||
## 适配说明
|
||||
|
||||
- 解锁 BootLoader 并安装 **KernelSU**、**Magisk** 的设备建议使用 [LSPosed](https://github.com/LSPosed/LSPosed)
|
||||
|
||||
- **太极 (无极)** 支持性不是很好,建议使用 [LSPatch](https://github.com/LSPosed/LSPatch)
|
||||
|
||||
- 支持一些第三方 Xposed 框架,但是不保证其稳定性
|
||||
|
||||
- 支持一些第三方免 Root 框架例如**应用转生**、**SandVXposed**,但是不推荐使用,可能会造成封号风险
|
||||
|
||||
- 如果在微信设置界面右上角你无法找到 **TSBattery**
|
||||
的图标,请尝试同时激活 [WeXposed (微X模块)](https://github.com/Xposed-Modules-Repo/com.fkzhang.wechatxposed)
|
||||
|
||||
## 发行渠道说明
|
||||
|
||||
- [Automatic Build on Commit](https://github.com/fankes/TSBattery/actions/workflows/commit_ci.yml)
|
||||
|
||||
上述更新为代码 `commit` 后自动触发,具体更新内容可点击上方的文字前往 **GitHub Actions** 进行查看,本更新由开源的流程自动编译发布,
|
||||
**不保证其稳定性**, 所发布的版本**仅供测试**,且不会特殊说明甚至可能会变更版本号或保持与当前稳定版相同的版本号。
|
||||
|
||||
如果你需要直接下载 CI 自动构建打包的安装包,请点击顶部的 `CI builds | Telegram` 标签加入 Telegram CI 自动构建频道。
|
||||
|
||||
- [Release](https://github.com/fankes/TSBattery/releases)
|
||||
- [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.tsbattery/releases)
|
||||
- [蓝奏云 **密码:tsbt**](https://fankes.lanzouy.com/b02zfz3sj)
|
||||
|
||||
上述更新为手动发布的稳定版,具体更新内容可点击上方的文字前往指定的发布页面查看,稳定版的更新将会同时发布到上述地址中,同步更新。
|
||||
|
||||
本模块发布地址仅限于上述所列出的地址,从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
|
||||
|
||||
## 请勿用于非法用途
|
||||
|
||||
本模块完全开源免费,如果好用你可以打赏支持开发,但是请不要用于非法用途。
|
||||
|
||||
## 项目推广
|
||||
|
||||
如果你正在寻找一个可以自动管理 Gradle 项目依赖的 Gradle 插件,你可以了解一下 [SweetDependency](https://github.com/HighCapable/SweetDependency) 项目。
|
||||
|
||||
如果你正在寻找一个可以自动生成属性键值的 Gradle 插件,你可以了解一下 [SweetProperty](https://github.com/HighCapable/SweetProperty) 项目。
|
||||
|
||||
本项目同样使用了 **SweetDependency** 和 **SweetProperty**。
|
||||
|
||||
## 捐赠支持
|
||||
|
||||
工作不易,无意外情况此项目将继续维护下去,提供更多可能,欢迎打赏。
|
||||
|
||||
<img src="https://github.com/fankes/fankes/blob/main/img-src/payment_code.jpg?raw=true" width = "500" alt="Payment Code"/>
|
||||
|
||||
## Star History
|
||||
|
||||

|
||||
|
||||
## 许可证
|
||||
|
||||
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
```
|
||||
Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
```
|
||||
|
||||
Powered by [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI)
|
||||
|
||||
版权所有 © 2017-2023 Fankes Studio(qzmmcn@163.com)
|
5
app/.gitignore
vendored
@@ -1 +1,4 @@
|
||||
/build
|
||||
/build
|
||||
/release
|
||||
/src/main/assets/xposed_init
|
||||
/src/main/resources/META-INF/yukihookapi_init
|
@@ -1,70 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
}
|
||||
|
||||
android {
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('/Users/fankes/ProjectPath/AndroidStudioProjects/TSBattery/keystore/public')
|
||||
storePassword '123456'
|
||||
keyAlias 'public'
|
||||
keyPassword '123456'
|
||||
v1SigningEnabled true
|
||||
v2SigningEnabled true
|
||||
}
|
||||
}
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.fankes.tsbattery"
|
||||
minSdkVersion 22
|
||||
//noinspection ExpiredTargetSdkVersion,OldTargetApi
|
||||
targetSdkVersion 26
|
||||
versionCode 6
|
||||
versionName "2.3"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
signingConfig signingConfigs.debug
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'de.robv.android.xposed:api:82'
|
||||
// 基础依赖包,必须要依赖
|
||||
implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
|
||||
// fragment快速实现(可选)
|
||||
implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
|
||||
// kotlin扩展(可选)
|
||||
implementation 'com.gyf.immersionbar:immersionbar-ktx:3.0.0'
|
||||
//noinspection GradleDependency,DifferentStdlibGradleVersion
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
//noinspection GradleDependency
|
||||
implementation 'androidx.core:core-ktx:1.5.0'
|
||||
//noinspection GradleDependency
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
//noinspection GradleDependency
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
//noinspection GradleDependency
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
//noinspection GradleDependency
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
//noinspection GradleDependency
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
}
|
84
app/build.gradle.kts
Normal file
@@ -0,0 +1,84 @@
|
||||
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
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = listOf(
|
||||
"-Xno-param-assertions",
|
||||
"-Xno-call-assertions",
|
||||
"-Xno-receiver-assertions"
|
||||
)
|
||||
}
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
viewBinding = true
|
||||
}
|
||||
lint { checkReleaseBuilds = false }
|
||||
androidResources.additionalParameters += listOf("--allow-reserved-package-id", "--package-id", "0x37")
|
||||
}
|
||||
|
||||
androidComponents {
|
||||
onVariants(selector().all()) {
|
||||
it.outputs.forEach { output ->
|
||||
val currentType = it.buildType
|
||||
val currentSuffix = property.github.ci.commit.id.let { suffix -> if (suffix.isNotBlank()) "-$suffix" else "" }
|
||||
val currentVersion = "${output.versionName.get()}$currentSuffix(${output.versionCode.get()})"
|
||||
if (output is com.android.build.api.variant.impl.VariantOutputImpl)
|
||||
output.outputFileName.set("${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)
|
||||
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)
|
||||
}
|
38
app/proguard-rules.pro
vendored
@@ -20,29 +20,41 @@
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-dontwarn
|
||||
-ignorewarnings
|
||||
-optimizationpasses 10
|
||||
-dontusemixedcaseclassnames
|
||||
-dontoptimize
|
||||
-verbose
|
||||
-overloadaggressively
|
||||
-repackageclasses o
|
||||
-allowaccessmodification
|
||||
-adaptclassstrings
|
||||
-adaptresourcefilenames
|
||||
-adaptresourcefilecontents
|
||||
|
||||
#-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
|
||||
-renamesourcefileattribute H
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
-renamesourcefileattribute P
|
||||
-keepattributes SourceFile,Signature,LineNumberTable
|
||||
|
||||
-keep class android.support**
|
||||
-keep class androidx**
|
||||
# 排除注入的 Activity
|
||||
-keep class com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
|
||||
|
||||
-keep public class * extends android.app.Activity
|
||||
-keep public class * extends android.app.Application
|
||||
-keep public class * extends android.app.Service
|
||||
-keep public class * extends android.content.ContentProvider
|
||||
-keep public class * extends android.app.backup.BackupAgentHelper
|
||||
-keep public class * extends android.preference.Preference
|
||||
# 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 {
|
||||
public static *** throwUninitializedProperty(...);
|
||||
public static *** throwUninitializedPropertyAccessException(...);
|
||||
}
|
||||
|
||||
-keep class * extends android.app.Activity
|
||||
-keep class * implements androidx.viewbinding.ViewBinding {
|
||||
<init>();
|
||||
*** inflate(android.view.LayoutInflater);
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
},
|
||||
"applicationId": "com.fankes.tsbattery",
|
||||
"variantName": "processReleaseResources",
|
||||
"elements": [
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"versionCode": 5,
|
||||
"versionName": "2.2",
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,7 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.fankes.tsbattery">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<uses-permission
|
||||
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||
tools:node="remove" />
|
||||
<permission
|
||||
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||
tools:node="remove" />
|
||||
|
||||
<!-- 作用域 APP -->
|
||||
<queries>
|
||||
<package android:name="com.tencent.mobileqq" />
|
||||
<package android:name="com.tencent.tim" />
|
||||
<package android:name="com.tencent.mm" />
|
||||
|
||||
<intent>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:name=".application.TSApplication"
|
||||
@@ -13,24 +33,24 @@
|
||||
android:theme="@style/Theme.TSBattery"
|
||||
tools:ignore="AllowBackup">
|
||||
|
||||
<!-- 是否是xposed模块 -->
|
||||
<meta-data
|
||||
android:name="xposedmodule"
|
||||
android:value="true" />
|
||||
|
||||
<!-- 模块描述 -->
|
||||
<meta-data
|
||||
android:name="xposeddescription"
|
||||
android:value="抵制毒瘤,拒绝疯狂耗电,Tencent 社交毒瘤一键省电模块(目前支持 QQ、TIM),通过干掉电源锁常驻减少电量消耗,理论支持最新版本。by 酷安 @星夜不荟" />
|
||||
|
||||
<!-- 最低xposed版本号 -->
|
||||
android:value="Tencent 社交毒瘤一键省电模块。\n目前支持 QQ、TIM、微信\n开发者:酷安 @星夜不荟" />
|
||||
<meta-data
|
||||
android:name="xposedminversion"
|
||||
android:value="82" />
|
||||
android:value="93" />
|
||||
<meta-data
|
||||
android:name="xposedscope"
|
||||
android:resource="@array/module_scope" />
|
||||
|
||||
<activity
|
||||
android:name="com.fankes.tsbattery.MainActivity"
|
||||
android:label="@string/app_name">
|
||||
android:name="com.fankes.tsbattery.ui.activity.MainActivity"
|
||||
android:exported="true"
|
||||
android:screenOrientation="behind">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="de.robv.android.xposed.category.MODULE_SETTINGS" />
|
||||
@@ -40,8 +60,11 @@
|
||||
<activity-alias
|
||||
android:name="com.fankes.tsbattery.Home"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:targetActivity="com.fankes.tsbattery.MainActivity">
|
||||
android:screenOrientation="behind"
|
||||
android:targetActivity="com.fankes.tsbattery.ui.activity.MainActivity">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
@@ -1 +0,0 @@
|
||||
com.fankes.tsbattery.hook.HookMain
|
@@ -1,226 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of TSBattery.
|
||||
*
|
||||
* TSBattery is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* TSBattery is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2021/9/4.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"DEPRECATION", "SetTextI18n", "SetWorldReadable", "WorldReadableFiles",
|
||||
"LocalVariableName", "SameParameterValue"
|
||||
)
|
||||
|
||||
package com.fankes.tsbattery
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
import com.fankes.tsbattery.hook.HookMedium
|
||||
import com.fankes.tsbattery.utils.FileUtils
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import java.io.File
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val moduleVersion = BuildConfig.VERSION_NAME
|
||||
private const val moduleSupport = "QQ 8.5.5~8.8.38、TIM 2+"
|
||||
|
||||
/** 声明当前实例 */
|
||||
var instance: MainActivity? = null
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
/** 设置自身实例 */
|
||||
instance = this
|
||||
setContentView(R.layout.activity_main)
|
||||
/** 隐藏系统的标题栏 */
|
||||
supportActionBar?.hide()
|
||||
/** 初始化沉浸状态栏 */
|
||||
ImmersionBar.with(this)
|
||||
.statusBarColor(R.color.white)
|
||||
.autoDarkModeEnable(false)
|
||||
.statusBarDarkFont(true)
|
||||
.navigationBarColor(R.color.white)
|
||||
.navigationBarDarkIcon(true)
|
||||
.fitsSystemWindows(true)
|
||||
.init()
|
||||
/** 判断 Hook 状态 */
|
||||
if (isHooked()) {
|
||||
findViewById<LinearLayout>(R.id.main_lin_status).setBackgroundResource(R.drawable.green_round)
|
||||
findViewById<ImageFilterView>(R.id.main_img_status).setImageResource(R.mipmap.succcess)
|
||||
findViewById<TextView>(R.id.main_text_status).text = "模块已激活"
|
||||
/** 写入激活的模块版本 */
|
||||
putString(HookMedium.ENABLE_MODULE_VERSION, moduleVersion)
|
||||
} else
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle("模块没有激活")
|
||||
.setMessage(
|
||||
"检测到模块没有激活,模块需要 Xposed 环境依赖,同时需要系统拥有 Root 权限(太极阴可以免 Root),请自行查看本页面使用帮助与说明第三条。\n" +
|
||||
"太极、应用转生、梦境(Pine)和第三方 Xposed 激活后可能不会提示激活,若想验证是否激活请打开“提示模块运行信息”自行检查,如果生效就代表模块运行正常,这里的激活状态只是一个显示意义上的存在。\n" +
|
||||
"太极(无极)在 MIUI 设备上会提示打开授权,请进行允许,然后再次打开本应用查看激活状态。"
|
||||
)
|
||||
.setPositiveButton("我知道了", null)
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
/** 设置文本 */
|
||||
findViewById<TextView>(R.id.main_text_version).text = "当前版本:$moduleVersion"
|
||||
findViewById<TextView>(R.id.main_text_support).text = "支持 $moduleSupport"
|
||||
/** 初始化 View */
|
||||
val protectModeSwitch = findViewById<SwitchCompat>(R.id.protect_mode_switch)
|
||||
val hideIconInLauncherSwitch = findViewById<SwitchCompat>(R.id.hide_icon_in_launcher_switch)
|
||||
val notifyModuleInfoSwitch = findViewById<SwitchCompat>(R.id.notify_module_info_switch)
|
||||
/** 获取 Sp 存储的信息 */
|
||||
protectModeSwitch.isChecked = getBoolean("_white_mode")
|
||||
hideIconInLauncherSwitch.isChecked = getBoolean("_hide_icon")
|
||||
notifyModuleInfoSwitch.isChecked = getBoolean("_tip_run_info")
|
||||
protectModeSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (!btn.isPressed) return@setOnCheckedChangeListener
|
||||
putBoolean(HookMedium.ENABLE_WHITE_MODE, b)
|
||||
}
|
||||
hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (!btn.isPressed) return@setOnCheckedChangeListener
|
||||
putBoolean(HookMedium.ENABLE_HIDE_ICON, b)
|
||||
packageManager.setComponentEnabledSetting(
|
||||
ComponentName(this@MainActivity, "com.fankes.tsbattery.Home"),
|
||||
if (b) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||
PackageManager.DONT_KILL_APP
|
||||
)
|
||||
}
|
||||
notifyModuleInfoSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (!btn.isPressed) return@setOnCheckedChangeListener
|
||||
putBoolean(HookMedium.ENABLE_RUN_INFO, b)
|
||||
}
|
||||
/** 项目地址点击事件 */
|
||||
findViewById<View>(R.id.link_with_project_address).setOnClickListener {
|
||||
try {
|
||||
startActivity(Intent().apply {
|
||||
action = "android.intent.action.VIEW"
|
||||
data = Uri.parse("https://github.com/fankes/TSBattery")
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "无法启动系统默认浏览器", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断模块是否激活
|
||||
* @return 激活状态
|
||||
*/
|
||||
private fun isHooked() = HookMedium.isHooked()
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
setWorldReadable()
|
||||
}
|
||||
|
||||
override fun onRestart() {
|
||||
super.onRestart()
|
||||
setWorldReadable()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
setWorldReadable()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取保存的值
|
||||
* @param key 名称
|
||||
* @return 保存的值
|
||||
*/
|
||||
private fun getBoolean(key: String) =
|
||||
getSharedPreferences(
|
||||
packageName + "_preferences",
|
||||
Context.MODE_PRIVATE
|
||||
).getBoolean(key, false)
|
||||
|
||||
/**
|
||||
* 保存值
|
||||
* @param key 名称
|
||||
* @param bool 值
|
||||
*/
|
||||
private fun putBoolean(key: String, bool: Boolean) {
|
||||
getSharedPreferences(
|
||||
packageName + "_preferences",
|
||||
Context.MODE_PRIVATE
|
||||
).edit().putBoolean(key, bool).apply()
|
||||
setWorldReadable()
|
||||
/** 延迟继续设置强制允许 SP 可读可写 */
|
||||
Handler().postDelayed({ setWorldReadable() }, 500)
|
||||
Handler().postDelayed({ setWorldReadable() }, 1000)
|
||||
Handler().postDelayed({ setWorldReadable() }, 1500)
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存值
|
||||
* @param key 名称
|
||||
* @param value 值
|
||||
*/
|
||||
private fun putString(key: String, value: String) {
|
||||
getSharedPreferences(
|
||||
packageName + "_preferences",
|
||||
Context.MODE_PRIVATE
|
||||
).edit().putString(key, value).apply()
|
||||
setWorldReadable()
|
||||
/** 延迟继续设置强制允许 SP 可读可写 */
|
||||
Handler().postDelayed({ setWorldReadable() }, 500)
|
||||
Handler().postDelayed({ setWorldReadable() }, 1000)
|
||||
Handler().postDelayed({ setWorldReadable() }, 1500)
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制设置 Sp 存储为全局可读可写
|
||||
* 以供模块使用
|
||||
*/
|
||||
private fun setWorldReadable() {
|
||||
try {
|
||||
if (FileUtils.getDefaultPrefFile(this).exists()) {
|
||||
for (file in arrayOf<File>(
|
||||
FileUtils.getDataDir(this),
|
||||
FileUtils.getPrefDir(this),
|
||||
FileUtils.getDefaultPrefFile(this)
|
||||
)) {
|
||||
file.setReadable(true, false)
|
||||
file.setExecutable(true, false)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "无法写入模块设置,请检查权限\n如果此提示一直显示,请不要双开模块", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
/** 销毁实例防止内存泄漏 */
|
||||
instance = null
|
||||
}
|
||||
}
|
@@ -1,35 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This file is part of TSBattery.
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* TSBattery is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* TSBattery is distributed in the hope that it will be useful,
|
||||
* 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 General Public License for more details.
|
||||
* 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 General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2021/9/4.
|
||||
* This file is created by fankes on 2021/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.fankes.tsbattery.application
|
||||
|
||||
import android.app.Application
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
|
||||
|
||||
class TSApplication : Application() {
|
||||
class TSApplication : ModuleApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
/** 禁止系统夜间模式对自己造成干扰 - 模块要什么夜间模式?😅 (其实是我懒) */
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||
/** 跟随系统夜间模式 */
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
}
|
||||
}
|
74
app/src/main/java/com/fankes/tsbattery/const/ConstFactory.kt
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/9/29.
|
||||
*/
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.tsbattery.const
|
||||
|
||||
import com.fankes.tsbattery.generated.AppProperties
|
||||
import com.fankes.tsbattery.wrapper.BuildConfigWrapper
|
||||
|
||||
/**
|
||||
* 包名常量定义类
|
||||
*/
|
||||
object PackageName {
|
||||
|
||||
/** QQ */
|
||||
const val QQ = "com.tencent.mobileqq"
|
||||
|
||||
/** TIM */
|
||||
const val TIM = "com.tencent.tim"
|
||||
|
||||
/** 微信 */
|
||||
const val WECHAT = "com.tencent.mm"
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转常量定义类
|
||||
*/
|
||||
object JumpEvent {
|
||||
|
||||
/** 启动模块设置 */
|
||||
const val OPEN_MODULE_SETTING = "tsbattery_open_module_settings"
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块版本常量定义类
|
||||
*/
|
||||
object ModuleVersion {
|
||||
|
||||
/** 当前 GitHub 提交的 ID (CI 自动构建) */
|
||||
const val GITHUB_COMMIT_ID = AppProperties.GITHUB_CI_COMMIT_ID
|
||||
|
||||
/** 版本名称 */
|
||||
const val NAME = BuildConfigWrapper.VERSION_NAME
|
||||
|
||||
/** 版本号 */
|
||||
const val CODE = BuildConfigWrapper.VERSION_CODE
|
||||
|
||||
/** 是否为 CI 自动构建版本 */
|
||||
val isCiMode = GITHUB_COMMIT_ID.isNotBlank()
|
||||
|
||||
/** 当前版本名称后缀 */
|
||||
val suffix = GITHUB_COMMIT_ID.let { if (it.isNotBlank()) "-$it" else "" }
|
||||
|
||||
override fun toString() = "$NAME$suffix($CODE)"
|
||||
}
|
128
app/src/main/java/com/fankes/tsbattery/data/ConfigData.kt
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/9/28.
|
||||
*/
|
||||
@file:Suppress("StaticFieldLeak")
|
||||
|
||||
package com.fankes.tsbattery.data
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.CompoundButton
|
||||
import com.highcapable.yukihookapi.hook.factory.prefs
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookPrefsBridge
|
||||
|
||||
/**
|
||||
* 全局配置存储控制类
|
||||
*/
|
||||
object ConfigData {
|
||||
|
||||
/** QQ、TIM 保守模式*/
|
||||
const val ENABLE_QQ_TIM_PROTECT_MODE = "enable_qq_tim_protect_mode"
|
||||
|
||||
/** 自动关闭 QQ、TIM 的 CoreService */
|
||||
const val ENABLE_KILL_QQ_TIM_CORESERVICE = "enable_kill_qq_tim_core_service"
|
||||
|
||||
/** 自动关闭 QQ、TIM 的 CoreService$KernelService */
|
||||
const val ENABLE_KILLE_QQ_TIM_CORESERVICE_CHILD = "enable_kill_qq_tim_core_service_child"
|
||||
|
||||
/** 停用全部省电功能 (停用模块) */
|
||||
const val DISABLE_ALL_HOOK = "disable_all_hook"
|
||||
|
||||
/** 当前的 [YukiHookPrefsBridge] */
|
||||
private var prefs: YukiHookPrefsBridge? = null
|
||||
|
||||
/**
|
||||
* 读取 [YukiHookPrefsBridge]
|
||||
* @param key 键值名称
|
||||
* @param value 键值内容
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private fun getBoolean(key: String, value: Boolean = false) = prefs?.getBoolean(key, value) ?: value
|
||||
|
||||
/**
|
||||
* 存入 [YukiHookPrefsBridge]
|
||||
* @param key 键值名称
|
||||
* @param value 键值内容
|
||||
*/
|
||||
private fun putBoolean(key: String, value: Boolean = false) = prefs?.edit { putBoolean(key, value) }
|
||||
|
||||
/**
|
||||
* 初始化 [YukiHookPrefsBridge]
|
||||
* @param context 实例
|
||||
*/
|
||||
fun init(context: Context) {
|
||||
prefs = context.prefs(name = "tsbattery_config").native()
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定到 [CompoundButton] 自动设置选中状态
|
||||
* @param key 键值名称
|
||||
* @param onChange 当改变时回调
|
||||
*/
|
||||
fun CompoundButton.bind(key: String, onChange: (Boolean) -> Unit = {}) {
|
||||
isChecked = getBoolean(key)
|
||||
setOnCheckedChangeListener { button, isChecked ->
|
||||
if (button.isPressed) {
|
||||
putBoolean(key, isChecked)
|
||||
onChange(isChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用 QQ、TIM 保守模式
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableQQTimProtectMode
|
||||
get() = getBoolean(ENABLE_QQ_TIM_PROTECT_MODE)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_QQ_TIM_PROTECT_MODE, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用自动关闭 QQ、TIM 的 CoreService
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableKillQQTimCoreService
|
||||
get() = getBoolean(ENABLE_KILL_QQ_TIM_CORESERVICE)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_KILL_QQ_TIM_CORESERVICE, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用自动关闭 QQ、TIM 的 CoreService$KernelService
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isEnableKillQQTimCoreServiceChild
|
||||
get() = getBoolean(ENABLE_KILLE_QQ_TIM_CORESERVICE_CHILD)
|
||||
set(value) {
|
||||
putBoolean(ENABLE_KILLE_QQ_TIM_CORESERVICE_CHILD, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否停用全部省电功能 (停用模块)
|
||||
* @return [Boolean]
|
||||
*/
|
||||
var isDisableAllHook
|
||||
get() = getBoolean(DISABLE_ALL_HOOK)
|
||||
set(value) {
|
||||
putBoolean(DISABLE_ALL_HOOK, value)
|
||||
}
|
||||
}
|
48
app/src/main/java/com/fankes/tsbattery/hook/HookEntry.kt
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/2/15.
|
||||
*/
|
||||
package com.fankes.tsbattery.hook
|
||||
|
||||
import com.fankes.tsbattery.const.PackageName
|
||||
import com.fankes.tsbattery.hook.entity.QQTIMHooker
|
||||
import com.fankes.tsbattery.hook.entity.WeChatHooker
|
||||
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
|
||||
import com.highcapable.yukihookapi.hook.factory.configs
|
||||
import com.highcapable.yukihookapi.hook.factory.encase
|
||||
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
|
||||
|
||||
@InjectYukiHookWithXposed
|
||||
object HookEntry : IYukiHookXposedInit {
|
||||
|
||||
/** 是否完全支持当前版本 */
|
||||
var isHookClientSupport = true
|
||||
|
||||
override fun onInit() = configs {
|
||||
debugLog { tag = "TSBattery" }
|
||||
isDebug = false
|
||||
isEnableDataChannel = false
|
||||
}
|
||||
|
||||
override fun onHook() = encase {
|
||||
loadApp(PackageName.QQ, PackageName.TIM) { loadHooker(QQTIMHooker) }
|
||||
loadApp(PackageName.WECHAT, WeChatHooker)
|
||||
}
|
||||
}
|
@@ -1,400 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of TSBattery.
|
||||
*
|
||||
* TSBattery is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* TSBattery is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2021/9/4.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "SameParameterValue")
|
||||
|
||||
package com.fankes.tsbattery.hook
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.Keep
|
||||
import com.fankes.tsbattery.utils.XPrefUtils
|
||||
import de.robv.android.xposed.*
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||
import java.util.*
|
||||
|
||||
@Keep
|
||||
class HookMain : IXposedHookLoadPackage {
|
||||
|
||||
/** 仅作用于替换的 Hook 方法体 */
|
||||
private val replaceToNull = object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam?): Any? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/** 仅作用于替换的 Hook 方法体 */
|
||||
private val replaceToTrue = object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam?): Any {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 干掉目标方法体封装
|
||||
* @param clazz 类名缩写
|
||||
* @param name 方法名
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.replaceToNull(clazz: String, name: String) {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"com.tencent.mobileqq.$clazz",
|
||||
classLoader,
|
||||
name,
|
||||
replaceToNull
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个类 BaseChatPie 是控制聊天界面的
|
||||
* 里面有两个随机混淆的方法
|
||||
* 这两个方法一个是挂起电源锁常驻亮屏
|
||||
* 一个是停止常驻亮屏
|
||||
* 不由分说每个版本混淆的方法名都会变
|
||||
* 所以说每个版本重新适配 - 也可以提交分支帮我适配
|
||||
* 8.8.17 版本是 bd be
|
||||
* 8.8.23 版本是 bf bg
|
||||
* 8.8.38 版本是 bi bj
|
||||
* ⚠️ Hook 错了方法会造成闪退!
|
||||
* @param version QQ 版本
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.hookBaseChatPie(version: String) {
|
||||
when (version) {
|
||||
"8.8.17" -> {
|
||||
replaceToNull("activity.aio.core.BaseChatPie", "bd")
|
||||
replaceToNull("activity.aio.core.BaseChatPie", "be")
|
||||
}
|
||||
"8.8.23" -> {
|
||||
replaceToNull("activity.aio.core.BaseChatPie", "bf")
|
||||
replaceToNull("activity.aio.core.BaseChatPie", "bg")
|
||||
}
|
||||
"8.8.38" -> {
|
||||
replaceToNull("activity.aio.core.BaseChatPie", "bi")
|
||||
replaceToNull("activity.aio.core.BaseChatPie", "bj")
|
||||
}
|
||||
else -> logD("$version not supported!")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the log
|
||||
* @param content
|
||||
*/
|
||||
private fun logD(content: String) {
|
||||
XposedBridge.log(content)
|
||||
Log.d("TSBattery", content)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the log
|
||||
* @param content
|
||||
*/
|
||||
private fun logE(content: String, e: Throwable? = null) {
|
||||
XposedBridge.log(content)
|
||||
Log.e("TSBattery", content, e)
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
|
||||
if (lpparam == null) return
|
||||
when (lpparam.packageName) {
|
||||
/** Hook 自身 */
|
||||
"com.fankes.tsbattery" ->
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"com.fankes.tsbattery.hook.HookMedium",
|
||||
lpparam.classLoader,
|
||||
"isHooked",
|
||||
replaceToTrue
|
||||
)
|
||||
/** 经过测试 QQ 与 TIM 这两个是一个模子里面的东西,所以他们的类名也基本上是一样的 */
|
||||
"com.tencent.mobileqq", "com.tencent.tim" -> {
|
||||
try {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"android.os.PowerManager\$WakeLock",
|
||||
lpparam.classLoader,
|
||||
"acquire",
|
||||
replaceToNull
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
logE("handleLoadPackage: hook wakeLock acquire() Failed", e)
|
||||
}
|
||||
try {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"android.os.PowerManager\$WakeLock",
|
||||
lpparam.classLoader,
|
||||
"acquire",
|
||||
Long::class.java,
|
||||
replaceToNull
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
logE("handleLoadPackage: hook wakeLock acquire(time) Failed", e)
|
||||
}
|
||||
/** 增加通知栏文本显示守护状态 */
|
||||
try {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"android.app.Notification\$Builder",
|
||||
lpparam.classLoader,
|
||||
"setContentText",
|
||||
CharSequence::class.java,
|
||||
object : XC_MethodHook() {
|
||||
override fun beforeHookedMethod(param: MethodHookParam?) {
|
||||
when (param?.args?.get(0) as? CharSequence?) {
|
||||
"QQ正在后台运行" ->
|
||||
param.args?.set(0, "QQ正在后台运行 - TSBattery 守护中")
|
||||
"TIM正在后台运行" ->
|
||||
param.args?.set(0, "TIM正在后台运行 - TSBattery 守护中")
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (e: Throwable) {
|
||||
logE("handleLoadPackage: hook Notification Failed", e)
|
||||
}
|
||||
/** 判断是否开启提示模块运行信息 */
|
||||
if (XPrefUtils.getBoolean(HookMedium.ENABLE_RUN_INFO))
|
||||
try {
|
||||
/**
|
||||
* Hook 启动界面的第一个 [Activity]
|
||||
* QQ 和 TIM 都是一样的类
|
||||
* 在里面加入提示运行信息的对话框测试模块是否激活
|
||||
*/
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"com.tencent.mobileqq.activity.SplashActivity",
|
||||
lpparam.classLoader,
|
||||
"doOnCreate",
|
||||
Bundle::class.java,
|
||||
object : XC_MethodHook() {
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam?) {
|
||||
val self = param?.thisObject as? Activity ?: return
|
||||
try {
|
||||
AlertDialog.Builder(
|
||||
self,
|
||||
android.R.style.Theme_Material_Light_Dialog
|
||||
).setCancelable(false)
|
||||
.setTitle("TSBattery 已激活")
|
||||
.setMessage(
|
||||
"[提示模块运行信息功能已打开]\n" +
|
||||
"模块工作看起来一切正常,请自行测试是否能达到省电效果。\n\n" +
|
||||
"已生效模块版本:${XPrefUtils.getString(HookMedium.ENABLE_MODULE_VERSION)}\n" +
|
||||
"当前模式:${if (XPrefUtils.getBoolean(HookMedium.ENABLE_WHITE_MODE)) "保守模式" else "完全模式"}" +
|
||||
"\n\n包名:${self.packageName}\n版本:${
|
||||
self.packageManager.getPackageInfo(
|
||||
self.packageName,
|
||||
0
|
||||
).versionName
|
||||
}(${
|
||||
self.packageManager.getPackageInfo(
|
||||
self.packageName,
|
||||
0
|
||||
).versionCode
|
||||
})" + "\n\nPS:模块只对挂后台锁屏情况下有省电效果,请不要将过多的群提醒,消息通知打开,这样子在使用过程时照样会极其耗电。\n" +
|
||||
"如果你不想看到此提示。请在模块设置中关闭“提示模块运行信息”,此设置默认关闭。\n" +
|
||||
"开发者 酷安 @星夜不荟\n未经允许禁止转载、修改或复制我的劳动成果。"
|
||||
)
|
||||
.setPositiveButton("我知道了", null)
|
||||
.show()
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(
|
||||
self,
|
||||
"模块已激活,但显示信息弹窗失败了\n$e",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
logE("handleLoadPackage: hook SplashActivity Failed", e)
|
||||
}
|
||||
/** 关闭保守模式后不再仅仅作用于系统电源锁 */
|
||||
if (!XPrefUtils.getBoolean(HookMedium.ENABLE_WHITE_MODE)) {
|
||||
try {
|
||||
/** 通过在 SplashActivity 里取到应用的版本号 */
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"com.tencent.mobileqq.activity.SplashActivity",
|
||||
lpparam.classLoader,
|
||||
"doOnCreate",
|
||||
Bundle::class.java,
|
||||
object : XC_MethodHook() {
|
||||
|
||||
override fun beforeHookedMethod(param: MethodHookParam?) {
|
||||
val self = param?.thisObject as? Activity ?: return
|
||||
val name = self.packageName
|
||||
val version =
|
||||
self.packageManager.getPackageInfo(name, 0).versionName
|
||||
/** 这个地方我们只处理 QQ */
|
||||
try {
|
||||
if (name == "com.tencent.mobileqq")
|
||||
lpparam.hookBaseChatPie(version)
|
||||
} catch (e: Exception) {
|
||||
logE("handleLoadPackage: hook BaseChatPie Failed", e)
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
logE("handleLoadPackage: hook BaseChatPie(first time) Failed", e)
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* 一个不知道是什么作用的电源锁
|
||||
* 同样直接干掉
|
||||
*/
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"com.tencent.mars.ilink.comm.WakerLock",
|
||||
lpparam.classLoader,
|
||||
"lock", Long::class.java,
|
||||
replaceToNull
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logE("handleLoadPackage: hook WakerLock Failed", e)
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* Hook 掉一个一像素保活 [Activity] 真的我怎么都想不到讯哥的程序员做出这种事情
|
||||
* 这个东西经过测试会在锁屏的时候吊起来,解锁的时候自动 finish(),无限耍流氓耗电
|
||||
*/
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"com.tencent.mobileqq.activity.QQLSUnlockActivity",
|
||||
lpparam.classLoader,
|
||||
"onCreate", Bundle::class.java,
|
||||
object : XC_MethodHook() {
|
||||
|
||||
private var origDevice = ""
|
||||
|
||||
override fun beforeHookedMethod(param: MethodHookParam?) {
|
||||
/** 由于在 onCreate 里有一行判断只要型号是 xiaomi 的设备就开电源锁,所以说这里临时替换成菊花厂 */
|
||||
origDevice = Build.MANUFACTURER
|
||||
if (Build.MANUFACTURER.toLowerCase(Locale.ROOT) == "xiaomi")
|
||||
XposedHelpers.setStaticObjectField(
|
||||
Build::class.java,
|
||||
"MANUFACTURER",
|
||||
"HUAWEI"
|
||||
)
|
||||
}
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam?) {
|
||||
(param?.thisObject as? Activity)?.finish()
|
||||
/** 这里再把型号替换回去 - 不影响应用变量等 Xposed 模块修改的型号 */
|
||||
XposedHelpers.setStaticObjectField(
|
||||
Build::class.java,
|
||||
"MANUFACTURER",
|
||||
origDevice
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
/**
|
||||
* 这个东西同上,不知道是啥时候调用
|
||||
* 反正也是一个一像素保活的 [Activity]
|
||||
* 讯哥的程序员真的有你的
|
||||
*/
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"com.tencent.mobileqq.activity.QQLSActivity\$14",
|
||||
lpparam.classLoader,
|
||||
"run",
|
||||
replaceToNull
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logE("handleLoadPackage: hook QQLSActivity Failed", e)
|
||||
}
|
||||
try {
|
||||
/**
|
||||
* 这个是毒瘤核心类
|
||||
* WakeLockMonitor
|
||||
* 这个名字真的起的特别诗情画意
|
||||
* 带给用户的却是 shit 一样的体验
|
||||
* 里面有各种使用 Handler 和 Timer 的各种耗时常驻后台耗电办法持续接收消息
|
||||
* 直接循环全部方法全部干掉
|
||||
*/
|
||||
lpparam.classLoader.loadClass("com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor")
|
||||
.apply {
|
||||
val lockClazz =
|
||||
lpparam.classLoader.loadClass("com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor\$WakeLockEntity")
|
||||
val hookClazz =
|
||||
lpparam.classLoader.loadClass("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam")
|
||||
val onHook = getDeclaredMethod(
|
||||
"onHook",
|
||||
String::class.java,
|
||||
Any::class.java,
|
||||
java.lang.reflect.Array.newInstance(
|
||||
Any::class.java,
|
||||
0
|
||||
).javaClass,
|
||||
Any::class.java
|
||||
).apply { isAccessible = true }
|
||||
val doReport =
|
||||
getDeclaredMethod(
|
||||
"doReport",
|
||||
lockClazz,
|
||||
Int::class.java
|
||||
).apply {
|
||||
isAccessible = true
|
||||
}
|
||||
val afterHookedMethod =
|
||||
getDeclaredMethod(
|
||||
"afterHookedMethod",
|
||||
hookClazz
|
||||
).apply { isAccessible = true }
|
||||
val beforeHookedMethod =
|
||||
getDeclaredMethod("beforeHookedMethod", hookClazz).apply {
|
||||
isAccessible = true
|
||||
}
|
||||
val onAppBackground =
|
||||
getDeclaredMethod("onAppBackground").apply {
|
||||
isAccessible = true
|
||||
}
|
||||
val onOtherProcReport =
|
||||
getDeclaredMethod(
|
||||
"onOtherProcReport",
|
||||
Bundle::class.java
|
||||
).apply { isAccessible = true }
|
||||
val onProcessRun30Min =
|
||||
getDeclaredMethod("onProcessRun30Min").apply {
|
||||
isAccessible = true
|
||||
}
|
||||
val onProcessBG5Min =
|
||||
getDeclaredMethod("onProcessBG5Min").apply {
|
||||
isAccessible = true
|
||||
}
|
||||
val writeReport =
|
||||
getDeclaredMethod(
|
||||
"writeReport",
|
||||
Boolean::class.java
|
||||
).apply { isAccessible = true }
|
||||
XposedBridge.hookMethod(onHook, replaceToNull)
|
||||
XposedBridge.hookMethod(doReport, replaceToNull)
|
||||
XposedBridge.hookMethod(afterHookedMethod, replaceToNull)
|
||||
XposedBridge.hookMethod(beforeHookedMethod, replaceToNull)
|
||||
XposedBridge.hookMethod(onAppBackground, replaceToNull)
|
||||
XposedBridge.hookMethod(onOtherProcReport, replaceToNull)
|
||||
XposedBridge.hookMethod(onProcessRun30Min, replaceToNull)
|
||||
XposedBridge.hookMethod(onProcessBG5Min, replaceToNull)
|
||||
XposedBridge.hookMethod(writeReport, replaceToNull)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logE("handleLoadPackage: hook WakerLockMonitor Failed", e)
|
||||
}
|
||||
logD("handleLoadPackage: hook Complete!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of TSBattery.
|
||||
*
|
||||
* TSBattery is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* TSBattery is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by fankes on 2021/9/4.
|
||||
*/
|
||||
|
||||
package com.fankes.tsbattery.hook
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.annotation.Keep
|
||||
import com.fankes.tsbattery.MainActivity
|
||||
|
||||
@Keep
|
||||
object HookMedium {
|
||||
|
||||
const val ENABLE_HIDE_ICON = "_hide_icon"
|
||||
const val ENABLE_RUN_INFO = "_tip_run_info"
|
||||
const val ENABLE_WHITE_MODE = "_white_mode"
|
||||
const val ENABLE_MODULE_VERSION = "_module_version"
|
||||
|
||||
/**
|
||||
* 判断模块是否激活
|
||||
* 在 [HookMain] 中 Hook 掉此方法
|
||||
* @return 激活状态
|
||||
*/
|
||||
fun isHooked(): Boolean {
|
||||
Log.d("TSBattery", "isHooked: true")
|
||||
return isExpModuleActive()
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增太极判断方式
|
||||
* @return 是否激活
|
||||
*/
|
||||
private fun isExpModuleActive(): Boolean {
|
||||
var isExp = false
|
||||
MainActivity.instance?.also {
|
||||
try {
|
||||
val uri = Uri.parse("content://me.weishu.exposed.CP/")
|
||||
var result: Bundle? = null
|
||||
try {
|
||||
result = it.contentResolver.call(uri, "active", null, null)
|
||||
} catch (e: RuntimeException) {
|
||||
// TaiChi is killed, try invoke
|
||||
try {
|
||||
val intent = Intent("me.weishu.exp.ACTION_ACTIVE")
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
it.startActivity(intent)
|
||||
} catch (e1: Throwable) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (result == null) result = it.contentResolver.call(uri, "active", null, null)
|
||||
if (result == null) return false
|
||||
isExp = result.getBoolean("active", false)
|
||||
} catch (ignored: Throwable) {
|
||||
}
|
||||
}
|
||||
return isExp
|
||||
}
|
||||
}
|
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/9/29.
|
||||
*/
|
||||
@file:Suppress("ConstPropertyName")
|
||||
|
||||
package com.fankes.tsbattery.hook.entity
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.app.ServiceCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.fankes.tsbattery.R
|
||||
import com.fankes.tsbattery.const.ModuleVersion
|
||||
import com.fankes.tsbattery.const.PackageName
|
||||
import com.fankes.tsbattery.data.ConfigData
|
||||
import com.fankes.tsbattery.hook.HookEntry
|
||||
import com.fankes.tsbattery.hook.factory.hookSystemWakeLock
|
||||
import com.fankes.tsbattery.hook.factory.isQQNightMode
|
||||
import com.fankes.tsbattery.hook.factory.jumpToModuleSettings
|
||||
import com.fankes.tsbattery.hook.factory.startModuleSettings
|
||||
import com.fankes.tsbattery.hook.helper.DexKitHelper
|
||||
import com.fankes.tsbattery.utils.factory.appVersionName
|
||||
import com.fankes.tsbattery.utils.factory.dp
|
||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||
import com.highcapable.yukihookapi.hook.factory.buildOf
|
||||
import com.highcapable.yukihookapi.hook.factory.current
|
||||
import com.highcapable.yukihookapi.hook.factory.field
|
||||
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.type.android.BuildClass
|
||||
import com.highcapable.yukihookapi.hook.type.android.BundleClass
|
||||
import com.highcapable.yukihookapi.hook.type.android.ContextClass
|
||||
import com.highcapable.yukihookapi.hook.type.android.IntentClass
|
||||
import com.highcapable.yukihookapi.hook.type.android.MessageClass
|
||||
import com.highcapable.yukihookapi.hook.type.java.AnyArrayClass
|
||||
import com.highcapable.yukihookapi.hook.type.java.AnyClass
|
||||
import com.highcapable.yukihookapi.hook.type.java.BooleanType
|
||||
import com.highcapable.yukihookapi.hook.type.java.CharSequenceClass
|
||||
import com.highcapable.yukihookapi.hook.type.java.IntType
|
||||
import com.highcapable.yukihookapi.hook.type.java.ListClass
|
||||
import com.highcapable.yukihookapi.hook.type.java.LongType
|
||||
import com.highcapable.yukihookapi.hook.type.java.StringClass
|
||||
import com.highcapable.yukihookapi.hook.type.java.UnitType
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
/**
|
||||
* Hook QQ、TIM
|
||||
*/
|
||||
object QQTIMHooker : YukiBaseHooker() {
|
||||
|
||||
/** QQ、TIM 存在的类 */
|
||||
const val JumpActivityClassName = "${PackageName.QQ}.activity.JumpActivity"
|
||||
|
||||
/** QQ、TIM 存在的类 */
|
||||
private val JumpActivityClass by lazyClassOrNull(JumpActivityClassName)
|
||||
|
||||
/** QQ、TIM 存在的类 (NT 版本不再存在) */
|
||||
private val QQSettingSettingActivityClass by lazyClassOrNull("${PackageName.QQ}.activity.QQSettingSettingActivity")
|
||||
|
||||
/** QQ 新版存在的类 (Pad 模式 - NT 版本不再存在) */
|
||||
private val QQSettingSettingFragmentClass by lazyClassOrNull("${PackageName.QQ}.fragment.QQSettingSettingFragment")
|
||||
|
||||
/** QQ、TIM 存在的类 (NT 版本不再存在) */
|
||||
private val AboutActivityClass by lazyClassOrNull("${PackageName.QQ}.activity.AboutActivity")
|
||||
|
||||
/** QQ 新版本存在的类 */
|
||||
private val GeneralSettingActivityClass by lazyClassOrNull("${PackageName.QQ}.activity.GeneralSettingActivity")
|
||||
|
||||
/** QQ 新版本 (NT) 存在的类 */
|
||||
private val MainSettingFragmentClass by lazyClassOrNull("${PackageName.QQ}.setting.main.MainSettingFragment")
|
||||
|
||||
/** QQ 新版本 (NT) 存在的类 */
|
||||
private val MainSettingConfigProviderClass by lazyClassOrNull("${PackageName.QQ}.setting.main.MainSettingConfigProvider")
|
||||
|
||||
/** QQ、TIM 新版本存在的类 */
|
||||
private val FormSimpleItemClass by lazyClassOrNull("${PackageName.QQ}.widget.FormSimpleItem")
|
||||
|
||||
/** QQ、TIM 旧版本存在的类 */
|
||||
private val FormCommonSingleLineItemClass by lazyClassOrNull("${PackageName.QQ}.widget.FormCommonSingleLineItem")
|
||||
|
||||
/** QQ、TIM 存在的类 */
|
||||
private val CoreServiceClass by lazyClassOrNull("${PackageName.QQ}.app.CoreService")
|
||||
|
||||
/** QQ、TIM 存在的类 */
|
||||
private val CoreService_KernelServiceClass by lazyClassOrNull("${PackageName.QQ}.app.CoreService\$KernelService")
|
||||
|
||||
/** 根据多个版本存的不同的类 */
|
||||
private val BaseChatPieClass by lazyClassOrNull(
|
||||
VariousClass(
|
||||
"${PackageName.QQ}.activity.aio.core.BaseChatPie",
|
||||
"${PackageName.QQ}.activity.BaseChatPie"
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* DexKit 搜索结果数据实现类
|
||||
*/
|
||||
private object DexKitData {
|
||||
var BaseChatPie_RemainScreenOnMethod: Method? = null
|
||||
var BaseChatPie_CancelRemainScreenOnMethod: Method? = null
|
||||
var SimpleItemProcessorClass: Class<*>? = null
|
||||
var SimpleItemProcessorClass_OnClickMethod: Method? = null
|
||||
}
|
||||
|
||||
/** 一个内部进程的名称 (与 X5 浏览器内核有关) */
|
||||
private val privilegedProcessName = "$packageName:privileged_process"
|
||||
|
||||
/** 默认的 [Configuration] */
|
||||
var baseConfiguration: Configuration? = null
|
||||
|
||||
/**
|
||||
* 当前是否为 QQ
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private val isQQ get() = packageName == PackageName.QQ
|
||||
|
||||
/**
|
||||
* 当前是否为 QQ 的 NT 版本
|
||||
*
|
||||
* 在 QQ NT 中 [AboutActivityClass] 已被移除 - 以此作为判断条件
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private val isQQNTVersion get() = isQQ && AboutActivityClass == null
|
||||
|
||||
/** 当前宿主的版本 */
|
||||
private var hostVersionName = "<unknown>"
|
||||
|
||||
/**
|
||||
* 通过 [Activity] or [Fragment] 实例得到上下文
|
||||
* @return [Activity] or null
|
||||
*/
|
||||
private fun Any.compatToActivity() = if (this is Activity) this else current().method { name = "getActivity"; superClass() }.invoke()
|
||||
|
||||
/** 使用 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 = UnitType.name
|
||||
}
|
||||
}.firstOrNull()?.getMethodInstance(classLoader)
|
||||
DexKitData.BaseChatPie_CancelRemainScreenOnMethod =
|
||||
findMethod {
|
||||
matcher {
|
||||
declaredClass(baseChatPieClassName)
|
||||
usingStrings("cancelRemainScreenOn")
|
||||
paramCount = 0
|
||||
returnType = UnitType.name
|
||||
}
|
||||
}.firstOrNull()?.getMethodInstance(classLoader)
|
||||
}
|
||||
val kotlinFunction0 = "kotlin.jvm.functions.Function0"
|
||||
findClass {
|
||||
searchPackages("${PackageName.QQ}.setting.processor")
|
||||
matcher {
|
||||
methods {
|
||||
add {
|
||||
name = "<init>"
|
||||
paramTypes(ContextClass.name, IntType.name, CharSequenceClass.name, IntType.name)
|
||||
}
|
||||
add {
|
||||
paramTypes(kotlinFunction0)
|
||||
returnType = UnitType.name
|
||||
}
|
||||
}
|
||||
fields { count(6..Int.MAX_VALUE) }
|
||||
}
|
||||
}.firstOrNull()?.name?.also { className ->
|
||||
DexKitData.SimpleItemProcessorClass = className.toClass()
|
||||
DexKitData.SimpleItemProcessorClass_OnClickMethod =
|
||||
findMethod {
|
||||
matcher {
|
||||
declaredClass = className
|
||||
paramTypes(kotlinFunction0)
|
||||
returnType = UnitType.name
|
||||
usingNumbers(2)
|
||||
}
|
||||
}.firstOrNull()?.getMethodInstance(classLoader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个类 QQ 的 BaseChatPie 是控制聊天界面的
|
||||
*
|
||||
* 里面有两个随机混淆的方法 ⬇
|
||||
*
|
||||
* remainScreenOn、cancelRemainScreenOn
|
||||
*
|
||||
* 这两个方法一个是挂起电源锁常驻亮屏 - 一个是停止常驻亮屏
|
||||
*/
|
||||
private fun hookQQBaseChatPie() {
|
||||
/**
|
||||
* 打印警告信息
|
||||
* @param index 序号
|
||||
*/
|
||||
fun warn(index: Int) {
|
||||
HookEntry.isHookClientSupport = false
|
||||
YLog.warn("$hostVersionName [$index] not support!")
|
||||
}
|
||||
DexKitData.BaseChatPie_RemainScreenOnMethod?.hook()?.intercept() ?: warn(index = 0)
|
||||
DexKitData.BaseChatPie_CancelRemainScreenOnMethod?.hook()?.intercept() ?: warn(index = 1)
|
||||
}
|
||||
|
||||
/** Hook CoreService QQ、TIM */
|
||||
private fun hookCoreService() {
|
||||
CoreServiceClass?.apply {
|
||||
if (isQQ) {
|
||||
method {
|
||||
name = "startTempService"
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "startCoreService"
|
||||
param(BooleanType)
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "onStartCommand"
|
||||
param(IntentClass, IntType, IntType)
|
||||
}.ignored().hook().replaceTo(any = 2)
|
||||
}
|
||||
method {
|
||||
name = "onCreate"
|
||||
}.ignored().hook().after {
|
||||
if (ConfigData.isEnableKillQQTimCoreService)
|
||||
instance<Service>().apply {
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
stopSelf()
|
||||
YLog.debug("Shutdown CoreService OK!")
|
||||
}
|
||||
}
|
||||
}
|
||||
CoreService_KernelServiceClass?.apply {
|
||||
method {
|
||||
name = "onCreate"
|
||||
}.ignored().hook().after {
|
||||
if (ConfigData.isEnableKillQQTimCoreServiceChild)
|
||||
instance<Service>().apply {
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
stopSelf()
|
||||
YLog.debug("Shutdown CoreService\$KernelService OK!")
|
||||
}
|
||||
}
|
||||
method {
|
||||
name = "onStartCommand"
|
||||
param(IntentClass, IntType, IntType)
|
||||
}.ignored().hook().replaceTo(any = 2)
|
||||
}
|
||||
}
|
||||
|
||||
/** Hook QQ 不省电的功能 */
|
||||
private fun hookQQDisgusting() {
|
||||
if (isQQ.not()) return
|
||||
/**
|
||||
* 干掉消息收发功能的电源锁
|
||||
* 每个版本的差异暂未做排查
|
||||
* 旧版本理论上没有这个类
|
||||
*/
|
||||
"${PackageName.QQ}.msf.service.y".toClassOrNull()
|
||||
?.method {
|
||||
name = "a"
|
||||
param(StringClass, LongType)
|
||||
returnType = UnitType
|
||||
}?.ignored()?.hook()?.intercept()
|
||||
/**
|
||||
* 干掉自动上传服务的电源锁
|
||||
* 每个版本的差异暂未做排查
|
||||
*/
|
||||
"com.tencent.upload.impl.UploadServiceImpl".toClassOrNull()
|
||||
?.method {
|
||||
name = "acquireWakeLockIfNot"
|
||||
}?.ignored()?.hook()?.intercept()
|
||||
/**
|
||||
* Hook 掉一个一像素保活 Activity 真的我怎么都想不到讯哥的程序员做出这种事情
|
||||
* 这个东西经过测试会在锁屏的时候吊起来,解锁的时候自动 finish(),无限耍流氓耗电
|
||||
* 2022/1/25 后期查证:锁屏界面消息快速回复窗口的解锁后拉起保活界面,也是毒瘤
|
||||
*/
|
||||
"${PackageName.QQ}.activity.QQLSUnlockActivity".toClassOrNull()
|
||||
?.method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
}?.ignored()?.hook {
|
||||
var origDevice = ""
|
||||
before {
|
||||
/** 由于在 onCreate 里有一行判断只要型号是 xiaomi 的设备就开电源锁,所以说这里临时替换成菊花厂 */
|
||||
origDevice = Build.MANUFACTURER
|
||||
if (Build.MANUFACTURER.lowercase() == "xiaomi")
|
||||
BuildClass.field { name = "MANUFACTURER" }.get().set("HUAWEI")
|
||||
}
|
||||
after {
|
||||
instance<Activity>().finish()
|
||||
/** 这里再把型号替换回去 - 不影响应用变量等 Xposed 模块修改的型号 */
|
||||
BuildClass.field { name = "MANUFACTURER" }.get().set(origDevice)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 这个东西同上
|
||||
* 反正也是一个一像素保活的 Activity
|
||||
* 讯哥的程序员真的有你的
|
||||
* 2022/1/25 后期查证:锁屏界面消息快速回复窗口
|
||||
*/
|
||||
VariousClass("${PackageName.QQ}.activity.QQLSActivity\$14", "ktq").toClassOrNull()
|
||||
?.method {
|
||||
name = "run"
|
||||
}?.ignored()?.hook()?.intercept()
|
||||
/**
|
||||
* 这个是毒瘤核心类
|
||||
* WakeLockMonitor
|
||||
* 这个名字真的起的特别诗情画意
|
||||
* 带给用户的却是 shit 一样的体验
|
||||
* 里面有各种使用 Handler 和 Timer 的各种耗时常驻后台耗电办法持续接收消息
|
||||
* 直接循环全部方法全部干掉
|
||||
* 👮🏻 经过排查 Play 版本没这个类...... Emmmm 不想说啥了
|
||||
* ✅ 备注:8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类
|
||||
*/
|
||||
"com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor".toClassOrNull()?.apply {
|
||||
method {
|
||||
name = "onHook"
|
||||
param(StringClass, AnyClass, AnyArrayClass, AnyClass)
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "doReport"
|
||||
param("com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor\$WakeLockEntity", IntType)
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "afterHookedMethod"
|
||||
param("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam")
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "beforeHookedMethod"
|
||||
param("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam")
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "onAppBackground"
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "onOtherProcReport"
|
||||
param(BundleClass)
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "onProcessRun30Min"
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "onProcessBG5Min"
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "writeReport"
|
||||
param(BooleanType)
|
||||
}.ignored().hook().intercept()
|
||||
}
|
||||
/**
|
||||
* 这个是毒瘤核心操作类
|
||||
* 功能同上、全部拦截
|
||||
* 👮🏻 经过排查 Play 版本也没这个类...... Emmmm 不想说啥了
|
||||
* ✅ 备注:8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类
|
||||
*/
|
||||
"com.tencent.qapmsdk.qqbattery.QQBatteryMonitor".toClassOrNull()?.apply {
|
||||
method {
|
||||
name = "start"
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "stop"
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "handleMessage"
|
||||
param(MessageClass)
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "startMonitorInner"
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "onAppBackground"
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "onAppForeground"
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "setLogWhite"
|
||||
paramCount = 2
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "setCmdWhite"
|
||||
paramCount = 2
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "onWriteLog"
|
||||
param(StringClass, StringClass)
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "onCmdRequest"
|
||||
param(StringClass)
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "addData"
|
||||
paramCount = 4
|
||||
}.ignored().hook().intercept()
|
||||
method {
|
||||
name = "onGpsScan"
|
||||
paramCount = 2
|
||||
}.ignored().hook().intercept()
|
||||
}
|
||||
}
|
||||
|
||||
/** Hook QQ 的设置界面添加模块设置入口 (新版) */
|
||||
private fun hookQQSettingsUi() {
|
||||
if (MainSettingFragmentClass == null) return YLog.error("Could not found main setting class, hook aborted")
|
||||
val kotlinUnit = "kotlin.Unit"
|
||||
val simpleItemProcessorClass = DexKitData.SimpleItemProcessorClass ?: return YLog.error("Could not found processor class, hook aborted")
|
||||
|
||||
/**
|
||||
* 创建入口点条目
|
||||
* @param context 当前实例
|
||||
* @return [Any]
|
||||
*/
|
||||
fun createTSEntryItem(context: Context): Any {
|
||||
/** 为了使用图标资源 ID - 这里需要重新注入模块资源防止不生效 */
|
||||
context.injectModuleAppResources()
|
||||
val iconResId = if (context.isQQNightMode()) R.mipmap.ic_tsbattery_entry_night else R.mipmap.ic_tsbattery_entry_day
|
||||
return simpleItemProcessorClass.buildOf(context, R.id.tsbattery_qq_entry_item_id, "TSBattery", iconResId) {
|
||||
param(ContextClass, IntType, CharSequenceClass, IntType)
|
||||
}?.also { entryItem ->
|
||||
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().field { name = "INSTANCE" }.get().any()
|
||||
} else method.invoke(any, args)
|
||||
}; onClickMethod.invoke(entryItem, proxyOnClick)
|
||||
} ?: error("Could not create TSBattery entry item")
|
||||
}
|
||||
MainSettingConfigProviderClass?.method {
|
||||
param(ContextClass)
|
||||
returnType = ListClass
|
||||
}?.hook()?.after {
|
||||
val context = args().first().cast<Context>() ?: return@after
|
||||
val processor = result<MutableList<Any?>>() ?: return@after
|
||||
processor.add(1, processor[0]?.javaClass?.buildOf(arrayListOf<Any>().apply { add(createTSEntryItem(context)) }, "", "") {
|
||||
param(ListClass, CharSequenceClass, CharSequenceClass)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook QQ 的设置界面添加模块设置入口 (旧版)
|
||||
* @param instance 当前设置界面实例
|
||||
*/
|
||||
private fun hookQQSettingsUiLegacy(instance: Any?) {
|
||||
/** 当前的顶级 Item 实例 */
|
||||
val formItemRefRoot = instance?.current()?.field {
|
||||
type { it == FormSimpleItemClass || it == FormCommonSingleLineItemClass }.index(num = 1)
|
||||
}?.cast<View?>()
|
||||
/** 创建一个新的 Item */
|
||||
FormSimpleItemClass?.buildOf<View>(instance?.compatToActivity()) { param(ContextClass) }?.current {
|
||||
method {
|
||||
name = "setLeftText"
|
||||
param(CharSequenceClass)
|
||||
}.call("TSBattery")
|
||||
method {
|
||||
name = "setRightText"
|
||||
param(CharSequenceClass)
|
||||
}.call(ModuleVersion.toString())
|
||||
method {
|
||||
name = "setBgType"
|
||||
param(IntType)
|
||||
}.call(if (isQQ) 0 else 2)
|
||||
}?.apply { setOnClickListener { context.startModuleSettings() } }?.also { item ->
|
||||
var listGroup = formItemRefRoot?.parent as? ViewGroup?
|
||||
val lparam = (if (listGroup?.childCount == 1) {
|
||||
listGroup = listGroup.parent as? ViewGroup
|
||||
(formItemRefRoot?.parent as? View?)?.layoutParams
|
||||
} else formItemRefRoot?.layoutParams)
|
||||
?: ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
/** 设置圆角和间距 */
|
||||
if (isQQ) (lparam as? ViewGroup.MarginLayoutParams?)?.setMargins(0, 15.dp(item.context), 0, 0)
|
||||
/** 将 Item 添加到设置界面 */
|
||||
listGroup?.also { if (isQQ) it.addView(item, lparam) else it.addView(item, 0, lparam) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHook() {
|
||||
searchUsingDexKit()
|
||||
onAppLifecycle(isOnFailureThrowToApp = false) {
|
||||
attachBaseContext { baseContext, hasCalledSuper ->
|
||||
if (hasCalledSuper.not()) baseConfiguration = baseContext.resources.configuration
|
||||
}
|
||||
onCreate {
|
||||
hostVersionName = appVersionName
|
||||
/** 不注入此进程防止部分系统发生 X5 浏览器内核崩溃问题 */
|
||||
if (processName.startsWith(privilegedProcessName)) return@onCreate
|
||||
ConfigData.init(context = this)
|
||||
registerModuleAppActivities(when {
|
||||
isQQNTVersion -> GeneralSettingActivityClass
|
||||
else -> AboutActivityClass
|
||||
})
|
||||
if (ConfigData.isDisableAllHook) return@onCreate
|
||||
hookSystemWakeLock()
|
||||
hookQQBaseChatPie()
|
||||
hookCoreService()
|
||||
hookQQDisgusting()
|
||||
YLog.info("All processes are completed for \"${processName.takeIf { it != packageName } ?: packageName}\"")
|
||||
}
|
||||
}
|
||||
/** 仅注入主进程 */
|
||||
withProcess(mainProcessName) {
|
||||
/** Hook 跳转事件 */
|
||||
JumpActivityClass?.method {
|
||||
name = "doOnCreate"
|
||||
param(BundleClass)
|
||||
}?.hook()?.after { instance<Activity>().jumpToModuleSettings() }
|
||||
/** Hook 设置界面入口点 */
|
||||
if (isQQNTVersion) hookQQSettingsUi()
|
||||
else {
|
||||
/** 将条目注入设置界面 (Activity) */
|
||||
QQSettingSettingActivityClass?.method {
|
||||
name = "doOnCreate"
|
||||
param(BundleClass)
|
||||
}?.hook()?.after { hookQQSettingsUiLegacy(instance) }
|
||||
/** 将条目注入设置界面 (Fragment) */
|
||||
QQSettingSettingFragmentClass?.method {
|
||||
name = "doOnCreateView"
|
||||
paramCount = 3
|
||||
}?.hook()?.after { hookQQSettingsUiLegacy(instance) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/9/29.
|
||||
*/
|
||||
@file:Suppress("ConstPropertyName")
|
||||
|
||||
package com.fankes.tsbattery.hook.entity
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import com.fankes.tsbattery.R
|
||||
import com.fankes.tsbattery.const.PackageName
|
||||
import com.fankes.tsbattery.data.ConfigData
|
||||
import com.fankes.tsbattery.hook.factory.hookSystemWakeLock
|
||||
import com.fankes.tsbattery.hook.factory.jumpToModuleSettings
|
||||
import com.fankes.tsbattery.hook.factory.startModuleSettings
|
||||
import com.fankes.tsbattery.utils.factory.absoluteStatusBarHeight
|
||||
import com.fankes.tsbattery.utils.factory.appVersionCode
|
||||
import com.fankes.tsbattery.utils.factory.appVersionName
|
||||
import com.fankes.tsbattery.utils.factory.dp
|
||||
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||
import com.highcapable.yukihookapi.hook.factory.current
|
||||
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.factory.processName
|
||||
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.type.android.ViewClass
|
||||
|
||||
/**
|
||||
* Hook 微信
|
||||
*
|
||||
* 具体功能还在画饼
|
||||
*/
|
||||
object WeChatHooker : YukiBaseHooker() {
|
||||
|
||||
/** 微信存在的类 - 未测试每个版本是否都存在 */
|
||||
const val LauncherUIClassName = "${PackageName.WECHAT}.ui.LauncherUI"
|
||||
|
||||
/** 微信存在的类 - 未测试每个版本是否都存在 */
|
||||
private val LauncherUIClass by lazyClassOrNull("${PackageName.WECHAT}.ui.LauncherUI")
|
||||
|
||||
/** 微信存在的类 - 未测试每个版本是否都存在 */
|
||||
private val EmptyActivityClass by lazyClassOrNull("${PackageName.WECHAT}.ui.EmptyActivity")
|
||||
|
||||
/** 微信存在的类 - 未测试每个版本是否都存在 */
|
||||
private val WelabMainUIClass by lazyClassOrNull("${PackageName.WECHAT}.plugin.welab.ui.WelabMainUI")
|
||||
|
||||
/** 微信存在的类 - 未测试每个版本是否都存在 */
|
||||
private val SettingsUIClass by lazyClassOrNull("${PackageName.WECHAT}.plugin.setting.ui.setting.SettingsUI")
|
||||
|
||||
/** TSBattery 图标 TAG 名称 */
|
||||
private const val TSBARRERY_ICON_TAG = "tsbattery_icon"
|
||||
|
||||
/**
|
||||
* 创建 TSBattery 图标
|
||||
* @param context 当前实例
|
||||
* @return [LinearLayout]
|
||||
*/
|
||||
private fun createPreferenceIcon(context: Context) = LinearLayout(context).apply {
|
||||
tag = TSBARRERY_ICON_TAG
|
||||
gravity = Gravity.END or Gravity.BOTTOM
|
||||
layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
addView(ImageView(context).apply {
|
||||
layoutParams = ViewGroup.MarginLayoutParams(20.dp(context), 20.dp(context)).apply {
|
||||
topMargin = context.absoluteStatusBarHeight + 15.dp(context)
|
||||
rightMargin = 20.dp(context)
|
||||
}
|
||||
setColorFilter(ResourcesCompat.getColor(resources, R.color.colorTextGray, null))
|
||||
setImageResource(R.drawable.ic_icon)
|
||||
if (Build.VERSION.SDK_INT >= 26) tooltipText = "TSBattery 设置"
|
||||
setOnClickListener { context.startModuleSettings() }
|
||||
})
|
||||
}
|
||||
|
||||
override fun onHook() {
|
||||
onAppLifecycle {
|
||||
onCreate {
|
||||
ConfigData.init(context = this)
|
||||
registerModuleAppActivities(when {
|
||||
EmptyActivityClass != null -> EmptyActivityClass
|
||||
WelabMainUIClass != null -> WelabMainUIClass
|
||||
else -> error("Inject WeChat Activity Proxy failed, unsupport version $appVersionName($appVersionCode)")
|
||||
})
|
||||
if (ConfigData.isDisableAllHook) return@onCreate
|
||||
hookSystemWakeLock()
|
||||
YLog.info("All processes are completed for \"${processName.takeIf { it != packageName } ?: packageName}\"")
|
||||
}
|
||||
}
|
||||
/** 仅注入主进程 */
|
||||
withProcess(mainProcessName) {
|
||||
/** Hook 跳转事件 */
|
||||
LauncherUIClass?.method {
|
||||
name = "onResume"
|
||||
emptyParam()
|
||||
}?.hook()?.after { instance<Activity>().jumpToModuleSettings(isFinish = false) }
|
||||
/** 向设置界面右上角添加按钮 */
|
||||
SettingsUIClass?.method {
|
||||
name = "onResume"
|
||||
emptyParam()
|
||||
}?.hook()?.after {
|
||||
SettingsUIClass?.method {
|
||||
name = "get_fragment"
|
||||
emptyParam()
|
||||
superClass(isOnlySuperClass = true)
|
||||
}?.get(instance)?.call()?.current()?.method {
|
||||
name = "getView"
|
||||
emptyParam()
|
||||
returnType = ViewClass
|
||||
superClass(isOnlySuperClass = true)
|
||||
}?.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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/9/29.
|
||||
*/
|
||||
package com.fankes.tsbattery.hook.factory
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.fankes.tsbattery.const.JumpEvent
|
||||
import com.fankes.tsbattery.const.PackageName
|
||||
import com.fankes.tsbattery.hook.entity.QQTIMHooker.toClass
|
||||
import com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
|
||||
import com.highcapable.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.type.android.PowerManager_WakeLockClass
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/** QQ、TIM 存在的类 */
|
||||
private const val MobileQQClass = "mqq.app.MobileQQ"
|
||||
|
||||
/** QQ、TIM 存在的类 */
|
||||
private val ThemeUtilClass = VariousClass("${PackageName.QQ}.theme.ThemeUtil", "${PackageName.QQ}.vas.theme.api.ThemeUtil")
|
||||
|
||||
/**
|
||||
* QQ、TIM 主题是否为夜间模式
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun Context.isQQNightMode() = runCatching {
|
||||
ThemeUtilClass.get(classLoader).method {
|
||||
name = "getUserCurrentThemeId"
|
||||
paramCount = 1
|
||||
}.get().string(MobileQQClass.toClass(classLoader)
|
||||
.field { name = "sMobileQQ" }.ignored().get().current(ignored = true)?.field { name = "mAppRuntime" }?.any()
|
||||
).let { it.endsWith("1103") || it.endsWith("2920") }
|
||||
}.getOrNull() ?: false
|
||||
|
||||
/** 启动模块设置 [Activity] */
|
||||
fun Context.startModuleSettings() {
|
||||
/** 为 QQ、TIM 适配夜间模式 */
|
||||
if (packageName == PackageName.QQ || packageName == PackageName.TIM)
|
||||
AppCompatDelegate.setDefaultNightMode(if (isQQNightMode()) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO)
|
||||
startActivity(Intent(this, ConfigActivity::class.java))
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转模块设置 [Activity]
|
||||
* @param isFinish 执行完成是否自动关闭当前活动
|
||||
*/
|
||||
fun Activity.jumpToModuleSettings(isFinish: Boolean = true) {
|
||||
if (intent.hasExtra(JumpEvent.OPEN_MODULE_SETTING)) {
|
||||
/** 宿主版本不匹配的时候自动结束宿主进程 */
|
||||
if (intent.getLongExtra(JumpEvent.OPEN_MODULE_SETTING, 0) != YukiHookAPI.Status.compiledTimestamp)
|
||||
exitProcess(status = 0)
|
||||
intent.removeExtra(JumpEvent.OPEN_MODULE_SETTING)
|
||||
startModuleSettings()
|
||||
if (isFinish) finish()
|
||||
}
|
||||
}
|
||||
|
||||
/** Hook 系统电源锁 */
|
||||
fun PackageParam.hookSystemWakeLock() {
|
||||
PowerManager_WakeLockClass.method {
|
||||
name = "acquireLocked"
|
||||
emptyParam()
|
||||
}.hook().intercept()
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 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) } }
|
||||
}
|
||||
}
|
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2021/9/4.
|
||||
*/
|
||||
@file:Suppress("SetTextI18n", "LocalVariableName", "SameParameterValue")
|
||||
|
||||
package com.fankes.tsbattery.ui.activity
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.core.view.isVisible
|
||||
import com.fankes.projectpromote.ProjectPromote
|
||||
import com.fankes.tsbattery.R
|
||||
import com.fankes.tsbattery.const.JumpEvent
|
||||
import com.fankes.tsbattery.const.ModuleVersion
|
||||
import com.fankes.tsbattery.const.PackageName
|
||||
import com.fankes.tsbattery.databinding.ActivityMainBinding
|
||||
import com.fankes.tsbattery.hook.entity.QQTIMHooker
|
||||
import com.fankes.tsbattery.hook.entity.WeChatHooker
|
||||
import com.fankes.tsbattery.ui.activity.base.BaseActivity
|
||||
import com.fankes.tsbattery.utils.factory.appVersionBrandOf
|
||||
import com.fankes.tsbattery.utils.factory.hideOrShowLauncherIcon
|
||||
import com.fankes.tsbattery.utils.factory.isInstall
|
||||
import com.fankes.tsbattery.utils.factory.isLauncherIconShowing
|
||||
import com.fankes.tsbattery.utils.factory.openBrowser
|
||||
import com.fankes.tsbattery.utils.factory.showDialog
|
||||
import com.fankes.tsbattery.utils.factory.snake
|
||||
import com.fankes.tsbattery.utils.tool.GithubReleaseTool
|
||||
import com.fankes.tsbattery.wrapper.BuildConfigWrapper
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
|
||||
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
companion object {
|
||||
private const val QQ_SUPPORT_VERSION = "理论支持 8.0.0+ 及以上版本。"
|
||||
private const val TIM_SUPPORT_VERSION = "2+、3+ (并未完全测试每个版本)。"
|
||||
private const val WECHAT_SUPPORT_VERSION = "全版本仅支持基础省电,更多功能依然画饼。"
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
/** 检查更新 */
|
||||
GithubReleaseTool.checkingForUpdate(context = this, ModuleVersion.NAME) { version, function ->
|
||||
binding.mainTextReleaseVersion.apply {
|
||||
text = "点击更新 $version"
|
||||
isVisible = true
|
||||
setOnClickListener { function() }
|
||||
}
|
||||
}
|
||||
/** 判断 Hook 状态 */
|
||||
if (YukiHookAPI.Status.isModuleActive) {
|
||||
binding.mainLinStatus.setBackgroundResource(R.drawable.bg_green_round)
|
||||
binding.mainImgStatus.setImageResource(R.drawable.ic_success)
|
||||
binding.mainTextStatus.text = "模块已激活"
|
||||
binding.mainTextApiWay.isVisible = true
|
||||
refreshActivateExecutor()
|
||||
/** 推广、恰饭 */
|
||||
ProjectPromote.show(activity = this, ModuleVersion.toString())
|
||||
} else
|
||||
showDialog {
|
||||
title = "模块没有激活"
|
||||
msg = "检测到模块没有激活,若你正在使用免 Root 框架例如 LSPatch、太极或无极,你可以忽略此提示。"
|
||||
confirmButton(text = "我知道了")
|
||||
noCancelable()
|
||||
}
|
||||
/** 设置安装状态 */
|
||||
binding.mainTextQqVer.text = PackageName.QQ.takeIf { isInstall(it) }?.let { appVersionBrandOf(it) } ?: "未安装"
|
||||
binding.mainTextTimVer.text = PackageName.TIM.takeIf { isInstall(it) }?.let { appVersionBrandOf(it) } ?: "未安装"
|
||||
binding.mainTextWechatVer.text = PackageName.WECHAT.takeIf { isInstall(it) }?.let { appVersionBrandOf(it) } ?: "未安装"
|
||||
/** 设置文本 */
|
||||
binding.mainTextVersion.text = "模块版本:${ModuleVersion.NAME}"
|
||||
/** 设置 CI 自动构建标识 */
|
||||
if (ModuleVersion.isCiMode)
|
||||
binding.mainTextReleaseVersion.apply {
|
||||
text = "CI ${ModuleVersion.GITHUB_COMMIT_ID}"
|
||||
isVisible = true
|
||||
setOnClickListener {
|
||||
showDialog {
|
||||
title = "CI 自动构建说明"
|
||||
msg = """
|
||||
你正在使用的是 CI 自动构建版本,Commit ID 为 ${ModuleVersion.GITHUB_COMMIT_ID}。
|
||||
|
||||
它是由代码提交后自动触发并构建、自动编译发布的,并未经任何稳定性测试,使用风险自负。
|
||||
|
||||
CI 构建的版本不支持太极 (也请不要提交 CI 版本的适配,因为它们是不稳定的),你可以使用 LSPosed / LSPatch。
|
||||
""".trimIndent()
|
||||
confirmButton(text = "我知道了")
|
||||
noCancelable()
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.mainQqItem.setOnClickListener {
|
||||
showDialog {
|
||||
title = "兼容的 QQ 版本"
|
||||
msg = QQ_SUPPORT_VERSION
|
||||
confirmButton(text = "我知道了")
|
||||
}
|
||||
/** 振动提醒 */
|
||||
it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
}
|
||||
binding.mainTimItem.setOnClickListener {
|
||||
showDialog {
|
||||
title = "兼容的 TIM 版本"
|
||||
msg = TIM_SUPPORT_VERSION
|
||||
confirmButton(text = "我知道了")
|
||||
}
|
||||
/** 振动提醒 */
|
||||
it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
}
|
||||
binding.mainWechatItem.setOnClickListener {
|
||||
showDialog {
|
||||
title = "兼容的微信版本"
|
||||
msg = WECHAT_SUPPORT_VERSION
|
||||
confirmButton(text = "我知道了")
|
||||
}
|
||||
/** 振动提醒 */
|
||||
it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
}
|
||||
/** 快捷操作 QQ */
|
||||
binding.quickQqButton.setOnClickListener { startModuleSettings(PackageName.QQ) }
|
||||
/** 快捷操作 TIM */
|
||||
binding.quickTimButton.setOnClickListener { startModuleSettings(PackageName.TIM) }
|
||||
/** 快捷操作微信 */
|
||||
binding.quickWechatButton.setOnClickListener { startModuleSettings(PackageName.WECHAT) }
|
||||
/** 项目地址按钮点击事件 */
|
||||
binding.titleGithubIcon.setOnClickListener { openBrowser(url = "https://github.com/fankes/TSBattery") }
|
||||
/** 恰饭! */
|
||||
binding.linkWithFollowMe.setOnClickListener {
|
||||
openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market")
|
||||
}
|
||||
/** 设置桌面图标显示隐藏 */
|
||||
binding.hideIconInLauncherSwitch.isChecked = isLauncherIconShowing.not()
|
||||
binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
|
||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||
hideOrShowLauncherIcon(b)
|
||||
}
|
||||
/** 判断当前启动模式 */
|
||||
if (packageName != BuildConfigWrapper.APPLICATION_ID) {
|
||||
binding.quickActionItem.isVisible = false
|
||||
binding.displaySettingItem.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动模块设置界面
|
||||
* @param packageName 包名
|
||||
*/
|
||||
private fun startModuleSettings(packageName: String) {
|
||||
if (isInstall(packageName)) runCatching {
|
||||
startActivity(Intent().apply {
|
||||
component = ComponentName(
|
||||
packageName,
|
||||
if (packageName != PackageName.WECHAT) QQTIMHooker.JumpActivityClassName else WeChatHooker.LauncherUIClassName
|
||||
)
|
||||
putExtra(JumpEvent.OPEN_MODULE_SETTING, YukiHookAPI.Status.compiledTimestamp)
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
})
|
||||
}.onFailure { snake(msg = "启动模块设置失败\n$it") } else snake(msg = "你没有安装此应用")
|
||||
}
|
||||
|
||||
/** 刷新模块激活使用的方式 */
|
||||
private fun refreshActivateExecutor() {
|
||||
if (YukiHookAPI.Status.Executor.apiLevel > 0)
|
||||
binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name} API ${YukiHookAPI.Status.Executor.apiLevel}"
|
||||
else binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name}"
|
||||
}
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/1/30.
|
||||
*/
|
||||
package com.fankes.tsbattery.ui.activity.base
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.fankes.tsbattery.R
|
||||
import com.fankes.tsbattery.utils.factory.isNotSystemInDarkMode
|
||||
import com.highcapable.yukihookapi.hook.factory.current
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base.ModuleAppCompatActivity
|
||||
|
||||
abstract class BaseActivity<VB : ViewBinding> : ModuleAppCompatActivity() {
|
||||
|
||||
override val moduleTheme get() = R.style.Theme_TSBattery
|
||||
|
||||
/** 获取绑定布局对象 */
|
||||
lateinit var binding: VB
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = current().generic()?.argument()?.method {
|
||||
name = "inflate"
|
||||
param(LayoutInflaterClass)
|
||||
}?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed")
|
||||
setContentView(binding.root)
|
||||
/** 隐藏系统的标题栏 */
|
||||
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()
|
||||
}
|
||||
|
||||
/** 回调 [onCreate] 方法 */
|
||||
abstract fun onCreate()
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/9/28.
|
||||
*/
|
||||
@file:Suppress("SetTextI18n", "DEPRECATION")
|
||||
|
||||
package com.fankes.tsbattery.ui.activity.parasitic
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import com.fankes.projectpromote.ProjectPromote
|
||||
import com.fankes.tsbattery.const.ModuleVersion
|
||||
import com.fankes.tsbattery.const.PackageName
|
||||
import com.fankes.tsbattery.data.ConfigData
|
||||
import com.fankes.tsbattery.data.ConfigData.bind
|
||||
import com.fankes.tsbattery.databinding.ActivityConfigBinding
|
||||
import com.fankes.tsbattery.hook.HookEntry
|
||||
import com.fankes.tsbattery.hook.entity.QQTIMHooker
|
||||
import com.fankes.tsbattery.ui.activity.MainActivity
|
||||
import com.fankes.tsbattery.ui.activity.base.BaseActivity
|
||||
import com.fankes.tsbattery.utils.factory.appIconOf
|
||||
import com.fankes.tsbattery.utils.factory.appNameOf
|
||||
import com.fankes.tsbattery.utils.factory.appVersionCode
|
||||
import com.fankes.tsbattery.utils.factory.appVersionName
|
||||
import com.fankes.tsbattery.utils.factory.showDialog
|
||||
import com.fankes.tsbattery.utils.factory.snake
|
||||
import com.fankes.tsbattery.utils.tool.GithubReleaseTool
|
||||
import com.fankes.tsbattery.wrapper.BuildConfigWrapper
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class ConfigActivity : BaseActivity<ActivityConfigBinding>() {
|
||||
|
||||
override fun onCreate() {
|
||||
/** 检查更新 */
|
||||
GithubReleaseTool.checkingForUpdate(context = this, ModuleVersion.NAME) { version, function ->
|
||||
binding.updateVersionText.apply {
|
||||
text = "点击更新 $version"
|
||||
isVisible = true
|
||||
setOnClickListener { function() }
|
||||
}
|
||||
}
|
||||
binding.titleBackIcon.setOnClickListener { finish() }
|
||||
binding.titleModuleIcon.setOnClickListener {
|
||||
showDialog {
|
||||
title = "打开模块主界面"
|
||||
msg = "点击确定后将打开模块主界面,如果未安装模块本体将尝试打开寄生界面。"
|
||||
confirmButton {
|
||||
runCatching {
|
||||
startActivity(Intent().apply {
|
||||
component = ComponentName(BuildConfigWrapper.APPLICATION_ID, MainActivity::class.java.name)
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
})
|
||||
}.onFailure {
|
||||
runCatching {
|
||||
startActivity(Intent(this@ConfigActivity, MainActivity::class.java))
|
||||
}.onFailure { snake(msg = "打开失败,请确认你已安装模块 APP 或在模块更新后重启过$appName\n$it") }
|
||||
}
|
||||
}
|
||||
cancelButton()
|
||||
}
|
||||
}
|
||||
binding.titleNameText.text = "TSBattery 设置 (${appName.trim()})"
|
||||
binding.appIcon.setImageDrawable(appIconOf())
|
||||
binding.appName.text = appName.trim()
|
||||
binding.appVersion.text = "$appVersionName($appVersionCode)"
|
||||
binding.moduleVersion.text = ModuleVersion.toString()
|
||||
binding.activeModeIcon.isVisible = HookEntry.isHookClientSupport
|
||||
binding.inactiveModeIcon.isGone = HookEntry.isHookClientSupport
|
||||
binding.unsupportItem.isGone = HookEntry.isHookClientSupport
|
||||
binding.executorInfoText.text = "${YukiHookAPI.Status.Executor.name} API ${YukiHookAPI.Status.Executor.apiLevel}"
|
||||
binding.needRestartTipText.replaceToAppName()
|
||||
binding.needRestartTipText.setOnClickListener {
|
||||
showDialog {
|
||||
title = "需要重新启动"
|
||||
msg = "你必须重新启动${appName}才能使当前更改生效,现在重新启动吗?"
|
||||
confirmButton {
|
||||
cancel()
|
||||
finish()
|
||||
exitProcess(status = 0)
|
||||
}
|
||||
cancelButton(text = "稍后再说") {
|
||||
cancel()
|
||||
it.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 刷新当前模式文本 */
|
||||
fun refreshCurrentModeText() {
|
||||
binding.currentModeText.text = when {
|
||||
ConfigData.isDisableAllHook -> "模块已停用"
|
||||
packageName == PackageName.WECHAT -> "基础省电模式"
|
||||
ConfigData.isEnableQQTimProtectMode -> "保守模式"
|
||||
else -> "完全模式"
|
||||
}
|
||||
}
|
||||
refreshCurrentModeText()
|
||||
/** 刷新配置条目显示隐藏状态 */
|
||||
fun refreshConfigItems() {
|
||||
binding.itemQqTimConfig.isVisible = packageName != PackageName.WECHAT && ConfigData.isDisableAllHook.not()
|
||||
}
|
||||
refreshConfigItems()
|
||||
binding.infoTipText.replaceToAppName()
|
||||
binding.qqTimProtectTipText.replaceToAppName()
|
||||
binding.disableAllHookSwitch.bind(ConfigData.DISABLE_ALL_HOOK) { refreshConfigItems(); refreshCurrentModeText(); showNeedRestartTip() }
|
||||
binding.qqTimProtectModeSwitch.bind(ConfigData.ENABLE_QQ_TIM_PROTECT_MODE) { refreshCurrentModeText(); showNeedRestartTip() }
|
||||
binding.qqTimCoreServiceSwitch.bind(ConfigData.ENABLE_KILL_QQ_TIM_CORESERVICE) { showNeedRestartTip() }
|
||||
binding.qqTimCoreServiceChildSwitch.bind(ConfigData.ENABLE_KILLE_QQ_TIM_CORESERVICE_CHILD) { showNeedRestartTip() }
|
||||
/** 推广、恰饭 */
|
||||
ProjectPromote.show(activity = this, ModuleVersion.toString())
|
||||
}
|
||||
|
||||
/** 显示需要重新启动提示 */
|
||||
private fun showNeedRestartTip() {
|
||||
binding.needRestartTipText.isVisible = true
|
||||
}
|
||||
|
||||
/** 替换占位符到当前 APP 名称 */
|
||||
private fun TextView.replaceToAppName() {
|
||||
text = text.toString().replace("{APP_NAME}", appName)
|
||||
}
|
||||
|
||||
/** 重新设置 DPI 防止 QQ、TIM 修改它 */
|
||||
override fun getResources(): Resources? = super.getResources().apply {
|
||||
if (packageName == PackageName.QQ || packageName == PackageName.TIM)
|
||||
QQTIMHooker.baseConfiguration?.also {
|
||||
updateConfiguration(configuration.apply {
|
||||
fontScale = it.fontScale
|
||||
densityDpi = it.densityDpi
|
||||
}, displayMetrics)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 APP 名称
|
||||
* @return [String]
|
||||
*/
|
||||
private val appName by lazy { appNameOf().let { if (packageName == PackageName.WECHAT) it else " $it " } }
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/1/8.
|
||||
*/
|
||||
@file:Suppress("SameParameterValue")
|
||||
|
||||
package com.fankes.tsbattery.ui.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import com.fankes.tsbattery.utils.factory.dp
|
||||
import com.fankes.tsbattery.utils.factory.isSystemInDarkMode
|
||||
import top.defaults.drawabletoolbox.DrawableBuilder
|
||||
|
||||
class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(context, attrs) {
|
||||
|
||||
private fun toColors(selected: Int, pressed: Int, normal: Int): ColorStateList {
|
||||
val colors = intArrayOf(selected, pressed, normal)
|
||||
val states = arrayOfNulls<IntArray>(3)
|
||||
states[0] = intArrayOf(android.R.attr.state_checked)
|
||||
states[1] = intArrayOf(android.R.attr.state_pressed)
|
||||
states[2] = intArrayOf()
|
||||
return ColorStateList(states, colors)
|
||||
}
|
||||
|
||||
private val thumbColor get() = if (context.isSystemInDarkMode) 0xFF7C7C7C else 0xFFCCCCCC
|
||||
|
||||
init {
|
||||
trackDrawable = DrawableBuilder()
|
||||
.rectangle()
|
||||
.rounded()
|
||||
.solidColor(0xFF656565.toInt())
|
||||
.height(20.dp(context))
|
||||
.cornerRadius(15.dp(context))
|
||||
.build()
|
||||
thumbDrawable = DrawableBuilder()
|
||||
.rectangle()
|
||||
.rounded()
|
||||
.solidColor(Color.WHITE)
|
||||
.size(20.dp(context), 20.dp(context))
|
||||
.cornerRadius(20.dp(context))
|
||||
.strokeWidth(8.dp(context))
|
||||
.strokeColor(Color.TRANSPARENT)
|
||||
.build()
|
||||
trackTintList = toColors(
|
||||
0xFF656565.toInt(),
|
||||
thumbColor.toInt(),
|
||||
thumbColor.toInt()
|
||||
)
|
||||
isSingleLine = true
|
||||
ellipsize = TextUtils.TruncateAt.END
|
||||
}
|
||||
}
|
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of TSBattery.
|
||||
*
|
||||
* TSBattery is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* TSBattery is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by zpp0196 on 2019/2/9.
|
||||
*/
|
||||
package com.fankes.tsbattery.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.fankes.tsbattery.BuildConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class FileUtils {
|
||||
|
||||
private static final String FILE_PREF_NAME = BuildConfig.APPLICATION_ID + "_preferences.xml";
|
||||
|
||||
public static boolean copyFile(File srcFile, File targetFile) {
|
||||
FileInputStream ins = null;
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
if (targetFile.exists()) {
|
||||
targetFile.delete();
|
||||
}
|
||||
File targetParent = targetFile.getParentFile();
|
||||
if (!targetParent.exists()) {
|
||||
targetParent.mkdirs();
|
||||
}
|
||||
targetFile.createNewFile();
|
||||
ins = new FileInputStream(srcFile);
|
||||
out = new FileOutputStream(targetFile);
|
||||
byte[] b = new byte[1024];
|
||||
int n;
|
||||
while ((n = ins.read(b)) != -1) {
|
||||
out.write(b, 0, n);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
if (ins != null) {
|
||||
ins.close();
|
||||
}
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static File getDataDir(Context context) {
|
||||
return new File(context.getApplicationInfo().dataDir);
|
||||
}
|
||||
|
||||
public static File getPrefDir(Context context) {
|
||||
return new File(getDataDir(context), "shared_prefs");
|
||||
}
|
||||
|
||||
public static File getDefaultPrefFile(Context context) {
|
||||
return new File(getPrefDir(context), FILE_PREF_NAME);
|
||||
}
|
||||
|
||||
public static File getBackupPrefsFile() {
|
||||
return new File(getBackupDir(), FILE_PREF_NAME);
|
||||
}
|
||||
|
||||
private static File getBackupDir() {
|
||||
return new File(Environment.getExternalStorageDirectory(), "QQPurify");
|
||||
}
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021. Fankes Studio(qzmmcn@163.com)
|
||||
*
|
||||
* This file is part of TSBattery.
|
||||
*
|
||||
* TSBattery is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* TSBattery is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file is Created by zpp0196 on 2018/4/11.
|
||||
*/
|
||||
package com.fankes.tsbattery.utils
|
||||
|
||||
import de.robv.android.xposed.XSharedPreferences
|
||||
|
||||
object XPrefUtils {
|
||||
|
||||
fun getBoolean(key: String) = pref.getBoolean(key, false)
|
||||
|
||||
fun getString(key: String) = pref.getString(key, "unknown")
|
||||
|
||||
private val pref: XSharedPreferences
|
||||
get() {
|
||||
val preferences = XSharedPreferences("com.fankes.tsbattery")
|
||||
preferences.makeWorldReadable()
|
||||
preferences.reload()
|
||||
return preferences
|
||||
}
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/1/7.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.fankes.tsbattery.utils.factory
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.fankes.tsbattery.R
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
import com.highcapable.yukihookapi.hook.factory.applyModuleTheme
|
||||
|
||||
/**
|
||||
* 构造对话框
|
||||
* @param initiate 对话框方法体
|
||||
*/
|
||||
inline fun Context.showDialog(initiate: DialogBuilder.() -> Unit) = DialogBuilder(context = this).apply(initiate).show()
|
||||
|
||||
/**
|
||||
* 对话框构造器
|
||||
* @param context 实例
|
||||
*/
|
||||
class DialogBuilder(val context: Context) {
|
||||
|
||||
/** 实例对象 */
|
||||
private var instance: AlertDialog.Builder? = null
|
||||
|
||||
/** 对话框实例 */
|
||||
private var dialogInstance: Dialog? = null
|
||||
|
||||
/** 自定义布局 */
|
||||
private var customLayoutView: View? = null
|
||||
|
||||
init {
|
||||
instance = MaterialAlertDialogBuilder(context.applyModuleTheme(R.style.Theme_TSBattery))
|
||||
}
|
||||
|
||||
/** 设置对话框不可关闭 */
|
||||
fun noCancelable() {
|
||||
instance?.setCancelable(false)
|
||||
}
|
||||
|
||||
/** 设置对话框标题 */
|
||||
var title
|
||||
get() = ""
|
||||
set(value) {
|
||||
instance?.setTitle(value)
|
||||
}
|
||||
|
||||
/** 设置对话框消息内容 */
|
||||
var msg
|
||||
get() = ""
|
||||
set(value) {
|
||||
instance?.setMessage(value)
|
||||
}
|
||||
|
||||
/** 设置进度条对话框消息内容 */
|
||||
var progressContent
|
||||
get() = ""
|
||||
set(value) {
|
||||
if (customLayoutView == null)
|
||||
customLayoutView = LinearLayout(context).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER or Gravity.START
|
||||
addView(CircularProgressIndicator(context).apply {
|
||||
isIndeterminate = true
|
||||
trackCornerRadius = 10.dp(context)
|
||||
})
|
||||
addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(20.dp(context), 5) })
|
||||
addView(TextView(context).apply {
|
||||
tag = "progressContent"
|
||||
text = value
|
||||
})
|
||||
setPadding(20.dp(context), 20.dp(context), 20.dp(context), 20.dp(context))
|
||||
}
|
||||
else customLayoutView?.findViewWithTag<TextView>("progressContent")?.text = value
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对话框确定按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun confirmButton(text: String = "确定", callback: () -> Unit = {}) {
|
||||
instance?.setPositiveButton(text) { _, _ -> callback() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对话框取消按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun cancelButton(text: String = "取消", callback: () -> Unit = {}) {
|
||||
instance?.setNegativeButton(text) { _, _ -> callback() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对话框第三个按钮
|
||||
* @param text 按钮文本内容
|
||||
* @param callback 点击事件
|
||||
*/
|
||||
fun neutralButton(text: String = "更多", callback: () -> Unit = {}) {
|
||||
instance?.setNeutralButton(text) { _, _ -> callback() }
|
||||
}
|
||||
|
||||
/** 取消对话框 */
|
||||
fun cancel() = dialogInstance?.cancel()
|
||||
|
||||
/** 显示对话框 */
|
||||
fun show() = runInSafe {
|
||||
instance?.create()?.apply {
|
||||
customLayoutView?.let { setView(it) }
|
||||
dialogInstance = this
|
||||
}?.show()
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/3/13.
|
||||
*/
|
||||
@file:Suppress("unused", "UnusedReceiverParameter")
|
||||
|
||||
package com.fankes.tsbattery.utils.factory
|
||||
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
|
||||
/**
|
||||
* 忽略异常返回值
|
||||
* @param result 回调 - 如果异常为空
|
||||
* @return [T] 发生异常时返回设定值否则返回正常值
|
||||
*/
|
||||
inline fun <T> safeOfNull(result: () -> T): T? = safeOf(default = null, result)
|
||||
|
||||
/**
|
||||
* 忽略异常返回值
|
||||
* @param result 回调 - 如果异常为 false
|
||||
* @return [Boolean] 发生异常时返回设定值否则返回正常值
|
||||
*/
|
||||
inline fun safeOfFalse(result: () -> Boolean) = safeOf(default = false, result)
|
||||
|
||||
/**
|
||||
* 忽略异常返回值
|
||||
* @param result 回调 - 如果异常为 true
|
||||
* @return [Boolean] 发生异常时返回设定值否则返回正常值
|
||||
*/
|
||||
inline fun safeOfTrue(result: () -> Boolean) = safeOf(default = true, result)
|
||||
|
||||
/**
|
||||
* 忽略异常返回值
|
||||
* @param result 回调 - 如果异常为 false
|
||||
* @return [String] 发生异常时返回设定值否则返回正常值
|
||||
*/
|
||||
inline fun safeOfNothing(result: () -> String) = safeOf(default = "", result)
|
||||
|
||||
/**
|
||||
* 忽略异常返回值
|
||||
* @param result 回调 - 如果异常为 false
|
||||
* @return [Int] 发生异常时返回设定值否则返回正常值
|
||||
*/
|
||||
inline fun safeOfNan(result: () -> Int) = safeOf(default = 0, result)
|
||||
|
||||
/**
|
||||
* 忽略异常返回值
|
||||
* @param default 异常返回值
|
||||
* @param result 正常回调值
|
||||
* @return [T] 发生异常时返回设定值否则返回正常值
|
||||
*/
|
||||
inline fun <T> safeOf(default: T, result: () -> T) = try {
|
||||
result()
|
||||
} catch (_: Throwable) {
|
||||
default
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略异常运行
|
||||
* @param msg 出错输出的消息 - 默认为空
|
||||
* @param block 正常回调
|
||||
*/
|
||||
inline fun <T> T.runInSafe(msg: String = "", block: () -> Unit) {
|
||||
runCatching(block).onFailure { if (msg.isNotBlank()) YLog.error(msg, it) }
|
||||
}
|
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/1/7.
|
||||
*/
|
||||
@file:Suppress("unused", "DiscouragedApi", "InternalInsetResource")
|
||||
|
||||
package com.fankes.tsbattery.utils.factory
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.PackageInfoFlags
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import com.fankes.tsbattery.wrapper.BuildConfigWrapper
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.Companion.appContext
|
||||
|
||||
/**
|
||||
* 系统深色模式是否开启
|
||||
* @return [Boolean] 是否开启
|
||||
*/
|
||||
val isSystemInDarkMode get() = appContext.isSystemInDarkMode
|
||||
|
||||
/**
|
||||
* 系统深色模式是否没开启
|
||||
* @return [Boolean] 是否开启
|
||||
*/
|
||||
inline val isNotSystemInDarkMode get() = !isSystemInDarkMode
|
||||
|
||||
/**
|
||||
* 系统深色模式是否开启
|
||||
* @return [Boolean] 是否开启
|
||||
*/
|
||||
val Context.isSystemInDarkMode get() = (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
||||
|
||||
/**
|
||||
* 系统深色模式是否没开启
|
||||
* @return [Boolean] 是否开启
|
||||
*/
|
||||
inline val Context.isNotSystemInDarkMode get() = !isSystemInDarkMode
|
||||
|
||||
/**
|
||||
* 得到 APP 安装包信息 (兼容)
|
||||
* @param packageName APP 包名
|
||||
* @param flag [PackageInfoFlags]
|
||||
* @return [PackageInfo] or null
|
||||
*/
|
||||
private fun Context.getPackageInfoCompat(packageName: String, flag: Number = 0) = runCatching {
|
||||
@Suppress("DEPRECATION", "KotlinRedundantDiagnosticSuppress")
|
||||
if (Build.VERSION.SDK_INT >= 33)
|
||||
packageManager?.getPackageInfo(packageName, PackageInfoFlags.of(flag.toLong()))
|
||||
else packageManager?.getPackageInfo(packageName, flag.toInt())
|
||||
}.getOrNull()
|
||||
|
||||
/**
|
||||
* 得到 APP 版本号 (兼容 [PackageInfo.getLongVersionCode])
|
||||
* @return [Int]
|
||||
*/
|
||||
private val PackageInfo.versionCodeCompat get() = PackageInfoCompat.getLongVersionCode(this)
|
||||
|
||||
/**
|
||||
* 判断 APP 是否安装
|
||||
* @param packageName APP 包名
|
||||
* @return [Boolean]
|
||||
*/
|
||||
fun Context.isInstall(packageName: String) = getPackageInfoCompat(packageName)?.let { true } ?: false
|
||||
|
||||
/**
|
||||
* 得到 APP 版本信息
|
||||
* @return [String]
|
||||
*/
|
||||
val Context.appVersionName get() = getPackageInfoCompat(packageName)?.versionName ?: ""
|
||||
|
||||
/**
|
||||
* 得到 APP 版本号
|
||||
* @return [Int]
|
||||
*/
|
||||
val Context.appVersionCode get() = getPackageInfoCompat(packageName)?.versionCodeCompat
|
||||
|
||||
/**
|
||||
* 得到 APP 版本信息与版本号
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [String]
|
||||
*/
|
||||
fun Context.appVersionBrandOf(packageName: String = getPackageName()) =
|
||||
getPackageInfoCompat(packageName)?.let { "${it.versionName}(${it.versionCodeCompat})" } ?: ""
|
||||
|
||||
/**
|
||||
* 得到 APP 名称
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [String]
|
||||
*/
|
||||
fun Context.appNameOf(packageName: String = getPackageName()) =
|
||||
getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: ""
|
||||
|
||||
/**
|
||||
* 得到 APP 图标
|
||||
* @param packageName APP 包名 - 默认为当前 APP
|
||||
* @return [Drawable] or null
|
||||
*/
|
||||
fun Context.appIconOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.applicationInfo?.loadIcon(packageManager)
|
||||
|
||||
/**
|
||||
* 网络连接是否正常
|
||||
* @return [Boolean] 网络是否连接
|
||||
*/
|
||||
val Context.isNetWorkSuccess
|
||||
get() = safeOfFalse {
|
||||
@Suppress("DEPRECATION")
|
||||
getSystemService<ConnectivityManager>()?.activeNetworkInfo != null
|
||||
}
|
||||
|
||||
/**
|
||||
* dp 转换为 pxInt
|
||||
* @param context 使用的实例
|
||||
* @return [Int]
|
||||
*/
|
||||
fun Number.dp(context: Context) = dpFloat(context).toInt()
|
||||
|
||||
/**
|
||||
* dp 转换为 pxFloat
|
||||
* @param context 使用的实例
|
||||
* @return [Float]
|
||||
*/
|
||||
fun Number.dpFloat(context: Context) = toFloat() * context.resources.displayMetrics.density
|
||||
|
||||
/**
|
||||
* 获取绝对状态栏高度
|
||||
* @return [Int]
|
||||
*/
|
||||
val Context.absoluteStatusBarHeight
|
||||
get() = safeOfNan {
|
||||
resources.getDimensionPixelSize(resources.getIdentifier("status_bar_height", "dimen", "android"))
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出 [Toast]
|
||||
* @param msg 提示内容
|
||||
*/
|
||||
fun toast(msg: String) = appContext.toast(msg)
|
||||
|
||||
/**
|
||||
* 弹出 [Toast]
|
||||
* @param msg 提示内容
|
||||
*/
|
||||
fun Context.toast(msg: String) = Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
|
||||
|
||||
/**
|
||||
* 弹出 [Snackbar]
|
||||
* @param msg 提示内容
|
||||
* @param actionText 按钮文本 - 不写默认取消按钮
|
||||
* @param callback 按钮事件回调
|
||||
*/
|
||||
fun Context.snake(msg: String, actionText: String = "", callback: () -> Unit = {}) =
|
||||
Snackbar.make((this as Activity).findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG).apply {
|
||||
if (actionText.isBlank()) return@apply
|
||||
setActionTextColor(if (isSystemInDarkMode) Color.BLACK else Color.WHITE)
|
||||
setAction(actionText) { callback() }
|
||||
}.show()
|
||||
|
||||
/**
|
||||
* 跳转 APP 自身设置界面
|
||||
* @param packageName 包名
|
||||
*/
|
||||
fun Context.openSelfSetting(packageName: String = appContext.packageName) = runCatching {
|
||||
if (isInstall(packageName))
|
||||
startActivity(Intent().apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
data = Uri.fromParts("package", packageName, null)
|
||||
})
|
||||
else snake(msg = "你没有安装此应用")
|
||||
}.onFailure { toast(msg = "启动 $packageName 应用信息失败") }
|
||||
|
||||
/**
|
||||
* 启动系统浏览器
|
||||
* @param url 网址
|
||||
* @param packageName 指定包名 - 可不填
|
||||
*/
|
||||
fun Context.openBrowser(url: String, packageName: String = "") = runCatching {
|
||||
startActivity(Intent().apply {
|
||||
if (packageName.isNotBlank()) setPackage(packageName)
|
||||
action = Intent.ACTION_VIEW
|
||||
data = Uri.parse(url)
|
||||
/** 防止顶栈一样重叠在自己的 APP 中 */
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
})
|
||||
}.onFailure {
|
||||
if (packageName.isNotBlank()) snake(msg = "启动 $packageName 失败")
|
||||
else snake(msg = "启动系统浏览器失败")
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏或显示启动器图标
|
||||
*
|
||||
* - 你可能需要 LSPosed 的最新版本以开启高版本系统中隐藏 APP 桌面图标功能
|
||||
* @param isShow 是否显示
|
||||
*/
|
||||
fun Context.hideOrShowLauncherIcon(isShow: Boolean) {
|
||||
packageManager?.setComponentEnabledSetting(
|
||||
ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home"),
|
||||
if (isShow) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||
PackageManager.DONT_KILL_APP
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取启动器图标状态
|
||||
* @return [Boolean] 是否显示
|
||||
*/
|
||||
val Context.isLauncherIconShowing
|
||||
get() = packageManager?.getComponentEnabledSetting(
|
||||
ComponentName(packageName, "${BuildConfigWrapper.APPLICATION_ID}.Home")
|
||||
) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2022/3/20.
|
||||
*/
|
||||
@file:Suppress("NewApi")
|
||||
|
||||
package com.fankes.tsbattery.utils.tool
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import android.icu.util.Calendar
|
||||
import android.icu.util.TimeZone
|
||||
import com.fankes.tsbattery.utils.factory.isNetWorkSuccess
|
||||
import com.fankes.tsbattery.utils.factory.openBrowser
|
||||
import com.fankes.tsbattery.utils.factory.openSelfSetting
|
||||
import com.fankes.tsbattery.utils.factory.runInSafe
|
||||
import com.fankes.tsbattery.utils.factory.showDialog
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.io.Serializable
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 获取 GitHub Release 最新版本工具类
|
||||
*/
|
||||
object GithubReleaseTool {
|
||||
|
||||
/** 仓库作者 */
|
||||
private const val REPO_AUTHOR = "fankes"
|
||||
|
||||
/** 仓库名称 */
|
||||
private const val REPO_NAME = "TSBattery"
|
||||
|
||||
/**
|
||||
* 获取最新版本信息
|
||||
* @param context 实例
|
||||
* @param version 当前版本
|
||||
* @param result 成功后回调 - ([String] 最新版本,[Function] 更新对话框方法体)
|
||||
*/
|
||||
fun checkingForUpdate(context: Context, version: String, result: (String, () -> Unit) -> Unit) = checkingInternetConnect(context) {
|
||||
OkHttpClient().newBuilder().build().newCall(
|
||||
Request.Builder()
|
||||
.url("https://api.github.com/repos/$REPO_AUTHOR/$REPO_NAME/releases/latest")
|
||||
.get()
|
||||
.build()
|
||||
).enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {}
|
||||
override fun onResponse(call: Call, response: Response) = runInSafe {
|
||||
JSONObject(response.body.string()).apply {
|
||||
GithubReleaseBean(
|
||||
name = getString("name"),
|
||||
htmlUrl = getString("html_url"),
|
||||
content = getString("body"),
|
||||
date = getString("published_at").localTime()
|
||||
).apply {
|
||||
fun showUpdate() = context.showDialog {
|
||||
title = "最新版本 $name"
|
||||
msg = "发布于 $date\n\n" +
|
||||
"更新日志\n\n" + content
|
||||
confirmButton(text = "更新") { context.openBrowser(htmlUrl) }
|
||||
cancelButton()
|
||||
}
|
||||
if (name != version) (context as? Activity?)?.runOnUiThread {
|
||||
showUpdate()
|
||||
result(name) { showUpdate() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查网络连接情况
|
||||
* @param context 实例
|
||||
* @param callback 已连接回调
|
||||
*/
|
||||
private fun checkingInternetConnect(context: Context, callback: () -> Unit) = runInSafe {
|
||||
if (context.isNetWorkSuccess)
|
||||
OkHttpClient().newBuilder().build().newCall(
|
||||
Request.Builder()
|
||||
.url("https://www.baidu.com")
|
||||
.get()
|
||||
.build()
|
||||
).enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
(context as? Activity?)?.runOnUiThread {
|
||||
context.showDialog {
|
||||
title = "网络不可用"
|
||||
msg = "应用的联网权限可能已被禁用,请开启联网权限以定期检查更新。"
|
||||
confirmButton(text = "去开启") { context.openSelfSetting() }
|
||||
cancelButton()
|
||||
noCancelable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) = runInSafe {
|
||||
(context as? Activity?)?.runOnUiThread { runInSafe { callback() } }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间为本地时区
|
||||
* @return [String] 本地时区时间
|
||||
*/
|
||||
private fun String.localTime() = replace("T", " ").replace("Z", "").let {
|
||||
runCatching {
|
||||
val local = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = Calendar.getInstance().timeZone }
|
||||
val current = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("GMT") }
|
||||
local.format(current.parse(it))
|
||||
}.getOrNull() ?: it
|
||||
}
|
||||
|
||||
/**
|
||||
* GitHub Release bean
|
||||
* @param name 版本名称
|
||||
* @param htmlUrl 网页地址
|
||||
* @param content 更新日志
|
||||
* @param date 发布时间
|
||||
*/
|
||||
private data class GithubReleaseBean(
|
||||
var name: String,
|
||||
var htmlUrl: String,
|
||||
var content: String,
|
||||
var date: String
|
||||
) : Serializable
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 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
|
||||
}
|
6
app/src/main/res/drawable-night/bg_dark_round.xml
Executable file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#66A6A6A6" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
6
app/src/main/res/drawable-night/bg_permotion_round.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#666E6E6E" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
10
app/src/main/res/drawable/bg_button_round.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="#777777">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#66DAD9D9" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
6
app/src/main/res/drawable/bg_dark_round.xml
Executable file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#661B1B1B" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#6A6A6A" />
|
||||
<solid android:color="#FF7043" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
6
app/src/main/res/drawable/bg_permotion_round.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#66E4E4E4" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
6
app/src/main/res/drawable/bg_red_round.xml
Executable file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#FF786F" />
|
||||
<corners android:radius="30dp" />
|
||||
</shape>
|
9
app/src/main/res/drawable/ic_about.xml
Normal 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>
|
9
app/src/main/res/drawable/ic_back.xml
Normal 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>
|
9
app/src/main/res/drawable/ic_error.xml
Normal 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>
|
12
app/src/main/res/drawable/ic_fast_op.xml
Normal 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>
|
9
app/src/main/res/drawable/ic_github.xml
Normal 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>
|
25
app/src/main/res/drawable/ic_home.xml
Normal 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>
|
@@ -3,7 +3,7 @@
|
||||
android:height="150dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#FFffff"
|
||||
android:pathData="M717.48,354.1a33.64,33.64 0,0 0,-17.41 -21.56,37.17 37.17,0 0,0 -28.42,-3.38c-65.54,17.77 -123.8,43.16 -174.69,76.19 -50.89,33.02 -93.9,70.76 -129.02,113.15 -35.12,42.5 -69.48,94.52 -102.96,156.11 -5.22,9.57 -6.66,18.69 -4.35,27.44a33.64,33.64 0,0 0,17.36 21.5,37.17 37.17,0 0,0 28.47,3.43 40.4,40.4 0,0 0,22.94 -17.36c7.99,-11.47 18.69,-28.16 32.1,-50.07 13.41,-21.91 23.04,-37.38 28.77,-46.39 40.29,-59.14 83.05,-104.96 128.15,-137.42 45.16,-32.46 102.4,-58.06 171.78,-76.85a37.58,37.58 0,0 0,22.99 -17.31,34 34,0 0,0 4.35,-27.44l-0.05,-0.05zM928.53,194c9.47,35.58 15.36,72.76 17.82,111.51 5.38,88.52 -9.37,169.47 -44.34,242.84 -35.02,73.37 -87.24,141.31 -156.67,203.93 -67.74,61.75 -142.64,103.73 -224.77,125.95a490.8,490.8 0,0 1,-164.25 16.13c-5.99,-0.36 -24.22,-3.89 -54.68,-10.5 -30.41,-6.66 -49.87,-8.81 -58.27,-6.55 -5.89,1.64 -11.52,9.57 -16.95,23.91 -5.43,14.34 -10.24,29.7 -14.34,46.08 -4.1,16.33 -10.24,32.05 -18.43,47.1 -8.19,15.1 -17.61,24.06 -28.26,26.93a60.47,60.47 0,0 1,-29.7 1.43,52.99 52.99,0 0,1 -20.58,-8.91 245.91,245.91 0,0 1,-26.06 -24.93,30.31 30.31,0 0,1 -7.53,-9.73 34.66,34.66 0,0 1,-2.87 -7.37c-3.48,-13.11 -1.43,-28.42 6.14,-45.93a209.92,209.92 0,0 1,27.6 -46.95c10.85,-13.82 20.58,-27.65 29.03,-41.63 8.5,-13.93 11.78,-24.47 9.93,-31.64 -0.41,-1.48 -4.86,-7.94 -13.36,-19.25a197.63,197.63 0,0 1,-15.36 -22.37,355.48 355.48,0 0,1 -20.48,-57.14 338.43,338.43 0,0 1,-8.86 -130.15,349.9 349.9,0 0,1 37.89,-121.5A504.68,504.68 0,0 1,204.3 351.74a531.35,531.35 0,0 1,97.95 -84.07c18.43,-12.24 43.72,-24.22 75.93,-35.94 32.1,-11.72 64.56,-22.32 97.38,-31.74 32.56,-9.42 65.02,-19.46 97.23,-30a455.37,455.37 0,0 0,86.32 -37.89c25.4,-14.64 43.42,-30.87 54.02,-48.64l11.78,-20.99 12.08,-20.17c7.99,-13.41 11.98,-18.48 11.83,-15.26 -0.1,3.28 5.79,-1.54 17.66,-14.44 11.93,-12.9 19.66,-15.87 23.24,-8.96 14.34,-3.89 29.49,1.23 45.62,15.36 16.13,14.08 30.41,32.72 42.8,55.86 12.44,23.19 23.04,45.21 31.74,66.15 8.7,20.94 14.9,38.55 18.64,52.74l0.05,0.26z"/>
|
||||
</vector>
|
||||
<path
|
||||
android:fillColor="#FFffff"
|
||||
android:pathData="M717.48,354.1a33.64,33.64 0,0 0,-17.41 -21.56,37.17 37.17,0 0,0 -28.42,-3.38c-65.54,17.77 -123.8,43.16 -174.69,76.19 -50.89,33.02 -93.9,70.76 -129.02,113.15 -35.12,42.5 -69.48,94.52 -102.96,156.11 -5.22,9.57 -6.66,18.69 -4.35,27.44a33.64,33.64 0,0 0,17.36 21.5,37.17 37.17,0 0,0 28.47,3.43 40.4,40.4 0,0 0,22.94 -17.36c7.99,-11.47 18.69,-28.16 32.1,-50.07 13.41,-21.91 23.04,-37.38 28.77,-46.39 40.29,-59.14 83.05,-104.96 128.15,-137.42 45.16,-32.46 102.4,-58.06 171.78,-76.85a37.58,37.58 0,0 0,22.99 -17.31,34 34,0 0,0 4.35,-27.44l-0.05,-0.05zM928.53,194c9.47,35.58 15.36,72.76 17.82,111.51 5.38,88.52 -9.37,169.47 -44.34,242.84 -35.02,73.37 -87.24,141.31 -156.67,203.93 -67.74,61.75 -142.64,103.73 -224.77,125.95a490.8,490.8 0,0 1,-164.25 16.13c-5.99,-0.36 -24.22,-3.89 -54.68,-10.5 -30.41,-6.66 -49.87,-8.81 -58.27,-6.55 -5.89,1.64 -11.52,9.57 -16.95,23.91 -5.43,14.34 -10.24,29.7 -14.34,46.08 -4.1,16.33 -10.24,32.05 -18.43,47.1 -8.19,15.1 -17.61,24.06 -28.26,26.93a60.47,60.47 0,0 1,-29.7 1.43,52.99 52.99,0 0,1 -20.58,-8.91 245.91,245.91 0,0 1,-26.06 -24.93,30.31 30.31,0 0,1 -7.53,-9.73 34.66,34.66 0,0 1,-2.87 -7.37c-3.48,-13.11 -1.43,-28.42 6.14,-45.93a209.92,209.92 0,0 1,27.6 -46.95c10.85,-13.82 20.58,-27.65 29.03,-41.63 8.5,-13.93 11.78,-24.47 9.93,-31.64 -0.41,-1.48 -4.86,-7.94 -13.36,-19.25a197.63,197.63 0,0 1,-15.36 -22.37,355.48 355.48,0 0,1 -20.48,-57.14 338.43,338.43 0,0 1,-8.86 -130.15,349.9 349.9,0 0,1 37.89,-121.5A504.68,504.68 0,0 1,204.3 351.74a531.35,531.35 0,0 1,97.95 -84.07c18.43,-12.24 43.72,-24.22 75.93,-35.94 32.1,-11.72 64.56,-22.32 97.38,-31.74 32.56,-9.42 65.02,-19.46 97.23,-30a455.37,455.37 0,0 0,86.32 -37.89c25.4,-14.64 43.42,-30.87 54.02,-48.64l11.78,-20.99 12.08,-20.17c7.99,-13.41 11.98,-18.48 11.83,-15.26 -0.1,3.28 5.79,-1.54 17.66,-14.44 11.93,-12.9 19.66,-15.87 23.24,-8.96 14.34,-3.89 29.49,1.23 45.62,15.36 16.13,14.08 30.41,32.72 42.8,55.86 12.44,23.19 23.04,45.21 31.74,66.15 8.7,20.94 14.9,38.55 18.64,52.74l0.05,0.26z" />
|
||||
</vector>
|
30
app/src/main/res/drawable/ic_info.xml
Normal 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>
|
@@ -3,12 +3,13 @@
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group android:scaleX="0.03609375"
|
||||
android:scaleY="0.03609375"
|
||||
android:translateX="35.52"
|
||||
android:translateY="35.52">
|
||||
<path
|
||||
android:fillColor="#FFffff"
|
||||
android:pathData="M717.48,354.1a33.64,33.64 0,0 0,-17.41 -21.56,37.17 37.17,0 0,0 -28.42,-3.38c-65.54,17.77 -123.8,43.16 -174.69,76.19 -50.89,33.02 -93.9,70.76 -129.02,113.15 -35.12,42.5 -69.48,94.52 -102.96,156.11 -5.22,9.57 -6.66,18.69 -4.35,27.44a33.64,33.64 0,0 0,17.36 21.5,37.17 37.17,0 0,0 28.47,3.43 40.4,40.4 0,0 0,22.94 -17.36c7.99,-11.47 18.69,-28.16 32.1,-50.07 13.41,-21.91 23.04,-37.38 28.77,-46.39 40.29,-59.14 83.05,-104.96 128.15,-137.42 45.16,-32.46 102.4,-58.06 171.78,-76.85a37.58,37.58 0,0 0,22.99 -17.31,34 34,0 0,0 4.35,-27.44l-0.05,-0.05zM928.53,194c9.47,35.58 15.36,72.76 17.82,111.51 5.38,88.52 -9.37,169.47 -44.34,242.84 -35.02,73.37 -87.24,141.31 -156.67,203.93 -67.74,61.75 -142.64,103.73 -224.77,125.95a490.8,490.8 0,0 1,-164.25 16.13c-5.99,-0.36 -24.22,-3.89 -54.68,-10.5 -30.41,-6.66 -49.87,-8.81 -58.27,-6.55 -5.89,1.64 -11.52,9.57 -16.95,23.91 -5.43,14.34 -10.24,29.7 -14.34,46.08 -4.1,16.33 -10.24,32.05 -18.43,47.1 -8.19,15.1 -17.61,24.06 -28.26,26.93a60.47,60.47 0,0 1,-29.7 1.43,52.99 52.99,0 0,1 -20.58,-8.91 245.91,245.91 0,0 1,-26.06 -24.93,30.31 30.31,0 0,1 -7.53,-9.73 34.66,34.66 0,0 1,-2.87 -7.37c-3.48,-13.11 -1.43,-28.42 6.14,-45.93a209.92,209.92 0,0 1,27.6 -46.95c10.85,-13.82 20.58,-27.65 29.03,-41.63 8.5,-13.93 11.78,-24.47 9.93,-31.64 -0.41,-1.48 -4.86,-7.94 -13.36,-19.25a197.63,197.63 0,0 1,-15.36 -22.37,355.48 355.48,0 0,1 -20.48,-57.14 338.43,338.43 0,0 1,-8.86 -130.15,349.9 349.9,0 0,1 37.89,-121.5A504.68,504.68 0,0 1,204.3 351.74a531.35,531.35 0,0 1,97.95 -84.07c18.43,-12.24 43.72,-24.22 75.93,-35.94 32.1,-11.72 64.56,-22.32 97.38,-31.74 32.56,-9.42 65.02,-19.46 97.23,-30a455.37,455.37 0,0 0,86.32 -37.89c25.4,-14.64 43.42,-30.87 54.02,-48.64l11.78,-20.99 12.08,-20.17c7.99,-13.41 11.98,-18.48 11.83,-15.26 -0.1,3.28 5.79,-1.54 17.66,-14.44 11.93,-12.9 19.66,-15.87 23.24,-8.96 14.34,-3.89 29.49,1.23 45.62,15.36 16.13,14.08 30.41,32.72 42.8,55.86 12.44,23.19 23.04,45.21 31.74,66.15 8.7,20.94 14.9,38.55 18.64,52.74l0.05,0.26z"/>
|
||||
</group>
|
||||
</vector>
|
||||
<group
|
||||
android:scaleX="0.03609375"
|
||||
android:scaleY="0.03609375"
|
||||
android:translateX="35.52"
|
||||
android:translateY="35.52">
|
||||
<path
|
||||
android:fillColor="#FFffff"
|
||||
android:pathData="M717.48,354.1a33.64,33.64 0,0 0,-17.41 -21.56,37.17 37.17,0 0,0 -28.42,-3.38c-65.54,17.77 -123.8,43.16 -174.69,76.19 -50.89,33.02 -93.9,70.76 -129.02,113.15 -35.12,42.5 -69.48,94.52 -102.96,156.11 -5.22,9.57 -6.66,18.69 -4.35,27.44a33.64,33.64 0,0 0,17.36 21.5,37.17 37.17,0 0,0 28.47,3.43 40.4,40.4 0,0 0,22.94 -17.36c7.99,-11.47 18.69,-28.16 32.1,-50.07 13.41,-21.91 23.04,-37.38 28.77,-46.39 40.29,-59.14 83.05,-104.96 128.15,-137.42 45.16,-32.46 102.4,-58.06 171.78,-76.85a37.58,37.58 0,0 0,22.99 -17.31,34 34,0 0,0 4.35,-27.44l-0.05,-0.05zM928.53,194c9.47,35.58 15.36,72.76 17.82,111.51 5.38,88.52 -9.37,169.47 -44.34,242.84 -35.02,73.37 -87.24,141.31 -156.67,203.93 -67.74,61.75 -142.64,103.73 -224.77,125.95a490.8,490.8 0,0 1,-164.25 16.13c-5.99,-0.36 -24.22,-3.89 -54.68,-10.5 -30.41,-6.66 -49.87,-8.81 -58.27,-6.55 -5.89,1.64 -11.52,9.57 -16.95,23.91 -5.43,14.34 -10.24,29.7 -14.34,46.08 -4.1,16.33 -10.24,32.05 -18.43,47.1 -8.19,15.1 -17.61,24.06 -28.26,26.93a60.47,60.47 0,0 1,-29.7 1.43,52.99 52.99,0 0,1 -20.58,-8.91 245.91,245.91 0,0 1,-26.06 -24.93,30.31 30.31,0 0,1 -7.53,-9.73 34.66,34.66 0,0 1,-2.87 -7.37c-3.48,-13.11 -1.43,-28.42 6.14,-45.93a209.92,209.92 0,0 1,27.6 -46.95c10.85,-13.82 20.58,-27.65 29.03,-41.63 8.5,-13.93 11.78,-24.47 9.93,-31.64 -0.41,-1.48 -4.86,-7.94 -13.36,-19.25a197.63,197.63 0,0 1,-15.36 -22.37,355.48 355.48,0 0,1 -20.48,-57.14 338.43,338.43 0,0 1,-8.86 -130.15,349.9 349.9,0 0,1 37.89,-121.5A504.68,504.68 0,0 1,204.3 351.74a531.35,531.35 0,0 1,97.95 -84.07c18.43,-12.24 43.72,-24.22 75.93,-35.94 32.1,-11.72 64.56,-22.32 97.38,-31.74 32.56,-9.42 65.02,-19.46 97.23,-30a455.37,455.37 0,0 0,86.32 -37.89c25.4,-14.64 43.42,-30.87 54.02,-48.64l11.78,-20.99 12.08,-20.17c7.99,-13.41 11.98,-18.48 11.83,-15.26 -0.1,3.28 5.79,-1.54 17.66,-14.44 11.93,-12.9 19.66,-15.87 23.24,-8.96 14.34,-3.89 29.49,1.23 45.62,15.36 16.13,14.08 30.41,32.72 42.8,55.86 12.44,23.19 23.04,45.21 31.74,66.15 8.7,20.94 14.9,38.55 18.64,52.74l0.05,0.26z" />
|
||||
</group>
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_success.xml
Normal 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>
|
9
app/src/main/res/drawable/ic_warn.xml
Normal 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>
|
418
app/src/main/res/layout/activity_config.xml
Normal file
@@ -0,0 +1,418 @@
|
||||
<?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="@drawable/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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/need_restart_tip_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="新的设置需要重新启动{APP_NAME}才能生效"
|
||||
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="@drawable/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="@drawable/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" />
|
||||
|
||||
<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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/executor_info_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
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.widget.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.widget.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.widget.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) 或 (MIPUSH) 可以尝试关闭。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<com.fankes.tsbattery.ui.widget.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>
|
@@ -1,36 +1,56 @@
|
||||
<?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="match_parent"
|
||||
android:background="@color/colorThemeBackground"
|
||||
android:orientation="vertical"
|
||||
tools:context=".MainActivity"
|
||||
tools:ignore="HardcodedText">
|
||||
tools:context=".ui.activity.MainActivity"
|
||||
tools:ignore="HardcodedText,UseCompoundDrawables,ContentDescription,UnusedAttribute">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:elevation="5dp"
|
||||
android:padding="15dp">
|
||||
android:elevation="0dp"
|
||||
android:gravity="center|start"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="13dp"
|
||||
android:paddingRight="15dp"
|
||||
android:paddingBottom="5dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
android:text="TSBattery"
|
||||
android:textColor="#FF323B42"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/title_github_icon"
|
||||
style="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:layout_width="27dp"
|
||||
android:layout_height="27dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:alpha="0.85"
|
||||
android:src="@drawable/ic_github"
|
||||
android:tint="@color/colorTextGray"
|
||||
android:tooltipText="项目地址" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_lin_status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="15dp"
|
||||
android:background="@drawable/dark_round"
|
||||
android:elevation="3dp"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="@drawable/bg_dark_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
@@ -39,14 +59,17 @@
|
||||
android:layout_height="25dp"
|
||||
android:layout_marginStart="25dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:src="@mipmap/warn"
|
||||
android:src="@drawable/ic_warn"
|
||||
android:tint="@color/white" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_status"
|
||||
@@ -57,34 +80,143 @@
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_version"
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:alpha="0.8"
|
||||
android:text="当前版本:%1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_support"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:alpha="0.8"
|
||||
android:text="支持 %1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="12sp" />
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_version"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.8"
|
||||
android:text="模块版本:%1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_release_version"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:background="@drawable/bg_orange_round"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingRight="5dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:text="点击更新 %1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="11sp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fadingEdgeLength="10dp"
|
||||
android:fillViewport="true"
|
||||
android:requiresFadingEdge="horizontal"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_qq_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:src="@mipmap/ic_qq_icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_qq_ver"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.8"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="%1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_tim_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:src="@mipmap/ic_tim_icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_tim_ver"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.8"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="%1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_wechat_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:src="@mipmap/ic_wechat_icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_wechat_ver"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.8"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="%1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_text_api_way"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:alpha="0.6"
|
||||
android:text="理论在小更新内还会生效,如果失效请看下方的联系方式"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="%1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
android:textSize="11sp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -94,7 +226,6 @@
|
||||
android:layout_marginBottom="10dp"
|
||||
android:fadingEdgeLength="10dp"
|
||||
android:fillViewport="true"
|
||||
android:overScrollMode="never"
|
||||
android:requiresFadingEdge="vertical">
|
||||
|
||||
<LinearLayout
|
||||
@@ -108,83 +239,175 @@
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/white_round"
|
||||
android:elevation="2dp"
|
||||
android:background="@drawable/bg_permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
android:orientation="horizontal"
|
||||
android:padding="15dp">
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/protect_mode_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="启用保守模式"
|
||||
android:textColor="#FF323B42"
|
||||
android:textSize="15sp" />
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:alpha="0.85"
|
||||
android:src="@drawable/ic_about"
|
||||
android:tint="@color/colorTextDark" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="此选项默认关闭,默认情况下模块将会干掉 QQ 和 TIM 自身的电源锁控制类,开启后模块将只对系统电源锁生效,如果你的 QQ 或 TIM 视频通话等设置发生了故障,可以尝试开启这个功能,开启后请重启 QQ 或 TIM。"
|
||||
android:textColor="#777777"
|
||||
android:textSize="12sp" />
|
||||
android:lineSpacingExtra="5dp"
|
||||
android:text="你可以点击上述每个图标查看最佳兼容的 APP 版本。\n没有标注的版本在适配范围内的 APP 适用性都将有效,但可能不能达到最佳使用效果,建议保持使用适配内的版本。"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/quick_action_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/bg_permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center|start">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:cardBackgroundColor="#FF00BCD4"
|
||||
app:cardCornerRadius="50dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:padding="2.5dp"
|
||||
android:src="@drawable/ic_fast_op" />
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.85"
|
||||
android:singleLine="true"
|
||||
android:text="快捷操作"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/quick_qq_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_button_round"
|
||||
android:gravity="center"
|
||||
android:padding="10dp"
|
||||
android:singleLine="true"
|
||||
android:text="QQ"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/quick_tim_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_button_round"
|
||||
android:gravity="center"
|
||||
android:padding="10dp"
|
||||
android:singleLine="true"
|
||||
android:text="TIM"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/quick_wechat_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_button_round"
|
||||
android:gravity="center"
|
||||
android:padding="10dp"
|
||||
android:singleLine="true"
|
||||
android:text="微信"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/display_setting_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/white_round"
|
||||
android:elevation="2dp"
|
||||
android:background="@drawable/bg_permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/notify_module_info_switch"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="提示模块运行信息"
|
||||
android:textColor="#FF323B42"
|
||||
android:textSize="15sp" />
|
||||
android:gravity="center|start">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="模块工作正常情况下不要开启,如果你想测试模块是否正常激活,可以打开此提示,开启后将会在启动 QQ 或 TIM 的时候提示运行信息。"
|
||||
android:textColor="#777777"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:cardBackgroundColor="#FFFF9800"
|
||||
app:cardCornerRadius="50dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/white_round"
|
||||
android:elevation="2dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
<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>
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.85"
|
||||
android:singleLine="true"
|
||||
android:text="显示设置"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.fankes.tsbattery.ui.widget.MaterialSwitch
|
||||
android:id="@+id/hide_icon_in_launcher_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="在桌面隐藏模块图标"
|
||||
android:textColor="#FF323B42"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
@@ -193,8 +416,18 @@
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="隐藏模块图标后,模块不会再在桌面显示,你可以在 EdXposed、太极、LsPosed 中找到模块设置并打开。"
|
||||
android:textColor="#777777"
|
||||
android:text="隐藏模块图标后界面可能会被关闭,将不会再在桌面显示,你可以在 EdXposed、LSPosed 中找到模块设置并打开。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="注意:请务必在 LSPosed 中关闭“强制显示桌面图标”功能"
|
||||
android:textColor="#FF5722"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -204,35 +437,42 @@
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/white_round"
|
||||
android:elevation="2dp"
|
||||
android:background="@drawable/bg_permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
android:padding="15dp"
|
||||
android:paddingTop="15dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:gravity="center|start">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:alpha="0.85"
|
||||
android:src="@mipmap/about"
|
||||
android:tint="#FF323B42" />
|
||||
android:layout_marginEnd="10dp"
|
||||
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
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.85"
|
||||
android:singleLine="true"
|
||||
android:text="使用帮助与说明"
|
||||
android:textColor="#FF323B42"
|
||||
android:text="使用帮助&说明"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -241,9 +481,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="Q.这个模块是做什么的?\nA.此模块的诞生来源于国内厂商毒瘤 APP 强行霸占后台耗电,QQ 在 8.6.0 版本以后也只是接入了 HMS 推送,但是可笑的是开发组却并没有删除之前疯狂耗电的接收消息方法,于是这个模块就诞生了。"
|
||||
android:textColor="#777777"
|
||||
android:lineSpacingExtra="10dp"
|
||||
android:text="Q.这个模块是做什么的?\nA.此模块的诞生来源于国内厂商毒瘤 APP 强行霸占后台耗电,QQ 在 8.6.0 版本以后也只是接入了 HMS 推送,但是可笑的是开发组却并没有删除之前疯狂耗电的接收消息方法,于是这个模块就因此而诞生了。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
@@ -251,9 +491,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:lineSpacingExtra="10dp"
|
||||
android:text="Q.原理是什么?\nA.模块有两套工作方式,一种是针对 QQ、TIM Hook 掉系统自身的电源锁“WakeLock”使其不能影响系统休眠,这样子在锁屏的时候 QQ、TIM 就可以进入睡眠状态。第二种就是针对 QQ、TIM 删除其自身的无用耗电疯狂循环检测后台强行保活服务。"
|
||||
android:textColor="#777777"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
@@ -261,9 +501,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="Q.如何使用?\nA.目前模块支持 EdXposed、LsPosed 以及太极(无极)框架,在太极和 LsPosed 的作用域中,只需勾选 QQ 和 TIM 即可,模块可以做到即插即用,激活后无需重启手机,重启 QQ 或 TIM 就可以了。"
|
||||
android:textColor="#777777"
|
||||
android:lineSpacingExtra="10dp"
|
||||
android:text="Q.如何使用?\nA.目前模块支持 LSPosed、LSPatch 以及太极和一些常见的免 Root 框架,在太极和 LSPosed 的作用域中,只需勾选 QQ、TIM、微信即可,模块可以做到即插即用,激活后无需重启手机,重启 QQ、TIM 或微信就可以了。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
@@ -271,9 +511,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="Q.激活后一定可以非常省电吗?\nA.并不,模块只能减少 QQ、TIM 的耗电,但是请务必记住这一点,省电只是一个理论上的东西,实际水平由你使用的系统和硬件决定,如果你在前台疯狂使用 QQ、TIM,那么照样会耗电,模块只能保证后台运行和锁屏时毒瘤不会消耗过多的无用的电量,仅此而已。"
|
||||
android:textColor="#777777"
|
||||
android:lineSpacingExtra="10dp"
|
||||
android:text="Q.配置界面在哪里?\nA.从 4.0 版本开始模块已将配置界面转移到目标 APP 中,你可以从以下途径找到模块配置界面:\nQQ: 设置 > TSBattery\nTIM: 设置 > TSBattery\n微信: 设置 > 右上角 TSBattery 图标\n你还可以从此界面上方的“快捷操作”中快速进入指定配置界面。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
@@ -281,59 +521,38 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:lineSpacingExtra="10dp"
|
||||
android:text="Q.激活后一定可以非常省电吗?\nA.并不,模块只能减少 QQ、TIM、微信的耗电,但是请务必记住这一点,省电只是一个理论上的东西,实际水平由你使用的系统和硬件决定,如果你在前台疯狂使用 QQ、TIM,那么照样会耗电,模块只能保证后台运行和锁屏时毒瘤不会消耗过多的无用的电量,仅此而已。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="10dp"
|
||||
android:text="Q.模块是否需要挂后台?\nA.模块完全不需要挂后台,模块只是一个控制和显示的工具,真正的任务交由 Hook 处理,若出现失效的情况请发送模块运行日志给我们而不是将模块挂后台。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="10dp"
|
||||
android:text="Q.关于目前微信的适配情况?\nA.微信适配尚在实验阶段,敬请期待。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="10dp"
|
||||
android:text="Q.如何反馈问题?\nA.酷安关注 @星夜不荟"
|
||||
android:textColor="#777777"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/link_with_project_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:background="@drawable/white_round"
|
||||
android:elevation="2dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center|start">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:alpha="0.85"
|
||||
android:src="@mipmap/about"
|
||||
android:tint="#FF323B42" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.85"
|
||||
android:singleLine="true"
|
||||
android:text="项目地址"
|
||||
android:textColor="#FF323B42"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="本软件是免费开源项目,遵循 GPL 协议,你可以点击这里前往 Github 查看源码以及获取模块更新。\n严禁以任何形式贩卖、商用本软件,否则开发者有权追究其法律责任。"
|
||||
android:textColor="#777777"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -344,30 +563,38 @@
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@drawable/white_round"
|
||||
android:elevation="2dp"
|
||||
android:background="@drawable/bg_permotion_round"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/link_with_follow_me"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="恰饭时间\n酷安关注我,获取我的更多应用"
|
||||
android:textColor="#FF323B42"
|
||||
android:text="恰饭时间\n点击前往酷安关注我,获取我的更多应用"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:src="@mipmap/qr_pay"
|
||||
tools:ignore="ContentDescription" />
|
||||
app:cardCornerRadius="15dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:src="@mipmap/bg_payment_code" />
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
@@ -376,9 +603,38 @@
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="开发者 酷安 @星夜不荟\n未经允许不得转载、修改复制我的劳动成果"
|
||||
android:textColor="#FF323B42"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@drawable/bg_permotion_round"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:src="@mipmap/ic_yukihookapi" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoLink="web"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:maxLines="2"
|
||||
android:text="此模块使用 YukiHookAPI 构建。\n了解更多 https://github.com/HighCapable/YukiHookAPI"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
Before Width: | Height: | Size: 4.3 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/bg_payment_code.jpg
Normal file
After Width: | Height: | Size: 122 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_qq_icon.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_tim_icon.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_tsbattery_entry_day.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_tsbattery_entry_night.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_wechat_icon.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_yukihookapi.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 4.0 KiB |
6
app/src/main/res/values-night/color.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorThemeBackground">#FF2D2D2D</color>
|
||||
<color name="colorTextDark">#FFCFCFCF</color>
|
||||
<color name="colorTextGray">#FFD3D3D3</color>
|
||||
</resources>
|
@@ -1,16 +1,16 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<resources>
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.TSBattery" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<style name="Theme.TSBattery" parent="Theme.Material3.DayNight">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<item name="colorPrimary">@color/colorPrimaryAccent</item>
|
||||
<item name="colorPrimaryVariant">@color/colorPrimaryAccent</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<item name="colorSecondary">@color/colorPrimaryAccent</item>
|
||||
<item name="colorSecondaryVariant">@color/colorPrimaryAccent</item>
|
||||
<item name="colorOnSecondary">@color/white</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
8
app/src/main/res/values/array.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="module_scope">
|
||||
<item>com.tencent.mobileqq</item>
|
||||
<item>com.tencent.tim</item>
|
||||
<item>com.tencent.mm</item>
|
||||
</string-array>
|
||||
</resources>
|
6
app/src/main/res/values/color.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorThemeBackground">#FFFFFFFF</color>
|
||||
<color name="colorTextDark">#FF777777</color>
|
||||
<color name="colorTextGray">#FF323B42</color>
|
||||
</resources>
|
@@ -1,10 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#656565</color>
|
||||
<color name="purple_500">#656565</color>
|
||||
<color name="purple_700">#656565</color>
|
||||
<color name="teal_200">#656565</color>
|
||||
<color name="teal_700">#656565</color>
|
||||
<color name="colorPrimaryAccent">#656565</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
4
app/src/main/res/values/ids.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item name="tsbattery_qq_entry_item_id" type="id" />
|
||||
</resources>
|
@@ -1,16 +1,16 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<resources>
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.TSBattery" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<style name="Theme.TSBattery" parent="Theme.Material3.DayNight">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/teal_700</item>
|
||||
<item name="colorPrimary">@color/colorPrimaryAccent</item>
|
||||
<item name="colorPrimaryVariant">@color/colorPrimaryAccent</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<item name="colorSecondary">@color/colorPrimaryAccent</item>
|
||||
<item name="colorSecondaryVariant">@color/colorPrimaryAccent</item>
|
||||
<item name="colorOnSecondary">@color/white</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
30
build.gradle
@@ -1,30 +0,0 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
ext.kotlin_version = "1.4.20"
|
||||
repositories {
|
||||
google()
|
||||
//noinspection JcenterRepositoryObsolete
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
//noinspection AndroidGradlePluginVersion
|
||||
classpath "com.android.tools.build:gradle:4.1.1"
|
||||
//noinspection DifferentKotlinGradleVersion
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
//noinspection JcenterRepositoryObsolete
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
5
build.gradle.kts
Normal file
@@ -0,0 +1,5 @@
|
||||
plugins {
|
||||
autowire(libs.plugins.android.application) apply false
|
||||
autowire(libs.plugins.kotlin.android) apply false
|
||||
autowire(libs.plugins.kotlin.ksp) apply false
|
||||
}
|
@@ -1,21 +1,18 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Compiler Configuration
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app"s APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
android.nonTransitiveRClass=true
|
||||
kotlin.code.style=official
|
||||
kotlin.incremental.useClasspathSnapshot=true
|
||||
# Project Configuration
|
||||
project.name=TSBattery
|
||||
project.android.compileSdk=34
|
||||
project.android.minSdk=24
|
||||
project.android.targetSdk=34
|
||||
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
|
78
gradle/sweet-dependency/sweet-dependency-config.yaml
Normal file
@@ -0,0 +1,78 @@
|
||||
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.1.2
|
||||
org.jetbrains.kotlin.android:
|
||||
alias: kotlin-android
|
||||
version: 1.9.10
|
||||
com.google.devtools.ksp:
|
||||
alias: kotlin-ksp
|
||||
version: 1.9.10-1.0.13
|
||||
|
||||
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.2.0
|
||||
ksp-xposed:
|
||||
version-ref: <this>::api
|
||||
org.luckypray:
|
||||
dexkit:
|
||||
version: 2.0.0-rc7
|
||||
com.github.duanhong169:
|
||||
drawabletoolbox:
|
||||
version: 1.0.7
|
||||
com.squareup.okhttp3:
|
||||
okhttp:
|
||||
version: 5.0.0-alpha.11
|
||||
androidx.core:
|
||||
core-ktx:
|
||||
version: 1.12.0
|
||||
androidx.appcompat:
|
||||
appcompat:
|
||||
version: 1.6.1
|
||||
com.google.android.material:
|
||||
material:
|
||||
version: 1.10.0
|
||||
androidx.constraintlayout:
|
||||
constraintlayout:
|
||||
version: 2.1.4
|
||||
androidx.test.ext:
|
||||
junit:
|
||||
version: 1.1.5
|
||||
androidx.test.espresso:
|
||||
espresso-core:
|
||||
version: 3.5.1
|
||||
junit:
|
||||
junit:
|
||||
version: 4.13.2
|
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
||||
#Sat Sep 04 04:05:23 CST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStoreBase=GRADLE_USER_HOME
|
BIN
img-src/banner.png
Normal file
After Width: | Height: | Size: 38 KiB |