mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-29 12:49:53 +00:00
desktop: saving settings in a safer way to handle process death (#4687)
* desktop: saving settings in a safer way to handle process death * enhancements * unused * changes * rename
This commit is contained in:
parent
912aaa2741
commit
05a5d161fb
9 changed files with 73 additions and 24 deletions
|
@ -19,6 +19,8 @@ actual val wallpapersDir: File = File(filesDir.absolutePath + File.separator + "
|
||||||
actual val coreTmpDir: File = File(filesDir.absolutePath + File.separator + "temp_files")
|
actual val coreTmpDir: File = File(filesDir.absolutePath + File.separator + "temp_files")
|
||||||
actual val dbAbsolutePrefixPath: String = dataDir.absolutePath + File.separator + "files"
|
actual val dbAbsolutePrefixPath: String = dataDir.absolutePath + File.separator + "files"
|
||||||
actual val preferencesDir = File(dataDir.absolutePath + File.separator + "shared_prefs")
|
actual val preferencesDir = File(dataDir.absolutePath + File.separator + "shared_prefs")
|
||||||
|
actual val preferencesTmpDir = File(tmpDir, "prefs_tmp")
|
||||||
|
.also { it.deleteRecursively() }
|
||||||
|
|
||||||
actual val chatDatabaseFileName: String = "files_chat.db"
|
actual val chatDatabaseFileName: String = "files_chat.db"
|
||||||
actual val agentDatabaseFileName: String = "files_agent.db"
|
actual val agentDatabaseFileName: String = "files_agent.db"
|
||||||
|
|
|
@ -3168,8 +3168,12 @@ class SharedPreference<T>(val get: () -> T, set: (T) -> Unit) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.set = { value ->
|
this.set = { value ->
|
||||||
set(value)
|
try {
|
||||||
_state.value = value
|
set(value)
|
||||||
|
_state.value = value
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error saving settings: ${e.stackTraceToString()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package chat.simplex.common.platform
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import chat.simplex.common.model.*
|
import chat.simplex.common.model.*
|
||||||
import chat.simplex.common.ui.theme.*
|
import chat.simplex.common.ui.theme.*
|
||||||
import chat.simplex.common.views.helpers.generalGetString
|
import chat.simplex.common.views.helpers.*
|
||||||
import chat.simplex.res.MR
|
import chat.simplex.res.MR
|
||||||
import com.charleskorn.kaml.*
|
import com.charleskorn.kaml.*
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
|
@ -11,6 +11,8 @@ import java.io.*
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.StandardCopyOption
|
||||||
|
|
||||||
expect val dataDir: File
|
expect val dataDir: File
|
||||||
expect val tmpDir: File
|
expect val tmpDir: File
|
||||||
|
@ -20,6 +22,7 @@ expect val wallpapersDir: File
|
||||||
expect val coreTmpDir: File
|
expect val coreTmpDir: File
|
||||||
expect val dbAbsolutePrefixPath: String
|
expect val dbAbsolutePrefixPath: String
|
||||||
expect val preferencesDir: File
|
expect val preferencesDir: File
|
||||||
|
expect val preferencesTmpDir: File
|
||||||
|
|
||||||
expect val chatDatabaseFileName: String
|
expect val chatDatabaseFileName: String
|
||||||
expect val agentDatabaseFileName: String
|
expect val agentDatabaseFileName: String
|
||||||
|
@ -142,16 +145,23 @@ fun readThemeOverrides(): List<ThemeOverrides> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val lock = "themesWriter"
|
||||||
|
|
||||||
fun writeThemeOverrides(overrides: List<ThemeOverrides>): Boolean =
|
fun writeThemeOverrides(overrides: List<ThemeOverrides>): Boolean =
|
||||||
try {
|
synchronized(lock) {
|
||||||
File(getPreferenceFilePath("themes.yaml")).outputStream().use {
|
try {
|
||||||
val string = yaml.encodeToString(ThemesFile(themes = overrides))
|
val themesFile = File(getPreferenceFilePath("themes.yaml"))
|
||||||
it.bufferedWriter().use { it.write(string) }
|
createTmpFileAndDelete(preferencesTmpDir) { tmpFile ->
|
||||||
|
val string = yaml.encodeToString(ThemesFile(themes = overrides))
|
||||||
|
tmpFile.bufferedWriter().use { it.write(string) }
|
||||||
|
themesFile.parentFile.mkdirs()
|
||||||
|
Files.move(tmpFile.toPath(), themesFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error writing themes file: ${e.stackTraceToString()}")
|
||||||
|
false
|
||||||
}
|
}
|
||||||
true
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
Log.e(TAG, "Error while writing themes file: ${e.stackTraceToString()}")
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fileReady(file: CIFile, filePath: String) =
|
private fun fileReady(file: CIFile, filePath: String) =
|
||||||
|
|
|
@ -102,7 +102,9 @@ object ThemeManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun applyTheme(theme: String) {
|
fun applyTheme(theme: String) {
|
||||||
appPrefs.currentTheme.set(theme)
|
if (appPrefs.currentTheme.get() != theme) {
|
||||||
|
appPrefs.currentTheme.set(theme)
|
||||||
|
}
|
||||||
CurrentColors.value = currentColors(null, null, chatModel.currentUser.value?.uiThemes, appPrefs.themeOverrides.get())
|
CurrentColors.value = currentColors(null, null, chatModel.currentUser.value?.uiThemes, appPrefs.themeOverrides.get())
|
||||||
platform.androidSetNightModeIfSupported()
|
platform.androidSetNightModeIfSupported()
|
||||||
val c = CurrentColors.value.colors
|
val c = CurrentColors.value.colors
|
||||||
|
|
|
@ -316,8 +316,9 @@ fun removeWallpaperFile(fileName: String? = null) {
|
||||||
WallpaperType.cachedImages.remove(fileName)
|
WallpaperType.cachedImages.remove(fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> createTmpFileAndDelete(onCreated: (File) -> T): T {
|
fun <T> createTmpFileAndDelete(dir: File = tmpDir, onCreated: (File) -> T): T {
|
||||||
val tmpFile = File(tmpDir, UUID.randomUUID().toString())
|
val tmpFile = File(dir, UUID.randomUUID().toString())
|
||||||
|
tmpFile.parentFile.mkdirs()
|
||||||
tmpFile.deleteOnExit()
|
tmpFile.deleteOnExit()
|
||||||
ChatModel.filesToDelete.add(tmpFile)
|
ChatModel.filesToDelete.add(tmpFile)
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -16,6 +16,8 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import chat.simplex.common.model.ChatController.appPrefs
|
||||||
|
import chat.simplex.common.model.ChatModel
|
||||||
import chat.simplex.common.model.*
|
import chat.simplex.common.model.*
|
||||||
import chat.simplex.common.model.ChatController.setConditionsNotified
|
import chat.simplex.common.model.ChatController.setConditionsNotified
|
||||||
import chat.simplex.common.model.ServerOperator.Companion.dummyOperatorInfo
|
import chat.simplex.common.model.ServerOperator.Companion.dummyOperatorInfo
|
||||||
|
@ -766,7 +768,9 @@ private val versionDescriptions: List<VersionDescription> = listOf(
|
||||||
private val lastVersion = versionDescriptions.last().version
|
private val lastVersion = versionDescriptions.last().version
|
||||||
|
|
||||||
fun setLastVersionDefault(m: ChatModel) {
|
fun setLastVersionDefault(m: ChatModel) {
|
||||||
m.controller.appPrefs.whatsNewVersion.set(lastVersion)
|
if (appPrefs.whatsNewVersion.get() != lastVersion) {
|
||||||
|
appPrefs.whatsNewVersion.set(lastVersion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shouldShowWhatsNew(m: ChatModel): Boolean {
|
fun shouldShowWhatsNew(m: ChatModel): Boolean {
|
||||||
|
|
|
@ -943,6 +943,7 @@
|
||||||
<string name="show_slow_api_calls">Show slow API calls</string>
|
<string name="show_slow_api_calls">Show slow API calls</string>
|
||||||
<string name="shutdown_alert_question">Shutdown?</string>
|
<string name="shutdown_alert_question">Shutdown?</string>
|
||||||
<string name="shutdown_alert_desc">Notifications will stop working until you re-launch the app</string>
|
<string name="shutdown_alert_desc">Notifications will stop working until you re-launch the app</string>
|
||||||
|
<string name="prefs_error_saving_settings">Error saving settings</string>
|
||||||
|
|
||||||
<!-- Address Items - UserAddressView.kt -->
|
<!-- Address Items - UserAddressView.kt -->
|
||||||
<string name="create_address">Create address</string>
|
<string name="create_address">Create address</string>
|
||||||
|
|
|
@ -17,6 +17,8 @@ actual val wallpapersDir: File = File(dataDir.absolutePath + File.separator + "s
|
||||||
actual val coreTmpDir: File = File(dataDir.absolutePath + File.separator + "tmp")
|
actual val coreTmpDir: File = File(dataDir.absolutePath + File.separator + "tmp")
|
||||||
actual val dbAbsolutePrefixPath: String = dataDir.absolutePath + File.separator + "simplex_v1"
|
actual val dbAbsolutePrefixPath: String = dataDir.absolutePath + File.separator + "simplex_v1"
|
||||||
actual val preferencesDir = File(desktopPlatform.configPath).also { it.parentFile.mkdirs() }
|
actual val preferencesDir = File(desktopPlatform.configPath).also { it.parentFile.mkdirs() }
|
||||||
|
actual val preferencesTmpDir = File(desktopPlatform.configPath, "tmp")
|
||||||
|
.also { it.deleteRecursively() }
|
||||||
|
|
||||||
actual val chatDatabaseFileName: String = "simplex_v1_chat.db"
|
actual val chatDatabaseFileName: String = "simplex_v1_chat.db"
|
||||||
actual val agentDatabaseFileName: String = "simplex_v1_agent.db"
|
actual val agentDatabaseFileName: String = "simplex_v1_agent.db"
|
||||||
|
|
|
@ -9,15 +9,16 @@ import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.*
|
import androidx.compose.ui.unit.*
|
||||||
import chat.simplex.common.simplexWindowState
|
import chat.simplex.common.simplexWindowState
|
||||||
import chat.simplex.common.views.helpers.*
|
import chat.simplex.common.views.helpers.*
|
||||||
|
import chat.simplex.res.MR
|
||||||
import com.jthemedetecor.OsThemeDetector
|
import com.jthemedetecor.OsThemeDetector
|
||||||
import com.russhwolf.settings.*
|
import com.russhwolf.settings.*
|
||||||
import dev.icerock.moko.resources.ImageResource
|
import dev.icerock.moko.resources.ImageResource
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
import dev.icerock.moko.resources.desc.desc
|
import dev.icerock.moko.resources.desc.desc
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.StandardCopyOption
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Executors
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
actual fun font(name: String, res: String, weight: FontWeight, style: FontStyle): Font =
|
actual fun font(name: String, res: String, weight: FontWeight, style: FontStyle): Font =
|
||||||
|
@ -37,10 +38,8 @@ catch (e: Exception) {
|
||||||
|
|
||||||
private val settingsFile =
|
private val settingsFile =
|
||||||
File(desktopPlatform.configPath + File.separator + "settings.properties")
|
File(desktopPlatform.configPath + File.separator + "settings.properties")
|
||||||
.also { it.parentFile.mkdirs() }
|
|
||||||
private val settingsThemesFile =
|
private val settingsThemesFile =
|
||||||
File(desktopPlatform.configPath + File.separator + "themes.properties")
|
File(desktopPlatform.configPath + File.separator + "themes.properties")
|
||||||
.also { it.parentFile.mkdirs() }
|
|
||||||
|
|
||||||
private val settingsProps =
|
private val settingsProps =
|
||||||
Properties()
|
Properties()
|
||||||
|
@ -61,11 +60,35 @@ private val settingsThemesProps =
|
||||||
Properties()
|
Properties()
|
||||||
.also { props -> try { settingsThemesFile.reader().use { props.load(it) } } catch (e: Exception) { /**/ } }
|
.also { props -> try { settingsThemesFile.reader().use { props.load(it) } } catch (e: Exception) { /**/ } }
|
||||||
|
|
||||||
|
private const val lock = "settingsSaver"
|
||||||
private val settingsWriterThread = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
actual val settings: Settings = PropertiesSettings(settingsProps) {
|
||||||
|
synchronized(lock) {
|
||||||
actual val settings: Settings = PropertiesSettings(settingsProps) { CoroutineScope(settingsWriterThread).launch { settingsFile.writer().use { settingsProps.store(it, "") } } }
|
try {
|
||||||
actual val settingsThemes: Settings = PropertiesSettings(settingsThemesProps) { CoroutineScope(settingsWriterThread).launch { settingsThemesFile.writer().use { settingsThemesProps.store(it, "") } } }
|
createTmpFileAndDelete(preferencesTmpDir) { tmpFile ->
|
||||||
|
tmpFile.writer().use { settingsProps.store(it, "") }
|
||||||
|
settingsFile.parentFile.mkdirs()
|
||||||
|
Files.move(tmpFile.toPath(), settingsFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.prefs_error_saving_settings), e.stackTraceToString())
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actual val settingsThemes: Settings = PropertiesSettings(settingsThemesProps) {
|
||||||
|
synchronized(lock) {
|
||||||
|
try {
|
||||||
|
createTmpFileAndDelete(preferencesTmpDir) { tmpFile ->
|
||||||
|
tmpFile.writer().use { settingsThemesProps.store(it, "") }
|
||||||
|
settingsThemesFile.parentFile.mkdirs()
|
||||||
|
Files.move(tmpFile.toPath(), settingsThemesFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.prefs_error_saving_settings), e.stackTraceToString())
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
actual fun windowOrientation(): WindowOrientation =
|
actual fun windowOrientation(): WindowOrientation =
|
||||||
if (simplexWindowState.windowState.size.width > simplexWindowState.windowState.size.height) {
|
if (simplexWindowState.windowState.size.width > simplexWindowState.windowState.size.height) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue