feat: coordinate infrared runtime state
This commit is contained in:
+27
@@ -0,0 +1,27 @@
|
||||
package com.flagship.abox.manager.infrared.application
|
||||
|
||||
import com.flagship.abox.manager.domain.DeviceName
|
||||
import com.flagship.abox.manager.domain.ProviderProfileId
|
||||
import com.flagship.abox.manager.domain.infrared.InfraredKeyRef
|
||||
import com.flagship.abox.manager.domain.infrared.LearnInfraredKey
|
||||
import com.flagship.abox.manager.domain.infrared.RetryInfraredKeyLearning
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class InfraredLearningCoordinator(
|
||||
private val learnInfraredKey: LearnInfraredKey,
|
||||
private val retryInfraredKeyLearning: RetryInfraredKeyLearning,
|
||||
private val externalScope: CoroutineScope,
|
||||
) {
|
||||
fun learn(profileId: ProviderProfileId, deviceName: DeviceName, displayName: String) {
|
||||
externalScope.launch {
|
||||
learnInfraredKey(profileId, deviceName, displayName)
|
||||
}
|
||||
}
|
||||
|
||||
fun retry(ref: InfraredKeyRef) {
|
||||
externalScope.launch {
|
||||
retryInfraredKeyLearning(ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
package com.flagship.abox.manager.infrared.di
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.preferencesDataStoreFile
|
||||
import androidx.room.Room
|
||||
import com.flagship.abox.manager.domain.InfraredGateway
|
||||
import com.flagship.abox.manager.domain.infrared.EpochMillisClock
|
||||
import com.flagship.abox.manager.domain.infrared.DeleteInfraredKey
|
||||
import com.flagship.abox.manager.domain.infrared.InfraredDeviceConfigurationRepository
|
||||
import com.flagship.abox.manager.domain.infrared.InfraredKeyFactory
|
||||
import com.flagship.abox.manager.domain.infrared.InfraredKeyId
|
||||
import com.flagship.abox.manager.domain.infrared.InfraredKeyIdGenerator
|
||||
import com.flagship.abox.manager.domain.infrared.InfraredKeyRepository
|
||||
import com.flagship.abox.manager.domain.infrared.LearnInfraredKey
|
||||
import com.flagship.abox.manager.domain.infrared.RecoverInterruptedInfraredLearning
|
||||
import com.flagship.abox.manager.domain.infrared.RenameInfraredKey
|
||||
import com.flagship.abox.manager.domain.infrared.RetryInfraredKeyLearning
|
||||
import com.flagship.abox.manager.domain.infrared.SendInfraredKey
|
||||
import com.flagship.abox.manager.infrared.application.InfraredLearningCoordinator
|
||||
import com.flagship.abox.manager.infrared.data.local.InfraredDao
|
||||
import com.flagship.abox.manager.infrared.data.local.InfraredDatabase
|
||||
import com.flagship.abox.manager.infrared.data.local.RoomInfraredKeyRepository
|
||||
import com.flagship.abox.manager.infrared.runtime.DataStoreInfraredRuntimeModeStore
|
||||
import com.flagship.abox.manager.infrared.runtime.InfraredRuntimeModeStore
|
||||
import com.flagship.abox.manager.infrared.runtime.InfraredRuntimeProfileResolver
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import java.util.UUID
|
||||
import javax.inject.Qualifier
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class InfraredApplicationScope
|
||||
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class InfraredDataStore
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object InfraredFeatureModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideInfraredDatabase(
|
||||
@ApplicationContext context: Context,
|
||||
): InfraredDatabase {
|
||||
return Room.databaseBuilder(
|
||||
context,
|
||||
InfraredDatabase::class.java,
|
||||
"abox_infrared.db",
|
||||
).build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideInfraredDao(database: InfraredDatabase): InfraredDao = database.infraredDao()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRoomInfraredKeyRepository(dao: InfraredDao): RoomInfraredKeyRepository =
|
||||
RoomInfraredKeyRepository(dao)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideInfraredKeyRepository(
|
||||
repository: RoomInfraredKeyRepository,
|
||||
): InfraredKeyRepository = repository
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideInfraredDeviceConfigurationRepository(
|
||||
repository: RoomInfraredKeyRepository,
|
||||
): InfraredDeviceConfigurationRepository = repository
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@InfraredDataStore
|
||||
fun provideInfraredPreferencesDataStore(
|
||||
@ApplicationContext context: Context,
|
||||
): DataStore<Preferences> {
|
||||
return PreferenceDataStoreFactory.create(
|
||||
produceFile = { context.preferencesDataStoreFile("infrared_preferences") },
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideInfraredRuntimeModeStore(
|
||||
@InfraredDataStore dataStore: DataStore<Preferences>,
|
||||
): InfraredRuntimeModeStore = DataStoreInfraredRuntimeModeStore(dataStore)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideInfraredRuntimeProfileResolver(): InfraredRuntimeProfileResolver =
|
||||
InfraredRuntimeProfileResolver()
|
||||
|
||||
@Provides
|
||||
fun provideInfraredKeyIdGenerator(): InfraredKeyIdGenerator =
|
||||
InfraredKeyIdGenerator { InfraredKeyId(UUID.randomUUID().toString()) }
|
||||
|
||||
@Provides
|
||||
fun provideEpochMillisClock(): EpochMillisClock =
|
||||
EpochMillisClock { System.currentTimeMillis() }
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideInfraredKeyFactory(
|
||||
idGenerator: InfraredKeyIdGenerator,
|
||||
clock: EpochMillisClock,
|
||||
): InfraredKeyFactory = InfraredKeyFactory(idGenerator, clock)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@InfraredApplicationScope
|
||||
fun provideInfraredApplicationScope(): CoroutineScope =
|
||||
CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideLearnInfraredKey(
|
||||
repository: InfraredKeyRepository,
|
||||
gateway: InfraredGateway,
|
||||
factory: InfraredKeyFactory,
|
||||
clock: EpochMillisClock,
|
||||
): LearnInfraredKey = LearnInfraredKey(repository, gateway, factory, clock)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRetryInfraredKeyLearning(
|
||||
repository: InfraredKeyRepository,
|
||||
gateway: InfraredGateway,
|
||||
clock: EpochMillisClock,
|
||||
): RetryInfraredKeyLearning = RetryInfraredKeyLearning(repository, gateway, clock)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSendInfraredKey(
|
||||
repository: InfraredKeyRepository,
|
||||
gateway: InfraredGateway,
|
||||
): SendInfraredKey = SendInfraredKey(repository, gateway)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRenameInfraredKey(
|
||||
repository: InfraredKeyRepository,
|
||||
clock: EpochMillisClock,
|
||||
): RenameInfraredKey = RenameInfraredKey(repository, clock)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDeleteInfraredKey(
|
||||
repository: InfraredKeyRepository,
|
||||
): DeleteInfraredKey = DeleteInfraredKey(repository)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRecoverInterruptedInfraredLearning(
|
||||
repository: InfraredKeyRepository,
|
||||
clock: EpochMillisClock,
|
||||
): RecoverInterruptedInfraredLearning = RecoverInterruptedInfraredLearning(repository, clock)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideInfraredLearningCoordinator(
|
||||
learnInfraredKey: LearnInfraredKey,
|
||||
retryInfraredKeyLearning: RetryInfraredKeyLearning,
|
||||
@InfraredApplicationScope scope: CoroutineScope,
|
||||
): InfraredLearningCoordinator = InfraredLearningCoordinator(
|
||||
learnInfraredKey = learnInfraredKey,
|
||||
retryInfraredKeyLearning = retryInfraredKeyLearning,
|
||||
externalScope = scope,
|
||||
)
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.flagship.abox.manager.infrared.runtime
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import com.flagship.abox.manager.domain.DomainError
|
||||
import com.flagship.abox.manager.domain.DomainResult
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import java.io.IOException
|
||||
|
||||
class DataStoreInfraredRuntimeModeStore(
|
||||
private val dataStore: DataStore<Preferences>,
|
||||
) : InfraredRuntimeModeStore {
|
||||
|
||||
companion object {
|
||||
private val KEY_RUNTIME_MODE = stringPreferencesKey("infrared_runtime_mode")
|
||||
}
|
||||
|
||||
override val mode: Flow<InfraredRuntimeMode> = dataStore.data
|
||||
.catch { exception ->
|
||||
if (exception is IOException) {
|
||||
emit(androidx.datastore.preferences.core.emptyPreferences())
|
||||
} else {
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
.map { preferences ->
|
||||
val storedName = preferences[KEY_RUNTIME_MODE]
|
||||
if (storedName != null) {
|
||||
try {
|
||||
InfraredRuntimeMode.valueOf(storedName)
|
||||
} catch (_: IllegalArgumentException) {
|
||||
InfraredRuntimeMode.FAKE
|
||||
}
|
||||
} else {
|
||||
InfraredRuntimeMode.FAKE
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun currentMode(): InfraredRuntimeMode {
|
||||
return mode.first()
|
||||
}
|
||||
|
||||
override suspend fun setMode(mode: InfraredRuntimeMode): DomainResult<Unit> {
|
||||
return try {
|
||||
dataStore.edit { preferences ->
|
||||
preferences[KEY_RUNTIME_MODE] = mode.name
|
||||
}
|
||||
DomainResult.Success(Unit)
|
||||
} catch (throwable: Throwable) {
|
||||
DomainResult.Failure(DomainError.Unknown(detail = "Failed to update runtime mode"))
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.flagship.abox.manager.infrared.runtime
|
||||
|
||||
import com.flagship.abox.manager.domain.DomainResult
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
enum class InfraredRuntimeMode {
|
||||
FAKE,
|
||||
REAL_ABSDK,
|
||||
}
|
||||
|
||||
interface InfraredRuntimeModeStore {
|
||||
val mode: Flow<InfraredRuntimeMode>
|
||||
suspend fun currentMode(): InfraredRuntimeMode
|
||||
suspend fun setMode(mode: InfraredRuntimeMode): DomainResult<Unit>
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.flagship.abox.manager.infrared.runtime
|
||||
|
||||
import com.flagship.abox.manager.domain.ProviderProfileId
|
||||
|
||||
class InfraredRuntimeProfileResolver {
|
||||
fun resolve(realProfileId: ProviderProfileId, mode: InfraredRuntimeMode): ProviderProfileId {
|
||||
return when (mode) {
|
||||
InfraredRuntimeMode.REAL_ABSDK -> realProfileId
|
||||
InfraredRuntimeMode.FAKE -> ProviderProfileId("${realProfileId.value}:infrared-fake")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user