From 6ab8e1e73dbcc65d693dd2ed2680c950139dadde Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 30 Dec 2023 20:29:12 -0500 Subject: [PATCH] Don't use reflection for handling backup options as boolean array Wasn't working correctly in release build, _probably_ because of R8 despite kotlin-reflect shipping with Proguard rules and us already keeping all Tachiyomi classes. --- .../screen/data/RestoreBackupScreen.kt | 1 - .../data/backup/create/BackupCreateJob.kt | 4 +- .../data/backup/create/BackupOptions.kt | 22 +++++++ .../data/backup/restore/BackupRestoreJob.kt | 4 +- .../data/backup/restore/RestoreOptions.kt | 14 +++++ core/build.gradle.kts | 1 - .../util/lang/BooleanDataClassExtensions.kt | 26 -------- .../lang/BooleanDataClassExtensionsTest.kt | 63 ------------------- 8 files changed, 38 insertions(+), 97 deletions(-) delete mode 100644 core/src/main/java/tachiyomi/core/util/lang/BooleanDataClassExtensions.kt delete mode 100644 core/src/test/kotlin/tachiyomi/core/util/lang/BooleanDataClassExtensionsTest.kt diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/RestoreBackupScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/RestoreBackupScreen.kt index f5fe02973f..1e5e36169e 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/RestoreBackupScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/RestoreBackupScreen.kt @@ -32,7 +32,6 @@ import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob import eu.kanade.tachiyomi.data.backup.restore.RestoreOptions import eu.kanade.tachiyomi.util.system.DeviceUtil import kotlinx.coroutines.flow.update -import tachiyomi.core.util.lang.anyEnabled import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.LabeledCheckbox import tachiyomi.presentation.core.components.LazyColumnWithAction diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt index 3d9af64032..cd607480d4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt @@ -24,8 +24,6 @@ import eu.kanade.tachiyomi.util.system.isRunning import eu.kanade.tachiyomi.util.system.setForegroundSafely import eu.kanade.tachiyomi.util.system.workManager import logcat.LogPriority -import tachiyomi.core.util.lang.asBooleanArray -import tachiyomi.core.util.lang.asDataClass import tachiyomi.core.util.system.logcat import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.storage.service.StorageManager @@ -49,7 +47,7 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete setForegroundSafely() - val options: BackupOptions = inputData.getBooleanArray(OPTIONS_KEY)?.asDataClass() + val options = inputData.getBooleanArray(OPTIONS_KEY)?.let { BackupOptions.fromBooleanArray(it) } ?: BackupOptions() return try { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupOptions.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupOptions.kt index 7fc4dff1c2..dbb848500d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupOptions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupOptions.kt @@ -15,6 +15,17 @@ data class BackupOptions( val privateSettings: Boolean = false, ) { + fun asBooleanArray() = booleanArrayOf( + libraryEntries, + categories, + chapters, + tracking, + history, + appSettings, + sourceSettings, + privateSettings, + ) + companion object { val libraryOptions = persistentListOf( Entry( @@ -66,6 +77,17 @@ data class BackupOptions( enabled = { it.appSettings || it.sourceSettings }, ), ) + + fun fromBooleanArray(array: BooleanArray) = BackupOptions( + libraryEntries = array[0], + categories = array[1], + chapters = array[2], + tracking = array[3], + history = array[4], + appSettings = array[5], + sourceSettings = array[6], + privateSettings = array[7], + ) } data class Entry( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt index 5d24e9fa63..180e8f055a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt @@ -20,8 +20,6 @@ import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.CancellationException import logcat.LogPriority import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.lang.asBooleanArray -import tachiyomi.core.util.lang.asDataClass import tachiyomi.core.util.system.logcat import tachiyomi.i18n.MR @@ -32,7 +30,7 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet override suspend fun doWork(): Result { val uri = inputData.getString(LOCATION_URI_KEY)?.toUri() - val options: RestoreOptions? = inputData.getBooleanArray(OPTIONS_KEY)?.asDataClass() + val options = inputData.getBooleanArray(OPTIONS_KEY)?.let { RestoreOptions.fromBooleanArray(it) } if (uri == null || options == null) { return Result.failure() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/RestoreOptions.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/RestoreOptions.kt index 3f5a9290c5..c824cb3d4f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/RestoreOptions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/RestoreOptions.kt @@ -10,6 +10,14 @@ data class RestoreOptions( val sourceSettings: Boolean = true, ) { + fun asBooleanArray() = booleanArrayOf( + library, + appSettings, + sourceSettings, + ) + + fun anyEnabled() = library || appSettings || sourceSettings + companion object { val options = persistentListOf( Entry( @@ -28,6 +36,12 @@ data class RestoreOptions( setter = { options, enabled -> options.copy(sourceSettings = enabled) }, ), ) + + fun fromBooleanArray(array: BooleanArray) = RestoreOptions( + library = array[0], + appSettings = array[1], + sourceSettings = array[2], + ) } data class Entry( diff --git a/core/build.gradle.kts b/core/build.gradle.kts index fd748d7000..e90a1fd060 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -33,7 +33,6 @@ dependencies { implementation(libs.unifile) - implementation(kotlinx.reflect) api(kotlinx.coroutines.core) api(kotlinx.serialization.json) api(kotlinx.serialization.json.okio) diff --git a/core/src/main/java/tachiyomi/core/util/lang/BooleanDataClassExtensions.kt b/core/src/main/java/tachiyomi/core/util/lang/BooleanDataClassExtensions.kt deleted file mode 100644 index d781c678f6..0000000000 --- a/core/src/main/java/tachiyomi/core/util/lang/BooleanDataClassExtensions.kt +++ /dev/null @@ -1,26 +0,0 @@ -package tachiyomi.core.util.lang - -import kotlin.reflect.KProperty1 -import kotlin.reflect.full.declaredMemberProperties -import kotlin.reflect.full.primaryConstructor - -fun T.asBooleanArray(): BooleanArray { - val constructorParams = this::class.primaryConstructor!!.parameters.map { it.name } - val properties = this::class.declaredMemberProperties - .filterIsInstance>() - return constructorParams - .map { param -> properties.find { it.name == param }!!.get(this) } - .toBooleanArray() -} - -inline fun BooleanArray.asDataClass(): T { - val properties = T::class.declaredMemberProperties.filterIsInstance>() - require(properties.size == this.size) { "Boolean array size does not match data class property count" } - return T::class.primaryConstructor!!.call(*this.toTypedArray()) -} - -fun T.anyEnabled(): Boolean { - return this::class.declaredMemberProperties - .filterIsInstance>() - .any { it.get(this) } -} diff --git a/core/src/test/kotlin/tachiyomi/core/util/lang/BooleanDataClassExtensionsTest.kt b/core/src/test/kotlin/tachiyomi/core/util/lang/BooleanDataClassExtensionsTest.kt deleted file mode 100644 index d75e7b3f82..0000000000 --- a/core/src/test/kotlin/tachiyomi/core/util/lang/BooleanDataClassExtensionsTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -package tachiyomi.core.util.lang - -import org.junit.jupiter.api.Assertions.assertArrayEquals -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.parallel.Execution -import org.junit.jupiter.api.parallel.ExecutionMode - -@Execution(ExecutionMode.CONCURRENT) -class BooleanDataClassExtensionsTest { - - @Test - fun `asBooleanArray converts data class to boolean array`() { - assertArrayEquals(booleanArrayOf(true, false), TestClass(foo = true, bar = false).asBooleanArray()) - assertArrayEquals(booleanArrayOf(false, true), TestClass(foo = false, bar = true).asBooleanArray()) - } - - @Test - fun `asBooleanArray throws error for invalid data classes`() { - assertThrows { - InvalidTestClass(foo = true, bar = "").asBooleanArray() - } - } - - @Test - fun `asDataClass converts from boolean array`() { - assertEquals(booleanArrayOf(true, false).asDataClass(), TestClass(foo = true, bar = false)) - assertEquals(booleanArrayOf(false, true).asDataClass(), TestClass(foo = false, bar = true)) - } - - @Test - fun `asDataClass throws error for invalid boolean array`() { - assertThrows { - booleanArrayOf(true).asDataClass() - } - } - - @Test - fun `anyEnabled returns based on if any boolean property is enabled`() { - assertTrue(TestClass(foo = false, bar = true).anyEnabled()) - assertFalse(TestClass(foo = false, bar = false).anyEnabled()) - } - - @Test - fun `anyEnabled throws error for invalid class`() { - assertThrows { - InvalidTestClass(foo = true, bar = "").anyEnabled() - } - } - - data class TestClass( - val foo: Boolean, - val bar: Boolean, - ) - - data class InvalidTestClass( - val foo: Boolean, - val bar: String, - ) -}