feat(ui): complete ABox app experience redesign #10
+32
-1
@@ -24,6 +24,8 @@ import com.flagship.abox.manager.infrared.remote.UiText
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
@@ -59,6 +61,7 @@ class RemotePanelViewModel @Inject constructor(
|
||||
private val localAcStateFlow = MutableStateFlow<AcStateSnapshot?>(null)
|
||||
private val messageFlow = MutableStateFlow<PanelUiMessage?>(null)
|
||||
private var acDebounceJob: Job? = null
|
||||
private var learningJob: Job? = null
|
||||
|
||||
private val remoteFlow = if (profileId == null || remoteId == null) {
|
||||
flowOf(null)
|
||||
@@ -128,6 +131,7 @@ class RemotePanelViewModel @Inject constructor(
|
||||
fun toggleLearnMode() {
|
||||
val newMode = !isLearnModeFlow.value
|
||||
isLearnModeFlow.value = newMode
|
||||
if (!newMode) cancelLearning()
|
||||
if (newMode) {
|
||||
messageFlow.value = PanelUiMessage.Info(UiText.StringResource(R.string.infrared_msg_learning_mode_enabled))
|
||||
}
|
||||
@@ -167,7 +171,7 @@ class RemotePanelViewModel @Inject constructor(
|
||||
if (state.value.learningButtonId != null) return
|
||||
|
||||
learningButtonIdFlow.value = buttonId
|
||||
viewModelScope.launch {
|
||||
learningJob = viewModelScope.launch {
|
||||
try {
|
||||
when (val res = learnVirtualRemoteKey(currentProfile, currentRemoteId, buttonId)) {
|
||||
is DomainResult.Success -> {
|
||||
@@ -175,12 +179,39 @@ class RemotePanelViewModel @Inject constructor(
|
||||
}
|
||||
is DomainResult.Failure -> handleDomainError(res.error)
|
||||
}
|
||||
} catch (cancelled: kotlinx.coroutines.CancellationException) {
|
||||
val binding = state.value.bindings.firstOrNull { it.buttonId == buttonId }
|
||||
if (binding != null) {
|
||||
withContext(NonCancellable) {
|
||||
virtualRemoteRepository.updateKeyBinding(
|
||||
binding.copy(
|
||||
status = com.flagship.abox.manager.domain.infrared.InfraredKeyStatus.FAILED,
|
||||
lastErrorCode = null,
|
||||
lastErrorDetail = "学习已取消,需要重新学习",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
throw cancelled
|
||||
} finally {
|
||||
learningButtonIdFlow.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelLearning() {
|
||||
learningJob?.cancel()
|
||||
learningJob = null
|
||||
learningButtonIdFlow.value = null
|
||||
isLearnModeFlow.value = false
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
cancelLearning()
|
||||
acDebounceJob?.cancel()
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
fun addCustomButton(displayName: String) {
|
||||
val currentProfile = profileId ?: return
|
||||
val currentRemoteId = remoteId ?: return
|
||||
|
||||
+25
@@ -226,4 +226,29 @@ class RemotePanelViewModelTest {
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cancellingLearningModeClearsActiveLearningState() = testScope.runTest {
|
||||
val res = createRemote(
|
||||
profileId = profileId,
|
||||
deviceName = device,
|
||||
displayName = "TV",
|
||||
type = RemoteDeviceType.TV,
|
||||
brandModel = "generic_tv",
|
||||
transmissionMode = TransmissionMode.STATIC_SLOTS,
|
||||
)
|
||||
val remoteId = (res as com.flagship.abox.manager.domain.DomainResult.Success).value
|
||||
val vm = createViewModel(remoteId.value)
|
||||
val job = launch { vm.state.collect {} }
|
||||
advanceUntilIdle()
|
||||
|
||||
vm.toggleLearnMode()
|
||||
runCurrent()
|
||||
vm.cancelLearning()
|
||||
runCurrent()
|
||||
|
||||
assertEquals(false, vm.state.value.isLearnMode)
|
||||
assertEquals(null, vm.state.value.learningButtonId)
|
||||
job.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user