From 715fa6cfe6a3e4809eccf562771d6909cbcc8c8f Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Thu, 3 Oct 2024 13:57:45 -0300 Subject: [PATCH] Encode icons in PNG, because JPEG doesn't support transparency This caused black squares around icons. --- .../seedvault/worker/IconManagerTest.kt | 17 +++++++++++++++++ .../stevesoltys/seedvault/worker/IconManager.kt | 15 ++++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/app/src/androidTest/java/com/stevesoltys/seedvault/worker/IconManagerTest.kt b/app/src/androidTest/java/com/stevesoltys/seedvault/worker/IconManagerTest.kt index d7b104374..70aec17ec 100644 --- a/app/src/androidTest/java/com/stevesoltys/seedvault/worker/IconManagerTest.kt +++ b/app/src/androidTest/java/com/stevesoltys/seedvault/worker/IconManagerTest.kt @@ -5,10 +5,14 @@ package com.stevesoltys.seedvault.worker +import android.content.pm.PackageInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import androidx.test.platform.app.InstrumentationRegistry +import com.github.luben.zstd.ZstdOutputStream import com.google.protobuf.ByteString +import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER +import com.stevesoltys.seedvault.metadata.BackupType import com.stevesoltys.seedvault.proto.SnapshotKt.blob import com.stevesoltys.seedvault.repo.AppBackupManager import com.stevesoltys.seedvault.repo.BackupData @@ -32,6 +36,7 @@ import org.junit.runner.RunWith import org.koin.core.component.KoinComponent import org.koin.core.component.inject import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream import kotlin.random.Random @RunWith(AndroidJUnit4::class) @@ -76,6 +81,11 @@ class IconManagerTest : KoinComponent { iconManager.uploadIcons() assertTrue(output.captured.isNotEmpty()) + // @pm@ is needed + val pmPackageInfo = PackageInfo().apply { packageName = MAGIC_PACKAGE_MANAGER } + val backupData = BackupData(emptyList(), emptyMap()) + snapshotCreator.onPackageBackedUp(pmPackageInfo, BackupType.KV, backupData) + // get snapshot and assert it has icon chunks val snapshot = snapshotCreator.finalizeSnapshot() assertTrue(snapshot.iconChunkIdsCount > 0) @@ -106,6 +116,13 @@ class IconManagerTest : KoinComponent { assertTrue(output2.captured.isNotEmpty()) assertArrayEquals(output1.captured, output2.captured) + + // print compressed and uncompressed size + val size = output1.captured.size.toFloat() / 1024 / 1024 + val outputStream = ByteArrayOutputStream() + ZstdOutputStream(outputStream).use { it.write(output1.captured) } + val compressedSize = outputStream.size().toFloat() / 1024 / 1024 + println("Icon size: $size MB, compressed $compressedSize MB") } } diff --git a/app/src/main/java/com/stevesoltys/seedvault/worker/IconManager.kt b/app/src/main/java/com/stevesoltys/seedvault/worker/IconManager.kt index 41598d009..7f0d347f6 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/worker/IconManager.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/worker/IconManager.kt @@ -6,7 +6,7 @@ package com.stevesoltys.seedvault.worker import android.content.Context -import android.graphics.Bitmap.CompressFormat.JPEG +import android.graphics.Bitmap.CompressFormat.PNG import android.graphics.BitmapFactory import android.graphics.drawable.Drawable import android.util.Log @@ -40,8 +40,7 @@ import java.util.zip.ZipEntry import java.util.zip.ZipInputStream import java.util.zip.ZipOutputStream -private const val ICON_SIZE = 128 -private const val ICON_QUALITY = 75 +private const val ICON_SIZE = 64 private const val CACHE_FOLDER = "restore-icons" private val TAG = IconManager::class.simpleName @@ -75,8 +74,10 @@ internal class IconManager( setLastModifiedTime(FileTime.fromMillis(0)) } zip.putNextEntry(entry) - // WEBP_LOSSY compression wasn't deterministic in our tests, so use JPEG - drawable.toBitmap(ICON_SIZE, ICON_SIZE).compress(JPEG, ICON_QUALITY, zip) + // WEBP_LOSSY compression wasn't deterministic in our tests, + // and JPEG doesn't support transparency (causing black squares), + // so use PNG + drawable.toBitmap(ICON_SIZE, ICON_SIZE).compress(PNG, 0, zip) entries.add(it.packageName) zip.closeEntry() } @@ -91,8 +92,8 @@ internal class IconManager( setLastModifiedTime(FileTime.fromMillis(0)) } zip.putNextEntry(entry) - // WEBP_LOSSY compression wasn't deterministic in our tests, so use JPEG - drawable.toBitmap(ICON_SIZE, ICON_SIZE).compress(JPEG, ICON_QUALITY, zip) + // For PNG choice see comment above + drawable.toBitmap(ICON_SIZE, ICON_SIZE).compress(PNG, 0, zip) zip.closeEntry() } }