feat(android): OutputPickerViewModel - sheet lifecycle + selection
android / Build + lint + test (push) Has been cancelled
android / Build + lint + test (push) Has been cancelled
Bluetooth slice (3/5). HiltViewModel projecting the controller's routesState Flow plus a sheetVisible MutableStateFlow that owns the sheet's open/close state. onChipTapped + onSheetDismissed forward to the controller's discovery toggle so active MediaRouter discovery only runs while the sheet is visible (battery cost). Compose UI + NowPlaying wiring land next.
This commit is contained in:
+60
@@ -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<RouteSnapshot> = controller.routesState
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(STOP_TIMEOUT_MS),
|
||||
initialValue = controller.routesState.value,
|
||||
)
|
||||
|
||||
private val sheetVisibleInternal = MutableStateFlow(false)
|
||||
val sheetVisible: StateFlow<Boolean> = 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user