feat(android): add persistent partyId storage matching Electron behavior
- Add AppSettingEntity and AppSettingDao to Database.kt for key-value storage - Add database migration (version 1 → 2) to create app_settings table - Modify TssRepository.registerParty() to load/create partyId from database - PartyId is now persisted across app restarts, matching Electron's getOrCreatePartyId() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e2451874ea
commit
b7fc488dcf
|
|
@ -552,7 +552,8 @@
|
|||
"Bash(\"/c/Users/dong/go/bin/go1.22.10.exe\" install golang.org/x/mobile/cmd/gomobile@c31d5b91ecc32c0d598b8fe8457d244ca0b4e815)",
|
||||
"Bash(\"/c/Users/dong/go/bin/go1.22.10.exe\" install golang.org/x/mobile/cmd/gobind@c31d5b91ecc32c0d598b8fe8457d244ca0b4e815)",
|
||||
"Bash(\"/c/Users/dong/go/bin/go1.22.10.exe\" mod tidy)",
|
||||
"Bash(adb devices:*)"
|
||||
"Bash(adb devices:*)",
|
||||
"Bash(adb logcat:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
|
|
|||
|
|
@ -66,14 +66,39 @@ interface ShareRecordDao {
|
|||
suspend fun getShareCount(): Int
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity for storing app settings (like persistent partyId)
|
||||
*/
|
||||
@Entity(tableName = "app_settings")
|
||||
data class AppSettingEntity(
|
||||
@PrimaryKey
|
||||
val key: String,
|
||||
|
||||
@ColumnInfo(name = "value")
|
||||
val value: String
|
||||
)
|
||||
|
||||
/**
|
||||
* DAO for app settings
|
||||
*/
|
||||
@Dao
|
||||
interface AppSettingDao {
|
||||
@Query("SELECT value FROM app_settings WHERE `key` = :key")
|
||||
suspend fun getValue(key: String): String?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun setValue(setting: AppSettingEntity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Room database
|
||||
*/
|
||||
@Database(
|
||||
entities = [ShareRecordEntity::class],
|
||||
version = 1,
|
||||
entities = [ShareRecordEntity::class, AppSettingEntity::class],
|
||||
version = 2,
|
||||
exportSchema = false
|
||||
)
|
||||
abstract class TssDatabase : RoomDatabase() {
|
||||
abstract fun shareRecordDao(): ShareRecordDao
|
||||
abstract fun appSettingDao(): AppSettingDao
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.durian.tssparty.data.repository
|
||||
|
||||
import android.util.Base64
|
||||
import com.durian.tssparty.data.local.AppSettingDao
|
||||
import com.durian.tssparty.data.local.AppSettingEntity
|
||||
import com.durian.tssparty.data.local.ShareRecordDao
|
||||
import com.durian.tssparty.data.local.ShareRecordEntity
|
||||
import com.durian.tssparty.data.local.TssNativeBridge
|
||||
|
|
@ -28,7 +30,8 @@ import javax.inject.Singleton
|
|||
class TssRepository @Inject constructor(
|
||||
private val grpcClient: GrpcClient,
|
||||
private val tssNativeBridge: TssNativeBridge,
|
||||
private val shareRecordDao: ShareRecordDao
|
||||
private val shareRecordDao: ShareRecordDao,
|
||||
private val appSettingDao: AppSettingDao
|
||||
) {
|
||||
private val _currentSession = MutableStateFlow<TssSession?>(null)
|
||||
val currentSession: StateFlow<TssSession?> = _currentSession.asStateFlow()
|
||||
|
|
@ -42,7 +45,9 @@ class TssRepository @Inject constructor(
|
|||
// Expose gRPC connection events for UI notifications
|
||||
val grpcConnectionEvents: SharedFlow<GrpcConnectionEvent> = grpcClient.connectionEvents
|
||||
|
||||
private var partyId: String = UUID.randomUUID().toString()
|
||||
// partyId is loaded once from database in registerParty() and cached here
|
||||
// This matches Electron's getOrCreatePartyId() pattern
|
||||
private lateinit var partyId: String
|
||||
private var messageCollectionJob: Job? = null
|
||||
private var sessionEventJob: Job? = null
|
||||
|
||||
|
|
@ -56,8 +61,14 @@ class TssRepository @Inject constructor(
|
|||
|
||||
/**
|
||||
* Get the current party ID
|
||||
* Note: This will throw UninitializedPropertyAccessException if called before registerParty()
|
||||
*/
|
||||
fun getPartyId(): String = partyId
|
||||
fun getPartyId(): String {
|
||||
if (!::partyId.isInitialized) {
|
||||
throw IllegalStateException("partyId not initialized. Call registerParty() first.")
|
||||
}
|
||||
return partyId
|
||||
}
|
||||
|
||||
// Track current message routing params for reconnection recovery
|
||||
private var currentMessageRoutingSessionId: String? = null
|
||||
|
|
@ -146,8 +157,23 @@ class TssRepository @Inject constructor(
|
|||
/**
|
||||
* Register this party with the router
|
||||
* Also subscribes to session events (matching Electron behavior)
|
||||
*
|
||||
* This method loads or creates a persistent partyId from the database (matching Electron's
|
||||
* getOrCreatePartyId pattern). The partyId is loaded ONCE here and cached in memory.
|
||||
*/
|
||||
suspend fun registerParty(): String {
|
||||
// Load or create persistent partyId (matching Electron's getOrCreatePartyId)
|
||||
val existingPartyId = appSettingDao.getValue("party_id")
|
||||
partyId = if (existingPartyId != null) {
|
||||
android.util.Log.d("TssRepository", "Loaded existing partyId: $existingPartyId")
|
||||
existingPartyId
|
||||
} else {
|
||||
val newPartyId = UUID.randomUUID().toString()
|
||||
appSettingDao.setValue(AppSettingEntity("party_id", newPartyId))
|
||||
android.util.Log.d("TssRepository", "Generated new partyId: $newPartyId")
|
||||
newPartyId
|
||||
}
|
||||
|
||||
grpcClient.registerParty(partyId, "temporary", "1.0.0")
|
||||
|
||||
// Subscribe to session events immediately after registration (like Electron does)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ package com.durian.tssparty.di
|
|||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.durian.tssparty.data.local.AppSettingDao
|
||||
import com.durian.tssparty.data.local.ShareRecordDao
|
||||
import com.durian.tssparty.data.local.TssDatabase
|
||||
import com.durian.tssparty.data.local.TssNativeBridge
|
||||
|
|
@ -20,6 +23,17 @@ import javax.inject.Singleton
|
|||
@InstallIn(SingletonComponent::class)
|
||||
object AppModule {
|
||||
|
||||
// Migration from version 1 to 2: add app_settings table
|
||||
private val MIGRATION_1_2 = object : Migration(1, 2) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"CREATE TABLE IF NOT EXISTS `app_settings` (" +
|
||||
"`key` TEXT NOT NULL PRIMARY KEY, " +
|
||||
"`value` TEXT NOT NULL)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGson(): Gson {
|
||||
|
|
@ -33,7 +47,9 @@ object AppModule {
|
|||
context,
|
||||
TssDatabase::class.java,
|
||||
"tss_party.db"
|
||||
).build()
|
||||
)
|
||||
.addMigrations(MIGRATION_1_2)
|
||||
.build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -42,6 +58,12 @@ object AppModule {
|
|||
return database.shareRecordDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppSettingDao(database: TssDatabase): AppSettingDao {
|
||||
return database.appSettingDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGrpcClient(): GrpcClient {
|
||||
|
|
@ -59,8 +81,9 @@ object AppModule {
|
|||
fun provideTssRepository(
|
||||
grpcClient: GrpcClient,
|
||||
tssNativeBridge: TssNativeBridge,
|
||||
shareRecordDao: ShareRecordDao
|
||||
shareRecordDao: ShareRecordDao,
|
||||
appSettingDao: AppSettingDao
|
||||
): TssRepository {
|
||||
return TssRepository(grpcClient, tssNativeBridge, shareRecordDao)
|
||||
return TssRepository(grpcClient, tssNativeBridge, shareRecordDao, appSettingDao)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue