feat: replace device shortcuts with device center

This commit is contained in:
2026-08-26 19:23:04 +08:00
parent e14b4cee27
commit 876acd1e7b
6 changed files with 381 additions and 17 deletions
@@ -0,0 +1,75 @@
package com.flagship.abox.manager.ui.devicecenter
import com.flagship.abox.manager.domain.DeviceCatalog
import com.flagship.abox.manager.domain.DeviceDescriptor
import com.flagship.abox.manager.domain.DeviceId
import com.flagship.abox.manager.domain.DeviceName
import com.flagship.abox.manager.domain.DeviceType
import com.flagship.abox.manager.domain.DomainResult
import com.flagship.abox.manager.domain.ProviderProfileId
import com.flagship.abox.manager.domain.DomainError
import com.flagship.abox.manager.devicecatalog.data.ConfiguredDeviceRepository
import com.flagship.abox.manager.testing.MainDispatcherRule
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class)
class DeviceCenterViewModelTest {
@get:Rule
val mainDispatcherRule = MainDispatcherRule(UnconfinedTestDispatcher())
@Test
fun catalogEmissionMapsCardsAndFilterLocally() = runTest {
val profile = ProviderProfileId("p1")
val catalog = TestCatalog(listOf(
descriptor(profile, "plug", DeviceType.SOCKET),
descriptor(profile, "sensor", DeviceType.TEMPERATURE_HUMIDITY),
))
val viewModel = DeviceCenterViewModel(catalog, TestConfiguredRepository())
viewModel.initialize(profile)
viewModel.setFilter(DeviceCategoryFilter.SOCKET)
assertEquals(listOf("plug"), viewModel.uiState.value.cards.map { it.displayName })
}
@Test
fun profileChangeClearsSelectionAndLoadsNewCards() = runTest {
val first = ProviderProfileId("one")
val second = ProviderProfileId("two")
val catalog = TestCatalog(listOf(descriptor(first, "old", DeviceType.SOCKET)))
val viewModel = DeviceCenterViewModel(catalog, TestConfiguredRepository())
viewModel.initialize(first)
viewModel.select(catalog.devices.single())
catalog.emit(listOf(descriptor(second, "new", DeviceType.INFRARED)))
viewModel.initialize(second)
assertEquals(second, viewModel.uiState.value.profileId)
assertEquals(null, viewModel.uiState.value.selectedCard)
assertEquals(listOf("new"), viewModel.uiState.value.cards.map { it.displayName })
}
private fun descriptor(profile: ProviderProfileId, name: String, type: DeviceType) =
DeviceDescriptor(DeviceId("${profile.value}:$name"), profile, DeviceName(name), type)
private class TestCatalog(initial: List<DeviceDescriptor>) : DeviceCatalog {
val devices = initial.toMutableList()
private val state = MutableStateFlow<DomainResult<List<DeviceDescriptor>>>(DomainResult.Success(initial))
override fun observeDevices(profileId: ProviderProfileId): Flow<DomainResult<List<DeviceDescriptor>>> = state
fun emit(value: List<DeviceDescriptor>) { devices.clear(); devices.addAll(value); state.value = DomainResult.Success(value) }
}
private class TestConfiguredRepository : ConfiguredDeviceRepository {
override fun observe(profileId: ProviderProfileId): Flow<DomainResult<List<DeviceDescriptor>>> =
MutableStateFlow(DomainResult.Success(emptyList()))
override suspend fun add(profileId: ProviderProfileId, name: DeviceName, type: DeviceType): DomainResult<DeviceDescriptor> =
DomainResult.Failure(DomainError.InvalidRequest())
override suspend fun remove(profileId: ProviderProfileId, deviceName: DeviceName): DomainResult<Unit> =
DomainResult.Success(Unit)
}
}