37 Commits
3.8 ... 3.99

Author SHA1 Message Date
81331dfe81 Update version to 3.99 | Support QQ 8.9.0 and 8.9.1 2022-07-19 22:11:34 +08:00
20f1ceca4d Support QQ 8.9.0 and 8.9.1 2022-07-19 22:02:04 +08:00
fc8589f1ee Fix hook BaseChatPie failure bug 2022-07-19 21:57:06 +08:00
Fankesyooni
67378b2ebf Merge pull request #19 from StarWishsama/patch-1
适配 QQ 8.9.0
2022-07-17 01:04:11 +08:00
NoRainCity
bf4ecd8345 Update QQ 8.9.0 2022-07-16 20:53:33 +08:00
6e8d900dc5 Update version to 3.98 | Support QQ 8.8.98 2022-07-05 22:49:18 +08:00
3d12d3f4a5 Added QQ version support situation 2022-07-05 22:45:17 +08:00
4b36437dac Modify BugHook luckily 2022-07-05 22:04:04 +08:00
93789f163b Support QQ 8.8.98 2022-07-05 22:01:49 +08:00
32d8937ddb Update version to 3.97 | Support QQ 8.8.95 2022-06-16 02:25:01 +08:00
38c9d78f1e Update Gradle & Kotlin & PlatformSDK
- Update Kotlin version to 1.7.0
- Update Gradle dependencies
- Merge legacy code
2022-06-10 17:13:58 +08:00
0ee03340da Merge hook function 2022-06-10 17:10:51 +08:00
54ee2358cf Make UI to Primary Theme 2022-06-08 15:07:23 +08:00
55708eb96a Merge DialogBuilderFactory with new code style 2022-06-07 16:55:29 +08:00
c4de8ae448 Update version to 3.96 | Support QQ 8.8.93 2022-06-04 03:20:38 +08:00
9e092742da Fix GithubReleaseTool to LocalTime 2022-06-04 03:04:54 +08:00
fb27c107f4 Added BuildConfig.VERSION_NAME changed 2022-06-03 23:54:21 +08:00
2be9a3d934 Update version to 3.95 2022-05-31 02:06:35 +08:00
d7dbecb65c Update YukiHookAPI 2022-05-31 02:02:26 +08:00
6c2b3f12b3 Merge code 2022-05-30 02:11:30 +08:00
f1c520586a Merge code 2022-05-30 02:03:14 +08:00
e9c99343c3 Merge code 2022-05-30 01:43:47 +08:00
20de713ffd Merge systemBar support with native 2022-05-30 00:47:47 +08:00
3b78e8a515 Repair Licenses 2022-05-30 00:16:53 +08:00
cf898092bd Update version to 3.9 | Support QQ 8.8.90 2022-05-30 00:10:35 +08:00
251a5151df Added YukiPromoteTool 2022-05-30 00:03:57 +08:00
59e93bc040 Support QQ 8.8.90 2022-05-29 22:51:25 +08:00
d440e85be4 Update YukiHookAPI 2022-05-29 04:12:54 +08:00
e1dc99de01 Update YukiHookAPI 2022-05-27 03:34:16 +08:00
4eab1f7046 Update YukiHookAPI 2022-05-25 04:36:25 +08:00
3fc294dd8d Update YukiHookAPI 2022-05-10 01:52:07 +08:00
a2c06cc201 Update YukiHookAPI 2022-05-06 15:01:27 +08:00
6f39d5fdab Update YukiHookAPI 2022-05-04 14:01:41 +08:00
5ada7585e3 Update YukiHookAPI 2022-05-04 10:14:29 +08:00
31b8f157e5 Update YukiHookAPI 2022-05-04 09:31:13 +08:00
e71f137f74 Update Kotlin version 2022-05-04 07:01:04 +08:00
9e9bbcc8ca Update YukiHookAPI 2022-05-04 07:00:17 +08:00
16 changed files with 221 additions and 144 deletions

View File

