From 99b736ef36f0464a4c51c1982ea6075fe53df8e0 Mon Sep 17 00:00:00 2001 From: Fankesyooni Date: Thu, 24 Mar 2022 15:21:41 +0800 Subject: [PATCH] Merge code --- .../com/fankes/tsbattery/hook/HookEntry.kt | 83 +++--- .../tsbattery/ui/activity/MainActivity.kt | 80 +++--- .../utils/factory/FunctionFactory.kt | 11 + app/src/main/res/layout/activity_main.xml | 237 +++++++----------- .../main/res/mipmap-xxhdpi/ic_tim_icon.png | Bin 3394 -> 5295 bytes 5 files changed, 195 insertions(+), 216 deletions(-) diff --git a/app/src/main/java/com/fankes/tsbattery/hook/HookEntry.kt b/app/src/main/java/com/fankes/tsbattery/hook/HookEntry.kt index e92fca7..18b0442 100644 --- a/app/src/main/java/com/fankes/tsbattery/hook/HookEntry.kt +++ b/app/src/main/java/com/fankes/tsbattery/hook/HookEntry.kt @@ -183,20 +183,19 @@ class HookEntry : YukiHookXposedInitProxy { */ private fun PackageParam.hookModuleRunningInfo(isQQTIM: Boolean) = when { - !prefs.getBoolean(ENABLE_RUN_INFO) -> {} - isQQTIM -> - SplashActivityClass.hook { - /** - * Hook 启动界面的第一个 [Activity] - * QQ 和 TIM 都是一样的类 - * 在里面加入提示运行信息的对话框测试模块是否激活 - */ - injectMember { - method { - name = "doOnCreate" - param(BundleClass) - } - afterHook { + isQQTIM -> SplashActivityClass.hook { + /** + * Hook 启动界面的第一个 [Activity] + * QQ 和 TIM 都是一样的类 + * 在里面加入提示运行信息的对话框测试模块是否激活 + */ + injectMember { + method { + name = "doOnCreate" + param(BundleClass) + } + afterHook { + if (prefs.getBoolean(ENABLE_RUN_INFO)) instance().apply { showDialog { title = "TSBattery 已激活" @@ -215,21 +214,21 @@ class HookEntry : YukiHookXposedInitProxy { noCancelable() } } - } } } - else -> - LauncherUIClass.hook { - /** - * Hook 启动界面的第一个 [Activity] - * 在里面加入提示运行信息的对话框测试模块是否激活 - */ - injectMember { - method { - name = "onCreate" - param(BundleClass) - } - afterHook { + } + else -> LauncherUIClass.hook { + /** + * Hook 启动界面的第一个 [Activity] + * 在里面加入提示运行信息的对话框测试模块是否激活 + */ + injectMember { + method { + name = "onCreate" + param(BundleClass) + } + afterHook { + if (prefs.getBoolean(ENABLE_RUN_INFO)) instance().apply { showDialog(isUseBlackTheme = true) { title = "TSBattery 已激活" @@ -247,9 +246,9 @@ class HookEntry : YukiHookXposedInitProxy { noCancelable() } } - } } } + } } /** @@ -281,23 +280,25 @@ class HookEntry : YukiHookXposedInitProxy { injectMember { method { name = "onCreate" } afterHook { - instance().apply { - stopForeground(true) - stopService(Intent(applicationContext, javaClass)) - loggerD(msg = "Shutdown CoreService OK!") - } + if (prefs.getBoolean(ENABLE_QQTIM_CORESERVICE_BAN)) + instance().apply { + stopForeground(true) + stopService(Intent(applicationContext, javaClass)) + loggerD(msg = "Shutdown CoreService OK!") + } } } - }.by { prefs.getBoolean(ENABLE_QQTIM_CORESERVICE_BAN) } + } CoreService_KernelServiceClass.hook { injectMember { method { name = "onCreate" } afterHook { - instance().apply { - stopForeground(true) - stopService(Intent(applicationContext, javaClass)) - loggerD(msg = "Shutdown CoreService\$KernelService OK!") - } + if (prefs.getBoolean(ENABLE_QQTIM_CORESERVICE_CHILD_BAN)) + instance().apply { + stopForeground(true) + stopService(Intent(applicationContext, javaClass)) + loggerD(msg = "Shutdown CoreService\$KernelService OK!") + } } } injectMember { @@ -307,7 +308,7 @@ class HookEntry : YukiHookXposedInitProxy { } replaceTo(any = 2) } - }.by { prefs.getBoolean(ENABLE_QQTIM_CORESERVICE_CHILD_BAN) } + } } override fun onInit() = configs { @@ -323,7 +324,7 @@ class HookEntry : YukiHookXposedInitProxy { hookCoreService(isQQ = true) hookModuleRunningInfo(isQQTIM = true) if (prefs.getBoolean(ENABLE_QQTIM_WHITE_MODE)) return@loadApp - /** 通过在 SplashActivity 里取到应用的版本号 */ + /** 通过在 [SplashActivityClass] 里取到应用的版本号 */ SplashActivityClass.hook { injectMember { method { diff --git a/app/src/main/java/com/fankes/tsbattery/ui/activity/MainActivity.kt b/app/src/main/java/com/fankes/tsbattery/ui/activity/MainActivity.kt index 5d475a8..89c6c8b 100644 --- a/app/src/main/java/com/fankes/tsbattery/ui/activity/MainActivity.kt +++ b/app/src/main/java/com/fankes/tsbattery/ui/activity/MainActivity.kt @@ -25,7 +25,7 @@ package com.fankes.tsbattery.ui.activity import android.content.ComponentName import android.content.pm.PackageManager -import androidx.core.view.isGone +import android.view.HapticFeedbackConstants import androidx.core.view.isVisible import com.fankes.tsbattery.BuildConfig import com.fankes.tsbattery.R @@ -42,14 +42,12 @@ import com.fankes.tsbattery.hook.HookConst.QQ_PACKAGE_NAME import com.fankes.tsbattery.hook.HookConst.TIM_PACKAGE_NAME import com.fankes.tsbattery.hook.HookConst.WECHAT_PACKAGE_NAME import com.fankes.tsbattery.ui.activity.base.BaseActivity -import com.fankes.tsbattery.utils.factory.isInstall -import com.fankes.tsbattery.utils.factory.openBrowser -import com.fankes.tsbattery.utils.factory.openSelfSetting -import com.fankes.tsbattery.utils.factory.showDialog +import com.fankes.tsbattery.utils.factory.* import com.fankes.tsbattery.utils.tool.GithubReleaseTool import com.highcapable.yukihookapi.hook.factory.isModuleActive import com.highcapable.yukihookapi.hook.factory.isTaiChiModuleActive import com.highcapable.yukihookapi.hook.factory.modulePrefs +import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus class MainActivity : BaseActivity() { @@ -79,6 +77,8 @@ class MainActivity : BaseActivity() { binding.mainLinStatus.setBackgroundResource(R.drawable.bg_green_round) binding.mainImgStatus.setImageResource(R.mipmap.ic_success) binding.mainTextStatus.text = "模块已激活" + binding.mainTextApiWay.isVisible = true + refreshActivateExecutor() /** 写入激活的模块版本 */ modulePrefs.putString(ENABLE_MODULE_VERSION, moduleVersion) } else @@ -89,9 +89,9 @@ class MainActivity : BaseActivity() { "请自行查看本页面使用帮助与说明第三条。\n" + "太极和第三方 Xposed 激活后" + "可能不会提示激活,若想验证是否激活请打开“提示模块运行信息”自行检查," + - "或观察 QQ、TIM 的常驻通知是否有“TSBattery 守护中”字样”," + - "如果生效就代表模块运行正常,这里的激活状态只是一个显示意义上的存在。\n" + - "太极(无极)在 MIUI 设备上会提示打开授权,请进行允许,然后再次打开本应用查看激活状态。" + "或观察 QQ、TIM 的常驻通知是否有“TSBattery 守护中”字样”。\n\n" + + "如果生效就代表模块运行正常,若你在未 Root 情况下激活模块,这里的激活状态只是一个显示意义上的存在。\n" + + "太极(无极)在 MIUI 设备上会提示打开授权,请进行允许,然后再次打开本模块查看激活状态。" confirmButton(text = "我知道了") noCancelable() } @@ -111,40 +111,37 @@ class MainActivity : BaseActivity() { noCancelable() } /** 设置安装状态 */ - binding.mainTextQqNoinstall.isGone = QQ_PACKAGE_NAME.isInstall - binding.mainTextTimNoinstall.isGone = TIM_PACKAGE_NAME.isInstall - binding.mainTextWechatNoinstall.isGone = WECHAT_PACKAGE_NAME.isInstall + binding.mainTextQqVer.text = if (QQ_PACKAGE_NAME.isInstall) version(QQ_PACKAGE_NAME) else "未安装" + binding.mainTextTimVer.text = if (TIM_PACKAGE_NAME.isInstall) version(TIM_PACKAGE_NAME) else "未安装" + binding.mainTextWechatVer.text = if (WECHAT_PACKAGE_NAME.isInstall) version(WECHAT_PACKAGE_NAME) else "未安装" /** 设置文本 */ binding.mainTextVersion.text = "模块版本:$moduleVersion $pendingFlag" - binding.mainTextSupportQq.apply { - text = qqSupportVersion - setOnClickListener { - showDialog { - title = "兼容的 QQ 版本" - msg = qqSupportVersion - confirmButton(text = "我知道了") - } + binding.mainQqItem.setOnClickListener { + showDialog { + title = "兼容的 QQ 版本" + msg = qqSupportVersion + confirmButton(text = "我知道了") } + /** 振动提醒 */ + it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) } - binding.mainTextSupportTim.apply { - text = timSupportVersion - setOnClickListener { - showDialog { - title = "兼容的 TIM 版本" - msg = timSupportVersion - confirmButton(text = "我知道了") - } + binding.mainTimItem.setOnClickListener { + showDialog { + title = "兼容的 TIM 版本" + msg = timSupportVersion + confirmButton(text = "我知道了") } + /** 振动提醒 */ + it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) } - binding.mainTextSupportWechat.apply { - text = wechatSupportVersion - setOnClickListener { - showDialog { - title = "兼容的微信版本" - msg = wechatSupportVersion - confirmButton(text = "我知道了") - } + binding.mainWechatItem.setOnClickListener { + showDialog { + title = "兼容的微信版本" + msg = wechatSupportVersion + confirmButton(text = "我知道了") } + /** 振动提醒 */ + it.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) } /** 获取 Sp 存储的信息 */ binding.qqtimProtectModeSwitch.isChecked = modulePrefs.getBoolean(ENABLE_QQTIM_WHITE_MODE) @@ -157,6 +154,7 @@ class MainActivity : BaseActivity() { binding.qqtimProtectModeSwitch.setOnCheckedChangeListener { btn, b -> if (!btn.isPressed) return@setOnCheckedChangeListener modulePrefs.putBoolean(ENABLE_QQTIM_WHITE_MODE, b) + snake(msg = "修改需要重启 QQ 以生效") } binding.qqTimCoreServiceSwitch.setOnCheckedChangeListener { btn, b -> if (!btn.isPressed) return@setOnCheckedChangeListener @@ -169,6 +167,7 @@ class MainActivity : BaseActivity() { binding.wechatDisableHookSwitch.setOnCheckedChangeListener { btn, b -> if (!btn.isPressed) return@setOnCheckedChangeListener modulePrefs.putBoolean(DISABLE_WECHAT_HOOK, b) + snake(msg = "修改需要重启微信以生效") } binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b -> if (!btn.isPressed) return@setOnCheckedChangeListener @@ -200,4 +199,15 @@ class MainActivity : BaseActivity() { openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market") } } + + /** 刷新模块激活使用的方式 */ + private fun refreshActivateExecutor() { + when { + YukiHookModuleStatus.executorVersion > 0 -> + binding.mainTextApiWay.text = + "Activated by ${YukiHookModuleStatus.executorName} API ${YukiHookModuleStatus.executorVersion}" + isTaiChiModuleActive -> binding.mainTextApiWay.text = "Activated by TaiChi" + else -> binding.mainTextApiWay.text = "Activated by anonymous" + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/fankes/tsbattery/utils/factory/FunctionFactory.kt b/app/src/main/java/com/fankes/tsbattery/utils/factory/FunctionFactory.kt index 6e935cd..43d0622 100644 --- a/app/src/main/java/com/fankes/tsbattery/utils/factory/FunctionFactory.kt +++ b/app/src/main/java/com/fankes/tsbattery/utils/factory/FunctionFactory.kt @@ -84,6 +84,17 @@ val Context.versionName get() = packageInfo.versionName ?: "" */ val Context.versionCode get() = packageInfo.versionCode +/** + * 得到版本信息与版本号 + * @param packageName 包名 + * @return [String] + */ +fun Context.version(packageName: String) = safeOfNothing { + packageManager?.getPackageInfo(packageName, 0)?.let { + "${it.versionName}(${it.versionCode})" + } ?: "" +} + /** * 网络连接是否正常 * @return [Boolean] 网络是否连接 diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 075bd07..56c91ea 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -111,154 +111,111 @@ android:visibility="gone" /> - + android:fadingEdgeLength="10dp" + android:fillViewport="true" + android:requiresFadingEdge="horizontal" + android:scrollbars="none"> - - - + android:gravity="center|start" + android:orientation="horizontal"> - + - - + - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + android:layout_marginTop="10dp" + android:alpha="0.6" + android:ellipsize="end" + android:singleLine="true" + android:text="%1" + android:textColor="@color/white" + android:textSize="11sp" + android:visibility="gone" /> @@ -300,7 +257,7 @@ android:layout_height="wrap_content" android:alpha="0.6" android:lineSpacingExtra="5dp" - android:text="上述列出的版本号为最佳兼容版本,你可以点击进行查看。\n没有标注的版本在适配范围内的 APP 适用性都将有效,但可能不能达到最佳使用效果。\n如果当前版本失效请看下方的联系方式。" + android:text="你可以点击上述每个图标查看最佳兼容的 APP 版本。\n没有标注的版本在适配范围内的 APP 适用性都将有效,但可能不能达到最佳使用效果,建议保持使用适配内的版本。" android:textColor="@color/colorTextGray" android:textSize="10sp" tools:ignore="SmallSp" /> diff --git a/app/src/main/res/mipmap-xxhdpi/ic_tim_icon.png b/app/src/main/res/mipmap-xxhdpi/ic_tim_icon.png index 2a282a8b1e02750036f738fb2c275c744920b364..6de2153bb479b4444b4707d028db0acf57f38ff6 100644 GIT binary patch delta 4281 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 D9{4F5