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

View File

@@ -20,7 +20,7 @@
*
* 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
@@ -67,7 +67,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
return
}
/** 返回按钮点击事件 */
binding.titleBackIcon.setOnClickListener { onBackPressed() }
binding.titleBackIcon.setOnClickListener { callOnBackPressed() }
/** 刷新适配器结果相关 */
refreshAdapterResult()
/** 设置上下按钮点击事件 */
@@ -117,7 +117,7 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
onBindViews<AdapterConfigBinding> { binding, position ->
iconDatas[position].also { bean ->
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.adpAppName.setTextColor(color)
}
@@ -203,6 +203,20 @@ class ConfigureActivity : BaseActivity<ActivityConfigBinding>() {
removeExtra("isNewAppSupport")
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 {
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 com.fankes.coloros.notify.R
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.type.android.LayoutInflaterClass
import java.lang.reflect.ParameterizedType
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
@@ -49,15 +49,11 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
isMainThreadRunning = true
javaClass.genericSuperclass.also { type ->
if (type is ParameterizedType) {
binding = (type.actualTypeArguments[0] as Class<VB>).method {
name = "inflate"
param(LayoutInflaterClass)
}.get().invoke<VB>(layoutInflater) ?: error("binding failed")
setContentView(binding.root)
} else error("binding but got wrong type")
}
binding = current().generic()?.argument()?.method {
name = "inflate"
param(LayoutInflaterClass)
}?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed")
setContentView(binding.root)
/** 隐藏系统的标题栏 */
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.
*/
@file:Suppress("DEPRECATION", "PrivateApi", "unused", "ObsoleteSdkInt")
@file:Suppress("unused", "ObsoleteSdkInt")
package com.fankes.coloros.notify.utils.factory
@@ -28,15 +28,15 @@ import android.app.Activity
import android.app.Notification
import android.app.Service
import android.app.WallpaperManager
import android.app.WallpaperManager.FLAG_SYSTEM
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
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.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
@@ -48,13 +48,17 @@ import android.os.Handler
import android.provider.Settings
import android.util.Base64
import android.widget.Toast
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.core.app.NotificationManagerCompat
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.highcapable.yukihookapi.hook.factory.classOf
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.hasClass
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.xposed.application.ModuleApplication.Companion.appContext
import com.topjohnwu.superuser.Shell
@@ -62,7 +66,6 @@ import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat
import java.util.*
/**
* 系统深色模式是否开启
* @return [Boolean] 是否开启
@@ -87,6 +90,12 @@ val Context.isSystemInDarkMode get() = (resources.configuration.uiMode and Confi
*/
inline val Context.isNotSystemInDarkMode get() = !isSystemInDarkMode
/**
* 系统版本是否高于或等于 Android 13
* @return [Boolean] 是否符合条件
*/
inline val isUpperOfAndroidT get() = Build.VERSION.SDK_INT > Build.VERSION_CODES.S
/**
* 系统版本是否高于或等于 Android 12
* @return [Boolean] 是否符合条件
@@ -103,7 +112,7 @@ inline val isLowerAndroidP get() = Build.VERSION.SDK_INT < Build.VERSION_CODES.P
* 当前设备是否是 ColorOS 定制 Android 系统
* @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 系统
@@ -146,67 +155,88 @@ val colorOSVersion get() = "$colorOSNumberVersion ${Build.DISPLAY}"
*/
val colorOSNumberVersion
get() = safeOf(default = "无法获取") {
(classOf(name = "com.oplus.os.OplusBuild").let {
it.field { name = "VERSIONS" }.ignoredError().get().array<String>().takeIf { e -> e.isNotEmpty() }
?.get(it.method { name = "getOplusOSVERSION" }.ignoredError().get().int() - 1)
"com.oplus.os.OplusBuild".toClassOrNull()?.let {
it.field { name = "VERSIONS" }.ignored().get().array<String>().takeIf { e -> e.isNotEmpty() }
?.get(it.method { name = "getOplusOSVERSION" }.ignored().get().int() - 1)
} ?: findPropString(
key = "ro.system.build.fingerprint", default = "无法获取"
).split("ssi:")[1].split("/")[0].trim())
).split("ssi:")[1].split("/")[0].trim()
}
/**
* 得到安装包信息
* @return [PackageInfo]
* 获取 [Drawable]
* @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]
*/
val String.isInstall
get() = safeOfFalse {
appContext.packageManager.getPackageInfo(
this, PackageManager.GET_UNINSTALLED_PACKAGES
)
true
}
/**
* 得到版本信息
* @return [String]
*/
val Context.versionName get() = packageInfo.versionName ?: ""
/**
* 得到版本号
* 获取颜色
* @param resId 属性资源 ID
* @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 名称
* @param name APP 包名
* @param packageName APP 包名 - 默认为当前 APP
* @return [String]
*/
fun Context.findAppName(name: String) =
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 包名
* @param packageName APP 包名 - 默认为当前 APP
* @return [Drawable] or null
*/
fun Context.findAppIcon(name: String) =
safeOfNull { packageManager?.getPackageInfo(name, 0)?.applicationInfo?.loadIcon(packageManager) }
fun Context.appIconOf(packageName: String = getPackageName()) = getPackageInfoCompat(packageName)?.applicationInfo?.loadIcon(packageManager)
/**
* 获取 APP 是否为 DEBUG 版本
* @param name APP 包名
* @param packageName APP 包名
* @return [Boolean]
*/
fun Context.isAppDebuggable(name: String) =
safeOfFalse { (packageManager?.getPackageInfo(name, 0)?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_DEBUGGABLE) ?: 0) != 0 }
fun Context.isAppDebuggable(packageName: String) =
safeOfFalse { (getPackageInfoCompat(packageName)?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_DEBUGGABLE) ?: 0) != 0 }
/**
* 对数值自动补零
@@ -246,8 +276,11 @@ val isNotNoificationEnabled get() = !NotificationManagerCompat.from(appContext).
* 网络连接是否正常
* @return [Boolean] 网络是否连接
*/
val isNetWorkSuccess
get() = safeOfFalse { appContext.getSystemService<ConnectivityManager>()?.activeNetworkInfo != null }
val Context.isNetWorkSuccess
get() = safeOfFalse {
@Suppress("DEPRECATION")
getSystemService<ConnectivityManager>()?.activeNetworkInfo != null
}
/**
* dp 转换为 pxInt
@@ -264,21 +297,32 @@ fun Number.dp(context: Context) = dpFloat(context).toInt()
fun Number.dpFloat(context: Context) = toFloat() * context.resources.displayMetrics.density
/**
* 获取系统壁纸颜
* 获取系统主题
*
* 由于 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] 无法获取时返回透明色
*/
val Context.wallpaperColor
get() = safeOfNan {
WallpaperManager.getInstance(this).getWallpaperColors(FLAG_SYSTEM)?.secondaryColor?.toArgb() ?: 0
WallpaperManager.getInstance(this).getWallpaperColors(WallpaperManager.FLAG_SYSTEM)?.primaryColor?.toArgb() ?: 0
}
/**
* 是否为白色
* @return [Boolean]
*/
val Int.isWhite
val Int.isWhiteColor
get() = safeOfTrue {
val r = this and 0xff0000 shr 16
val g = this and 0x00ff00 shr 8
@@ -335,10 +379,10 @@ val String.bitmap: Bitmap get() = unbase64.bitmap
* @return [String]
*/
fun findPropString(key: String, default: String = "") = safeOf(default) {
(classOf(name = "android.os.SystemProperties").method {
"android.os.SystemProperties".toClassOrNull()?.method {
name = "get"
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 包名
*/
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
@@ -457,4 +501,7 @@ fun Long.stampToDate(format: String = "yyyy-MM-dd HH:mm:ss") =
* @param ms 毫秒 - 默认150
* @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 已连接回调
*/
private fun checkingInternetConnect(context: Context, result: () -> Unit) = runInSafe {
if (isNetWorkSuccess)
if (context.isNetWorkSuccess)
OkHttpClient().newBuilder().build().newCall(
Request.Builder()
.url("https://www.baidu.com")

View File

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