85 Commits
4.1 ... 4.3

Author SHA1 Message Date
6b0fe1014e Update version to 4.3 | Support QQ 8.9.53~8.9.70 versions 2023-07-26 02:59:29 +08:00
bdf52ba463 Modify support QQ 8.9.70 2023-07-26 02:46:17 +08:00
c2b95b9133 Modify support QQ 8.9.70 (QQ-NT) hook entry item 2023-07-26 02:45:56 +08:00
2f64fb9ea9 Modify support QQ 8.9.58~8.9.68 2023-07-18 22:40:08 +08:00
f48277f434 Modify optimize code in QQTIMHooker 2023-07-18 22:35:28 +08:00
047f746afb Modify support QQ 8.9.55 2023-05-17 07:35:17 +08:00
ac885baa64 Update Gradle dependencies 2023-05-17 07:35:09 +08:00
4332881dad Modify support QQ 8.9.53 2023-04-27 01:23:06 +08:00
49c0655412 Update YukiHookAPI 2023-04-25 06:57:15 +08:00
7eeda27937 Update YukiHookAPI 2023-04-21 01:25:06 +08:00
a8692e8a33 Update version to 4.25 | Support QQ 8.9.30~8.9.50 versions 2023-04-17 06:19:55 +08:00
39ec0e8ef7 Modify change need restart dialog to prompt in ConfigActivity, activity_config 2023-04-17 05:59:55 +08:00
1dc22b90bb Modify merge to YukiHookAPI new usage 2023-04-17 05:50:35 +08:00
da955fc157 Update YukiHookAPI 2023-04-17 05:45:05 +08:00
94752010ec Modify merge contents of build.gradle into constant definitions 2023-04-15 22:35:36 +08:00
f9bc6fc013 Modify support QQ 8.9.50 2023-04-14 17:48:06 +08:00
dcd95e4d8d Update Gradle & Kotlin
- Update Kotlin version to 1.8.20
- Update Gradle version to 8.0.2
- Update Gradle dependencies
2023-04-08 00:00:35 +08:00
7f63f93165 Modify support QQ 8.9.38 2023-04-05 05:16:10 +08:00
e95af0bad9 Modify support QQ 8.9.35 2023-03-20 18:13:55 +08:00
344104903b Modify support QQ 8.9.33 2023-03-03 11:51:55 +08:00
249cec2bee Modify support QQ 8.9.30 2023-02-22 06:19:35 +08:00
42387603c6 Modify change package resources id cause some same host modules conflict with it in build.gradle 2023-02-22 06:17:48 +08:00
fb3aa96c0a Update Gradle dependencies 2023-02-22 06:15:05 +08:00
f7c31d9786 Added star history chart in README 2023-02-19 14:35:35 +08:00
8d00aca110 Fix "GitHub" spelling in all files 2023-02-07 05:29:35 +08:00
00a1aaad5b Modify change ProgressBar to CircularProgressIndicator in DialogBuilderFactory 2023-02-03 23:19:05 +08:00
a4602df9cb Modify optimize code in MainActivity 2023-02-03 01:43:05 +08:00
d5624e0e24 Update YukiHookAPI 2023-02-01 04:25:05 +08:00
60f9af38b2 Update .gitignore 2023-02-01 04:23:15 +08:00
d3bea2ec83 Update Gradle dependencies 2023-02-01 03:30:05 +08:00
d085659fbb Added new bug report issues template 2023-01-31 19:12:35 +08:00
f2ec8bf33e Update YukiHookAPI 2023-01-21 00:59:49 +08:00
3233e772bb Update Android Gradle Plugin to 7.4.0 2023-01-21 00:59:32 +08:00
e58da11553 Update Gradle dependencies 2023-01-19 22:27:32 +08:00
90d4c04593 Update README.md 2023-01-18 00:49:01 +08:00
bcb7f6b2c5 Modify rename ui/view to ui/widget 2023-01-17 11:14:27 +08:00
596726e8c7 Fix the central color problem of views such as CheckBox 2023-01-16 22:39:27 +08:00
0a52914911 Update copyright date to 2023 for all existing file 2023-01-14 10:57:18 +08:00
3c30c532a4 Update version to 4.2 | Support many QQ 8.9.x version 2023-01-14 00:37:02 +08:00
8f7772d047 Modify remove localTime function time second format and change description text in GithubReleaseTool 2023-01-14 00:16:45 +08:00
c148a535c8 Modify change promote message in YukiPromoteTool 2023-01-14 00:13:53 +08:00
a5448a2c61 Modify remove "endsWith" method's param name statement 2023-01-14 00:12:55 +08:00
a8a01e8afa Modify change related description text in activity_main 2023-01-14 00:08:34 +08:00
c322c3dc29 Modify change related description text in MainActivity 2023-01-14 00:00:33 +08:00
2e9761f01a Added executor info and change some description text in ConfigActivity, activity_config 2023-01-13 23:57:52 +08:00
0fd1a33533 Modify remove "replace" method's param name statement 2023-01-13 23:20:26 +08:00
e465739f95 Modify remove instanceClass param for hookQQSettingsUI function in QQTIMHooker 2023-01-13 22:38:14 +08:00
52f992a6cd Modify change related description text in activity_config 2023-01-13 22:24:25 +08:00
37be07f11d Modify change isOnFailureThrowToApp to false for onAppLifecycle event in QQTIMHooker 2023-01-13 22:19:53 +08:00
d8d9fc8c41 Modify make HookEntry singleton 2023-01-13 22:13:50 +08:00
f27fdb2f25 Modify merge to YukiHookAPI new usage 2023-01-13 04:56:15 +08:00
3629ca3df5 Update Gradle & Kotlin
- Update Kotlin version to 1.7.22
- Update Gradle version to 7.6
- Update Gradle dependencies
2023-01-13 04:53:45 +08:00
12a1c12f8a Update YukiHookAPI 2023-01-13 04:50:50 +08:00
bc41ac2bdc Fix internal browser X5 kernel issues (cause QQ crashed) 2023-01-12 15:00:52 +08:00
5f13e203f0 Fix WeChat SettingsUI's top right TSBattery icon lost problem when version >= 8.0.28 2023-01-12 03:17:01 +08:00
69a68999a5 Fix not found QQSettingSettingFragment Class when not in Pad Mode (disabled error output) 2023-01-11 02:45:02 +08:00
bc48d59b59 Fix TaiChi activation state lost problem for API version higher than 30 2023-01-02 17:43:24 +08:00
cadf7b8873 Modify support QQ 8.9.28 2023-01-01 01:06:11 +08:00
7f0c8dd2b5 Modify support QQ 8.9.25 2022-12-16 02:21:14 +08:00
67349c45fd Modify support QQ 8.9.23 2022-11-30 22:37:34 +08:00
4ab612d5fa Modify add release channel description, release status description in README 2022-11-26 00:30:31 +08:00
bb4b942dae Added automatic build workflows for Github Actions 2022-11-26 00:29:54 +08:00
ff0931b7bc Modify support QQ 8.9.20 2022-11-18 01:50:26 +08:00
fc42a0ddf0 Modify support QQ 8.9.19 2022-11-08 23:45:27 +08:00
abab096ae0 Modify support QQ 8.9.18 2022-11-03 10:35:47 +08:00
ce8e786b5b Modify change logo url to raw in README 2022-11-03 10:35:32 +08:00
dd6c69b337 Modify remove wrap/part at the end of file 2022-11-03 10:18:47 +08:00
69564cccf6 Update Gradle & PlatformSDK
- Update Kotlin version to 1.7.20
2022-11-03 10:17:50 +08:00
a8c16d7823 Modify support QQ Pad Mode for QQ Settings UI entry item since 8.9.15 2022-10-22 02:00:57 +08:00
3616919978 Update Gradle dependencies 2022-10-22 01:11:11 +08:00
c5dfbcbe0d Modify support QQ 8.9.15 2022-10-22 01:09:32 +08:00
f21e8769e0 Modify change not support version log to warn level in QQTIMHooker 2022-10-21 21:24:57 +08:00
eada17db44 Update Gradle & PlatformSDK
- Update Android Gradle Plugin version to 7.3.1
- Update Kotlin version to 1.7.20
2022-10-20 00:20:13 +08:00
67209859c4 Modify merge to new usage in HookEntry 2022-10-04 07:29:37 +08:00
a65b3c1dbe Update YukiHookAPI 2022-10-04 07:28:56 +08:00
6375aa7844 Update README.md 2022-10-02 22:57:46 +08:00
cbd6fd038d Modify merge all png elements to svg elements 2022-10-02 22:55:44 +08:00
7fcf9228f4 Modify change icon to svg in activity_main 2022-10-02 21:38:49 +08:00
d995c69b5c Fix variable name shadowed bug in QQTIMHooker 2022-10-02 21:26:17 +08:00
376a2a5890 Modify compatible with API 33 2022-10-01 02:16:55 +08:00
e2ff60e2ef Modify standard code naming in FunctionFactory 2022-10-01 00:46:56 +08:00
ad0b6d253a Update YukiHookAPI 2022-09-30 23:47:24 +08:00
76af063ed8 Modify change code note in MainActivity 2022-09-30 22:21:16 +08:00
e0fdf269a0 Modify optimize code style in HookEntry 2022-09-30 22:14:07 +08:00
b1d4cd4017 Added Project icon 2022-09-30 22:10:48 +08:00
59 changed files with 1027 additions and 361 deletions

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

