Remove Google DataStore. save data directly into files. enable obfuscation.

This commit is contained in:
Thomas Andres Gomez 2024-11-08 16:38:25 +01:00
parent 81d725e224
commit ba0cc30a1a
8 changed files with 108 additions and 90 deletions

View file

@ -23,10 +23,7 @@ kotlin {
implementation(libs.androidx.lifecycle.viewmodel)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.datastore.preferences)
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
implementation(libs.kotlinx.serialization.json)
}
commonTest.dependencies {
@ -61,7 +58,7 @@ compose.desktop {
}
buildTypes.release.proguard {
obfuscate.set(false) // Obfuscation crash when try to use datastore.
obfuscate.set(true) // Obfuscation crash at runtime when try to use datastore.
configurationFiles.from(project.file("compose-desktop.pro"))
}
}

View file

@ -1,20 +0,0 @@
package com.pixelized.desktop.lwa
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import com.pixelized.desktop.lwa.utils.OperatingSystem
import okio.Path.Companion.toPath
fun createDataStore(producePath: () -> String): DataStore<Preferences> {
return PreferenceDataStoreFactory.createWithPath(produceFile = { producePath().toPath() })
}
fun dataStorePath(): String {
val root = when {
OperatingSystem.isWindows() -> "${OperatingSystem.home}\\AppData\\Roaming\\Pixelized\\"
OperatingSystem.isMacintosh() -> "${OperatingSystem.home}/Library/Pixelized/"
else -> ""
}
return "${root}characterssheet.preferences_pb"
}

View file

@ -0,0 +1,27 @@
package com.pixelized.desktop.lwa.repository
private fun operatingSystem(
name: String = System.getProperty("os.name")
): OperatingSystem {
return when {
name.contains(other = "win", ignoreCase = true) -> OperatingSystem.Windows
name.contains(other = "mac", ignoreCase = true) -> OperatingSystem.Macintosh
else -> error("Unsupported operating system: $name")
}
}
enum class OperatingSystem(
val home: String,
) {
Windows(home = System.getProperty("user.home")),
Macintosh(home = System.getProperty("user.home")),
}
fun storePath(
operatingSystem: OperatingSystem = operatingSystem()
): String {
return when (operatingSystem) {
OperatingSystem.Windows -> "${operatingSystem.home}\\AppData\\Roaming\\Pixelized\\"
OperatingSystem.Macintosh -> "${operatingSystem.home}/Library/Pixelized/"
}
}

View file

@ -1,37 +0,0 @@
package com.pixelized.desktop.lwa.repository.characterSheet
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
class CharacterSheetPreference(
private val dataStore: DataStore<Preferences>,
) {
suspend fun save(sheets: List<CharacterSheet>) {
dataStore.edit {
it[characterSheetKey] = Json.encodeToString(sheets)
}
}
suspend fun load(): List<CharacterSheet> {
return loadFlow().first()
}
fun loadFlow(): Flow<List<CharacterSheet>> {
return dataStore.data.map {
it[characterSheetKey]?.let { json ->
Json.decodeFromString<List<CharacterSheet>>(json)
} ?: emptyList()
}
}
companion object {
private val characterSheetKey = stringPreferencesKey("CharacterSheetsPrefKey")
}
}

View file

