Private
Public Access
1
0

Settings: Save server, username, password

This commit is contained in:
2024-02-26 23:03:10 -08:00
parent 175a83ca21
commit 077b12b1ac
4 changed files with 91 additions and 7 deletions

View File

@@ -61,6 +61,7 @@ dependencies {
// Kordophone lib
implementation project(':backend')
implementation 'androidx.security:security-crypto-ktx:1.1.0-alpha06'
// Navigation
def nav_version = "2.6.0"

View File

@@ -98,8 +98,8 @@ fun SettingsFormView(
})
}
var usernameInput by remember { mutableStateOf(TextFieldValue()) }
var passwordInput by remember { mutableStateOf(TextFieldValue()) }
var usernameInput by remember { mutableStateOf(TextFieldValue(userName.value)) }
var passwordInput by remember { mutableStateOf(TextFieldValue(password.value)) }
SettingsTextField(
name = "Authentication",
icon = R.drawable.account_circle,

View File

@@ -23,6 +23,15 @@ class SettingsViewModel @Inject constructor(
private val _passwordPreference: MutableStateFlow<String> = MutableStateFlow("")
var passwordPreference = _passwordPreference.asStateFlow()
init {
val serverConfig = serverConfigRepository.serverConfig.value
serverConfig.serverName?.let { _serverPreference.value = it }
serverConfig.authentication?.let {
_usernamePreference.value = it.username
_passwordPreference.value = it.password
}
}
fun saveServerPreference(serverName: String) {
_serverPreference.value = serverName
@@ -36,7 +45,7 @@ class SettingsViewModel @Inject constructor(
_passwordPreference.value = password
serverConfigRepository.applyConfig {
authentication = ServerAuthentication(username, password)
this.authentication = ServerAuthentication(username, password)
}
}
}

View File

@@ -1,28 +1,102 @@
package net.buzzert.kordophonedroid.ui.shared
import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.lang.reflect.Constructor
import javax.inject.Inject
import javax.inject.Singleton
data class ServerConfig(
var serverName: String? = null,
var authentication: ServerAuthentication? = null,
) {
companion object {
private const val SHARED_PREF_NAME = "KordophonePreferences"
fun loadFromSettings(context: Context): ServerConfig {
val prefs = getSharedPreferences(context)
return ServerConfig(
serverName = prefs.getString("serverName", null),
authentication = ServerAuthentication.loadFromEncryptedSettings(context)
)
}
private fun getSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
}
}
fun saveToSettings(context: Context) {
val prefs = getSharedPreferences(context)
prefs.edit {
putString("serverName", serverName)
apply()
}
authentication?.saveToEncryptedSettings(context)
}
}
data class ServerAuthentication(
val username: String,
val password: String,
) {
companion object {
fun loadFromEncryptedSettings(context: Context): ServerAuthentication? {
val prefs = getEncryptedSharedPreferences(context)
val username = prefs.getString("username", null)
val password = prefs.getString("password", null)
if (username != null && password != null) {
return ServerAuthentication(username, password)
}
return null
}
private fun getEncryptedSharedPreferences(context: Context): SharedPreferences {
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
return EncryptedSharedPreferences(
context,
"secrets",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
}
fun saveToEncryptedSettings(context: Context) {
val prefs = getEncryptedSharedPreferences(context)
prefs.edit {
putString("username", username)
putString("password", password)
apply()
}
}
}
@Singleton
class ServerConfigRepository @Inject constructor() {
class ServerConfigRepository @Inject constructor(
@ApplicationContext val context: Context,
) {
// TODO: Initial config should be loaded from device settings.
private val _serverConfig = MutableStateFlow(ServerConfig()) // Initial config
private val _serverConfig = MutableStateFlow(ServerConfig.loadFromSettings(context)) // Initial config
val serverConfig: StateFlow<ServerConfig> = _serverConfig
fun applyConfig(applicator: ServerConfig.() -> Unit) {
val config = _serverConfig.value.copy()
_serverConfig.value = config.apply(applicator)
_serverConfig.value.saveToSettings(context)
}
}