@@ -0,0 +1,104 @@
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:
- 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

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

@@ -0,0 +1,63 @@
name: Automatic Build on Commit
on:
workflow_dispatch:
push:
branches: [ master ]
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '!.github/workflows/**'
jobs:
build:
name: Build CI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1
with:
cmake-version: '3.22.1'
- name: Prepare Java 11
uses: actions/setup-java@v3
with:
java-version: 11
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') }}
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_FILE=$(find app/build/outputs/apk/debug -name '*.apk')" >> $GITHUB_ENV
echo "RELEASE_APK_FILE=$(find app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
- name: Upload Artifacts(debug)
uses: actions/upload-artifact@v3
with:
path: ${{ env.DEBUG_APK_FILE }}
name: app-debug
- name: Upload Artifacts(release)
uses: actions/upload-artifact@v3
with:
path: ${{ env.RELEASE_APK_FILE }}
name: app-release

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

@@ -0,0 +1,62 @@
name: Pull Request Checker
on:
pull_request:
branches: [ master ]
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '!.github/workflows/**'
jobs:
build:
name: Pull request check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1
with:
cmake-version: '3.22.1'
- name: Prepare Java 11
uses: actions/setup-java@v3
with:
java-version: 11
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') }}
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_FILE=$(find app/build/outputs/apk/debug -name '*.apk')" >> $GITHUB_ENV
echo "RELEASE_APK_FILE=$(find app/build/outputs/apk/release -name '*.apk')" >> $GITHUB_ENV
- name: Upload Artifacts(debug)
uses: actions/upload-artifact@v3
with:
path: ${{ env.DEBUG_APK_FILE }}
name: app-debug
- name: Upload Artifacts(release)
uses: actions/upload-artifact@v3
with:
path: ${{ env.RELEASE_APK_FILE }}
name: app-release

BIN
.idea/icon.png generated Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

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

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.7.20" />
</component>
</project>

View File

@@ -0,0 +1,6 @@
{
"keyAlias": "public",
"keyPassword": "123456",
"storeFileName": "universal.p12",
"storePassword": "123456"
}

View File

@@ -2,12 +2,12 @@
[![Blank](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/fankes/TSBattery)
[![Blank](https://img.shields.io/badge/license-AGPL3.0-blue)](https://github.com/fankes/TSBattery/blob/master/LICENSE)
[![Blank](https://img.shields.io/badge/version-v4.1-green)](https://github.com/fankes/TSBattery/releases)
[![Blank](https://img.shields.io/badge/version-v4.3-green)](https://github.com/fankes/TSBattery/releases)
[![Blank](https://img.shields.io/github/downloads/fankes/TSBattery/total?label=Release)](https://github.com/fankes/TSBattery/releases)
[![Blank](https://img.shields.io/github/downloads/Xposed-Modules-Repo/com.fankes.tsbattery/total?label=LSPosed%20Repo&logo=Android&style=flat&labelColor=F48FB1&logoColor=ffffff)](https://github.com/Xposed-Modules-Repo/com.fankes.tsbattery/releases)
[![Telegram](https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram)](https://t.me/XiaofangInternet)
<br/><br/>
![banner](https://github.com/fankes/TSBattery/blob/master/banner.png)<br/>
![banner](https://github.com/fankes/TSBattery/blob/master/banner.png?raw=true)<br/>
A new way to save your battery avoid cancer apps hacker it.
TSBattery 是一个旨在使 QQ、TIM、微信 变得更省电的开源 Xposed 模块。
@@ -18,24 +18,47 @@ TSBattery 是一个旨在使 QQ、TIM、微信 变得更省电的开源 Xposed
## 适配说明
- 支持并建议使用 **LSPosed**(若作用域没有自动出现推荐请勾选 QQ、TIM、微信)
- 解锁 BL 并安装 **Magisk** 的设备建议使用 [LSPosed](https://github.com/LSPosed/LSPosed)
- 可以使用 **~~EdXposed~~**,但随时停止支持
- **太极无极 · 阴** 支持性不是很好,建议使用 **太极无极 · 阳****LSPatch (推荐)**
- **太极无极 · 阴** 支持性不是很好,建议使用 [LSPatch](https://github.com/LSPosed/LSPatch)
- 支持 **Pine**(梦境模块) 但是部分功能有限制
- 支持一些第三方免 Root 框架例如**应用转生**、**SandVXposed**,但是不推荐使用,可能会造成封号风险
- 请不要使用 **~~应用转生~~**,发生封号情况后果自负
- 如果微信不能在 **LSPosed** 中生效,请尝试勾选任意包含微信作用域的模块,例如 **微X模块**
- 如果在微信设置界面右上角你无法找到 **TSBattery** 的图标,请尝试同时激活 [WeXposed (微X模块)](https://github.com/Xposed-Modules-Repo/com.fkzhang.wechatxposed)
## 请勿用于非法用途
- 本模块完全开源免费,如果好用你可以打赏支持开发,但是请不要用于非法用途。
本模块完全开源免费,如果好用你可以打赏支持开发,但是请不要用于非法用途。
- 本模块发布地址仅有 [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.tsbattery/releases)、
[Release](https://github.com/fankes/TSBattery/releases) 及 [蓝奏云](https://fankes.lanzouy.com/b02zfz3sj),从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
## 发行渠道说明
- [Automatic Build on Commit](https://github.com/fankes/TSBattery/actions/workflows/commit_ci.yml)
上述更新为代码 `commit` 后自动触发,具体更新内容可点击上方的文字前往 **GitHub Actions** 进行查看,本更新由开源的流程自动编译发布,**不保证其稳定性**,所发布的版本**仅供测试**,且不会特殊说明甚至可能会变更版本号或保持与当前稳定版相同的版本号。
- [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)
上述更新为手动发布的稳定版,具体更新内容可点击上方的文字前往指定的发布页面查看,稳定版的更新将会同时发布到上述地址中,同步更新。
本模块发布地址仅限于上述所列出的地址,从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
## 发行状态说明
![Blank](https://img.shields.io/badge/build-passing-brightgreen)
上述状态为当前稳定版与自动构建版本一致或当前代码改动与稳定版无功能差异。
![Blank](https://img.shields.io/badge/build-pending-dbab09)
上述状态为存在自动构建版本和新功能的更新但当前并未发布稳定版,处于预发行状态。
![Blank](https://img.shields.io/badge/build-problem-red)
上述状态为当前发行的稳定版可能存在严重问题但并未及时进行修复且并未发布稳定版。
## 开始贡献
@@ -43,12 +66,16 @@ TSBattery 是一个旨在使 QQ、TIM、微信 变得更省电的开源 Xposed
- [CONTRIBUTING](https://github.com/fankes/TSBattery/blob/master/CONTRIBUTING.md)
## Star History
![Star History Chart](https://api.star-history.com/svg?repos=fankes/TSBattery&type=Date)
## 许可证
- [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
```
Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
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
@@ -66,4 +93,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI)
版权所有 © 2019-2022 Fankes Studio(qzmmcn@163.com)
版权所有 © 2017-2023 Fankes Studio(qzmmcn@163.com)

2
app/.gitignore vendored
View File

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

View File

@@ -1,38 +1,51 @@
import groovy.json.JsonSlurper
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'com.google.devtools.ksp' version '1.7.10-1.0.6'
id 'org.jetbrains.kotlin.android'
id 'com.google.devtools.ksp'
}
android {
signingConfigs {
debug {
storeFile file('../keystore/public')
storePassword '123456'
keyAlias 'public'
keyPassword '123456'
universal {
def dirPath = rootProject.ext.app.signingConfigs.secretConfigsDirPath
def fileName = rootProject.ext.app.signingConfigs.secretConfigsFileName
def configs = new JsonSlurper().parse(file("${dirPath}/${fileName}"))
keyAlias configs.keyAlias
keyPassword configs.keyPassword
storeFile file("${dirPath}/${configs.storeFileName}")
storePassword configs.storePassword
v1SigningEnabled true
v2SigningEnabled true
}
}
compileSdkVersion 33
namespace 'com.fankes.tsbattery'
compileSdk rootProject.ext.android.compileSdk
defaultConfig {
applicationId "com.fankes.tsbattery"
minSdk 22
targetSdk 33
versionCode rootProject.ext.appVersionCode
versionName rootProject.ext.appVersionName
applicationId 'com.fankes.tsbattery'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
minSdk rootProject.ext.android.minSdk
targetSdk rootProject.ext.android.targetSdk
versionCode rootProject.ext.app.versionCode
versionName rootProject.ext.app.versionName
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
buildTypes {
debug {
minifyEnabled false
signingConfig signingConfigs.universal
}
release {
minifyEnabled rootProject.ext.enableR8
shrinkResources rootProject.ext.enableR8
zipAlignEnabled rootProject.ext.enableR8
signingConfig signingConfigs.debug
minifyEnabled true
shrinkResources true
zipAlignEnabled true
signingConfig signingConfigs.universal
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
@@ -54,20 +67,20 @@ android {
lintOptions {
checkReleaseBuilds false
}
aaptOptions.additionalParameters '--allow-reserved-package-id', '--package-id', '0x64'
aaptOptions.additionalParameters '--allow-reserved-package-id', '--package-id', '0x37'
}
dependencies {
compileOnly 'de.robv.android.xposed:api:82'
implementation 'com.highcapable.yukihookapi:api:1.1.2'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.1.2'
implementation 'com.highcapable.yukihookapi:api:1.1.11'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.1.11'
implementation 'com.github.duanhong169:drawabletoolbox:1.0.7'
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.7'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

View File

@@ -37,6 +37,12 @@
# 排除注入的 Activity
-keep class com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
# 防止某些类被 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(...);

View File

@@ -11,6 +11,10 @@
<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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it

View File

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

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -32,13 +32,10 @@ import com.highcapable.yukihookapi.hook.factory.encase
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
@InjectYukiHookWithXposed(isUsingResourcesHook = false)
class HookEntry : IYukiHookXposedInit {
companion object {
object HookEntry : IYukiHookXposedInit {
/** 是否完全支持当前版本 */
var isHookClientSupport = true
}
override fun onInit() = configs {
debugLog { tag = "TSBattery" }
@@ -47,8 +44,7 @@ class HookEntry : IYukiHookXposedInit {
}
override fun onHook() = encase {
loadApp(PackageName.QQ) { loadHooker(QQTIMHooker) }
loadApp(PackageName.TIM) { loadHooker(QQTIMHooker) }
loadApp(PackageName.WECHAT) { loadHooker(WeChatHooker) }
loadApp(PackageName.QQ, PackageName.TIM) { loadHooker(QQTIMHooker) }
loadApp(PackageName.WECHAT, WeChatHooker)
}
}

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
@@ -23,28 +23,34 @@ 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.BuildConfig
import com.fankes.tsbattery.R
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.utils.factory.appVersionName
import com.fankes.tsbattery.utils.factory.dp
import com.fankes.tsbattery.utils.factory.versionName
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.*
import com.highcapable.yukihookapi.hook.log.loggerD
import com.highcapable.yukihookapi.hook.log.loggerE
import com.highcapable.yukihookapi.hook.log.loggerI
import com.highcapable.yukihookapi.hook.log.loggerW
import com.highcapable.yukihookapi.hook.type.android.*
import com.highcapable.yukihookapi.hook.type.java.*
import java.lang.reflect.Proxy
/**
* Hook QQ、TIM
@@ -54,12 +60,24 @@ object QQTIMHooker : YukiBaseHooker() {
/** QQ、TIM 存在的类 */
const val JumpActivityClass = "${PackageName.QQ}.activity.JumpActivity"
/** QQ、TIM 存在的类 */
/** QQ、TIM 存在的类 (NT 版本不再存在) */
private const val QQSettingSettingActivityClass = "${PackageName.QQ}.activity.QQSettingSettingActivity"
/** QQ、TIM 存在的类 */
/** QQ 新版存在的类 (Pad 模式 - NT 版本不再存在) */
private const val QQSettingSettingFragmentClass = "${PackageName.QQ}.fragment.QQSettingSettingFragment"
/** QQ、TIM 存在的类 (NT 版本不再存在) */
private const val AboutActivityClass = "${PackageName.QQ}.activity.AboutActivity"
/** QQ 新版本存在的类 */
private const val GeneralSettingActivityClass = "${PackageName.QQ}.activity.GeneralSettingActivity"
/** QQ 新版本 (NT) 存在的类 */
private const val MainSettingFragmentClass = "${PackageName.QQ}.setting.main.MainSettingFragment"
/** QQ 新版本 (NT) 存在的类 */
private const val MainSettingConfigProviderClass = "${PackageName.QQ}.setting.main.MainSettingConfigProvider"
/** QQ、TIM 新版本存在的类 */
private const val FormSimpleItemClass = "${PackageName.QQ}.widget.FormSimpleItem"
@@ -73,8 +91,10 @@ object QQTIMHooker : YukiBaseHooker() {
private const val CoreService_KernelServiceClass = "${PackageName.QQ}.app.CoreService\$KernelService"
/** 根据多个版本存的不同的类 */
private val BaseChatPieClass =
VariousClass("${PackageName.QQ}.activity.aio.core.BaseChatPie", "${PackageName.QQ}.activity.BaseChatPie")
private val BaseChatPieClass = VariousClass("${PackageName.QQ}.activity.aio.core.BaseChatPie", "${PackageName.QQ}.activity.BaseChatPie")
/** 一个内部进程的名称 (与 X5 浏览器内核有关) */
private val privilegedProcessName = "$packageName:privileged_process"
/** 默认的 [Configuration] */
var baseConfiguration: Configuration? = null
@@ -85,8 +105,22 @@ object QQTIMHooker : YukiBaseHooker() {
*/
private val isQQ get() = packageName == PackageName.QQ
/**
* 当前是否为 QQ 的 NT 版本
*
* 在 QQ NT 中 [AboutActivityClass] 已被移除 - 以此作为判断条件
* @return [Boolean]
*/
private val isQQNTVersion get() = isQQ && AboutActivityClass.hasClass().not()
/** 当前宿主的版本 */
private var appVersionName = "<unknown>"
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()
/**
* 这个类 QQ 的 BaseChatPie 是控制聊天界面的
@@ -106,118 +140,146 @@ object QQTIMHooker : YukiBaseHooker() {
* - ❗Hook 错了方法会造成闪退!
*/
private fun hookQQBaseChatPie() {
if (isQQ) when (appVersionName) {
if (isQQ) when (hostVersionName) {
"8.0.0" -> {
hookBaseChatPie(methodName = "bq")
hookBaseChatPie(methodName = "aL")
hookBaseChatPie("bq")
hookBaseChatPie("aL")
}
"8.0.5", "8.0.7" -> {
hookBaseChatPie(methodName = "bw")
hookBaseChatPie(methodName = "aQ")
hookBaseChatPie("bw")
hookBaseChatPie("aQ")
}
"8.1.0", "8.1.3" -> {
hookBaseChatPie(methodName = "bE")
hookBaseChatPie(methodName = "aT")
hookBaseChatPie("bE")
hookBaseChatPie("aT")
}
"8.1.5" -> {
hookBaseChatPie(methodName = "bF")
hookBaseChatPie(methodName = "aT")
hookBaseChatPie("bF")
hookBaseChatPie("aT")
}
"8.1.8", "8.2.0", "8.2.6" -> {
hookBaseChatPie(methodName = "bC")
hookBaseChatPie(methodName = "aT")
hookBaseChatPie("bC")
hookBaseChatPie("aT")
}
"8.2.7", "8.2.8", "8.2.11", "8.3.0" -> {
hookBaseChatPie(methodName = "bE")
hookBaseChatPie(methodName = "aV")
hookBaseChatPie("bE")
hookBaseChatPie("aV")
}
"8.3.5" -> {
hookBaseChatPie(methodName = "bR")
hookBaseChatPie(methodName = "aX")
hookBaseChatPie("bR")
hookBaseChatPie("aX")
}
"8.3.6" -> {
hookBaseChatPie(methodName = "cp")
hookBaseChatPie(methodName = "aX")
hookBaseChatPie("cp")
hookBaseChatPie("aX")
}
"8.3.9" -> {
hookBaseChatPie(methodName = "cj")
hookBaseChatPie(methodName = "aT")
hookBaseChatPie("cj")
hookBaseChatPie("aT")
}
"8.4.1", "8.4.5" -> {
hookBaseChatPie(methodName = "ck")
hookBaseChatPie(methodName = "aT")
hookBaseChatPie("ck")
hookBaseChatPie("aT")
}
"8.4.8", "8.4.10", "8.4.17", "8.4.18", "8.5.0" -> {
hookBaseChatPie(methodName = "remainScreenOn")
hookBaseChatPie(methodName = "cancelRemainScreenOn")
hookBaseChatPie("remainScreenOn")
hookBaseChatPie("cancelRemainScreenOn")
}
"8.5.5" -> {
hookBaseChatPie(methodName = "bT")
hookBaseChatPie(methodName = "aN")
hookBaseChatPie("bT")
hookBaseChatPie("aN")
}
"8.6.0", "8.6.5", "8.7.0", "8.7.5", "8.7.8", "8.8.0", "8.8.3", "8.8.5" -> {
hookBaseChatPie(methodName = "ag")
hookBaseChatPie(methodName = "ah")
hookBaseChatPie("ag")
hookBaseChatPie("ah")
}
"8.8.11", "8.8.12" -> {
hookBaseChatPie(methodName = "bc")
hookBaseChatPie(methodName = "bd")
hookBaseChatPie("bc")
hookBaseChatPie("bd")
}
"8.8.17", "8.8.20" -> {
hookBaseChatPie(methodName = "bd")
hookBaseChatPie(methodName = "be")
hookBaseChatPie("bd")
hookBaseChatPie("be")
}
"8.8.23", "8.8.28" -> {
hookBaseChatPie(methodName = "bf")
hookBaseChatPie(methodName = "bg")
hookBaseChatPie("bf")
hookBaseChatPie("bg")
}
"8.8.33" -> {
hookBaseChatPie(methodName = "bg")
hookBaseChatPie(methodName = "bh")
hookBaseChatPie("bg")
hookBaseChatPie("bh")
}
"8.8.35", "8.8.38" -> {
hookBaseChatPie(methodName = "bi")
hookBaseChatPie(methodName = "bj")
hookBaseChatPie("bi")
hookBaseChatPie("bj")
}
"8.8.50" -> {
hookBaseChatPie(methodName = "bj")
hookBaseChatPie(methodName = "bk")
hookBaseChatPie("bj")
hookBaseChatPie("bk")
}
"8.8.55", "8.8.68", "8.8.80" -> {
hookBaseChatPie(methodName = "bk")
hookBaseChatPie(methodName = "bl")
hookBaseChatPie("bk")
hookBaseChatPie("bl")
}
"8.8.83", "8.8.85", "8.8.88", "8.8.90" -> {
hookBaseChatPie(methodName = "bl")
hookBaseChatPie(methodName = "bm")
hookBaseChatPie("bl")
hookBaseChatPie("bm")
}
"8.8.93", "8.8.95" -> {
hookBaseChatPie(methodName = "J3")
hookBaseChatPie(methodName = "S")
hookBaseChatPie("J3")
hookBaseChatPie("S")
}
"8.8.98" -> {
hookBaseChatPie(methodName = "M3")
hookBaseChatPie(methodName = "S")
hookBaseChatPie("M3")
hookBaseChatPie("S")
}
"8.9.0", "8.9.1", "8.9.2" -> {
hookBaseChatPie(methodName = "N3")
hookBaseChatPie(methodName = "S")
hookBaseChatPie("N3")
hookBaseChatPie("S")
}
"8.9.3", "8.9.5" -> {
hookBaseChatPie(methodName = "H3")
hookBaseChatPie(methodName = "P")
hookBaseChatPie("H3")
hookBaseChatPie("P")
}
"8.9.8", "8.9.10" -> {
hookBaseChatPie(methodName = "H3")
hookBaseChatPie(methodName = "N")
hookBaseChatPie("H3")
hookBaseChatPie("N")
}
"8.9.13" -> {
hookBaseChatPie(methodName = "y3")
hookBaseChatPie(methodName = "H")
hookBaseChatPie("y3")
hookBaseChatPie("H")
}
"8.9.15", "8.9.18", "8.9.19", "8.9.20" -> {
hookBaseChatPie("w3")
hookBaseChatPie("H")
}
"8.9.23", "8.9.25" -> {
hookBaseChatPie("z3")
hookBaseChatPie("H")
}
"8.9.28", "8.9.30", "8.9.33" -> {
hookBaseChatPie("A3")
hookBaseChatPie("H")
}
"8.9.35", "8.9.38", "8.9.50" -> {
hookBaseChatPie("B3")
hookBaseChatPie("H")
}
"8.9.53", "8.9.55", "8.9.58" -> {
hookBaseChatPie("C3")
hookBaseChatPie("H")
}
"8.9.63", "8.9.68" -> {
hookBaseChatPie("t3")
hookBaseChatPie("J")
}
"8.9.70" -> {
hookBaseChatPie("u3")
hookBaseChatPie("J")
}
else -> {
HookEntry.isHookClientSupport = false
loggerD(msg = "$appVersionName not supported!")
loggerW(msg = "$hostVersionName not supported!")
}
}
}
@@ -308,7 +370,7 @@ object QQTIMHooker : YukiBaseHooker() {
injectMember {
method {
name = "a"
param(StringType, LongType)
param(StringClass, LongType)
returnType = UnitType
}
intercept()
@@ -375,7 +437,7 @@ object QQTIMHooker : YukiBaseHooker() {
injectMember {
method {
name = "onHook"
param(StringType, AnyType, AnyArrayClass, AnyType)
param(StringClass, AnyClass, AnyArrayClass, AnyClass)
}
intercept()
}
@@ -478,14 +540,14 @@ object QQTIMHooker : YukiBaseHooker() {
injectMember {
method {
name = "onWriteLog"
param(StringType, StringType)
param(StringClass, StringClass)
}
intercept()
}
injectMember {
method {
name = "onCmdRequest"
param(StringType)
param(StringClass)
}
intercept()
}
@@ -506,15 +568,113 @@ object QQTIMHooker : YukiBaseHooker() {
}.ignoredHookClassNotFoundFailure()
}
/** Hook QQ 的设置界面添加模块设置入口 (新版) */
private fun hookQQSettingsUi() {
if (MainSettingFragmentClass.hasClass().not()) return loggerE(msg = "Could not found main setting class, hook aborted")
val kotlinUnit = "kotlin.Unit"
val kotlinFunction0 = "kotlin.jvm.functions.Function0"
val simpleItemProcessorClass = searchClass {
from("${PackageName.QQ}.setting.processor").absolute()
constructor { param(ContextClass, IntType, CharSequenceClass, IntType) }
method {
param(kotlinFunction0)
returnType = UnitType
}
field().count { it >= 6 }
}.get() ?: return loggerE(msg = "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 = simpleItemProcessorClass.method {
param { it[0].name == kotlinFunction0 }
paramCount = 1
returnType = UnitType
}.giveAll().lastOrNull() ?: 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.hook {
injectMember {
method {
param(ContextClass)
returnType = ListClass
}
afterHook {
val context = args().first().cast<Context>() ?: return@afterHook
val processor = result<MutableList<Any?>>() ?: return@afterHook
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.name == FormSimpleItemClass || it.name == FormCommonSingleLineItemClass }.index(num = 1)
}?.cast<View?>()
/** 创建一个新的 Item */
FormSimpleItemClass.toClassOrNull()?.buildOf<View>(instance?.compatToActivity()) { param(ContextClass) }?.current {
method {
name = "setLeftText"
param(CharSequenceClass)
}.call("TSBattery")
method {
name = "setRightText"
param(CharSequenceClass)
}.call("${BuildConfig.VERSION_NAME}(${BuildConfig.VERSION_CODE})")
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() {
onAppLifecycle {
onAppLifecycle(isOnFailureThrowToApp = false) {
attachBaseContext { baseContext, hasCalledSuper ->
if (hasCalledSuper.not()) baseConfiguration = baseContext.resources.configuration
}
onCreate {
appVersionName = versionName
hostVersionName = appVersionName
/** 不注入此进程防止部分系统发生 X5 浏览器内核崩溃问题 */
if (processName.startsWith(privilegedProcessName)) return@onCreate
ConfigData.init(context = this)
registerModuleAppActivities(AboutActivityClass)
if (isQQNTVersion)
registerModuleAppActivities(GeneralSettingActivityClass)
else registerModuleAppActivities(AboutActivityClass)
if (ConfigData.isDisableAllHook) return@onCreate
hookSystemWakeLock()
hookQQBaseChatPie()
@@ -535,48 +695,29 @@ object QQTIMHooker : YukiBaseHooker() {
afterHook { instance<Activity>().jumpToModuleSettings() }
}
}
/** 将条目注入设置界面 */
/** Hook 设置界面入口点 */
if (isQQNTVersion) hookQQSettingsUi()
else {
/** 将条目注入设置界面 (Activity) */
QQSettingSettingActivityClass.hook {
injectMember {
method {
name = "doOnCreate"
param(BundleClass)
}
afterHook {
/** 当前的顶级 Item 实例 */
val formItemRefRoot = field {
type(FormSimpleItemClass).index(num = 1)
}.ignored().get(instance).cast() ?: field {
type(FormCommonSingleLineItemClass).index(num = 1)
}.ignored().get(instance).cast<View?>()
/** 创建一个新的 Item */
FormSimpleItemClass.toClassOrNull()?.buildOf<View>(instance) { param(ContextClass) }?.current {
afterHook { hookQQSettingsUiLegacy(instance) }
}
}
/** 将条目注入设置界面 (Fragment) */
QQSettingSettingFragmentClass.hook {
injectMember {
method {
name = "setLeftText"
param(CharSequenceType)
}.call("TSBattery")
method {
name = "setRightText"
param(CharSequenceType)
}.call(BuildConfig.VERSION_NAME)
method {
name = "setBgType"
param(IntType)
}.call(if (isQQ) 0 else 2)
}?.apply { setOnClickListener { instance<Activity>().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) }
}
}
}
name = "doOnCreateView"
paramCount = 3
}
afterHook { hookQQSettingsUiLegacy(instance) }
}
}.ignoredHookClassNotFoundFailure()
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
@@ -22,8 +22,10 @@
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
@@ -35,16 +37,16 @@ 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.fankes.tsbattery.utils.factory.versionCode
import com.fankes.tsbattery.utils.factory.versionName
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.processName
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
import com.highcapable.yukihookapi.hook.log.loggerI
import com.highcapable.yukihookapi.hook.type.android.BundleClass
import com.highcapable.yukihookapi.hook.type.android.ViewClass
/**
* Hook 微信
@@ -65,6 +67,30 @@ object WeChatHooker : YukiBaseHooker() {
/** 微信存在的类 - 未测试每个版本是否都存在 */
private const val SettingsUIClass = "${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 {
@@ -73,7 +99,7 @@ object WeChatHooker : YukiBaseHooker() {
when {
EmptyActivityClass.hasClass() -> EmptyActivityClass
WelabMainUIClass.hasClass() -> WelabMainUIClass
else -> error("Inject WeChat Activity Proxy failed, unsupport version $versionName($versionCode)")
else -> error("Inject WeChat Activity Proxy failed, unsupport version $appVersionName($appVersionCode)")
}
)
if (ConfigData.isDisableAllHook) return@onCreate
@@ -97,32 +123,26 @@ object WeChatHooker : YukiBaseHooker() {
SettingsUIClass.hook {
injectMember {
method {
name = "onCreate"
param(BundleClass)
name = "onResume"
emptyParam()
}
afterHook {
method {
name = "get_fragment"
emptyParam()
superClass(isOnlySuperClass = true)
}.get(instance).call()?.current()
?.field { name = "mController" }
?.current()?.method { name = "getContentView" }
?.invoke<ViewGroup>()?.addView(LinearLayout(instance()).apply {
context.injectModuleAppResources()
gravity = Gravity.END or Gravity.BOTTOM
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() }
})
})
}.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))
}
}
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
@@ -47,13 +47,13 @@ private val ThemeUtilClass = VariousClass("${PackageName.QQ}.theme.ThemeUtil", "
* QQ、TIM 主题是否为夜间模式
* @return [Boolean]
*/
private fun Context.isQQNightMode() = runCatching {
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(suffix = "1103") || it.endsWith(suffix = "2920") }
).let { it.endsWith("1103") || it.endsWith("2920") }
}.getOrNull() ?: false
/** 启动模块设置 [Activity] */

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
@@ -53,7 +53,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
"8.8.20", "8.8.23", "8.8.28", "8.8.33", "8.8.35", "8.8.38", "8.8.50",
"8.8.55", "8.8.68", "8.8.80", "8.8.83", "8.8.85", "8.8.88", "8.8.90",
"8.8.93", "8.8.95", "8.8.98", "8.9.0", "8.9.1", "8.9.2", "8.9.3",
"8.9.5", "8.9.8", "8.9.10", "8.9.13"
"8.9.5", "8.9.8", "8.9.10", "8.9.13", "8.9.15", "8.9.18", "8.9.19",
"8.9.20", "8.9.23", "8.9.25", "8.9.28", "8.9.30", "8.9.33",
"8.9.35", "8.9.38", "8.9.50", "8.9.53", "8.9.55", "8.9.58",
"8.9.63", "8.9.68", "8.9.70"
)
private val qqSupportVersion by lazy {
if (qqSupportVersions.isNotEmpty()) {
@@ -62,8 +65,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
"${value.trim().let { it.substring(0, it.lastIndex) }}\n\n其余版本请自行测试是否有效。"
} else "empty"
}
private const val timSupportVersion = "2+、3+ (并未完全测试每个版本)"
private const val wechatSupportVersion = "全版本仅支持基础省电,更多功能依然画饼"
private const val timSupportVersion = "2+、3+ (并未完全测试每个版本)"
private const val wechatSupportVersion = "全版本仅支持基础省电,更多功能依然画饼"
/** 预发布的版本标识 */
private const val pendingFlag = ""
@@ -81,7 +84,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
/** 判断 Hook 状态 */
if (YukiHookAPI.Status.isModuleActive) {
binding.mainLinStatus.setBackgroundResource(R.drawable.bg_green_round)
binding.mainImgStatus.setImageResource(R.mipmap.ic_success)
binding.mainImgStatus.setImageResource(R.drawable.ic_success)
binding.mainTextStatus.text = "模块已激活"
binding.mainTextApiWay.isVisible = true
refreshActivateExecutor()
@@ -95,9 +98,9 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
noCancelable()
}
/** 设置安装状态 */
binding.mainTextQqVer.text = if (PackageName.QQ.isInstall) version(PackageName.QQ) else "未安装"
binding.mainTextTimVer.text = if (PackageName.TIM.isInstall) version(PackageName.TIM) else "未安装"
binding.mainTextWechatVer.text = if (PackageName.WECHAT.isInstall) version(PackageName.WECHAT) else "未安装"
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 = "模块版本:${BuildConfig.VERSION_NAME} $pendingFlag"
binding.mainQqItem.setOnClickListener {
@@ -127,12 +130,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
/** 振动提醒 */
it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
}
/** 获取 Sp 存储的信息 */
binding.hideIconInLauncherSwitch.isChecked = isLauncherIconShowing.not()
binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
if (btn.isPressed.not()) return@setOnCheckedChangeListener
hideOrShowLauncherIcon(b)
}
/** 快捷操作 QQ */
binding.quickQqButton.setOnClickListener { startModuleSettings(PackageName.QQ) }
/** 快捷操作 TIM */
@@ -145,6 +142,12 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
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)
}
}
/**
@@ -152,7 +155,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
* @param packageName 包名
*/
private fun startModuleSettings(packageName: String) {
if (packageName.isInstall) runCatching {
if (isInstall(packageName)) runCatching {
startActivity(Intent().apply {
component = ComponentName(
packageName,
@@ -166,12 +169,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
/** 刷新模块激活使用的方式 */
private fun refreshActivateExecutor() {
when {
YukiHookAPI.Status.executorVersion > 0 ->
binding.mainTextApiWay.text =
"Activated by ${YukiHookAPI.Status.executorName} API ${YukiHookAPI.Status.executorVersion}"
YukiHookAPI.Status.isTaiChiModuleActive -> binding.mainTextApiWay.text = "Activated by TaiChi"
else -> binding.mainTextApiWay.text = "Activated by anonymous"
}
if (YukiHookAPI.Status.Executor.apiLevel > 0)
binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name} API ${YukiHookAPI.Status.Executor.apiLevel}"
else binding.mainTextApiWay.text = "Activated by ${YukiHookAPI.Status.Executor.name}"
}
}

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
@@ -40,6 +40,7 @@ import com.fankes.tsbattery.ui.activity.MainActivity
import com.fankes.tsbattery.ui.activity.base.BaseActivity
import com.fankes.tsbattery.utils.factory.*
import com.fankes.tsbattery.utils.tool.GithubReleaseTool
import com.highcapable.yukihookapi.YukiHookAPI
import kotlin.system.exitProcess
class ConfigActivity : BaseActivity<ActivityConfigBinding>() {
@@ -70,20 +71,37 @@ class ConfigActivity : BaseActivity<ActivityConfigBinding>() {
}
}
binding.titleNameText.text = "TSBattery 设置 (${appName.trim()})"
binding.appIcon.setImageDrawable(findAppIcon())
binding.appIcon.setImageDrawable(appIconOf())
binding.appName.text = appName.trim()
binding.appVersion.text = "${versionName}($versionCode)"
binding.appVersion.text = "${appVersionName}($appVersionCode)"
binding.moduleVersion.text = "${BuildConfig.VERSION_NAME}(${BuildConfig.VERSION_CODE})"
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 -> "已启用完全模式"
packageName == PackageName.WECHAT -> "基础省电模式"
ConfigData.isEnableQQTimProtectMode -> "保守模式"
else -> "完全模式"
}
}
refreshCurrentModeText()
@@ -94,29 +112,20 @@ class ConfigActivity : BaseActivity<ActivityConfigBinding>() {
refreshConfigItems()
binding.infoTipText.replaceToAppName()
binding.qqTimProtectTipText.replaceToAppName()
binding.disableAllHookSwitch.bind(ConfigData.DISABLE_ALL_HOOK) { refreshConfigItems(); refreshCurrentModeText(); showRestartDialog() }
binding.qqTimProtectModeSwitch.bind(ConfigData.ENABLE_QQ_TIM_PROTECT_MODE) { refreshCurrentModeText(); showRestartDialog() }
binding.qqTimCoreServiceSwitch.bind(ConfigData.ENABLE_KILL_QQ_TIM_CORESERVICE) { showRestartDialog() }
binding.qqTimCoreServiceChildSwitch.bind(ConfigData.ENABLE_KILLE_QQ_TIM_CORESERVICE_CHILD) { showRestartDialog() }
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() }
}
/** 显示重新启动对话框 */
private fun showRestartDialog() {
showDialog {
title = "需要重新启动"
msg = "你必须重新启动${appName}才能使当前更改生效,现在重新启动吗?"
confirmButton {
cancel()
finish()
exitProcess(status = 0)
}
cancelButton(text = "稍后再说")
}
/** 显示需要重新启动提示 */
private fun showNeedRestartTip() {
binding.needRestartTipText.isVisible = true
}
/** 替换占位符到当前 APP 名称 */
private fun TextView.replaceToAppName() {
text = text.toString().replace(oldValue = "{APP_NAME}", appName)
text = text.toString().replace("{APP_NAME}", appName)
}
/** 重新设置 DPI 防止 QQ、TIM 修改它 */
@@ -134,5 +143,5 @@ class ConfigActivity : BaseActivity<ActivityConfigBinding>() {
* 获取当前 APP 名称
* @return [String]
*/
private val appName by lazy { findAppName().let { if (packageName == PackageName.WECHAT) it else " $it " } }
private val appName by lazy { appNameOf().let { if (packageName == PackageName.WECHAT) it else " $it " } }
}

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* 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
@@ -21,7 +21,7 @@
*/
@file:Suppress("SameParameterValue")
package com.fankes.tsbattery.ui.view
package com.fankes.tsbattery.ui.widget
import android.content.Context
import android.content.res.ColorStateList

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
@@ -29,11 +29,11 @@ import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.ProgressBar
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.annotation.CauseProblemsApi
import com.highcapable.yukihookapi.hook.factory.applyModuleTheme
@@ -89,7 +89,10 @@ class DialogBuilder(val context: Context) {
customLayoutView = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER or Gravity.START
addView(ProgressBar(context))
addView(CircularProgressIndicator(context).apply {
isIndeterminate = true
trackCornerRadius = 10.dp(context)
})
addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(20.dp(context), 5) })
addView(TextView(context).apply {
tag = "progressContent"

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
@@ -19,7 +19,7 @@
*
* This file is Created by fankes on 2022/1/7.
*/
@file:Suppress("DEPRECATION", "unused", "DiscouragedApi", "InternalInsetResource")
@file:Suppress("unused", "DiscouragedApi", "InternalInsetResource")
package com.fankes.tsbattery.utils.factory
@@ -29,14 +29,17 @@ 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.BuildConfig
import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.Companion.appContext
@@ -66,76 +69,75 @@ val Context.isSystemInDarkMode get() = (resources.configuration.uiMode and Confi
inline val Context.isNotSystemInDarkMode get() = !isSystemInDarkMode
/**
* 得到安装包信息
* @return [PackageInfo]
* 得到 APP 安装包信息 (兼容)
* @param packageName APP 包名
* @param flag [PackageInfoFlags]
* @return [PackageInfo] or null
*/
val Context.packageInfo get() = packageManager?.getPackageInfo(packageName, 0) ?: PackageInfo()
private fun Context.getPackageInfoCompat(packageName: String, flag: Number = 0) = runCatching {
@Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= 33)
packageManager?.getPackageInfo(packageName, PackageInfoFlags.of(flag.toLong()))
else packageManager?.getPackageInfo(packageName, flag.toInt())
}.getOrNull()
/**
* 判断应用是否安装
* @return [Boolean]
*/
val String.isInstall
get() = try {
appContext.packageManager.getPackageInfo(
this,
PackageManager.GET_UNINSTALLED_PACKAGES
)
true
} catch (e: Exception) {
false
}
/**
* 得到版本信息
* @return [String]
*/
val Context.versionName get() = packageInfo.versionName ?: ""
/**
* 得到版本号
* 得到 APP 版本号 (兼容 [PackageInfo.getLongVersionCode])
* @return [Int]
*/
val Context.versionCode get() = packageInfo.versionCode
private val PackageInfo.versionCodeCompat get() = PackageInfoCompat.getLongVersionCode(this)
/**
* 得到版本信息与版本号
* @param packageName 包名
* 判断 APP 是否安装
* @param packageName APP 包名
* @return [Boolean]
*/
fun Context.isInstall(packageName: String) = getPackageInfoCompat(packageName)?.let { true } ?: false
/**
* 得到 APP 版本信息
* @return [String]
*/
fun Context.version(packageName: String) = safeOfNothing {
packageManager?.getPackageInfo(packageName, 0)?.let {
"${it.versionName}(${it.versionCode})"
} ?: ""
}
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 name APP 包名 - 默认为当前 APP
* @param packageName APP 包名 - 默认为当前 APP
* @return [String]
*/
fun Context.findAppName(name: String = packageName) =
safeOfNothing { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadLabel(packageManager).toString() }
fun Context.appNameOf(packageName: String = getPackageName()) =
getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: ""
/**
* 得到 APP 图标
* @param name APP 包名 - 默认为当前 APP
* @param packageName APP 包名 - 默认为当前 APP
* @return [Drawable] or null
*/
fun Context.findAppIcon(name: String = packageName) =
safeOfNull { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadIcon(packageManager) }
fun Context.appIconOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.applicationInfo?.loadIcon(packageManager)
/**
* 网络连接是否正常
* @return [Boolean] 网络是否连接
*/
val isNetWorkSuccess get() = appContext.isNetWorkSuccess
/**
* 网络连接是否正常
* @return [Boolean] 网络是否连接
*/
val Context.isNetWorkSuccess get() = safeOfFalse { getSystemService<ConnectivityManager>()?.activeNetworkInfo != null }
val Context.isNetWorkSuccess
get() = safeOfFalse {
@Suppress("DEPRECATION")
getSystemService<ConnectivityManager>()?.activeNetworkInfo != null
}
/**
* dp 转换为 pxInt
@@ -190,7 +192,7 @@ fun Context.snake(msg: String, actionText: String = "", callback: () -> Unit = {
* @param packageName 包名
*/
fun Context.openSelfSetting(packageName: String = appContext.packageName) = runCatching {
if (packageName.isInstall)
if (isInstall(packageName))
startActivity(Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
@@ -36,7 +36,7 @@ import java.io.Serializable
import java.util.*
/**
* 获取 Github Release 最新版本工具类
* 获取 GitHub Release 最新版本工具类
*/
object GithubReleaseTool {
@@ -103,7 +103,7 @@ object GithubReleaseTool {
(context as? Activity?)?.runOnUiThread {
context.showDialog {
title = "网络不可用"
msg = "模块的联网权限可能已被禁用,请开启联网权限以定期检查更新。"
msg = "应用的联网权限可能已被禁用,请开启联网权限以定期检查更新。"
confirmButton(text = "去开启") { context.openSelfSetting() }
cancelButton()
noCancelable()
@@ -121,16 +121,16 @@ object GithubReleaseTool {
* 格式化时间为本地时区
* @return [String] 本地时区时间
*/
private fun String.localTime() = replace(oldValue = "T", newValue = " ").replace(oldValue = "Z", newValue = "").let {
private fun String.localTime() = replace("T", " ").replace("Z", "").let {
runCatching {
val local = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT).apply { timeZone = Calendar.getInstance().timeZone }
val current = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("GMT") }
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
* GitHub Release bean
* @param name 版本名称
* @param htmlUrl 网页地址
* @param content 更新日志

View File

@@ -1,6 +1,6 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
@@ -26,7 +26,7 @@ import com.fankes.tsbattery.BuildConfig
import com.fankes.tsbattery.utils.factory.openBrowser
import com.fankes.tsbattery.utils.factory.showDialog
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.factory.prefs
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
/**
@@ -42,12 +42,12 @@ object YukiPromoteTool {
* @param context 实例
*/
fun promote(context: Context) {
fun saveReaded() = context.modulePrefs.put(YUKI_PROMOTE_READED, value = true)
if (context.modulePrefs.get(YUKI_PROMOTE_READED).not())
fun saveReaded() = context.prefs().edit { put(YUKI_PROMOTE_READED, value = true) }
if (context.prefs().get(YUKI_PROMOTE_READED).not())
context.showDialog {
title = "面向开发者的推广"
msg = "你想快速拥有一个自己的 Xposed 模块吗,你只需要拥有基础的 Android 开发经验以及使用 Kotlin 编程语言即可。\n\n" +
"快来体验 YukiHookAPI这是一个使用 Kotlin 重新构建的高效 Xposed Hook API助你的开发变得更轻松。"
"快来体验 YukiHookAPI这是一个使用 Kotlin 构建的高效 Hook API 与 Xposed 模块解决方案,助你的开发变得更轻松。"
confirmButton(text = "去看看") {
context.openBrowser(url = "https://github.com/fankes/YukiHookAPI")
saveReaded()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,8 @@
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group android:scaleX="0.03609375"
<group
android:scaleX="0.03609375"
android:scaleY="0.03609375"
android:translateX="35.52"
android:translateY="35.52">

View File

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

View File

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

View File

@@ -26,7 +26,7 @@
android:layout_height="20dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
android:src="@mipmap/ic_back"
android:src="@drawable/ic_back"
android:tint="@color/colorTextGray"
android:tooltipText="返回" />
@@ -83,6 +83,21 @@
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"
@@ -178,7 +193,7 @@
android:id="@+id/active_mode_icon"
android:layout_width="13dp"
android:layout_height="13dp"
android:src="@mipmap/ic_success"
android:src="@drawable/ic_success"
android:tint="#FF26A69A"
android:visibility="gone" />
@@ -186,7 +201,7 @@
android:id="@+id/inactive_mode_icon"
android:layout_width="13dp"
android:layout_height="13dp"
android:src="@mipmap/ic_error"
android:src="@drawable/ic_error"
android:tint="#FF7043"
android:visibility="gone" />
</androidx.cardview.widget.CardView>
@@ -204,7 +219,7 @@
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:text="检测到未适配的版本"
android:text="未适配"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
@@ -227,6 +242,26 @@
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>
@@ -266,7 +301,7 @@
android:paddingRight="15dp"
android:paddingBottom="15dp">
<com.fankes.tsbattery.ui.view.MaterialSwitch
<com.fankes.tsbattery.ui.widget.MaterialSwitch
android:id="@+id/disable_all_hook_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
@@ -306,7 +341,7 @@
android:paddingTop="10dp"
android:paddingRight="15dp">
<com.fankes.tsbattery.ui.view.MaterialSwitch
<com.fankes.tsbattery.ui.widget.MaterialSwitch
android:id="@+id/qq_tim_protect_mode_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
@@ -340,7 +375,7 @@
android:paddingRight="15dp"
android:paddingBottom="15dp">
<com.fankes.tsbattery.ui.view.MaterialSwitch
<com.fankes.tsbattery.ui.widget.MaterialSwitch
android:id="@+id/qq_tim_core_service_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
@@ -355,11 +390,11 @@
android:layout_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="关闭后可能会影响消息接收与视频通话,但是会达到省电效果,如果你的系统拥有推送服务(HMS)或(GMS)可以尝试关闭。"
android:text="关闭后可能会影响消息接收与视频通话,但是会达到省电效果,如果你的系统拥有推送服务 (HMS) 或 (MIPUSH) 可以尝试关闭。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<com.fankes.tsbattery.ui.view.MaterialSwitch
<com.fankes.tsbattery.ui.widget.MaterialSwitch
android:id="@+id/qq_tim_core_service_child_switch"
android:layout_width="match_parent"
android:layout_height="35dp"

View File

@@ -36,7 +36,7 @@
android:layout_height="27dp"
android:layout_marginEnd="5dp"
android:alpha="0.85"
android:src="@mipmap/ic_github"
android:src="@drawable/ic_github"
android:tint="@color/colorTextGray"
android:tooltipText="项目地址" />
</LinearLayout>
@@ -59,7 +59,7 @@
android:layout_height="25dp"
android:layout_marginStart="25dp"
android:layout_marginEnd="5dp"
android:src="@mipmap/ic_warn"
android:src="@drawable/ic_warn"
android:tint="@color/white" />
<LinearLayout
@@ -250,7 +250,7 @@
android:layout_height="15dp"
android:layout_marginEnd="15dp"
android:alpha="0.85"
android:src="@mipmap/ic_about"
android:src="@drawable/ic_about"
android:tint="@color/colorTextDark" />
<TextView
@@ -282,11 +282,21 @@
android:layout_marginBottom="10dp"
android:gravity="center|start">
<ImageView
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_shot_icon" />
app:cardBackgroundColor="#FF00BCD4"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2.5dp"
android:src="@drawable/ic_fast_op" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
@@ -364,11 +374,21 @@
android:layout_height="wrap_content"
android:gravity="center|start">
<ImageView
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_home" />
app:cardBackgroundColor="#FFFF9800"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2.5dp"
android:src="@drawable/ic_home" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
@@ -380,7 +400,7 @@
android:textSize="12sp" />
</LinearLayout>
<com.fankes.tsbattery.ui.view.MaterialSwitch
<com.fankes.tsbattery.ui.widget.MaterialSwitch
android:id="@+id/hide_icon_in_launcher_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -428,11 +448,21 @@
android:layout_marginBottom="15dp"
android:gravity="center|start">
<ImageView
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_help" />
app:cardBackgroundColor="#FF03A9F4"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2dp"
android:src="@drawable/ic_info" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
@@ -470,7 +500,7 @@
android:layout_marginBottom="10dp"
android:alpha="0.8"
android:lineSpacingExtra="10dp"
android:text="Q.如何使用?\nA.目前模块支持 LSPosed、LSPatch 以及太极(无极)框架,在太极和 LSPosed 的作用域中,只需勾选 QQ、TIM、微信即可模块可以做到即插即用激活后无需重启手机重启 QQ、TIM 或微信就可以了。"
android:text="Q.如何使用?\nA.目前模块支持 LSPosed、LSPatch 以及太极和一些常见的免 Root 框架,在太极和 LSPosed 的作用域中,只需勾选 QQ、TIM、微信即可模块可以做到即插即用激活后无需重启手机重启 QQ、TIM 或微信就可以了。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,24 @@
plugins {
id 'com.android.application' version '7.3.0' apply false
id 'com.android.library' version '7.3.0' apply false
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
id 'com.android.application' version '7.4.1' apply false
id 'com.android.library' version '7.4.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.20' apply false
id 'com.google.devtools.ksp' version '1.8.20-1.0.10' apply false
}
ext {
appVersionName = "4.1"
appVersionCode = 26
enableR8 = true
android = [
compileSdk: 33,
minSdk : 21,
targetSdk : 33
]
app = [
versionName : '4.3',
versionCode : 29,
signingConfigs: [
secretConfigsDirPath : "${projectDir.getAbsolutePath()}/.secret",
secretConfigsFileName: "key_store_secret.json"
]
]
}
task clean(type: Delete) {

View File

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