mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	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.
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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(
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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(
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,6 @@ dependencies {
 | 
			
		||||
 | 
			
		||||
    implementation(libs.unifile)
 | 
			
		||||
 | 
			
		||||
    implementation(kotlinx.reflect)
 | 
			
		||||
    api(kotlinx.coroutines.core)
 | 
			
		||||
    api(kotlinx.serialization.json)
 | 
			
		||||
    api(kotlinx.serialization.json.okio)
 | 
			
		||||
 
 | 
			
		||||
@@ -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 : Any> T.asBooleanArray(): BooleanArray {
 | 
			
		||||
    val constructorParams = this::class.primaryConstructor!!.parameters.map { it.name }
 | 
			
		||||
    val properties = this::class.declaredMemberProperties
 | 
			
		||||
        .filterIsInstance<KProperty1<T, Boolean>>()
 | 
			
		||||
    return constructorParams
 | 
			
		||||
        .map { param -> properties.find { it.name == param }!!.get(this) }
 | 
			
		||||
        .toBooleanArray()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <reified T : Any> BooleanArray.asDataClass(): T {
 | 
			
		||||
    val properties = T::class.declaredMemberProperties.filterIsInstance<KProperty1<T, Boolean>>()
 | 
			
		||||
    require(properties.size == this.size) { "Boolean array size does not match data class property count" }
 | 
			
		||||
    return T::class.primaryConstructor!!.call(*this.toTypedArray())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun <T : Any> T.anyEnabled(): Boolean {
 | 
			
		||||
    return this::class.declaredMemberProperties
 | 
			
		||||
        .filterIsInstance<KProperty1<T, Boolean>>()
 | 
			
		||||
        .any { it.get(this) }
 | 
			
		||||
}
 | 
			
		||||
@@ -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<ClassCastException> {
 | 
			
		||||
            InvalidTestClass(foo = true, bar = "").asBooleanArray()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun `asDataClass converts from boolean array`() {
 | 
			
		||||
        assertEquals(booleanArrayOf(true, false).asDataClass<TestClass>(), TestClass(foo = true, bar = false))
 | 
			
		||||
        assertEquals(booleanArrayOf(false, true).asDataClass<TestClass>(), TestClass(foo = false, bar = true))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun `asDataClass throws error for invalid boolean array`() {
 | 
			
		||||
        assertThrows<IllegalArgumentException> {
 | 
			
		||||
            booleanArrayOf(true).asDataClass<TestClass>()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @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<ClassCastException> {
 | 
			
		||||
            InvalidTestClass(foo = true, bar = "").anyEnabled()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data class TestClass(
 | 
			
		||||
        val foo: Boolean,
 | 
			
		||||
        val bar: Boolean,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class InvalidTestClass(
 | 
			
		||||
        val foo: Boolean,
 | 
			
		||||
        val bar: String,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user