@ -1,7 +1,5 @@
package com.pixelized.desktop.lwa.repository.characterSheet
import com.pixelized.desktop.lwa.createDataStore
import com.pixelized.desktop.lwa.dataStorePath
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
@ -11,11 +9,9 @@ import kotlinx.coroutines.flow.stateIn
object CharacterSheetRepository {
private val scope = CoroutineScope(Dispatchers.IO)
private val preferences = CharacterSheetPreference(
dataStore = createDataStore { dataStorePath() }
)
private val store = CharacterSheetStore()
private val sheets = preferences.loadFlow()
private val sheets = store.loadFlow()
.stateIn(
scope = scope,
started = SharingStarted.Eagerly,
@ -38,8 +34,8 @@ object CharacterSheetRepository {
)
}
suspend fun save(characterSheet: CharacterSheet) {
val savedSheets = preferences.load().toMutableList()
fun save(characterSheet: CharacterSheet) {
val savedSheets = store.load().toMutableList()
val savedIndex = savedSheets.indexOfFirst { it.id == characterSheet.id }
if (savedIndex >= 0) {
// this sheet is already saved. update it
@ -49,13 +45,13 @@ object CharacterSheetRepository {
savedSheets.add(characterSheet)
}
// save the list of characters sheet.
preferences.save(sheets = savedSheets)
store.save(sheets = savedSheets)
}
suspend fun delete(id: String) {
val savedSheets = preferences.load().toMutableList()
fun delete(id: String) {
val savedSheets = store.load().toMutableList()
savedSheets.removeIf { it.id == id }
preferences.save(sheets = savedSheets)
store.save(sheets = savedSheets)
}
}

View file

@ -0,0 +1,69 @@
package com.pixelized.desktop.lwa.repository.characterSheet
import com.pixelized.desktop.lwa.repository.storePath
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File
class CharacterSheetStore {
private val root = File(storePath()).also { it.mkdirs() }
private val files = File(root, NAME).also { it.createNewFile() }
private val flow = MutableStateFlow(value = load())
@Throws(
CharacterSheetStoreException::class,
FileWriteException::class,
JsonConversionException::class,
)
fun save(sheets: List<CharacterSheet>) {
val json = try {
Json.encodeToString(sheets)
} catch (exception: Exception) {
throw JsonConversionException(root = exception)
}
try {
files.writeText(
text = json,
charset = Charsets.UTF_8,
)
} catch (exception: Exception) {
throw FileWriteException(root = exception)
}
flow.value = sheets
}
@Throws(
CharacterSheetStoreException::class,
FileReadException::class,
JsonConversionException::class,
)
fun load(): List<CharacterSheet> {
val json = try {
files.readText(charset = Charsets.UTF_8)
} catch (exception: Exception) {
throw FileReadException(root = exception)
}
return if (json.isBlank()) {
emptyList()
} else {
try {
Json.decodeFromString<List<CharacterSheet>>(json)
} catch (exception: Exception) {
throw JsonConversionException(root = exception)
}
}
}
fun loadFlow(): StateFlow<List<CharacterSheet>> = flow
companion object {
private const val NAME = "characters_sheet.json"
}
sealed class CharacterSheetStoreException(root: Exception) : Exception(root)
class JsonConversionException(root: Exception) : CharacterSheetStoreException(root)
class FileWriteException(root: Exception) : CharacterSheetStoreException(root)
class FileReadException(root: Exception) : CharacterSheetStoreException(root)
}

View file

@ -1,14 +0,0 @@
package com.pixelized.desktop.lwa.utils
object OperatingSystem {
private val name get() = System.getProperty("os.name")
val home: String
get() = when {
isWindows() || isMacintosh() -> System.getProperty("user.home")
else -> ""
}
fun isWindows(): Boolean = name.contains("win", ignoreCase = true)
fun isMacintosh(): Boolean = name.contains("mac", ignoreCase = true)
}

View file

@ -1,11 +1,11 @@
[versions]
kotlin = "2.0.21"
kotlinx-coroutines = "1.9.0"
kotlinx-json = "1.7.3"
junit = "4.13.2"
compose-multiplatform = "1.7.0"
androidx-lifecycle = "2.8.3"
androidx-navigation = "2.8.0-alpha10"
androidx-datastore = "1.1.1"
[libraries]
# Test
@ -17,8 +17,8 @@ junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-lifecycle-viewmodel = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtime-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
androidx-navigation-compose = { group = "org.jetbrains.androidx.navigation", name = "navigation-compose", version.ref = "androidx-navigation" }
androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences-core", version.ref = "androidx-datastore" }
kotlinx-coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-json" }
[plugins]
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }