diff --git a/android/app/src/main/java/com/fabledsword/minstrel/player/output/OutputPickerViewModel.kt b/android/app/src/main/java/com/fabledsword/minstrel/player/output/OutputPickerViewModel.kt new file mode 100644 index 00000000..19362d60 --- /dev/null +++ b/android/app/src/main/java/com/fabledsword/minstrel/player/output/OutputPickerViewModel.kt @@ -0,0 +1,60 @@ +package com.fabledsword.minstrel.player.output + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.stateIn +import javax.inject.Inject + +/** + * NowPlaying-scoped projection over [OutputPickerController]. The + * controller is the long-lived Hilt singleton (its MediaRouter + * callback must not churn with screen lifecycle); this ViewModel is + * a thin lens over its [OutputPickerController.routesState] Flow + * plus the sheet's visibility state. + * + * Sheet open/close are forwarded to the controller's discovery + * toggle so active MediaRouter discovery only runs while the sheet + * is actually visible. + */ +@HiltViewModel +class OutputPickerViewModel @Inject constructor( + private val controller: OutputPickerController, +) : ViewModel() { + + val routes: StateFlow = controller.routesState + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(STOP_TIMEOUT_MS), + initialValue = controller.routesState.value, + ) + + private val sheetVisibleInternal = MutableStateFlow(false) + val sheetVisible: StateFlow = sheetVisibleInternal.asStateFlow() + + fun onChipTapped() { + sheetVisibleInternal.value = true + controller.upgradeDiscovery() + } + + fun onSheetDismissed() { + sheetVisibleInternal.value = false + controller.downgradeDiscovery() + } + + fun onRouteSelected(route: OutputRoute) { + controller.select(route) + sheetVisibleInternal.value = false + controller.downgradeDiscovery() + } + + private companion object { + // SharingStarted timeout so quick screen-orientation changes + // don't tear down + re-subscribe the controller's Flow. + const val STOP_TIMEOUT_MS = 5_000L + } +}