- Replace all TypeToken usage with JsonParser/gson.fromJson (fixes R8/ProGuard crash) - Add parseJsonArray/parseJsonObject helpers for API response parsing - Fix contentToMarkdown to properly convert TipTap JSON objects to Markdown using tiptapToMarkdown instead of returning raw JSON - Fix API endpoints to handle unwrapped response formats - Add ProGuard rules for Gson TypeToken preservation - Redirect build dir to /tmp for Samba compatibility
141 lines
4.7 KiB
Kotlin
141 lines
4.7 KiB
Kotlin
package com.docmost.app.data
|
|
|
|
import android.content.Context
|
|
import android.content.SharedPreferences
|
|
import androidx.security.crypto.EncryptedSharedPreferences
|
|
import androidx.security.crypto.MasterKeys
|
|
import com.google.gson.Gson
|
|
import com.google.gson.JsonParser
|
|
|
|
|
|
data class Credentials(
|
|
val url: String,
|
|
val email: String,
|
|
val password: String,
|
|
val authToken: String = "",
|
|
val avatarUrl: String? = null
|
|
)
|
|
|
|
class SessionManager(private val context: Context) {
|
|
|
|
private val gson = Gson()
|
|
|
|
private val prefs: SharedPreferences by lazy {
|
|
createEncryptedPrefs(context, PREFS_NAME)
|
|
}
|
|
|
|
private val savedAccountsPrefs: SharedPreferences by lazy {
|
|
createEncryptedPrefs(context, SAVED_ACCOUNTS_PREFS)
|
|
}
|
|
|
|
private fun createEncryptedPrefs(context: Context, prefsName: String): SharedPreferences {
|
|
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
|
return try {
|
|
EncryptedSharedPreferences.create(
|
|
prefsName,
|
|
masterKeyAlias,
|
|
context,
|
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
)
|
|
} catch (e: Exception) {
|
|
context.getSharedPreferences(prefsName, Context.MODE_PRIVATE).edit().clear().apply()
|
|
EncryptedSharedPreferences.create(
|
|
prefsName,
|
|
masterKeyAlias,
|
|
context,
|
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
)
|
|
}
|
|
}
|
|
|
|
fun saveCredentials(credentials: Credentials) {
|
|
try {
|
|
val json = gson.toJson(credentials)
|
|
prefs.edit().putString(KEY_CREDENTIALS, json).apply()
|
|
} catch (e: Exception) {
|
|
clear()
|
|
}
|
|
}
|
|
|
|
fun getCredentials(): Credentials? {
|
|
val json = try {
|
|
prefs.getString(KEY_CREDENTIALS, null)
|
|
} catch (e: Exception) {
|
|
clear()
|
|
null
|
|
} ?: return null
|
|
return try {
|
|
gson.fromJson(json, Credentials::class.java)
|
|
} catch (e: Exception) {
|
|
null
|
|
}
|
|
}
|
|
|
|
fun saveAuthToken(token: String) {
|
|
val current = getCredentials() ?: return
|
|
saveCredentials(current.copy(authToken = token))
|
|
}
|
|
|
|
fun clear() {
|
|
try {
|
|
prefs.edit().clear().apply()
|
|
} catch (_: Exception) {}
|
|
}
|
|
|
|
fun hasSession(): Boolean {
|
|
val creds = getCredentials() ?: return false
|
|
return creds.url.isNotBlank() && creds.email.isNotBlank() && creds.authToken.isNotBlank()
|
|
}
|
|
|
|
fun saveAccount(credentials: Credentials) {
|
|
try {
|
|
val accounts = getSavedAccounts().toMutableList()
|
|
accounts.removeAll { it.email == credentials.email && it.url == credentials.url }
|
|
accounts.add(credentials)
|
|
val json = gson.toJson(accounts)
|
|
savedAccountsPrefs.edit().putString(KEY_SAVED_ACCOUNTS, json).apply()
|
|
} catch (e: Exception) {
|
|
context.getSharedPreferences(SAVED_ACCOUNTS_PREFS, Context.MODE_PRIVATE).edit().clear().commit()
|
|
val json = gson.toJson(listOf(credentials))
|
|
savedAccountsPrefs.edit().putString(KEY_SAVED_ACCOUNTS, json).commit()
|
|
}
|
|
}
|
|
|
|
fun getSavedAccounts(): List<Credentials> {
|
|
val json = try {
|
|
savedAccountsPrefs.getString(KEY_SAVED_ACCOUNTS, null)
|
|
} catch (e: Exception) {
|
|
context.getSharedPreferences(SAVED_ACCOUNTS_PREFS, Context.MODE_PRIVATE).edit().clear().commit()
|
|
null
|
|
} ?: return emptyList()
|
|
return try {
|
|
val array = JsonParser.parseString(json).asJsonArray
|
|
array.map { gson.fromJson(it, Credentials::class.java) }
|
|
} catch (e: Exception) {
|
|
emptyList()
|
|
}
|
|
}
|
|
|
|
fun removeAccount(email: String, url: String) {
|
|
try {
|
|
val accounts = getSavedAccounts().toMutableList()
|
|
accounts.removeAll { it.email == email && it.url == url }
|
|
val json = gson.toJson(accounts)
|
|
savedAccountsPrefs.edit().putString(KEY_SAVED_ACCOUNTS, json).apply()
|
|
} catch (_: Exception) {}
|
|
}
|
|
|
|
fun hasAccount(email: String, url: String): Boolean {
|
|
return getSavedAccounts().any { it.email == email && it.url == url }
|
|
}
|
|
|
|
companion object {
|
|
private const val PREFS_NAME = "docmost_session"
|
|
private const val KEY_CREDENTIALS = "credentials"
|
|
private const val SAVED_ACCOUNTS_PREFS = "docmost_saved_accounts"
|
|
private const val KEY_SAVED_ACCOUNTS = "saved_accounts"
|
|
}
|
|
}
|