144 lines
5.1 KiB
Kotlin
144 lines
5.1 KiB
Kotlin
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"
|
||
)
|