fix: clean up infrared learning cancellation
This commit is contained in:
+32
-1
@@ -24,6 +24,8 @@ import com.flagship.abox.manager.infrared.remote.UiText
|
|||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.NonCancellable
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
@@ -59,6 +61,7 @@ class RemotePanelViewModel @Inject constructor(
|
|||||||
private val localAcStateFlow = MutableStateFlow<AcStateSnapshot?>(null)
|
private val localAcStateFlow = MutableStateFlow<AcStateSnapshot?>(null)
|
||||||
private val messageFlow = MutableStateFlow<PanelUiMessage?>(null)
|
private val messageFlow = MutableStateFlow<PanelUiMessage?>(null)
|
||||||
private var acDebounceJob: Job? = null
|
private var acDebounceJob: Job? = null
|
||||||
|
private var learningJob: Job? = null
|
||||||
|
|
||||||
private val remoteFlow = if (profileId == null || remoteId == null) {
|
private val remoteFlow = if (profileId == null || remoteId == null) {
|
||||||
flowOf(null)
|
flowOf(null)
|
||||||
@@ -128,6 +131,7 @@ class RemotePanelViewModel @Inject constructor(
|
|||||||
fun toggleLearnMode() {
|
fun toggleLearnMode() {
|
||||||
val newMode = !isLearnModeFlow.value
|
val newMode = !isLearnModeFlow.value
|
||||||
isLearnModeFlow.value = newMode
|
isLearnModeFlow.value = newMode
|
||||||
|
if (!newMode) cancelLearning()
|
||||||
if (newMode) {
|
if (newMode) {
|
||||||
messageFlow.value = PanelUiMessage.Info(UiText.StringResource(R.string.infrared_msg_learning_mode_enabled))
|
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
|
if (state.value.learningButtonId != null) return
|
||||||
|
|
||||||
learningButtonIdFlow.value = buttonId
|
learningButtonIdFlow.value = buttonId
|
||||||
viewModelScope.launch {
|
learningJob = viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
when (val res = learnVirtualRemoteKey(currentProfile, currentRemoteId, buttonId)) {
|
when (val res = learnVirtualRemoteKey(currentProfile, currentRemoteId, buttonId)) {
|
||||||
is DomainResult.Success -> {
|
is DomainResult.Success -> {
|
||||||
@@ -175,12 +179,39 @@ class RemotePanelViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
is DomainResult.Failure -> handleDomainError(res.error)
|
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 {
|
} finally {
|
||||||
learningButtonIdFlow.value = null
|
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) {
|
fun addCustomButton(displayName: String) {
|
||||||
val currentProfile = profileId ?: return
|
val currentProfile = profileId ?: return
|
||||||
val currentRemoteId = remoteId ?: return
|
val currentRemoteId = remoteId ?: return
|
||||||
|
|||||||
+25
@@ -226,4 +226,29 @@ class RemotePanelViewModelTest {
|
|||||||
|
|
||||||
job.cancel()
|
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