Remove Google DataStore. save data directly into files. enable obfuscation.
This commit is contained in:
parent
81d725e224
commit
ba0cc30a1a
8 changed files with 108 additions and 90 deletions
|
|
@ -23,10 +23,7 @@ kotlin {
|
||||||
implementation(libs.androidx.lifecycle.viewmodel)
|
implementation(libs.androidx.lifecycle.viewmodel)
|
||||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||||
implementation(libs.androidx.navigation.compose)
|
implementation(libs.androidx.navigation.compose)
|
||||||
implementation(libs.androidx.datastore.preferences)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTest.dependencies {
|
commonTest.dependencies {
|
||||||
|
|
@ -61,7 +58,7 @@ compose.desktop {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes.release.proguard {
|
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"))
|
configurationFiles.from(project.file("compose-desktop.pro"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
|
||||||
}
|
|
||||||
|
|
@ -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/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package com.pixelized.desktop.lwa.repository.characterSheet
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
|
@ -11,11 +9,9 @@ import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
object CharacterSheetRepository {
|
object CharacterSheetRepository {
|
||||||
private val scope = CoroutineScope(Dispatchers.IO)
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
private val preferences = CharacterSheetPreference(
|
private val store = CharacterSheetStore()
|
||||||
dataStore = createDataStore { dataStorePath() }
|
|
||||||
)
|
|
||||||
|
|
||||||
private val sheets = preferences.loadFlow()
|
private val sheets = store.loadFlow()
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope = scope,
|
scope = scope,
|
||||||
started = SharingStarted.Eagerly,
|
started = SharingStarted.Eagerly,
|
||||||
|
|
@ -38,8 +34,8 @@ object CharacterSheetRepository {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun save(characterSheet: CharacterSheet) {
|
fun save(characterSheet: CharacterSheet) {
|
||||||
val savedSheets = preferences.load().toMutableList()
|
val savedSheets = store.load().toMutableList()
|
||||||
val savedIndex = savedSheets.indexOfFirst { it.id == characterSheet.id }
|
val savedIndex = savedSheets.indexOfFirst { it.id == characterSheet.id }
|
||||||
if (savedIndex >= 0) {
|
if (savedIndex >= 0) {
|
||||||
// this sheet is already saved. update it
|
// this sheet is already saved. update it
|
||||||
|
|
@ -49,13 +45,13 @@ object CharacterSheetRepository {
|
||||||
savedSheets.add(characterSheet)
|
savedSheets.add(characterSheet)
|
||||||
}
|
}
|
||||||
// save the list of characters sheet.
|
// save the list of characters sheet.
|
||||||
preferences.save(sheets = savedSheets)
|
store.save(sheets = savedSheets)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun delete(id: String) {
|
fun delete(id: String) {
|
||||||
val savedSheets = preferences.load().toMutableList()
|
val savedSheets = store.load().toMutableList()
|
||||||
savedSheets.removeIf { it.id == id }
|
savedSheets.removeIf { it.id == id }
|
||||||
preferences.save(sheets = savedSheets)
|
store.save(sheets = savedSheets)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
[versions]
|
[versions]
|
||||||
kotlin = "2.0.21"
|
kotlin = "2.0.21"
|
||||||
kotlinx-coroutines = "1.9.0"
|
kotlinx-coroutines = "1.9.0"
|
||||||
|
kotlinx-json = "1.7.3"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
compose-multiplatform = "1.7.0"
|
compose-multiplatform = "1.7.0"
|
||||||
androidx-lifecycle = "2.8.3"
|
androidx-lifecycle = "2.8.3"
|
||||||
androidx-navigation = "2.8.0-alpha10"
|
androidx-navigation = "2.8.0-alpha10"
|
||||||
androidx-datastore = "1.1.1"
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# Test
|
# 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-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-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-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-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]
|
[plugins]
|
||||||
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
|
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue