Files
mobile-android/app/src/main/java/com/rosetta/messenger/data/AccountManager.kt

144 lines
5.1 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.rosetta.messenger.data
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
private val Context.accountDataStore: DataStore<Preferences> by preferencesDataStore(name = "account_store")
/**
* Manages encrypted account storage using DataStore
*/
class AccountManager(private val context: Context) {
companion object {
private val CURRENT_PUBLIC_KEY = stringPreferencesKey("current_public_key")
private val ACCOUNTS_JSON = stringPreferencesKey("accounts_json")
private val IS_LOGGED_IN = booleanPreferencesKey("is_logged_in")
private const val PREFS_NAME = "rosetta_account_prefs"
private const val KEY_LAST_LOGGED = "last_logged_public_key"
}
// Use SharedPreferences for last logged account - more reliable for immediate reads
private val sharedPrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
val currentPublicKey: Flow<String?> = context.accountDataStore.data.map { preferences ->
preferences[CURRENT_PUBLIC_KEY]
}
val isLoggedIn: Flow<Boolean> = context.accountDataStore.data.map { preferences ->
preferences[IS_LOGGED_IN] ?: false
}
val accountsJson: Flow<String?> = context.accountDataStore.data.map { preferences ->
preferences[ACCOUNTS_JSON]
}
// Synchronous read from SharedPreferences - always up to date
fun getLastLoggedPublicKey(): String? {
val publicKey = sharedPrefs.getString(KEY_LAST_LOGGED, null)
return publicKey
}
// Synchronous write to SharedPreferences
fun setLastLoggedPublicKey(publicKey: String) {
val success = sharedPrefs.edit().putString(KEY_LAST_LOGGED, publicKey).commit() // commit() is synchronous
// Verify immediately
val saved = sharedPrefs.getString(KEY_LAST_LOGGED, null)
}
suspend fun saveAccount(account: EncryptedAccount) {
context.accountDataStore.edit { preferences ->
val existingJson = preferences[ACCOUNTS_JSON]
val accounts = if (existingJson != null) {
parseAccounts(existingJson).toMutableList()
} else {
mutableListOf()
}
// Remove existing account with same public key
accounts.removeAll { it.publicKey == account.publicKey }
accounts.add(account)
preferences[ACCOUNTS_JSON] = serializeAccounts(accounts)
}
}
suspend fun getAccount(publicKey: String): EncryptedAccount? {
val preferences = context.accountDataStore.data.first()
val json = preferences[ACCOUNTS_JSON]
return if (json != null) parseAccounts(json).find { it.publicKey == publicKey } else null
}
suspend fun getAllAccounts(): List<EncryptedAccount> {
val preferences = context.accountDataStore.data.first()
val json = preferences[ACCOUNTS_JSON]
return if (json != null) parseAccounts(json) else emptyList()
}
suspend fun setCurrentAccount(publicKey: String) {
// ⚡ ВАЖНО: Сначала сохраняем в SharedPreferences синхронно
setLastLoggedPublicKey(publicKey)
// Потом в DataStore асинхронно (для совместимости с потоками)
context.accountDataStore.edit { preferences ->
preferences[CURRENT_PUBLIC_KEY] = publicKey
preferences[IS_LOGGED_IN] = true
}
}
suspend fun logout() {
context.accountDataStore.edit { preferences ->
preferences[IS_LOGGED_IN] = false
}
}
suspend fun clearAll() {
context.accountDataStore.edit { it.clear() }
}
private fun serializeAccounts(accounts: List<EncryptedAccount>): String {
return accounts.joinToString("|||") { account ->
"${account.publicKey}::${account.encryptedPrivateKey}::${account.encryptedSeedPhrase}::${account.name}"
}
}
private fun parseAccounts(json: String): List<EncryptedAccount> {
if (json.isBlank()) return emptyList()
return json.split("|||").mapNotNull { accountStr ->
val parts = accountStr.split("::")
if (parts.size >= 4) {
EncryptedAccount(
publicKey = parts[0],
encryptedPrivateKey = parts[1],
encryptedSeedPhrase = parts[2],
name = parts[3]
)
} else null
}
}
}
data class EncryptedAccount(
val publicKey: String,
val encryptedPrivateKey: String,
val encryptedSeedPhrase: String,
val name: String = "Account"
)
data class DecryptedAccount(
val publicKey: String,
val privateKey: String,
val seedPhrase: List<String>,
val privateKeyHash: String,
val name: String = "Account"
)