From 97161f86921d485ffae4bc005d01b7513eea4da2 Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Thu, 6 Oct 2022 03:19:09 +0800 Subject: [PATCH] Modify merge YukiHookAPI new usage and compatible with API 33 --- .../fankes/coloros/notify/hook/HookEntry.kt | 2 +- .../notify/hook/entity/SystemUIHooker.kt | 92 +++++------ .../notify/ui/activity/ConfigureActivity.kt | 34 ++-- .../notify/ui/activity/base/BaseActivity.kt | 16 +- .../utils/factory/BackPressedEventFactory.kt | 66 ++++++++ .../notify/utils/factory/FunctionFactory.kt | 149 ++++++++++++------ .../notify/utils/tool/GithubReleaseTool.kt | 2 +- .../notify/utils/tool/IconAdaptationTool.kt | 6 +- 8 files changed, 235 insertions(+), 132 deletions(-) create mode 100644 app/src/main/java/com/fankes/coloros/notify/utils/factory/BackPressedEventFactory.kt diff --git a/app/src/main/java/com/fankes/coloros/notify/hook/HookEntry.kt b/app/src/main/java/com/fankes/coloros/notify/hook/HookEntry.kt index 527eb09..f957856 100644 --- a/app/src/main/java/com/fankes/coloros/notify/hook/HookEntry.kt +++ b/app/src/main/java/com/fankes/coloros/notify/hook/HookEntry.kt @@ -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 } diff --git a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt index 64d9d14..bde0acd 100644 --- a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt +++ b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt @@ -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()?.also { context -> - NotificationEntryClass.clazz.method { + NotificationEntryClass.toClass().method { name = "getSbn" }.get(args().first().any()).invoke()?.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()?.performClick() + }?.get(holder)?.invoke()?.performClick() } } }.ignoredHookClassNotFoundFailure() @@ -804,19 +798,19 @@ object SystemUIHooker : YukiBaseHooker() { injectMember { method { name = "resolveHeaderViews" } afterHook { - NotificationHeaderViewWrapperClass.clazz + NotificationHeaderViewWrapperClass.toClass() .field { name = "mIcon" }.get(instance).cast()?.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() }.also { nf -> nf?.notification?.also { - it.smallIcon.loadDrawable(context).also { iconDrawable -> + it.smallIcon.loadDrawable(context)?.also { iconDrawable -> compatNotifyIcon( context = context, nf = nf, diff --git a/app/src/main/java/com/fankes/coloros/notify/ui/activity/ConfigureActivity.kt b/app/src/main/java/com/fankes/coloros/notify/ui/activity/ConfigureActivity.kt index a3bb351..b24a469 100644 --- a/app/src/main/java/com/fankes/coloros/notify/ui/activity/ConfigureActivity.kt +++ b/app/src/main/java/com/fankes/coloros/notify/ui/activity/ConfigureActivity.kt @@ -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() { return } /** 返回按钮点击事件 */ - binding.titleBackIcon.setOnClickListener { onBackPressed() } + binding.titleBackIcon.setOnClickListener { callOnBackPressed() } /** 刷新适配器结果相关 */ refreshAdapterResult() /** 设置上下按钮点击事件 */ @@ -117,7 +117,7 @@ class ConfigureActivity : BaseActivity() { onBindViews { 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() { removeExtra("isNewAppSupport") removeExtra("isShowUpdDialog") } + /** 设置返回监听事件 */ + addOnBackPressedEvent { + if (MainActivity.isActivityLive.not()) + showDialog { + title = "提示" + msg = "要返回模块主页吗?" + confirmButton { + releaseEventAndBack() + navigate() + } + cancelButton { releaseEventAndBack() } + } + else releaseEventAndBack() + } } /** 开始手动同步 */ @@ -239,18 +253,4 @@ class ConfigureActivity : BaseActivity() { 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() - } - cancelButton { super.onBackPressed() } - } - else super.onBackPressed() - } } \ No newline at end of file diff --git a/app/src/main/java/com/fankes/coloros/notify/ui/activity/base/BaseActivity.kt b/app/src/main/java/com/fankes/coloros/notify/ui/activity/base/BaseActivity.kt index 9c0a2ec..20d2f8d 100644 --- a/app/src/main/java/com/fankes/coloros/notify/ui/activity/base/BaseActivity.kt +++ b/app/src/main/java/com/fankes/coloros/notify/ui/activity/base/BaseActivity.kt @@ -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 : AppCompatActivity() { @@ -49,15 +49,11 @@ abstract class BaseActivity : 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).method { - name = "inflate" - param(LayoutInflaterClass) - }.get().invoke(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(layoutInflater) ?: error("binding failed") + setContentView(binding.root) /** 隐藏系统的标题栏 */ supportActionBar?.hide() /** 初始化沉浸状态栏 */ diff --git a/app/src/main/java/com/fankes/coloros/notify/utils/factory/BackPressedEventFactory.kt b/app/src/main/java/com/fankes/coloros/notify/utils/factory/BackPressedEventFactory.kt new file mode 100644 index 0000000..2d0a624 --- /dev/null +++ b/app/src/main/java/com/fankes/coloros/notify/utils/factory/BackPressedEventFactory.kt @@ -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. + *

+ * + * 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 + * + * + * 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() + +/** + * 手动调用返回事件 + * @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) +} \ No newline at end of file diff --git a/app/src/main/java/com/fankes/coloros/notify/utils/factory/FunctionFactory.kt b/app/src/main/java/com/fankes/coloros/notify/utils/factory/FunctionFactory.kt index 46fe698..211ed61 100644 --- a/app/src/main/java/com/fankes/coloros/notify/utils/factory/FunctionFactory.kt +++ b/app/src/main/java/com/fankes/coloros/notify/utils/factory/FunctionFactory.kt @@ -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().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().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()?.activeNetworkInfo != null } +val Context.isNetWorkSuccess + get() = safeOfFalse { + @Suppress("DEPRECATION") + getSystemService()?.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) } \ No newline at end of file +fun Any?.delayedRun(ms: Long = 150, it: () -> Unit) = runInSafe { + @Suppress("DEPRECATION") + Handler().postDelayed({ it() }, ms) +} \ No newline at end of file diff --git a/app/src/main/java/com/fankes/coloros/notify/utils/tool/GithubReleaseTool.kt b/app/src/main/java/com/fankes/coloros/notify/utils/tool/GithubReleaseTool.kt index 8b83e38..7e6679b 100644 --- a/app/src/main/java/com/fankes/coloros/notify/utils/tool/GithubReleaseTool.kt +++ b/app/src/main/java/com/fankes/coloros/notify/utils/tool/GithubReleaseTool.kt @@ -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") diff --git a/app/src/main/java/com/fankes/coloros/notify/utils/tool/IconAdaptationTool.kt b/app/src/main/java/com/fankes/coloros/notify/utils/tool/IconAdaptationTool.kt index 7ba9010..ea4028c 100644 --- a/app/src/main/java/com/fankes/coloros/notify/utils/tool/IconAdaptationTool.kt +++ b/app/src/main/java/com/fankes/coloros/notify/utils/tool/IconAdaptationTool.kt @@ -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 )