@@ -2,7 +2,7 @@
[![Blank](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/fankes/TSBattery) [![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/license-AGPL3.0-blue)](https://github.com/fankes/TSBattery/blob/master/LICENSE)
[![Blank](https://img.shields.io/badge/version-v3.8-green)](https://github.com/fankes/TSBattery/releases) [![Blank](https://img.shields.io/badge/version-v3.99-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/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) [![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) [![Telegram](https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram)](https://t.me/XiaofangInternet)

View File

@@ -1,7 +1,7 @@
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'kotlin-android' id 'kotlin-android'
id 'com.google.devtools.ksp' version '1.6.20-1.0.5' id 'com.google.devtools.ksp' version '1.7.0-1.0.6'
} }
android { android {
@@ -15,8 +15,7 @@ android {
v2SigningEnabled true v2SigningEnabled true
} }
} }
compileSdkVersion 31 compileSdkVersion 32
buildToolsVersion "31.0.0"
defaultConfig { defaultConfig {
applicationId "com.fankes.tsbattery" applicationId "com.fankes.tsbattery"
@@ -31,6 +30,8 @@ android {
buildTypes { buildTypes {
release { release {
minifyEnabled rootProject.ext.enableR8 minifyEnabled rootProject.ext.enableR8
shrinkResources rootProject.ext.enableR8
zipAlignEnabled rootProject.ext.enableR8
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
@@ -50,34 +51,20 @@ android {
buildFeatures { buildFeatures {
viewBinding true viewBinding true
} }
} lintOptions {
checkReleaseBuilds false
/** 移除无效耗时 lint Task */ }
tasks.whenTaskAdded {
task -> if (task.name == "lintVitalRelease") task.enabled = false
}
/** 移除无效耗时 lint Task */
tasks.whenTaskAdded {
task -> if (task.name == "lintVitalAnalyzeRelease") task.enabled = false
}
/** 移除无效耗时 lint Task */
tasks.whenTaskAdded {
task -> if (task.name == "lintVitalReportRelease") task.enabled = false
} }
dependencies { dependencies {
compileOnly 'de.robv.android.xposed:api:82' compileOnly 'de.robv.android.xposed:api:82'
implementation 'com.highcapable.yukihookapi:api:1.0.80' implementation 'com.highcapable.yukihookapi:api:1.0.92'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.80' ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.92'
implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.7'
implementation 'com.geyifeng.immersionbar:immersionbar:3.2.0' implementation 'androidx.core:core-ktx:1.8.0'
implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.0' implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'androidx.core:core-ktx:1.7.0' implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

View File

@@ -54,7 +54,7 @@ import com.highcapable.yukihookapi.hook.type.android.*
import com.highcapable.yukihookapi.hook.type.java.* import com.highcapable.yukihookapi.hook.type.java.*
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
@InjectYukiHookWithXposed @InjectYukiHookWithXposed(isUsingResourcesHook = false)
class HookEntry : IYukiHookXposedInit { class HookEntry : IYukiHookXposedInit {
companion object { companion object {
@@ -84,6 +84,9 @@ class HookEntry : IYukiHookXposedInit {
) )
} }
/** 是否完全支持当前版本 */
private var isHookClientSupport = true
/** /**
* 这个类 QQ 的 BaseChatPie 是控制聊天界面的 * 这个类 QQ 的 BaseChatPie 是控制聊天界面的
* *
@@ -130,11 +133,27 @@ class HookEntry : IYukiHookXposedInit {
interceptBaseChatPie(methodName = "bk") interceptBaseChatPie(methodName = "bk")
interceptBaseChatPie(methodName = "bl") interceptBaseChatPie(methodName = "bl")
} }
"8.8.83", "8.8.85", "8.8.88" -> { "8.8.83", "8.8.85", "8.8.88", "8.8.90" -> {
interceptBaseChatPie(methodName = "bl") interceptBaseChatPie(methodName = "bl")
interceptBaseChatPie(methodName = "bm") interceptBaseChatPie(methodName = "bm")
} }
else -> loggerD(msg = "$version not supported!") "8.8.93", "8.8.95" -> {
interceptBaseChatPie(methodName = "J3")
interceptBaseChatPie(methodName = "S")
}
"8.8.98" -> {
interceptBaseChatPie(methodName = "M3")
interceptBaseChatPie(methodName = "S")
}
/** 8.9.0 贡献者StarWishsama */
"8.9.0", "8.9.1" -> {
interceptBaseChatPie(methodName = "N3")
interceptBaseChatPie(methodName = "S")
}
else -> {
isHookClientSupport = false
loggerD(msg = "$version not supported!")
}
} }
} }
@@ -147,6 +166,7 @@ class HookEntry : IYukiHookXposedInit {
injectMember { injectMember {
method { method {
name = methodName name = methodName
emptyParam()
returnType = UnitType returnType = UnitType
} }
intercept() intercept()
@@ -159,7 +179,7 @@ class HookEntry : IYukiHookXposedInit {
injectMember { injectMember {
method { method {
name = "acquireLocked" name = "acquireLocked"
returnType = UnitType emptyParam()
} }
intercept() intercept()
} }
@@ -187,11 +207,12 @@ class HookEntry : IYukiHookXposedInit {
/** /**
* 提示模块运行信息 QQ、TIM、微信 * 提示模块运行信息 QQ、TIM、微信
* @param isQQTIM 是否为 QQ、TIM * @param isQQ 是否为 QQ
* @param isTIM 是否为 TIM
*/ */
private fun PackageParam.hookModuleRunningInfo(isQQTIM: Boolean) = private fun PackageParam.hookModuleRunningInfo(isQQ: Boolean = false, isTIM: Boolean = false) =
when { when {
isQQTIM -> SplashActivityClass.hook { isQQ || isTIM -> SplashActivityClass.hook {
/** /**
* Hook 启动界面的第一个 [Activity] * Hook 启动界面的第一个 [Activity]
* QQ 和 TIM 都是一样的类 * QQ 和 TIM 都是一样的类
@@ -208,7 +229,9 @@ class HookEntry : IYukiHookXposedInit {
showDialog { showDialog {
title = "TSBattery 已激活" title = "TSBattery 已激活"
msg = "[提示模块运行信息功能已打开]\n\n" + msg = "[提示模块运行信息功能已打开]\n\n" +
"模块工作看起来一切正常,请自行测试是否能达到省电效果。\n\n" + (if (isQQ && isHookClientSupport.not())
"❎ 当前版本 $versionName($versionCode) 不在兼容列表,请自行测试是否生效~\n\n"
else "✅ 模块工作看起来一切正常,请自行测试是否能达到省电效果。\n\n") +
"已生效模块版本:${prefs.get(DataConst.ENABLE_MODULE_VERSION)}\n" + "已生效模块版本:${prefs.get(DataConst.ENABLE_MODULE_VERSION)}\n" +
"当前模式:${if (prefs.get(DataConst.ENABLE_QQTIM_WHITE_MODE)) "保守模式" else "完全模式"}" + "当前模式:${if (prefs.get(DataConst.ENABLE_QQTIM_WHITE_MODE)) "保守模式" else "完全模式"}" +
"\n\n包名:${packageName}\n版本:$versionName($versionCode)" + "\n\n包名:${packageName}\n版本:$versionName($versionCode)" +
@@ -347,7 +370,7 @@ class HookEntry : IYukiHookXposedInit {
method { method {
name = "setRightText" name = "setRightText"
param(CharSequenceType) param(CharSequenceType)
}.call(prefs.get(DataConst.ENABLE_MODULE_VERSION)) }.call("${if (isQQ && isHookClientSupport.not()) "❎" else "✅"} ${prefs.get(DataConst.ENABLE_MODULE_VERSION)}")
method { method {
name = "setBgType" name = "setBgType"
param(IntType) param(IntType)
@@ -357,7 +380,9 @@ class HookEntry : IYukiHookXposedInit {
instance<Activity>().apply { instance<Activity>().apply {
showDialog { showDialog {
title = "TSBattery 守护中" title = "TSBattery 守护中"
msg = "已生效模块版本:${prefs.get(DataConst.ENABLE_MODULE_VERSION)}\n" + msg = (if (isQQ && isHookClientSupport.not())
"❎ 当前版本 $versionName($versionCode) 不在兼容列表,请自行测试是否生效~\n\n" else "") +
"已生效模块版本:${prefs.get(DataConst.ENABLE_MODULE_VERSION)}\n" +
"当前模式:${if (prefs.get(DataConst.ENABLE_QQTIM_WHITE_MODE)) "保守模式" else "完全模式"}" + "当前模式:${if (prefs.get(DataConst.ENABLE_QQTIM_WHITE_MODE)) "保守模式" else "完全模式"}" +
"\n\n包名:${packageName}\n版本:$versionName($versionCode)" + "\n\n包名:${packageName}\n版本:$versionName($versionCode)" +
"\n\n模块只对挂后台锁屏情况下有省电效果," + "\n\n模块只对挂后台锁屏情况下有省电效果," +
@@ -400,6 +425,7 @@ class HookEntry : IYukiHookXposedInit {
debugTag = "TSBattery" debugTag = "TSBattery"
isDebug = false isDebug = false
isEnableModulePrefsCache = false isEnableModulePrefsCache = false
isEnableDataChannel = false
} }
override fun onHook() = encase { override fun onHook() = encase {
@@ -407,19 +433,11 @@ class HookEntry : IYukiHookXposedInit {
hookSystemWakeLock() hookSystemWakeLock()
hookNotification() hookNotification()
hookCoreService(isQQ = true) hookCoreService(isQQ = true)
hookModuleRunningInfo(isQQTIM = true) hookModuleRunningInfo(isQQ = true)
hookQQSettingsSettingActivity(isQQ = true) hookQQSettingsSettingActivity(isQQ = true)
if (prefs.get(DataConst.ENABLE_QQTIM_WHITE_MODE)) return@loadApp if (prefs.get(DataConst.ENABLE_QQTIM_WHITE_MODE)) return@loadApp
/** 通过在 [SplashActivityClass] 里取到应用的版本号 */ /** 通过在生命周期里取到应用的版本号 */
SplashActivityClass.hook { onAppLifecycle { onCreate { hookQQBaseChatPie(versionName) } }
injectMember {
method {
name = "doOnCreate"
param(BundleClass)
}
afterHook { hookQQBaseChatPie(instance<Activity>().versionName) }
}
}
/** /**
* 干掉消息收发功能的电源锁 * 干掉消息收发功能的电源锁
* 每个版本的差异暂未做排查 * 每个版本的差异暂未做排查
@@ -628,14 +646,14 @@ class HookEntry : IYukiHookXposedInit {
hookSystemWakeLock() hookSystemWakeLock()
hookNotification() hookNotification()
hookCoreService(isQQ = false) hookCoreService(isQQ = false)
hookModuleRunningInfo(isQQTIM = true) hookModuleRunningInfo(isTIM = true)
hookQQSettingsSettingActivity(isQQ = false) hookQQSettingsSettingActivity(isQQ = false)
} }
loadApp(WECHAT_PACKAGE_NAME) { loadApp(WECHAT_PACKAGE_NAME) {
if (prefs.get(DataConst.DISABLE_WECHAT_HOOK)) return@loadApp if (prefs.get(DataConst.DISABLE_WECHAT_HOOK)) return@loadApp
hookSystemWakeLock() hookSystemWakeLock()
hookModuleRunningInfo(isQQTIM = false) hookModuleRunningInfo()
loggerD(msg = "ウイチャット:それが機能するかどうかはわかりませんでした") loggerD(msg = "ウイチャット:それが機能するかどうかはわかりませんでした")
} }
} }
} }

View File

@@ -37,18 +37,29 @@ import com.fankes.tsbattery.hook.HookConst.WECHAT_PACKAGE_NAME
import com.fankes.tsbattery.ui.activity.base.BaseActivity import com.fankes.tsbattery.ui.activity.base.BaseActivity
import com.fankes.tsbattery.utils.factory.* import com.fankes.tsbattery.utils.factory.*
import com.fankes.tsbattery.utils.tool.GithubReleaseTool import com.fankes.tsbattery.utils.tool.GithubReleaseTool
import com.highcapable.yukihookapi.hook.factory.isModuleActive import com.fankes.tsbattery.utils.tool.YukiPromoteTool
import com.highcapable.yukihookapi.hook.factory.isTaiChiModuleActive import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.factory.modulePrefs import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
class MainActivity : BaseActivity<ActivityMainBinding>() { class MainActivity : BaseActivity<ActivityMainBinding>() {
companion object { companion object {
private const val moduleVersion = BuildConfig.VERSION_NAME private const val moduleVersion = BuildConfig.VERSION_NAME
private const val qqSupportVersion = private val qqSupportVersions = arrayOf(
"8.2.11(Play)、8.8.17、8.8.23、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.2.11、8.5.5~8.8.88)" "8.2.11(Play)", "8.8.17", "8.8.23",
"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"
)
private val qqSupportVersion by lazy {
if (qqSupportVersions.isNotEmpty()) {
var value = ""
qqSupportVersions.forEach { value += "$it" }
"${value.trim().let { it.substring(0, it.lastIndex) }}\n\n其余版本请自行测试是否有效。"
} else "empty"
}
private const val timSupportVersion = "2+、3+ (并未完全测试每个版本)" private const val timSupportVersion = "2+、3+ (并未完全测试每个版本)"
private const val wechatSupportVersion = "全版本仅支持基础省电,更多功能依然画饼" private const val wechatSupportVersion = "全版本仅支持基础省电,更多功能依然画饼"
@@ -66,7 +77,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
} }
} }
/** 判断 Hook 状态 */ /** 判断 Hook 状态 */
if (isModuleActive) { if (YukiHookAPI.Status.isModuleActive) {
binding.mainLinStatus.setBackgroundResource(R.drawable.bg_green_round) binding.mainLinStatus.setBackgroundResource(R.drawable.bg_green_round)
binding.mainImgStatus.setImageResource(R.mipmap.ic_success) binding.mainImgStatus.setImageResource(R.mipmap.ic_success)
binding.mainTextStatus.text = "模块已激活" binding.mainTextStatus.text = "模块已激活"
@@ -74,6 +85,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
refreshActivateExecutor() refreshActivateExecutor()
/** 写入激活的模块版本 */ /** 写入激活的模块版本 */
modulePrefs.put(DataConst.ENABLE_MODULE_VERSION, moduleVersion) modulePrefs.put(DataConst.ENABLE_MODULE_VERSION, moduleVersion)
/** 推广、恰饭 */
YukiPromoteTool.promote(context = this)
} else } else
showDialog { showDialog {
title = "模块没有激活" title = "模块没有激活"
@@ -89,14 +102,14 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
noCancelable() noCancelable()
} }
/** 推荐使用 LSPosed */ /** 推荐使用 LSPosed */
if (isTaiChiModuleActive) if (YukiHookAPI.Status.isTaiChiModuleActive)
showDialog { showDialog {
title = "兼容性提示" title = "兼容性提示"
msg = "若你的设备已 Root推荐使用 LSPosed 激活模块,太极可能会出现模块设置无法保存的问题。" msg = "若你的设备已 Root推荐使用 LSPosed 激活模块,太极可能会出现模块设置无法保存的问题。"
confirmButton(text = "我知道了") confirmButton(text = "我知道了")
} }
/** 检测应用转生 */ /** 检测应用转生 - 如果模块已激活就不再检测 */
if (("com.bug.xposed").isInstall) if (("com.bug.xposed").isInstall && YukiHookAPI.Status.isModuleActive.not())
showDialog { showDialog {
title = "环境异常" title = "环境异常"
msg = "检测到“应用转生”已被安装,为了保证模块的安全和稳定,请卸载更换其他 Hook 框架后才能继续使用。" msg = "检测到“应用转生”已被安装,为了保证模块的安全和稳定,请卸载更换其他 Hook 框架后才能继续使用。"
@@ -201,10 +214,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
/** 刷新模块激活使用的方式 */ /** 刷新模块激活使用的方式 */
private fun refreshActivateExecutor() { private fun refreshActivateExecutor() {
when { when {
YukiHookModuleStatus.executorVersion > 0 -> YukiHookAPI.Status.executorVersion > 0 ->
binding.mainTextApiWay.text = binding.mainTextApiWay.text =
"Activated by ${YukiHookModuleStatus.executorName} API ${YukiHookModuleStatus.executorVersion}" "Activated by ${YukiHookAPI.Status.executorName} API ${YukiHookAPI.Status.executorVersion}"
isTaiChiModuleActive -> binding.mainTextApiWay.text = "Activated by TaiChi" YukiHookAPI.Status.isTaiChiModuleActive -> binding.mainTextApiWay.text = "Activated by TaiChi"
else -> binding.mainTextApiWay.text = "Activated by anonymous" else -> binding.mainTextApiWay.text = "Activated by anonymous"
} }
} }

View File

@@ -23,12 +23,14 @@
package com.fankes.tsbattery.ui.activity.base package com.fankes.tsbattery.ui.activity.base
import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.fankes.tsbattery.R import com.fankes.tsbattery.R
import com.fankes.tsbattery.utils.factory.isNotSystemInDarkMode import com.fankes.tsbattery.utils.factory.isNotSystemInDarkMode
import com.gyf.immersionbar.ktx.immersionBar
import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
import java.lang.reflect.ParameterizedType import java.lang.reflect.ParameterizedType
@@ -52,13 +54,14 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
/** 隐藏系统的标题栏 */ /** 隐藏系统的标题栏 */
supportActionBar?.hide() supportActionBar?.hide()
/** 初始化沉浸状态栏 */ /** 初始化沉浸状态栏 */
immersionBar { WindowCompat.getInsetsController(window, window.decorView).apply {
statusBarColor(R.color.colorThemeBackground) isAppearanceLightStatusBars = isNotSystemInDarkMode
autoDarkModeEnable(true) isAppearanceLightNavigationBars = isNotSystemInDarkMode
statusBarDarkFont(isNotSystemInDarkMode) }
navigationBarColor(R.color.colorThemeBackground) ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
navigationBarDarkIcon(isNotSystemInDarkMode) window?.statusBarColor = it
fitsSystemWindows(true) window?.navigationBarColor = it
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) window?.navigationBarDividerColor = it
} }
/** 装载子类 */ /** 装载子类 */
onCreate() onCreate()

View File

@@ -19,7 +19,7 @@
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */
@file:Suppress("DEPRECATION", "CanvasSize") @file:Suppress("DEPRECATION", "CanvasSize", "OVERRIDE_DEPRECATION")
package com.fankes.tsbattery.utils.drawable.drawabletoolbox package com.fankes.tsbattery.utils.drawable.drawabletoolbox

View File

@@ -28,26 +28,21 @@ import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.view.Gravity import android.view.Gravity
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.highcapable.yukihookapi.annotation.CauseProblemsApi
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
/** /**
* 构造对话框 * 构造对话框
* @param isUseBlackTheme 是否使用深色主题 * @param isUseBlackTheme 是否使用深色主题
* @param it 对话框方法体 * @param initiate 对话框方法体
*/ */
fun Context.showDialog(isUseBlackTheme: Boolean = false, it: DialogBuilder.() -> Unit) = fun Context.showDialog(isUseBlackTheme: Boolean = false, initiate: DialogBuilder.() -> Unit) =
DialogBuilder(this, isUseBlackTheme).apply(it).show() DialogBuilder(context = this, isUseBlackTheme).apply(initiate).show()
/** /**
* 对话框构造器 * 对话框构造器
@@ -60,9 +55,7 @@ class DialogBuilder(val context: Context, private val isUseBlackTheme: Boolean)
private var instanceAndroid: android.app.AlertDialog.Builder? = null // 实例对象 private var instanceAndroid: android.app.AlertDialog.Builder? = null // 实例对象
private var dialogInstance: Dialog? = null // 对话框实例 private var dialogInstance: Dialog? = null // 对话框实例
private var customLayoutView: View? = null // 自定义布局
@CauseProblemsApi
var customLayoutView: View? = null // 自定义布局
/** /**
* 是否需要使用 AndroidX 风格对话框 * 是否需要使用 AndroidX 风格对话框
@@ -125,49 +118,37 @@ class DialogBuilder(val context: Context, private val isUseBlackTheme: Boolean)
else customLayoutView?.findViewWithTag<TextView>("progressContent")?.text = value else customLayoutView?.findViewWithTag<TextView>("progressContent")?.text = value
} }
/**
* 设置对话框自定义布局
* @return [ViewBinding]
*/
inline fun <reified T : ViewBinding> bind() =
T::class.java.method {
name = "inflate"
param(LayoutInflaterClass)
}.get().invoke<T>(LayoutInflater.from(context))?.apply {
customLayoutView = root
} ?: error("binding failed")
/** /**
* 设置对话框确定按钮 * 设置对话框确定按钮
* @param text 按钮文本内容 * @param text 按钮文本内容
* @param it 点击事件 * @param callback 点击事件
*/ */
fun confirmButton(text: String = "确定", it: () -> Unit = {}) { fun confirmButton(text: String = "确定", callback: () -> Unit = {}) {
if (isUsingAndroidX) if (isUsingAndroidX)
runInSafe { instanceAndroidX?.setPositiveButton(text) { _, _ -> it() } } runInSafe { instanceAndroidX?.setPositiveButton(text) { _, _ -> callback() } }
else runInSafe { instanceAndroid?.setPositiveButton(text) { _, _ -> it() } } else runInSafe { instanceAndroid?.setPositiveButton(text) { _, _ -> callback() } }
} }
/** /**
* 设置对话框取消按钮 * 设置对话框取消按钮
* @param text 按钮文本内容 * @param text 按钮文本内容
* @param it 点击事件 * @param callback 点击事件
*/ */
fun cancelButton(text: String = "取消", it: () -> Unit = {}) { fun cancelButton(text: String = "取消", callback: () -> Unit = {}) {
if (isUsingAndroidX) if (isUsingAndroidX)
runInSafe { instanceAndroidX?.setNegativeButton(text) { _, _ -> it() } } runInSafe { instanceAndroidX?.setNegativeButton(text) { _, _ -> callback() } }
else runInSafe { instanceAndroid?.setNegativeButton(text) { _, _ -> it() } } else runInSafe { instanceAndroid?.setNegativeButton(text) { _, _ -> callback() } }
} }
/** /**
* 设置对话框第三个按钮 * 设置对话框第三个按钮
* @param text 按钮文本内容 * @param text 按钮文本内容
* @param it 点击事件 * @param callback 点击事件
*/ */
fun neutralButton(text: String = "更多", it: () -> Unit = {}) { fun neutralButton(text: String = "更多", callback: () -> Unit = {}) {
if (isUsingAndroidX) if (isUsingAndroidX)
runInSafe { instanceAndroidX?.setNeutralButton(text) { _, _ -> it() } } runInSafe { instanceAndroidX?.setNeutralButton(text) { _, _ -> callback() } }
else runInSafe { instanceAndroid?.setNeutralButton(text) { _, _ -> it() } } else runInSafe { instanceAndroid?.setNeutralButton(text) { _, _ -> callback() } }
} }
/** 取消对话框 */ /** 取消对话框 */

View File

@@ -137,13 +137,13 @@ fun toast(msg: String) = Toast.makeText(appContext, msg, Toast.LENGTH_SHORT).sho
* 弹出 [Snackbar] * 弹出 [Snackbar]
* @param msg 提示内容 * @param msg 提示内容
* @param actionText 按钮文本 - 不写默认取消按钮 * @param actionText 按钮文本 - 不写默认取消按钮
* @param it 按钮事件回调 * @param callback 按钮事件回调
*/ */
fun Context.snake(msg: String, actionText: String = "", it: () -> Unit = {}) = fun Context.snake(msg: String, actionText: String = "", callback: () -> Unit = {}) =
Snackbar.make((this as Activity).findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG).apply { Snackbar.make((this as Activity).findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG).apply {
if (actionText.isBlank()) return@apply if (actionText.isBlank()) return@apply
setActionTextColor(if (isSystemInDarkMode) Color.BLACK else Color.WHITE) setActionTextColor(if (isSystemInDarkMode) Color.BLACK else Color.WHITE)
setAction(actionText) { it() } setAction(actionText) { callback() }
}.show() }.show()
/** /**

View File

@@ -19,15 +19,21 @@
* *
* This file is Created by fankes on 2022/3/20. * This file is Created by fankes on 2022/3/20.
*/ */
@file:Suppress("NewApi")
package com.fankes.tsbattery.utils.tool package com.fankes.tsbattery.utils.tool
import android.app.Activity import android.app.Activity
import android.content.Context 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.* import com.fankes.tsbattery.utils.factory.*
import okhttp3.* import okhttp3.*
import org.json.JSONObject import org.json.JSONObject
import java.io.IOException import java.io.IOException
import java.io.Serializable import java.io.Serializable
import java.util.*
/** /**
* 获取 Github Release 最新版本工具类 * 获取 Github Release 最新版本工具类
@@ -44,9 +50,9 @@ object GithubReleaseTool {
* 获取最新版本信息 * 获取最新版本信息
* @param context 实例 * @param context 实例
* @param version 当前版本 * @param version 当前版本
* @param it 成功后回调 - ([String] 最新版本,[Function] 更新对话框方法体) * @param result 成功后回调 - ([String] 最新版本,[Function] 更新对话框方法体)
*/ */
fun checkingForUpdate(context: Context, version: String, it: (String, () -> Unit) -> Unit) = checkingInternetConnect(context) { fun checkingForUpdate(context: Context, version: String, result: (String, () -> Unit) -> Unit) = checkingInternetConnect(context) {
OkHttpClient().newBuilder().build().newCall( OkHttpClient().newBuilder().build().newCall(
Request.Builder() Request.Builder()
.url("https://api.github.com/repos/$REPO_AUTHOR/$REPO_NAME/releases/latest") .url("https://api.github.com/repos/$REPO_AUTHOR/$REPO_NAME/releases/latest")
@@ -56,12 +62,12 @@ object GithubReleaseTool {
override fun onFailure(call: Call, e: IOException) {} override fun onFailure(call: Call, e: IOException) {}
override fun onResponse(call: Call, response: Response) = runInSafe { override fun onResponse(call: Call, response: Response) = runInSafe {
JSONObject(response.body?.string() ?: "").apply { JSONObject(response.body.string()).apply {
GithubReleaseBean( GithubReleaseBean(
name = getString("name"), name = getString("name"),
htmlUrl = getString("html_url"), htmlUrl = getString("html_url"),
content = getString("body"), content = getString("body"),
date = getString("published_at").replace(oldValue = "T", newValue = " ").replace(oldValue = "Z", newValue = "") date = getString("published_at").localTime()
).apply { ).apply {
fun showUpdate() = context.showDialog { fun showUpdate() = context.showDialog {
title = "最新版本 $name" title = "最新版本 $name"
@@ -72,7 +78,7 @@ object GithubReleaseTool {
} }
if (name != version) (context as? Activity?)?.runOnUiThread { if (name != version) (context as? Activity?)?.runOnUiThread {
showUpdate() showUpdate()
it(name) { showUpdate() } result(name) { showUpdate() }
} }
} }
} }
@@ -83,9 +89,9 @@ object GithubReleaseTool {
/** /**
* 检查网络连接情况 * 检查网络连接情况
* @param context 实例 * @param context 实例
* @param it 已连接回调 * @param callback 已连接回调
*/ */
private fun checkingInternetConnect(context: Context, it: () -> Unit) = runInSafe { private fun checkingInternetConnect(context: Context, callback: () -> Unit) = runInSafe {
if (isNetWorkSuccess) if (isNetWorkSuccess)
OkHttpClient().newBuilder().build().newCall( OkHttpClient().newBuilder().build().newCall(
Request.Builder() Request.Builder()
@@ -106,11 +112,23 @@ object GithubReleaseTool {
} }
override fun onResponse(call: Call, response: Response) = runInSafe { override fun onResponse(call: Call, response: Response) = runInSafe {
(context as? Activity?)?.runOnUiThread { runInSafe { it() } } (context as? Activity?)?.runOnUiThread { runInSafe { callback() } }
} }
}) })
} }
/**
* 格式化时间为本地时区
* @return [String] 本地时区时间
*/
private fun String.localTime() = replace(oldValue = "T", newValue = " ").replace(oldValue = "Z", newValue = "").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") }
local.format(current.parse(it))
}.getOrNull() ?: it
}
/** /**
* Github Release bean * Github Release bean
* @param name 版本名称 * @param name 版本名称

View File

@@ -0,0 +1,59 @@
/*
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/TSBattery
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2022/5/30.
*/
package com.fankes.tsbattery.utils.tool
import android.content.Context
import com.fankes.tsbattery.BuildConfig
import com.fankes.tsbattery.utils.factory.openBrowser
import com.fankes.tsbattery.utils.factory.showDialog
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
/**
* [YukiHookAPI] 的自动推广工具类
*/
object YukiPromoteTool {
/** 推广已读存储键值 */
private val YUKI_PROMOTE_READED = PrefsData("yuki_promote_readed_${BuildConfig.VERSION_NAME}", false)
/**
* 显示推广对话框
* @param context 实例
*/
fun promote(context: Context) {
fun saveReaded() = context.modulePrefs.put(YUKI_PROMOTE_READED, value = true)
if (context.modulePrefs.get(YUKI_PROMOTE_READED).not())
context.showDialog {
title = "面向开发者的推广"
msg = "你想快速拥有一个自己的 Xposed 模块吗,你只需要拥有基础的 Android 开发经验以及使用 Kotlin 编程语言即可。\n\n" +
"快来体验 YukiHookAPI这是一个使用 Kotlin 重新构建的高效 Xposed Hook API助你的开发变得更轻松。"
confirmButton(text = "去看看") {
context.openBrowser(url = "https://github.com/fankes/YukiHookAPI")
saveReaded()
}
cancelButton(text = "我不是开发者") { saveReaded() }
noCancelable()
}
}
}

View File

@@ -2,13 +2,13 @@
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.TSBattery" parent="Theme.Material3.DayNight"> <style name="Theme.TSBattery" parent="Theme.Material3.DayNight">
<!-- Primary brand color. --> <!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item> <item name="colorPrimary">@color/colorPrimaryAccent</item>
<item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorPrimaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnPrimary">@color/black</item> <item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. --> <!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item> <item name="colorSecondary">@color/colorPrimaryAccent</item>
<item name="colorSecondaryVariant">@color/teal_200</item> <item name="colorSecondaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnSecondary">@color/black</item> <item name="colorOnSecondary">@color/colorPrimaryAccent</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->

View File

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

View File

@@ -2,13 +2,13 @@
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.TSBattery" parent="Theme.Material3.DayNight"> <style name="Theme.TSBattery" parent="Theme.Material3.DayNight">
<!-- Primary brand color. --> <!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item> <item name="colorPrimary">@color/colorPrimaryAccent</item>
<item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorPrimaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnPrimary">@color/teal_700</item> <item name="colorOnPrimary">@color/colorPrimaryAccent</item>
<!-- Secondary brand color. --> <!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item> <item name="colorSecondary">@color/colorPrimaryAccent</item>
<item name="colorSecondaryVariant">@color/teal_700</item> <item name="colorSecondaryVariant">@color/colorPrimaryAccent</item>
<item name="colorOnSecondary">@color/black</item> <item name="colorOnSecondary">@color/colorPrimaryAccent</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->

View File

@@ -1,12 +1,12 @@
plugins { plugins {
id 'com.android.application' version '7.1.3' apply false id 'com.android.application' version '7.2.1' apply false
id 'com.android.library' version '7.1.3' apply false id 'com.android.library' version '7.2.1' apply false
id 'org.jetbrains.kotlin.android' version '1.6.20' apply false id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
} }
ext { ext {
appVersionName = "3.8" appVersionName = "3.99"
appVersionCode = 16 appVersionCode = 22
enableR8 = true enableR8 = true
} }

View File

@@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html # http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process. # Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 org.gradle.jvmargs=-XX:+UseParallelGC
# When configured, Gradle will run in incubating parallel mode. # When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit # 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 # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
@@ -18,4 +18,6 @@ android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX # Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete": # Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official kotlin.code.style=official
# Incremental
kotlin.incremental.useClasspathSnapshot=true

View File

@@ -1,6 +1,6 @@
#Mon Feb 14 23:27:58 CST 2022 #Wed May 25 04:34:58 CST 2022
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME