Modify merge YukiHookAPI new usage and compatible with API 33

This commit is contained in:
2022-10-06 03:19:09 +08:00
parent 6159308cbb
commit 97161f8692
8 changed files with 235 additions and 132 deletions

View File

@@ -36,7 +36,7 @@ import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
class HookEntry : IYukiHookXposedInit { class HookEntry : IYukiHookXposedInit {
override fun onInit() = configs { override fun onInit() = configs {
debugTag = "ColorOSNotify" debugLog { tag = "ColorOSNotify" }
isDebug = false isDebug = false
} }

View File

@@ -56,10 +56,7 @@ import com.fankes.coloros.notify.utils.tool.IconAdaptationTool
import com.fankes.coloros.notify.utils.tool.SystemUITool import com.fankes.coloros.notify.utils.tool.SystemUITool
import com.highcapable.yukihookapi.hook.bean.VariousClass import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.current import com.highcapable.yukihookapi.hook.factory.*
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.hasMethod
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.loggerD import com.highcapable.yukihookapi.hook.log.loggerD
import com.highcapable.yukihookapi.hook.log.loggerE import com.highcapable.yukihookapi.hook.log.loggerE
import com.highcapable.yukihookapi.hook.log.loggerW import com.highcapable.yukihookapi.hook.log.loggerW
@@ -234,12 +231,10 @@ object SystemUIHooker : YukiBaseHooker() {
* @return [Boolean] * @return [Boolean]
*/ */
private val isOldNotificationBackground private val isOldNotificationBackground
get() = safeOfFalse { get() = NotificationBackgroundViewClass.toClassOrNull()?.hasMethod {
NotificationBackgroundViewClass.clazz.hasMethod { name = "drawCustom"
name = "drawCustom" paramCount = 2
paramCount = 2 } ?: false
}
}
/** /**
* 打印日志 * 打印日志
@@ -257,7 +252,7 @@ object SystemUIHooker : YukiBaseHooker() {
isGrayscale: Boolean isGrayscale: Boolean
) { ) {
if (prefs.get(DataConst.ENABLE_MODULE_LOG)) loggerD( if (prefs.get(DataConst.ENABLE_MODULE_LOG)) loggerD(
msg = "$tag --> [${context.findAppName(packageName)}][$packageName] " + msg = "$tag --> [${context.appNameOf(packageName)}][$packageName] " +
"custom [$isCustom] " + "custom [$isCustom] " +
"grayscale [$isGrayscale]" "grayscale [$isGrayscale]"
) )
@@ -278,14 +273,14 @@ object SystemUIHooker : YukiBaseHooker() {
/** 刷新状态栏小图标 */ /** 刷新状态栏小图标 */
private fun refreshStatusBarIcons() = runInSafe { private fun refreshStatusBarIcons() = runInSafe {
val nfField = StatusBarIconViewClass.clazz.field { name = "mNotification" } val nfField = StatusBarIconViewClass.toClass().field { name = "mNotification" }
val sRadiusField = StatusBarIconViewClass.clazz.field { name = "sIconRadiusFraction" } val sRadiusField = StatusBarIconViewClass.toClass().field { name = "sIconRadiusFraction" }
val sNfSizeField = StatusBarIconViewClass.clazz.field { name = "sNotificationRoundIconSize" } val sNfSizeField = StatusBarIconViewClass.toClass().field { name = "sNotificationRoundIconSize" }
val roundUtil = RoundRectDrawableUtil_CompanionClass.clazz.method { val roundUtil = RoundRectDrawableUtil_CompanionClass.toClass().method {
name = "getRoundRectDrawable" name = "getRoundRectDrawable"
param(DrawableClass, FloatType, IntType, IntType, ContextClass) param(DrawableClass, FloatType, IntType, IntType, ContextClass)
}.onNoSuchMethod { loggerE(msg = "Your system not support \"getRoundRectDrawable\"!", e = it) } }.onNoSuchMethod { loggerE(msg = "Your system not support \"getRoundRectDrawable\"!", e = it) }
.get(RoundRectDrawableUtilClass.clazz.field { name = "Companion" }.get().self) .get(RoundRectDrawableUtilClass.toClass().field { name = "Companion" }.get().any())
/** 启动一个线程防止卡顿 */ /** 启动一个线程防止卡顿 */
Thread { Thread {
(notificationIconContainer?.children?.toList() ?: notificationIconInstances.takeIf { it.isNotEmpty() })?.forEach { (notificationIconContainer?.children?.toList() ?: notificationIconInstances.takeIf { it.isNotEmpty() })?.forEach {
@@ -295,6 +290,7 @@ object SystemUIHooker : YukiBaseHooker() {
/** 得到原始通知图标 */ /** 得到原始通知图标 */
val iconDrawable = nf.notification.smallIcon.loadDrawable(it.context) val iconDrawable = nf.notification.smallIcon.loadDrawable(it.context)
?: return@Thread loggerW(msg = "refreshStatusBarIcons got null smallIcon")
/** 获取优化后的状态栏通知图标 */ /** 获取优化后的状态栏通知图标 */
compatStatusIcon( compatStatusIcon(
context = it.context, context = it.context,
@@ -318,12 +314,10 @@ object SystemUIHooker : YukiBaseHooker() {
/** 刷新通知小图标 */ /** 刷新通知小图标 */
private fun refreshNotificationIcons() = runInSafe { private fun refreshNotificationIcons() = runInSafe {
notificationPresenter?.current { notificationPresenter?.current()?.method {
method { name = "updateNotificationsOnDensityOrFontScaleChanged"
name = "updateNotificationsOnDensityOrFontScaleChanged" emptyParam()
emptyParam() }?.call()
}.call()
}
modifyNotifyPanelAlpha(notificationPlayerView, isTint = true) modifyNotifyPanelAlpha(notificationPlayerView, isTint = true)
} }
@@ -336,7 +330,7 @@ object SystemUIHooker : YukiBaseHooker() {
* @return [Boolean] * @return [Boolean]
*/ */
private fun isGrayscaleIcon(context: Context?, drawable: Drawable?) = private fun isGrayscaleIcon(context: Context?, drawable: Drawable?) =
ContrastColorUtilClass.clazz.let { ContrastColorUtilClass.toClassOrNull()?.let {
drawable is VectorDrawable || it.method { drawable is VectorDrawable || it.method {
name = "isGrayscaleIcon" name = "isGrayscaleIcon"
param(DrawableClass) param(DrawableClass)
@@ -344,7 +338,7 @@ object SystemUIHooker : YukiBaseHooker() {
name = "getInstance" name = "getInstance"
param(ContextClass) param(ContextClass)
}.get().invoke(context)).boolean(drawable) }.get().invoke(context)).boolean(drawable)
} } ?: false
/** /**
* 适配通知栏、状态栏来自系统推送的彩色 APP 图标 * 适配通知栏、状态栏来自系统推送的彩色 APP 图标
@@ -431,7 +425,7 @@ object SystemUIHooker : YukiBaseHooker() {
prefs.get(DataConst.ENABLE_NOTIFY_ICON_FORCE_APP_ICON) && isEnableHookColorNotifyIcon(isHooking = false) -> prefs.get(DataConst.ENABLE_NOTIFY_ICON_FORCE_APP_ICON) && isEnableHookColorNotifyIcon(isHooking = false) ->
iconView.apply { iconView.apply {
/** 重新设置图标 */ /** 重新设置图标 */
setImageDrawable(appIcons[packageName] ?: context.findAppIcon(packageName)) setImageDrawable(appIcons[packageName] ?: context.appIconOf(packageName))
/** 设置默认样式 */ /** 设置默认样式 */
setDefaultNotifyIconViewStyle() setDefaultNotifyIconViewStyle()
} }
@@ -451,7 +445,7 @@ object SystemUIHooker : YukiBaseHooker() {
val newStyle = (if (context.isSystemInDarkMode) 0xffdcdcdc else Color.WHITE).toInt() val newStyle = (if (context.isSystemInDarkMode) 0xffdcdcdc else Color.WHITE).toInt()
/** 原生着色 */ /** 原生着色 */
val a12Style = if (isUpperOfAndroidS) context.wallpaperColor else val a12Style = if (isUpperOfAndroidS) context.systemAccentColor else
(if (context.isSystemInDarkMode) 0xff707173 else oldStyle).toInt() (if (context.isSystemInDarkMode) 0xff707173 else oldStyle).toInt()
/** 旧版图标着色 */ /** 旧版图标着色 */
@@ -538,8 +532,8 @@ object SystemUIHooker : YukiBaseHooker() {
colorFilter = null colorFilter = null
} }
/** 注册 */ /** 注册生命周期 */
private fun register() { private fun registerLifecycle() {
/** 解锁后重新刷新状态栏图标防止系统重新设置它 */ /** 解锁后重新刷新状态栏图标防止系统重新设置它 */
onAppLifecycle { registerReceiver(Intent.ACTION_USER_PRESENT) { _, _ -> if (isUsingCachingMethod) refreshStatusBarIcons() } } onAppLifecycle { registerReceiver(Intent.ACTION_USER_PRESENT) { _, _ -> if (isUsingCachingMethod) refreshStatusBarIcons() } }
/** 刷新图标缓存 */ /** 刷新图标缓存 */
@@ -577,8 +571,8 @@ object SystemUIHooker : YukiBaseHooker() {
} }
override fun onHook() { override fun onHook() {
/** 注册 */ /** 注册生命周期 */
register() registerLifecycle()
/** 缓存图标数据 */ /** 缓存图标数据 */
cachingIconDatas() cachingIconDatas()
/** 移除开发者警告通知 */ /** 移除开发者警告通知 */
@@ -635,24 +629,24 @@ object SystemUIHooker : YukiBaseHooker() {
param(NotificationEntryClass, BooleanType) param(NotificationEntryClass, BooleanType)
} }
afterHook { afterHook {
IconBuilderClass.clazz.field { name = "context" } IconBuilderClass.toClass().field { name = "context" }
.get(field { name = "iconBuilder" }.get(instance).cast()).cast<Context>()?.also { context -> .get(field { name = "iconBuilder" }.get(instance).cast()).cast<Context>()?.also { context ->
NotificationEntryClass.clazz.method { NotificationEntryClass.toClass().method {
name = "getSbn" name = "getSbn"
}.get(args().first().any()).invoke<StatusBarNotification>()?.also { nf -> }.get(args().first().any()).invoke<StatusBarNotification>()?.also { nf ->
nf.notification.smallIcon.loadDrawable(context).also { iconDrawable -> nf.notification.smallIcon.loadDrawable(context)?.also { iconDrawable ->
compatStatusIcon( compatStatusIcon(
context = context, context = context,
nf = nf, nf = nf,
isGrayscaleIcon = isGrayscaleIcon(context, iconDrawable).also { isGrayscaleIcon = isGrayscaleIcon(context, iconDrawable).also {
/** 缓存第一次的 APP 小图标 */ /** 缓存第一次的 APP 小图标 */
if (it.not()) context.findAppIcon(nf.packageName) if (it.not()) context.appIconOf(nf.packageName)
?.also { e -> appIcons[nf.packageName] = e } ?.also { e -> appIcons[nf.packageName] = e }
}, },
packageName = nf.packageName, packageName = nf.packageName,
drawable = iconDrawable drawable = iconDrawable
).also { pair -> ).also { pair ->
if (pair.second) StatusBarIconClass.clazz.field { if (pair.second) StatusBarIconClass.toClass().field {
name = "icon" name = "icon"
type = IconClass type = IconClass
}.get(result).set(Icon.createWithBitmap(pair.first.toBitmap())) }.get(result).set(Icon.createWithBitmap(pair.first.toBitmap()))
@@ -679,7 +673,7 @@ object SystemUIHooker : YukiBaseHooker() {
/** 注入通知控制器实例 */ /** 注入通知控制器实例 */
StatusBarNotificationPresenterClass.hook { StatusBarNotificationPresenterClass.hook {
injectMember { injectMember {
allConstructors() allMembers(MembersType.CONSTRUCTOR)
afterHook { notificationPresenter = instance } afterHook { notificationPresenter = instance }
} }
} }
@@ -772,30 +766,30 @@ object SystemUIHooker : YukiBaseHooker() {
superClass(isOnlySuperClass = true) superClass(isOnlySuperClass = true)
}.get(instance).any() }.get(instance).any()
/** 记录媒体通知 [View] */ /** 记录媒体通知 [View] */
notificationPlayerView = PlayerViewHolderClass.clazz.method { notificationPlayerView = PlayerViewHolderClass.toClassOrNull()?.method {
name = "getPlayer" name = "getPlayer"
emptyParam() emptyParam()
}.get(holder).invoke() }?.get(holder)?.invoke()
/** 设置背景着色 */ /** 设置背景着色 */
modifyNotifyPanelAlpha(notificationPlayerView, isTint = true) modifyNotifyPanelAlpha(notificationPlayerView, isTint = true)
/** 当前是否正在播放 */ /** 当前是否正在播放 */
val isPlaying = MediaDataClass.clazz.method { val isPlaying = MediaDataClass.toClassOrNull()?.method {
name = "isPlaying" name = "isPlaying"
emptyParam() emptyParam()
}.get(args().first().any()).boolean() }?.get(args().first().any())?.boolean() ?: false
/** 当前通知是否展开 */ /** 当前通知是否展开 */
val isExpanded = OplusMediaViewControllerClass.clazz.method { val isExpanded = OplusMediaViewControllerClass.toClassOrNull()?.method {
name = "getExpanded" name = "getExpanded"
emptyParam() emptyParam()
}.get(field { name = "mOplusMediaViewController" }.get(instance).self).boolean() }?.get(field { name = "mOplusMediaViewController" }.get(instance).any())?.boolean() ?: false
/** 符合条件后执行 */ /** 符合条件后执行 */
if (prefs.get(DataConst.ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP).not() || isExpanded || isPlaying.not()) return@afterHook if (prefs.get(DataConst.ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP).not() || isExpanded || isPlaying.not()) return@afterHook
/** 模拟手动展开通知 */ /** 模拟手动展开通知 */
BasePlayViewHolderClass.clazz.method { BasePlayViewHolderClass.toClassOrNull()?.method {
name = "getExpandButton" name = "getExpandButton"
emptyParam() emptyParam()
}.get(holder).invoke<View>()?.performClick() }?.get(holder)?.invoke<View>()?.performClick()
} }
} }
}.ignoredHookClassNotFoundFailure() }.ignoredHookClassNotFoundFailure()
@@ -804,19 +798,19 @@ object SystemUIHooker : YukiBaseHooker() {
injectMember { injectMember {
method { name = "resolveHeaderViews" } method { name = "resolveHeaderViews" }
afterHook { afterHook {
NotificationHeaderViewWrapperClass.clazz NotificationHeaderViewWrapperClass.toClass()
.field { name = "mIcon" }.get(instance).cast<ImageView>()?.apply { .field { name = "mIcon" }.get(instance).cast<ImageView>()?.apply {
ExpandableNotificationRowClass.clazz ExpandableNotificationRowClass.toClass()
.method { name = "getEntry" } .method { name = "getEntry" }
.get(NotificationViewWrapperClass.clazz.field { .get(NotificationViewWrapperClass.toClass().field {
name = "mRow" name = "mRow"
}.get(instance).self).call()?.let { }.get(instance).any()).call()?.let {
it.javaClass.method { it.javaClass.method {
name = "getSbn" name = "getSbn"
}.get(it).invoke<StatusBarNotification>() }.get(it).invoke<StatusBarNotification>()
}.also { nf -> }.also { nf ->
nf?.notification?.also { nf?.notification?.also {
it.smallIcon.loadDrawable(context).also { iconDrawable -> it.smallIcon.loadDrawable(context)?.also { iconDrawable ->
compatNotifyIcon( compatNotifyIcon(
context = context, context = context,
nf = nf, nf = nf,

View File

@@ -20,7 +20,7 @@
* *
* This file is Created by fankes on 2022/1/30. * This file is Created by fankes on 2022/1/30.
*/ */
@file:Suppress("SetTextI18n", "InflateParams", "DEPRECATION") @file:Suppress("SetTextI18n", "InflateParams")
package com.fankes.coloros.notify.ui.activity package com.fankes.coloros.notify.ui.activity
@@ -67,7 +67,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
return return
} }
/** 返回按钮点击事件 */ /** 返回按钮点击事件 */
binding.titleBackIcon.setOnClickListener { onBackPressed() } binding.titleBackIcon.setOnClickListener { callOnBackPressed() }
/** 刷新适配器结果相关 */ /** 刷新适配器结果相关 */
refreshAdapterResult() refreshAdapterResult()
/** 设置上下按钮点击事件 */ /** 设置上下按钮点击事件 */
@@ -117,7 +117,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
onBindViews<AdapterConfigBinding> { binding, position -> onBindViews<AdapterConfigBinding> { binding, position ->
iconDatas[position].also { bean -> iconDatas[position].also { bean ->
binding.adpAppIcon.setImageBitmap(bean.iconBitmap) binding.adpAppIcon.setImageBitmap(bean.iconBitmap)
(if (bean.iconColor != 0) bean.iconColor else resources.getColor(R.color.colorTextGray)).also { color -> (if (bean.iconColor != 0) bean.iconColor else resources.colorOf(R.color.colorTextGray)).also { color ->
binding.adpAppIcon.setColorFilter(color) binding.adpAppIcon.setColorFilter(color)
binding.adpAppName.setTextColor(color) binding.adpAppName.setTextColor(color)
} }
@@ -203,6 +203,20 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
removeExtra("isNewAppSupport") removeExtra("isNewAppSupport")
removeExtra("isShowUpdDialog") removeExtra("isShowUpdDialog")
} }
/** 设置返回监听事件 */
addOnBackPressedEvent {
if (MainActivity.isActivityLive.not())
showDialog {
title = "提示"
msg = "要返回模块主页吗?"
confirmButton {
releaseEventAndBack()
navigate<MainActivity>()
}
cancelButton { releaseEventAndBack() }
}
else releaseEventAndBack()
}
} }
/** 开始手动同步 */ /** 开始手动同步 */
@@ -239,18 +253,4 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
else iconAllDatas.filter { else iconAllDatas.filter {
it.appName.lowercase().contains(filterText.lowercase()) || it.packageName.lowercase().contains(filterText.lowercase()) it.appName.lowercase().contains(filterText.lowercase()) || it.packageName.lowercase().contains(filterText.lowercase())
} }
override fun onBackPressed() {
if (MainActivity.isActivityLive.not())
showDialog {
title = "提示"
msg = "要返回模块主页吗?"
confirmButton {
super.onBackPressed()
navigate<MainActivity>()
}
cancelButton { super.onBackPressed() }
}
else super.onBackPressed()
}
} }

View File

@@ -31,9 +31,9 @@ import androidx.core.view.WindowCompat
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.fankes.coloros.notify.R import com.fankes.coloros.notify.R
import com.fankes.coloros.notify.utils.factory.isNotSystemInDarkMode import com.fankes.coloros.notify.utils.factory.isNotSystemInDarkMode
import com.highcapable.yukihookapi.hook.factory.current
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
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() { abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
@@ -49,15 +49,11 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
isMainThreadRunning = true isMainThreadRunning = true
javaClass.genericSuperclass.also { type -> binding = current().generic()?.argument()?.method {
if (type is ParameterizedType) { name = "inflate"
binding = (type.actualTypeArguments[0] as Class<VB>).method { param(LayoutInflaterClass)
name = "inflate" }?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed")
param(LayoutInflaterClass) setContentView(binding.root)
}.get().invoke<VB>(layoutInflater) ?: error("binding failed")
setContentView(binding.root)
} else error("binding but got wrong type")
}
/** 隐藏系统的标题栏 */ /** 隐藏系统的标题栏 */
supportActionBar?.hide() supportActionBar?.hide()
/** 初始化沉浸状态栏 */ /** 初始化沉浸状态栏 */

View File

@@ -0,0 +1,66 @@
/*
* ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/ColorOSNotifyIcon
*
* 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.
* <p>
*
* 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/10/6.
*/
package com.fankes.coloros.notify.utils.factory
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
/** 已添加的返回监听事件 */
private val onBackPressedCallbacks = HashMap<AppCompatActivity, OnBackPressedCallback>()
/**
* 手动调用返回事件
* @param ignored 是否忽略现有返回监听事件立即返回 - 否则将执行返回事件 - 默认否
*/
fun AppCompatActivity.callOnBackPressed(ignored: Boolean = false) {
if (isDestroyed) return
onBackPressedCallbacks[this]?.isEnabled = ignored.not()
onBackPressedDispatcher.onBackPressed()
}
/**
* 添加返回监听事件
* @param callback 回调事件
*/
fun AppCompatActivity.addOnBackPressedEvent(callback: OnBackPressedEvent.() -> Unit) {
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
OnBackPressedEvent(this@addOnBackPressedEvent).apply(callback)
}
}.also { result ->
onBackPressedCallbacks.computeIfAbsent(this) {
onBackPressedDispatcher.addCallback(result)
result
}
}
}
/**
* 返回监听事件实现类
* @param instance 当前实例
*/
class OnBackPressedEvent(private val instance: AppCompatActivity) {
/** 立即释放返回事件并调用返回功能 */
fun releaseEventAndBack() = instance.callOnBackPressed(ignored = true)
}

View File

@@ -20,7 +20,7 @@
* *
* This file is Created by fankes on 2022/1/7. * This file is Created by fankes on 2022/1/7.
*/ */
@file:Suppress("DEPRECATION", "PrivateApi", "unused", "ObsoleteSdkInt") @file:Suppress("unused", "ObsoleteSdkInt")
package com.fankes.coloros.notify.utils.factory package com.fankes.coloros.notify.utils.factory
@@ -28,15 +28,15 @@ import android.app.Activity
import android.app.Notification import android.app.Notification
import android.app.Service import android.app.Service
import android.app.WallpaperManager import android.app.WallpaperManager
import android.app.WallpaperManager.FLAG_SYSTEM
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager.PackageInfoFlags
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Color import android.graphics.Color
@@ -48,13 +48,17 @@ import android.os.Handler
import android.provider.Settings import android.provider.Settings
import android.util.Base64 import android.util.Base64
import android.widget.Toast import android.widget.Toast
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.content.pm.PackageInfoCompat
import androidx.core.content.res.ResourcesCompat
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.factory.classOf
import com.highcapable.yukihookapi.hook.factory.field import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.hasClass import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.factory.toClassOrNull
import com.highcapable.yukihookapi.hook.type.java.StringType import com.highcapable.yukihookapi.hook.type.java.StringType
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.Companion.appContext import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.Companion.appContext
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
@@ -62,7 +66,6 @@ import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
/** /**
* 系统深色模式是否开启 * 系统深色模式是否开启
* @return [Boolean] 是否开启 * @return [Boolean] 是否开启
@@ -87,6 +90,12 @@ val Context.isSystemInDarkMode get() = (resources.configuration.uiMode and Confi
*/ */
inline val Context.isNotSystemInDarkMode get() = !isSystemInDarkMode inline val Context.isNotSystemInDarkMode get() = !isSystemInDarkMode
/**
* 系统版本是否高于或等于 Android 13
* @return [Boolean] 是否符合条件
*/
inline val isUpperOfAndroidT get() = Build.VERSION.SDK_INT > Build.VERSION_CODES.S
/** /**
* 系统版本是否高于或等于 Android 12 * 系统版本是否高于或等于 Android 12
* @return [Boolean] 是否符合条件 * @return [Boolean] 是否符合条件
@@ -103,7 +112,7 @@ inline val isLowerAndroidP get() = Build.VERSION.SDK_INT < Build.VERSION_CODES.P
* 当前设备是否是 ColorOS 定制 Android 系统 * 当前设备是否是 ColorOS 定制 Android 系统
* @return [Boolean] 是否符合条件 * @return [Boolean] 是否符合条件
*/ */
val isColorOS by lazy { ("oppo.R").hasClass || ("com.color.os.ColorBuild").hasClass || ("oplus.R").hasClass } val isColorOS by lazy { "oppo.R".hasClass() || "com.color.os.ColorBuild".hasClass() || "oplus.R".hasClass() }
/** /**
* 当前设备是否不是 ColorOS 定制 Android 系统 * 当前设备是否不是 ColorOS 定制 Android 系统
@@ -146,67 +155,88 @@ val colorOSVersion get() = "$colorOSNumberVersion ${Build.DISPLAY}"
*/ */
val colorOSNumberVersion val colorOSNumberVersion
get() = safeOf(default = "无法获取") { get() = safeOf(default = "无法获取") {
(classOf(name = "com.oplus.os.OplusBuild").let { "com.oplus.os.OplusBuild".toClassOrNull()?.let {
it.field { name = "VERSIONS" }.ignoredError().get().array<String>().takeIf { e -> e.isNotEmpty() } it.field { name = "VERSIONS" }.ignored().get().array<String>().takeIf { e -> e.isNotEmpty() }
?.get(it.method { name = "getOplusOSVERSION" }.ignoredError().get().int() - 1) ?.get(it.method { name = "getOplusOSVERSION" }.ignored().get().int() - 1)
} ?: findPropString( } ?: findPropString(
key = "ro.system.build.fingerprint", default = "无法获取" key = "ro.system.build.fingerprint", default = "无法获取"
).split("ssi:")[1].split("/")[0].trim()) ).split("ssi:")[1].split("/")[0].trim()
} }
/** /**
* 得到安装包信息 * 获取 [Drawable]
* @return [PackageInfo] * @param resId 属性资源 ID
* @return [Drawable]
*/ */
val Context.packageInfo get() = packageManager?.getPackageInfo(packageName, 0) ?: PackageInfo() fun Resources.drawableOf(@DrawableRes resId: Int) = ResourcesCompat.getDrawable(this, resId, null) ?: error("Invalid resources")
/** /**
* 判断应用是否安装 * 获取颜色
* @return [Boolean] * @param resId 属性资源 ID
*/
val String.isInstall
get() = safeOfFalse {
appContext.packageManager.getPackageInfo(
this, PackageManager.GET_UNINSTALLED_PACKAGES
)
true
}
/**
* 得到版本信息
* @return [String]
*/
val Context.versionName get() = packageInfo.versionName ?: ""
/**
* 得到版本号
* @return [Int] * @return [Int]
*/ */
val Context.versionCode get() = packageInfo.versionCode fun Resources.colorOf(@ColorRes resId: Int) = ResourcesCompat.getColor(this, resId, null)
/**
* 得到 APP 安装包信息 (兼容)
* @param packageName APP 包名
* @param flag [PackageInfoFlags]
* @return [PackageInfo] or null
*/
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()
/**
* 得到 APP 版本号 (兼容 [PackageInfo.getLongVersionCode])
* @return [Int]
*/
private val PackageInfo.versionCodeCompat get() = PackageInfoCompat.getLongVersionCode(this)
/**
* 判断 APP 是否安装
* @param packageName APP 包名
* @return [Boolean]
*/
fun Context.isInstall(packageName: String) = getPackageInfoCompat(packageName)?.let { true } ?: false
/**
* 得到 APP 版本信息
* @return [String]
*/
val Context.appVersionName get() = getPackageInfoCompat(packageName)?.versionName ?: ""
/**
* 得到 APP 版本号
* @return [Int]
*/
val Context.appVersionCode get() = getPackageInfoCompat(packageName)?.versionCodeCompat
/** /**
* 得到 APP 名称 * 得到 APP 名称
* @param name APP 包名 * @param packageName APP 包名 - 默认为当前 APP
* @return [String] * @return [String]
*/ */
fun Context.findAppName(name: String) = fun Context.appNameOf(packageName: String = getPackageName()) =
safeOfNothing { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadLabel(packageManager).toString() } getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: ""
/** /**
* 得到 APP 图标 * 得到 APP 图标
* @param name APP 包名 * @param packageName APP 包名 - 默认为当前 APP
* @return [Drawable] or null * @return [Drawable] or null
*/ */
fun Context.findAppIcon(name: String) = fun Context.appIconOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.applicationInfo?.loadIcon(packageManager)
safeOfNull { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadIcon(packageManager) }
/** /**
* 获取 APP 是否为 DEBUG 版本 * 获取 APP 是否为 DEBUG 版本
* @param name APP 包名 * @param packageName APP 包名
* @return [Boolean] * @return [Boolean]
*/ */
fun Context.isAppDebuggable(name: String) = fun Context.isAppDebuggable(packageName: String) =
safeOfFalse { (packageManager?.getPackageInfo(name, 0)?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_DEBUGGABLE) ?: 0) != 0 } safeOfFalse { (getPackageInfoCompat(packageName)?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_DEBUGGABLE) ?: 0) != 0 }
/** /**
* 对数值自动补零 * 对数值自动补零
@@ -246,8 +276,11 @@ val isNotNoificationEnabled get() = !NotificationManagerCompat.from(appContext).
* 网络连接是否正常 * 网络连接是否正常
* @return [Boolean] 网络是否连接 * @return [Boolean] 网络是否连接
*/ */
val isNetWorkSuccess val Context.isNetWorkSuccess
get() = safeOfFalse { appContext.getSystemService<ConnectivityManager>()?.activeNetworkInfo != null } get() = safeOfFalse {
@Suppress("DEPRECATION")
getSystemService<ConnectivityManager>()?.activeNetworkInfo != null
}
/** /**
* dp 转换为 pxInt * dp 转换为 pxInt
@@ -264,21 +297,32 @@ fun Number.dp(context: Context) = dpFloat(context).toInt()
fun Number.dpFloat(context: Context) = toFloat() * context.resources.displayMetrics.density fun Number.dpFloat(context: Context) = toFloat() * context.resources.displayMetrics.density
/** /**
* 获取系统壁纸颜 * 获取系统主题
* *
* 由于 ColorOS 阉割了 [android.R.color.system_accent1_600] 这里取系统壁纸颜色做补偿 * 由于 ColorOS 阉割了 [android.R.color.system_accent1_600] 这里取系统壁纸颜色做补偿
*
* 从 ColorOS 13 (Android 13) 开始使用系统取色方案
* @return [Int] Android < 13 返回 [wallpaperColor]
*/
val Context.systemAccentColor
get() = safeOf(wallpaperColor) {
if (isUpperOfAndroidT) resources.colorOf(android.R.color.system_accent1_600) else wallpaperColor
}
/**
* 获取系统壁纸颜色
* @return [Int] 无法获取时返回透明色 * @return [Int] 无法获取时返回透明色
*/ */
val Context.wallpaperColor val Context.wallpaperColor
get() = safeOfNan { get() = safeOfNan {
WallpaperManager.getInstance(this).getWallpaperColors(FLAG_SYSTEM)?.secondaryColor?.toArgb() ?: 0 WallpaperManager.getInstance(this).getWallpaperColors(WallpaperManager.FLAG_SYSTEM)?.primaryColor?.toArgb() ?: 0
} }
/** /**
* 是否为白色 * 是否为白色
* @return [Boolean] * @return [Boolean]
*/ */
val Int.isWhite val Int.isWhiteColor
get() = safeOfTrue { get() = safeOfTrue {
val r = this and 0xff0000 shr 16 val r = this and 0xff0000 shr 16
val g = this and 0x00ff00 shr 8 val g = this and 0x00ff00 shr 8
@@ -335,10 +379,10 @@ val String.bitmap: Bitmap get() = unbase64.bitmap
* @return [String] * @return [String]
*/ */
fun findPropString(key: String, default: String = "") = safeOf(default) { fun findPropString(key: String, default: String = "") = safeOf(default) {
(classOf(name = "android.os.SystemProperties").method { "android.os.SystemProperties".toClassOrNull()?.method {
name = "get" name = "get"
param(StringType, StringType) param(StringType, StringType)
}.get().invoke(key, default)) ?: default }?.get()?.invoke(key, default) ?: default
} }
/** /**
@@ -412,7 +456,7 @@ fun Context.openBrowser(url: String, packageName: String = "") = runCatching {
* @param packageName 包名 * @param packageName 包名
*/ */
fun Context.openSelfSetting(packageName: String = appContext.packageName) = runCatching { fun Context.openSelfSetting(packageName: String = appContext.packageName) = runCatching {
if (packageName.isInstall) if (isInstall(packageName))
startActivity(Intent().apply { startActivity(Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
@@ -457,4 +501,7 @@ fun Long.stampToDate(format: String = "yyyy-MM-dd HH:mm:ss") =
* @param ms 毫秒 - 默认150 * @param ms 毫秒 - 默认150
* @param it 方法体 * @param it 方法体
*/ */
fun Any?.delayedRun(ms: Long = 150, it: () -> Unit) = runInSafe { Handler().postDelayed({ it() }, ms) } fun Any?.delayedRun(ms: Long = 150, it: () -> Unit) = runInSafe {
@Suppress("DEPRECATION")
Handler().postDelayed({ it() }, ms)
}

View File

@@ -91,7 +91,7 @@ object GithubReleaseTool {
* @param result 已连接回调 * @param result 已连接回调
*/ */
private fun checkingInternetConnect(context: Context, result: () -> Unit) = runInSafe { private fun checkingInternetConnect(context: Context, result: () -> Unit) = runInSafe {
if (isNetWorkSuccess) if (context.isNetWorkSuccess)
OkHttpClient().newBuilder().build().newCall( OkHttpClient().newBuilder().build().newCall(
Request.Builder() Request.Builder()
.url("https://www.baidu.com") .url("https://www.baidu.com")

View File

@@ -129,12 +129,12 @@ object IconAdaptationTool {
) )
notify(packageName.hashCode(), Notification.Builder(context, NOTIFY_CHANNEL).apply { notify(packageName.hashCode(), Notification.Builder(context, NOTIFY_CHANNEL).apply {
setShowWhen(true) setShowWhen(true)
setContentTitle("您已安装 ${context.findAppName(packageName)}") setContentTitle("您已安装 ${context.appNameOf(packageName)}")
setContentText("尚未适配此应用,点按打开在线规则。") setContentText("尚未适配此应用,点按打开在线规则。")
setColor(0xFF2993F0.toInt()) setColor(0xFF2993F0.toInt())
setAutoCancel(true) setAutoCancel(true)
setSmallIcon(Icon.createWithBitmap(smallIcon)) setSmallIcon(Icon.createWithBitmap(smallIcon))
setLargeIcon(context.findAppIcon(packageName)?.toBitmap()) setLargeIcon(context.appIconOf(packageName)?.toBitmap())
setContentIntent( setContentIntent(
PendingIntent.getActivity( PendingIntent.getActivity(
context, packageName.hashCode(), context, packageName.hashCode(),
@@ -146,7 +146,7 @@ object IconAdaptationTool {
flags = Intent.FLAG_ACTIVITY_NEW_TASK flags = Intent.FLAG_ACTIVITY_NEW_TASK
}.apply { }.apply {
putExtra("isNewAppSupport", true) putExtra("isNewAppSupport", true)
putExtra("appName", context.findAppName(packageName)) putExtra("appName", context.appNameOf(packageName))
putExtra("pkgName", packageName) putExtra("pkgName", packageName)
}, if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE }, if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE
) )