Skip to content

Commit

Permalink
Migrate to XML Settings from Preferences (#722)
Browse files Browse the repository at this point in the history
* Migrate to XML Settings from Preferences

* Lint
  • Loading branch information
Syer10 authored Oct 29, 2023
1 parent 60015bc commit 583a2f0
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,61 @@ package xyz.nulldev.androidcompat.io.sharedprefs

import android.content.SharedPreferences
import com.russhwolf.settings.ExperimentalSettingsApi
import com.russhwolf.settings.PreferencesSettings
import com.russhwolf.settings.PropertiesSettings
import com.russhwolf.settings.Settings
import com.russhwolf.settings.serialization.decodeValue
import com.russhwolf.settings.serialization.decodeValueOrNull
import com.russhwolf.settings.serialization.encodeValue
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.builtins.SetSerializer
import kotlinx.serialization.builtins.serializer
import java.util.prefs.PreferenceChangeListener
import java.util.prefs.Preferences
import mu.KotlinLogging
import xyz.nulldev.ts.config.ApplicationRootDir
import java.util.Properties
import kotlin.io.path.Path
import kotlin.io.path.createParentDirectories
import kotlin.io.path.deleteIfExists
import kotlin.io.path.exists
import kotlin.io.path.inputStream
import kotlin.io.path.outputStream

@OptIn(ExperimentalSerializationApi::class, ExperimentalSettingsApi::class)
class JavaSharedPreferences(key: String) : SharedPreferences {
private val javaPreferences = Preferences.userRoot().node("suwayomi/tachidesk/$key")
private val preferences = PreferencesSettings(javaPreferences)
private val listeners = mutableMapOf<SharedPreferences.OnSharedPreferenceChangeListener, PreferenceChangeListener>()
companion object {
private val logger = KotlinLogging.logger {}
}

private val file = Path(ApplicationRootDir, "settings", "$key.xml")
private val properties =
Properties().also { properties ->
try {
if (file.exists()) {
file.inputStream().use { properties.loadFromXML(it) }
}
} catch (e: Exception) {
logger.error(e) { "Error loading settings from $key" }
}
}
private val preferences =
PropertiesSettings(
properties,
onModify = { properties ->
try {
if (properties.isEmpty) {
file.deleteIfExists()
} else {
file.createParentDirectories()
file.outputStream().use {
properties.storeToXML(it, null)
}
}
} catch (e: Exception) {
logger.error(e) { "Error saving settings in $key" }
}
},
)
private val listeners = mutableMapOf<SharedPreferences.OnSharedPreferenceChangeListener, (String) -> Unit>()

// TODO: 2021-05-29 Need to find a way to get this working with all pref types
override fun getAll(): MutableMap<String, *> {
Expand Down Expand Up @@ -90,17 +129,21 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
}

override fun edit(): SharedPreferences.Editor {
return Editor(preferences)
return Editor(preferences) { key ->
listeners.forEach { (_, listener) ->
listener(key)
}
}
}

class Editor(private val preferences: PreferencesSettings) : SharedPreferences.Editor {
class Editor(private val preferences: Settings, private val notify: (String) -> Unit) : SharedPreferences.Editor {
private val actions = mutableListOf<Action>()

private sealed class Action {
data class Add(val key: String, val value: Any) : Action()

data class Remove(val key: String) : Action()
object Clear : Action()
data object Clear : Action()
}

override fun putString(
Expand Down Expand Up @@ -182,7 +225,7 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
actions.forEach {
@Suppress("UNCHECKED_CAST")
when (it) {
is Action.Add ->
is Action.Add -> {
when (val value = it.value) {
is Set<*> -> preferences.encodeValue(SetSerializer(String.serializer()), it.key, value as Set<String>)
is String -> preferences.putString(it.key, value)
Expand All @@ -192,6 +235,8 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
is Double -> preferences.putDouble(it.key, value)
is Boolean -> preferences.putBoolean(it.key, value)
}
notify(it.key)
}
is Action.Remove -> {
preferences.remove(it.key)
/**
Expand All @@ -205,6 +250,8 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
preferences.remove(key)
}
}

notify(it.key)
}
Action.Clear -> preferences.clear()
}
Expand All @@ -213,23 +260,18 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
}

override fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
val javaListener =
PreferenceChangeListener {
listener.onSharedPreferenceChanged(this, it.key)
}
val javaListener: (String) -> Unit = {
listener.onSharedPreferenceChanged(this, it)
}
listeners[listener] = javaListener
javaPreferences.addPreferenceChangeListener(javaListener)
}

override fun unregisterOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
val registeredListener = listeners.remove(listener)
if (registeredListener != null) {
javaPreferences.removePreferenceChangeListener(registeredListener)
}
listeners.remove(listener)
}

fun deleteAll(): Boolean {
javaPreferences.removeNode()
preferences.clear()
return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package suwayomi.tachidesk.manga.impl.backup.proto
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

import android.app.Application
import android.content.Context
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import kotlinx.coroutines.flow.combine
import mu.KotlinLogging
Expand Down Expand Up @@ -38,21 +40,22 @@ import suwayomi.tachidesk.manga.model.table.toDataClass
import suwayomi.tachidesk.server.ApplicationDirs
import suwayomi.tachidesk.server.serverConfig
import suwayomi.tachidesk.util.HAScheduler
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.InputStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.concurrent.TimeUnit
import java.util.prefs.Preferences
import kotlin.time.Duration.Companion.days

object ProtoBackupExport : ProtoBackupBase() {
private val logger = KotlinLogging.logger { }
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private var backupSchedulerJobId: String = ""
private const val LAST_AUTOMATED_BACKUP_KEY = "lastAutomatedBackupKey"
private val preferences = Preferences.userNodeForPackage(ProtoBackupExport::class.java)
private val preferences = Injekt.get<Application>().getSharedPreferences("manga/impl/backup/proto", Context.MODE_PRIVATE)

init {
serverConfig.subscribeTo(
Expand All @@ -77,7 +80,7 @@ object ProtoBackupExport : ProtoBackupBase() {
val task = {
cleanupAutomatedBackups()
createAutomatedBackup()
preferences.putLong(LAST_AUTOMATED_BACKUP_KEY, System.currentTimeMillis())
preferences.edit().putLong(LAST_AUTOMATED_BACKUP_KEY, System.currentTimeMillis()).apply()
}

val (hour, minute) = serverConfig.backupTime.value.split(":").map { it.toInt() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ object DownloadManager {
Injekt.get<Application>().getSharedPreferences(DownloadManager::class.jvmName, Context.MODE_PRIVATE)

private fun loadDownloadQueue(): List<Int> {
return sharedPreferences.getStringSet(DOWNLOAD_QUEUE_KEY, emptySet())?.mapNotNull { it.toInt() } ?: emptyList()
return sharedPreferences.getStringSet(DOWNLOAD_QUEUE_KEY, emptySet())?.mapNotNull { it.toInt() }.orEmpty()
}

private fun saveDownloadQueue() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package suwayomi.tachidesk.manga.impl.update

import android.app.Application
import android.content.Context
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -27,9 +29,10 @@ import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.manga.model.table.MangaStatus
import suwayomi.tachidesk.server.serverConfig
import suwayomi.tachidesk.util.HAScheduler
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Date
import java.util.concurrent.ConcurrentHashMap
import java.util.prefs.Preferences
import kotlin.math.absoluteValue
import kotlin.time.Duration.Companion.hours

Expand All @@ -48,7 +51,7 @@ class Updater : IUpdater {

private val lastUpdateKey = "lastUpdateKey"
private val lastAutomatedUpdateKey = "lastAutomatedUpdateKey"
private val preferences = Preferences.userNodeForPackage(Updater::class.java)
private val preferences = Injekt.get<Application>().getSharedPreferences("manga/impl/update", Context.MODE_PRIVATE)

private var currentUpdateTaskId = ""

Expand Down Expand Up @@ -80,7 +83,7 @@ class Updater : IUpdater {

private fun autoUpdateTask() {
val lastAutomatedUpdate = preferences.getLong(lastAutomatedUpdateKey, 0)
preferences.putLong(lastAutomatedUpdateKey, System.currentTimeMillis())
preferences.edit().putLong(lastAutomatedUpdateKey, System.currentTimeMillis()).apply()

if (status.value.running) {
logger.debug { "Global update is already in progress" }
Expand Down Expand Up @@ -178,7 +181,7 @@ class Updater : IUpdater {
clear: Boolean?,
forceAll: Boolean,
) {
preferences.putLong(lastUpdateKey, System.currentTimeMillis())
preferences.edit().putLong(lastUpdateKey, System.currentTimeMillis()).apply()

if (clear == true) {
reset()
Expand Down
44 changes: 44 additions & 0 deletions server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package suwayomi.tachidesk.server
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

import android.app.Application
import android.content.Context
import ch.qos.logback.classic.Level
import com.typesafe.config.ConfigRenderOptions
import eu.kanade.tachiyomi.App
Expand All @@ -31,6 +33,8 @@ import suwayomi.tachidesk.server.database.databaseUp
import suwayomi.tachidesk.server.generated.BuildConfig
import suwayomi.tachidesk.server.util.AppMutex.handleAppMutex
import suwayomi.tachidesk.server.util.SystemTray
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import xyz.nulldev.androidcompat.AndroidCompat
import xyz.nulldev.androidcompat.AndroidCompatInitializer
import xyz.nulldev.ts.config.ApplicationRootDir
Expand All @@ -42,6 +46,9 @@ import xyz.nulldev.ts.config.setLogLevelFor
import java.io.File
import java.security.Security
import java.util.Locale
import java.util.prefs.Preferences
import kotlin.io.path.exists
import kotlin.io.path.outputStream

private val logger = KotlinLogging.logger {}

Expand Down Expand Up @@ -211,6 +218,10 @@ fun applicationSetup() {
}
}, ignoreInitialValue = false)

val preferences = Preferences.userRoot().node("suwayomi/tachidesk")
migratePreferences(null, preferences)
preferences.removeNode()

// Disable jetty's logging
System.setProperty("org.eclipse.jetty.util.log.announce", "false")
System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog")
Expand Down Expand Up @@ -250,3 +261,36 @@ fun applicationSetup() {
// start DownloadManager and restore + resume downloads
DownloadManager.restoreAndResumeDownloads()
}

fun migratePreferences(
parent: String?,
rootNode: Preferences,
) {
val subNodes = rootNode.childrenNames()

for (subNodeName in subNodes) {
val subNode = rootNode.node(subNodeName)
val key =
if (parent != null) {
"$parent/$subNodeName"
} else {
subNodeName
}
val preferences = Injekt.get<Application>().getSharedPreferences(key, Context.MODE_PRIVATE)

val items: Map<String, String?> =
subNode.keys().associateWith {
subNode[it, null]?.ifBlank { null }
}

preferences.edit().apply {
items.forEach { (key, value) ->
if (value != null) {
putString(key, value)
}
}
}.apply()

migratePreferences(key, subNode) // Recursively migrate sub-level nodes
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package suwayomi.tachidesk.server.util
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

import android.app.Application
import android.content.Context
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.awaitSuccess
Expand Down Expand Up @@ -45,6 +47,8 @@ import suwayomi.tachidesk.server.ApplicationDirs
import suwayomi.tachidesk.server.generated.BuildConfig
import suwayomi.tachidesk.server.serverConfig
import suwayomi.tachidesk.util.HAScheduler
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.InputStream
Expand All @@ -53,7 +57,6 @@ import java.net.URL
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.util.Date
import java.util.prefs.Preferences
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.seconds

Expand Down Expand Up @@ -124,7 +127,7 @@ object WebInterfaceManager {

private const val LAST_WEBUI_UPDATE_CHECK_KEY = "lastWebUIUpdateCheckKey"

private val preferences = Preferences.userNodeForPackage(WebInterfaceManager::class.java)
private val preferences = Injekt.get<Application>().getSharedPreferences("server/util", Context.MODE_PRIVATE)
private var currentUpdateTaskId: String = ""

private val json: Json by injectLazy()
Expand Down Expand Up @@ -326,7 +329,7 @@ object WebInterfaceManager {
}

private suspend fun checkForUpdate() {
preferences.putLong(LAST_WEBUI_UPDATE_CHECK_KEY, System.currentTimeMillis())
preferences.edit().putLong(LAST_WEBUI_UPDATE_CHECK_KEY, System.currentTimeMillis()).apply()
val localVersion = getLocalVersion()

if (!isUpdateAvailable(localVersion).second) {
Expand Down

0 comments on commit 583a2f0

Please sign in to comment.