fix: adaptive icon patching

- Non-adaptive icon patching was replacing the adaptive icon axml file when it was unnecessary
- Adaptive icon patching was only running for API 28+ despite being supported on API 26+
This commit is contained in:
rushiiMachine 2025-05-25 15:04:31 -07:00
parent b2a8111f96
commit 291766b864
No known key found for this signature in database
GPG key ID: DCBE5952BB3B6420
3 changed files with 34 additions and 32 deletions

View file

@ -50,7 +50,7 @@ class PatchIconsStep(private val options: PatchOptions) : Step(), KoinComponent
override val localizedName = R.string.patch_step_patch_icon
// Adaptive icons are available starting with Android 8
private val isAdaptiveIconsAvailable = Build.VERSION.SDK_INT >= 28
private val isAdaptiveIconsAvailable = Build.VERSION.SDK_INT >= 26
// Monochrome icons are always patched starting with Android 12
private val isMonochromeIconsAvailable = Build.VERSION.SDK_INT >= 31
@ -90,9 +90,15 @@ class PatchIconsStep(private val options: PatchOptions) : Step(), KoinComponent
arsc: BinaryResourceFile,
) {
container.log("Parsing AndroidManifest.xml and obtaining adaptive square/round icon file paths")
val iconRscIds = AxmlUtil.readManifestIconInfo(apk)
val squareIconFile = arsc.getMainArscChunk().getResourceFileName(iconRscIds.squareIcon, "anydpi-v26")
val roundIconFile = arsc.getMainArscChunk().getResourceFileName(iconRscIds.roundIcon, "anydpi-v26")
val iconResourceIds = AxmlUtil.readManifestIconInfo(apk)
val squareIconFile = arsc.getMainArscChunk().getResourceFileName(
resourceId = iconResourceIds.squareIcon,
configurationName = "anydpi-v26"
)
val roundIconFile = arsc.getMainArscChunk().getResourceFileName(
resourceId = iconResourceIds.roundIcon,
configurationName = "anydpi-v26"
)
var foregroundIcon: BinaryResourceIdentifier? = null
var backgroundIcon: BinaryResourceIdentifier? = null
@ -174,9 +180,15 @@ class PatchIconsStep(private val options: PatchOptions) : Step(), KoinComponent
arsc: BinaryResourceFile,
) {
container.log("Parsing AndroidManifest.xml and obtaining all square/round launcher icon file paths")
val iconRscIds = AxmlUtil.readManifestIconInfo(apk)
val squareIconFiles = arsc.getMainArscChunk().getResourceFileNames(iconRscIds.squareIcon)
val roundIconFiles = arsc.getMainArscChunk().getResourceFileNames(iconRscIds.roundIcon)
val iconResourceIds = AxmlUtil.readManifestIconInfo(apk)
val squareIconFiles = arsc.getMainArscChunk().getResourceFileNames(
resourceId = iconResourceIds.squareIcon,
configurations = { it.toString() != "anydpi-v26" },
)
val roundIconFiles = arsc.getMainArscChunk().getResourceFileNames(
resourceId = iconResourceIds.roundIcon,
configurations = { it.toString() != "anydpi-v26" },
)
val allIconFiles = squareIconFiles + roundIconFiles
val backgroundColor = when (options.iconReplacement) {

View file

@ -125,45 +125,35 @@ object ArscUtil {
*
* @param resourceId The target resource id.
* @param configurationName The target configuration variant of the resource. (ex: `anydpi-v26`, `xxhdpi`, `ldtrl-mpi`, etc.)
* @return The string value of the resource, which should be a file path into the apk.
* @return The string value of the resource for the specified configuration.
*/
fun ResourceTableChunk.getResourceFileName(
resourceId: BinaryResourceIdentifier,
configurationName: String,
): String {
val packageChunk = this.packages.find { it.id == resourceId.packageId() }
?: error("Unable to find target resource")
val typeChunk = packageChunk.getTypeChunks(resourceId.typeId())
.find { it.configuration.toString() == configurationName }
?: error("Unable to find target resource")
val entry = try {
typeChunk.getEntry(resourceId.entryId())!!
} catch (_: Throwable) {
error("Unable to find target resource")
}
if (entry.isComplex || entry.value().type() != BinaryResourceValue.Type.STRING)
error("Target resource value type is not STRING")
val valueIdx = entry.value().data()
val value = this.stringPool.getString(valueIdx)
return value
return getResourceFileNames(
resourceId = resourceId,
configurations = { it.toString() == configurationName },
).single()
}
/**
* In an arsc file, for a specific resource in all configurations, get all values.
* In an arsc file, for a specific resource in all matching configurations, get the values.
*
* @param resourceId The target resource id.
* @return The string value of all configuration values of the resource, which should be file paths into the apk.
* @param configurations A predicate whether to add the value into a matching type chunk.
* @return The string values of the resources, for each configuration.
*/
fun ResourceTableChunk.getResourceFileNames(resourceId: BinaryResourceIdentifier): List<String> {
fun ResourceTableChunk.getResourceFileNames(
resourceId: BinaryResourceIdentifier,
configurations: (BinaryResourceConfiguration) -> Boolean,
): List<String> {
val packageChunk = this.packages.find { it.id == resourceId.packageId() }
?: error("Unable to find target resource")
val typeChunks = packageChunk.getTypeChunks(resourceId.typeId())
.filter { configurations(it.configuration) }
val entries = typeChunks.map { typeChunk ->
val entry = typeChunk.getEntry(resourceId.entryId())
?: error("Unable to find target resource in type chunk " + typeChunk.configuration)

View file

@ -75,7 +75,7 @@ fun IconOptionsScreenContent(
selectedImage: () -> ByteArray?,
setSelectedImage: (Uri) -> Unit,
) {
val isAdaptiveIconsAvailable = Build.VERSION.SDK_INT >= 28
val isAdaptiveIconsAvailable = Build.VERSION.SDK_INT >= 26
Scaffold(
topBar = { IconOptionsAppBar() },