diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 2a8b527..26ff174 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -18,14 +18,14 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: true - - name: Set up JDK 20 - uses: actions/setup-java@v4 + - name: Set up JDK + uses: actions/setup-java@v5 with: - java-version: "20" + java-version: "21" distribution: "temurin" - name: Build with Gradle and Sign diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2dd1c1c..3988a8d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -54,7 +54,7 @@ android { isMinifyEnabled = true isShrinkResources = true vcsInfo.include = false - proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } @@ -127,7 +127,7 @@ fun getGitHeadRefsSuffix(project: Project): String { // 示例:"90312cd9157587d11779ed7be776e3220050b308\n" refFile.readText(Charsets.UTF_8).replace(Regex("""\s"""), "").subSequence(0, 7) } else { - string.substring(0, 7) + string.take(7) } println("commit_id: $result") return result.toString() diff --git a/app/src/main/java/com/gswxxn/restoresplashscreen/hook/NewSystemUIHooker.kt b/app/src/main/java/com/gswxxn/restoresplashscreen/hook/NewSystemUIHooker.kt index 1885a9a..55a52e2 100644 --- a/app/src/main/java/com/gswxxn/restoresplashscreen/hook/NewSystemUIHooker.kt +++ b/app/src/main/java/com/gswxxn/restoresplashscreen/hook/NewSystemUIHooker.kt @@ -12,6 +12,7 @@ import com.highcapable.yukihookapi.hook.factory.constructor import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.log.YLog import com.highcapable.yukihookapi.hook.type.android.ActivityInfoClass +import com.highcapable.yukihookapi.hook.type.android.ComponentInfoClass import com.highcapable.yukihookapi.hook.type.android.DrawableClass import com.highcapable.yukihookapi.hook.type.java.FloatType import com.highcapable.yukihookapi.hook.type.java.IntType @@ -54,7 +55,7 @@ object NewSystemUIHooker: YukiBaseHooker() { "com.android.launcher3.icons.IconProvider".toClass().method { name = "getIcon" paramCount(2) - param { IntType in it && ActivityInfoClass in it } + param { IntType in it && (ActivityInfoClass in it || ComponentInfoClass in it) } }.give()!! } val normalizeAndWrapToAdaptiveIcon = HookManager { diff --git a/app/src/main/java/com/gswxxn/restoresplashscreen/hook/base/BaseHookHandler.kt b/app/src/main/java/com/gswxxn/restoresplashscreen/hook/base/BaseHookHandler.kt index 3c34364..2546a39 100644 --- a/app/src/main/java/com/gswxxn/restoresplashscreen/hook/base/BaseHookHandler.kt +++ b/app/src/main/java/com/gswxxn/restoresplashscreen/hook/base/BaseHookHandler.kt @@ -9,6 +9,8 @@ abstract class BaseHookHandler { lateinit var baseHooker: YukiBaseHooker val appContext get() = baseHooker.appContext + val appClassLoader get() = baseHooker.appClassLoader + val appUserId get() = baseHooker.appUserId val appResources get() = baseHooker.appResources val prefs get() = baseHooker.prefs diff --git a/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/GenerateHookHandler.kt b/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/GenerateHookHandler.kt index bcac875..aac4cc2 100644 --- a/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/GenerateHookHandler.kt +++ b/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/GenerateHookHandler.kt @@ -1,6 +1,7 @@ package com.gswxxn.restoresplashscreen.hook.systemui import android.content.pm.ActivityInfo +import android.content.pm.ApplicationInfo import com.gswxxn.restoresplashscreen.data.DataConst import com.gswxxn.restoresplashscreen.data.StartingWindowInfo import com.gswxxn.restoresplashscreen.hook.NewSystemUIHooker @@ -18,6 +19,7 @@ import kotlinx.coroutines.launch object GenerateHookHandler: BaseHookHandler() { var currentPackageName = "" var currentActivity = "" + var currentApplicationInfo = null as ApplicationInfo? var exceptCurrentApp = false var isHooking = false @@ -27,6 +29,7 @@ object GenerateHookHandler: BaseHookHandler() { private fun resetCache() { currentPackageName = "" currentActivity = "" + currentApplicationInfo = null isHooking = false exceptCurrentApp = false @@ -54,6 +57,7 @@ object GenerateHookHandler: BaseHookHandler() { isHooking = true currentPackageName = activityInfo.packageName currentActivity = activityInfo.targetActivity ?: "" + currentApplicationInfo = activityInfo.applicationInfo exceptCurrentApp = isExcept() printLog( diff --git a/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/IconHookHandler.kt b/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/IconHookHandler.kt index d2bb6e4..1d8ce5d 100644 --- a/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/IconHookHandler.kt +++ b/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/IconHookHandler.kt @@ -20,6 +20,7 @@ import com.gswxxn.restoresplashscreen.data.DataConst import com.gswxxn.restoresplashscreen.hook.NewSystemUIHooker import com.gswxxn.restoresplashscreen.hook.base.BaseHookHandler import com.gswxxn.restoresplashscreen.hook.systemui.GenerateHookHandler.currentActivity +import com.gswxxn.restoresplashscreen.hook.systemui.GenerateHookHandler.currentApplicationInfo import com.gswxxn.restoresplashscreen.hook.systemui.GenerateHookHandler.currentPackageName import com.gswxxn.restoresplashscreen.utils.GraphicUtils import com.gswxxn.restoresplashscreen.utils.IconPackManager @@ -27,6 +28,7 @@ import com.gswxxn.restoresplashscreen.utils.MIUIIconsHelper import com.gswxxn.restoresplashscreen.utils.YukiHelper.atLeastMIUI14 import com.gswxxn.restoresplashscreen.utils.YukiHelper.getDevPrefs import com.gswxxn.restoresplashscreen.utils.YukiHelper.isColorOS +import com.gswxxn.restoresplashscreen.utils.YukiHelper.isMIUI import com.gswxxn.restoresplashscreen.utils.YukiHelper.printLog import com.gswxxn.restoresplashscreen.wrapper.TransparentAdaptiveIconDrawable import com.highcapable.yukihookapi.hook.factory.current @@ -53,7 +55,7 @@ object IconHookHandler : BaseHookHandler() { private var currentUseBigMIUILagerIcon: Boolean? = null private var currentIconDrawable: Drawable? = null private val iconPackManager by lazy { IconPackManager(appContext!!, prefs.get(DataConst.ICON_PACK_PACKAGE_NAME)) } - private val miuiIcons by lazy { MIUIIconsHelper(appContext!!) } + private val miuiIcons by lazy { MIUIIconsHelper(appContext!!, appClassLoader!!) } /** * 重置当前应用的属性 @@ -173,25 +175,30 @@ object IconHookHandler : BaseHookHandler() { // 不使用自带的图标缩放, 防止在 MIUI 上出现图标白边及图标错位 NewSystemUIHooker.Members.normalizeAndWrapToAdaptiveIcon.addBeforeHook { - val scale = instance.current() - .method { name = "getNormalizer"; superClass() }.call()!!.current() - .method { name = "getScale"; paramCount(4); superClass() } - .invoke( - args.first { it is Drawable }, - args.first { it is RectF }, - null, - null - )!! - args(args.indexOfFirst { it is FloatArray }).cast()!![0] = scale - - val oriDrawable = args.first { it is Drawable } as Drawable - val returnType = NewSystemUIHooker.Members.normalizeAndWrapToAdaptiveIcon.returnType - - printLog("normalizeAndWrapToAdaptiveIcon(): avoid shrink icon by system ui") - result = if (returnType == AdaptiveIconDrawable::class.java) - TransparentAdaptiveIconDrawable(oriDrawable) - else - oriDrawable + if (Thread.currentThread().stackTrace.any { it.methodName == "makeSplashScreenContentView" }) { + printLog("normalizeAndWrapToAdaptiveIcon(): avoid shrink icon by system ui") + val boolShrinkNonAdaptiveIconsIndex = args.indexOfFirst { it is Boolean } + if (boolShrinkNonAdaptiveIconsIndex != -1) { + args(boolShrinkNonAdaptiveIconsIndex).setFalse() + } else { + val scale = instance.current(ignored = true) + .method { name = "getNormalizer"; superClass() }.call()?.current() + ?.method { name = "getScale"; paramCount(4); superClass() } + ?.invoke( + args.first { it is Drawable }, + args.first { it is RectF }, + null, + null + ) ?: 0.92f + args(args.indexOfFirst { it is FloatArray }).cast()!![0] = scale + val oriDrawable = args.first { it is Drawable } as Drawable + val returnType = NewSystemUIHooker.Members.normalizeAndWrapToAdaptiveIcon.returnType + result = if (returnType == AdaptiveIconDrawable::class.java) + TransparentAdaptiveIconDrawable(oriDrawable) + else + oriDrawable + } + } } NewSystemUIHooker.Members.createIconBitmap_BaseIconFactory.addBeforeHook { args(0).cast()?.let { drawable -> @@ -338,14 +345,18 @@ object IconHookHandler : BaseHookHandler() { currentPackageName == "com.android.settings" && currentActivity == "com.android.settings.BackgroundApplicationsManager" -> appContext!!.packageManager.getApplicationIcon("com.android.settings") -// isMIUI && miuiIcons.isSupportMIUIModeIcon && currentPackageName != "com.android.fileexplorer" -> { // 在 MIUI 上优先获取完美图标 -// miuiIcons.getFancyIconDrawable(currentPackageName) ?: -// appContext!!.packageManager.getApplicationIcon(currentPackageName) -// } + isMIUI && miuiIcons.isSupportMIUIModeIcon && currentPackageName != "com.android.fileexplorer" -> { // 在 MIUI 上优先获取完美图标 + miuiIcons.getFancyIconDrawable( + currentPackageName, + appUserId, + currentApplicationInfo, + appContext!!.packageManager + ) ?: appContext!!.packageManager.getApplicationIcon(currentPackageName) + } else -> appContext!!.packageManager.getApplicationIcon(currentPackageName) } } return null } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/gswxxn/restoresplashscreen/ui/ConfigAppsActivity.kt b/app/src/main/java/com/gswxxn/restoresplashscreen/ui/ConfigAppsActivity.kt index 9cdb282..d324411 100644 --- a/app/src/main/java/com/gswxxn/restoresplashscreen/ui/ConfigAppsActivity.kt +++ b/app/src/main/java/com/gswxxn/restoresplashscreen/ui/ConfigAppsActivity.kt @@ -1,6 +1,5 @@ package com.gswxxn.restoresplashscreen.ui -import android.content.Context import android.content.Intent import android.content.res.Configuration import android.text.Editable @@ -130,11 +129,11 @@ class ConfigAppsActivity : BaseActivity(), CoroutineS binding.searchEditText.apply { // 焦点事件 setOnFocusChangeListener { v, hasFocus -> - val imm = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + val imm = v.context.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager if (hasFocus) { showView(false, binding.appListTitle, binding.configDescription, binding.configTitleFilter, binding.overallSettings) // 弹出软键盘 - imm.showSoftInput(binding.searchEditText, InputMethodManager.SHOW_FORCED) + imm.showSoftInput(binding.searchEditText, 0) } else { // 隐藏软键盘 imm.hideSoftInputFromWindow(this.windowToken, 0) @@ -162,7 +161,7 @@ class ConfigAppsActivity : BaseActivity(), CoroutineS cView = holder.root cView.tag = holder } else { - holder = cView?.tag as AdapterConfigBinding + holder = cView.tag as AdapterConfigBinding } getItem(position).also { item -> // 设置图标 diff --git a/app/src/main/java/com/gswxxn/restoresplashscreen/utils/MIUIIconsHelper.kt b/app/src/main/java/com/gswxxn/restoresplashscreen/utils/MIUIIconsHelper.kt index 9435679..c0f5aa1 100644 --- a/app/src/main/java/com/gswxxn/restoresplashscreen/utils/MIUIIconsHelper.kt +++ b/app/src/main/java/com/gswxxn/restoresplashscreen/utils/MIUIIconsHelper.kt @@ -2,28 +2,30 @@ package com.gswxxn.restoresplashscreen.utils import android.annotation.SuppressLint import android.content.Context -import android.graphics.drawable.AdaptiveIconDrawable +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager import android.graphics.drawable.Drawable -import android.graphics.drawable.LayerDrawable import android.provider.Settings import com.gswxxn.restoresplashscreen.hook.NewSystemUIHooker.hook -import com.gswxxn.restoresplashscreen.utils.GraphicUtils.getCenterDrawable import com.highcapable.yukihookapi.hook.factory.current import com.highcapable.yukihookapi.hook.factory.field import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.factory.toClass +import com.highcapable.yukihookapi.hook.factory.toClassOrNull import com.highcapable.yukihookapi.hook.log.YLog +import com.highcapable.yukihookapi.hook.type.android.ApplicationInfoClass import com.highcapable.yukihookapi.hook.type.android.ContextClass import com.highcapable.yukihookapi.hook.type.android.UserHandleClass import com.highcapable.yukihookapi.hook.type.java.BooleanType import com.highcapable.yukihookapi.hook.type.java.LongType import com.highcapable.yukihookapi.hook.type.java.StringClass +import com.highcapable.yukihookapi.hook.type.java.JavaClass /** * 用于从 MIUI 桌面检索大图标的辅助类 */ @SuppressLint("DiscouragedApi") -class MIUIIconsHelper(private val context: Context) { +class MIUIIconsHelper(private val context: Context, private val classLoader: ClassLoader) { private val miuiHomeContext = context.createPackageContext( "com.miui.home", Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY @@ -32,10 +34,53 @@ class MIUIIconsHelper(private val context: Context) { if (YukiHelper.atLeastMIUI14) "com.miui.maml.util.LargeIconsHelper".toClass(miuiHomeContext.classLoader) else null - private val appIconsHelper = "com.miui.maml.util.AppIconsHelper".toClass(miuiHomeContext.classLoader) + private val dependencyClazz by lazy { + "com.miui.systemui.MiuiDependency".toClassOrNull(classLoader) + ?: "com.android.systemui.Dependency".toClassOrNull(classLoader) + } + private val mDependencyGet by lazy { + dependencyClazz?.method { + name = "get" + paramCount(1) + param(JavaClass) + }?.ignored()?.give() + } + private val interfacesImplManagerClazz by lazy { + "com.miui.systemui.interfacesmanager.InterfacesImplManager".toClassOrNull(classLoader) + } + private val mImplManagerGet by lazy { + interfacesImplManagerClazz?.method { + name = "getImpl" + paramCount(1) + param(JavaClass) + }?.give() + } + private val appIconsManagerClazz by lazy { + "com.miui.systemui.graphics.AppIconsManager".toClass(classLoader) + } + private val loadAppIcon by lazy { + appIconsManagerClazz.getDeclaredMethod("loadAppIcon", + StringClass, Int::class.java, ApplicationInfoClass, PackageManager::class.java + ) + } + private val appIconsManager by lazy { + mDependencyGet?.invoke(null, appIconsManagerClazz) + ?: mImplManagerGet?.invoke(null, appIconsManagerClazz) + } + private val drawableUtilsClazz by lazy { + "com.miui.utils.DrawableUtils".toClass(classLoader) + } + private val getFancyChildOrSelf by lazy { + drawableUtilsClazz.method { + name = "getFancyChildOrSelf" + paramCount(2) + param(Drawable::class.java, BooleanType) + }.ignored().give() + } + /** 当前是否启用 MIUI 完美图标 */ val isSupportMIUIModeIcon by lazy { - Settings.System.getInt(context.contentResolver, "key_miui_mod_icon_enable", 0) == 1 + Settings.System.getInt(context.contentResolver, "key_miui_mod_icon_enable", 0) == 1 || getFancyChildOrSelf != null } init { @@ -151,33 +196,18 @@ class MIUIIconsHelper(private val context: Context) { * 从指定的应用程序包中获取完美的图标 Drawable * * @param packageName 要获取图标的应用程序包的包名。 + * @param userId 应用程序的用户 ID。 + * @param applicationInfo 应用程序的 ApplicationInfo 对象。 + * @param packageManager 用于获取应用程序信息的 PackageManager 实例。 * @return 如果成功获取到完美图标,则返回一个 BitmapDrawable;如果发生错误,则返回 null。 */ - fun getFancyIconDrawable(packageName: String) = try { - val drawable = appIconsHelper.method { - name = "getIconDrawable" - param(ContextClass, StringClass, StringClass, LongType) - }.get().call( - context, + fun getFancyIconDrawable(packageName: String, userId: Int, applicationInfo: ApplicationInfo?, packageManager: PackageManager?) = try { + loadAppIcon.invoke(appIconsManager, packageName, - null, - getCacheTime(packageName), + userId, + applicationInfo, + packageManager ) - - if (drawable is AdaptiveIconDrawable && drawable.javaClass.name == "com.miui.maml.MamlAdaptiveIconDrawable"){ - val layer0QuietDrawable = drawable.background.current().method { name = "getQuietDrawable" }.invoke()!! - val layerFancyDrawables = drawable.current().method { name = "getLayerFancyDrawables" }.invoke>()!! - - layerFancyDrawables.add(0, layer0QuietDrawable) - - getCenterDrawable( - LayerDrawable(layerFancyDrawables.toTypedArray()), - 0.65f, - context.resources - ) - } else if (drawable?.javaClass?.name == "com.miui.maml.FancyDrawable") { - drawable - } else null } catch (e: Throwable) { YLog.error(msg = "Failed to get FancyIconDrawable with package: $packageName", e = e) null diff --git a/gradle/sweet-dependency/sweet-dependency-config.yaml b/gradle/sweet-dependency/sweet-dependency-config.yaml index ffb214a..c0a3f3e 100644 --- a/gradle/sweet-dependency/sweet-dependency-config.yaml +++ b/gradle/sweet-dependency/sweet-dependency-config.yaml @@ -22,13 +22,13 @@ repositories: plugins: com.android.application: - version: 8.7.3 + version: 8.13.0 org.jetbrains.kotlin.android: - version: 2.1.0 + version: 2.1.21 org.jetbrains.kotlin.plugin.serialization: - version: 2.1.0 + version: 2.1.21 com.google.devtools.ksp: - version: 2.1.0-1.0.29 + version: 2.1.21-2.0.2 libraries: de.robv.android.xposed: @@ -44,12 +44,12 @@ libraries: version: 1.0.0 androidx.compose.material3: material3: - version: 1.3.1 + version: 1.4.0 org.jetbrains.kotlinx: kotlinx-coroutines-android: - version: 1.9.0 + version: 1.10.2 kotlinx-serialization-json: - version: 1.7.3 + version: 1.9.0 androidx.annotation: annotation: version: 1.9.1 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df97d72..37f853b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle.kts b/settings.gradle.kts index f919793..cb7e306 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,7 +9,7 @@ pluginManagement { } plugins { id("com.highcapable.sweetdependency") version "1.0.4" - id("com.highcapable.sweetproperty") version "1.0.5" + id("com.highcapable.sweetproperty") version "1.0.8" } sweetProperty { project(":app") {