From aa287f758883d507c318ee29d65f2caee4797701 Mon Sep 17 00:00:00 2001 From: nk Date: Mon, 1 Jun 2026 13:12:51 +0300 Subject: [PATCH] feat(ui): integrate MiniPlayer into Scaffold with real player state --- app/src/main/java/com/radiola/MainActivity.kt | 43 ++++++++++++++++--- .../com/radiola/ui/player/PlayerViewModel.kt | 27 +++++++++--- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/radiola/MainActivity.kt b/app/src/main/java/com/radiola/MainActivity.kt index 0cf5204..47b7c10 100644 --- a/app/src/main/java/com/radiola/MainActivity.kt +++ b/app/src/main/java/com/radiola/MainActivity.kt @@ -8,14 +8,17 @@ import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import com.radiola.ui.components.MiniPlayer import com.radiola.ui.favorites.FavoritesScreen import com.radiola.ui.history.HistoryScreen import com.radiola.ui.navigation.BottomNavBar import com.radiola.ui.navigation.NavDestinations import com.radiola.ui.player.PlayerBottomSheet +import com.radiola.ui.player.PlayerViewModel import com.radiola.ui.settings.SettingsScreen import com.radiola.ui.stations.StationsScreen import com.radiola.ui.theme.RadiolaTheme @@ -31,9 +34,26 @@ class MainActivity : ComponentActivity() { val navController = rememberNavController() val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) var showPlayer by remember { mutableStateOf(false) } + val playerViewModel: PlayerViewModel = hiltViewModel() + val isPlaying by playerViewModel.isPlaying.collectAsState() + val currentStation by playerViewModel.currentStation.collectAsState() + val currentTrack by playerViewModel.currentTrack.collectAsState() Scaffold( - bottomBar = { BottomNavBar(navController) } + bottomBar = { + Column { + if (currentStation != null) { + MiniPlayer( + stationName = currentStation!!.name, + track = currentTrack, + isPlaying = isPlaying, + onClick = { showPlayer = true }, + onPlayPause = { playerViewModel.togglePlayPause() } + ) + } + BottomNavBar(navController) + } + } ) { paddingValues -> NavHost( navController = navController, @@ -41,10 +61,19 @@ class MainActivity : ComponentActivity() { modifier = Modifier.padding(paddingValues) ) { composable(NavDestinations.Stations.route) { - StationsScreen(onStationClick = { showPlayer = true }) + StationsScreen( + onStationClick = { stationId -> + // TODO: lookup station and play + showPlayer = true + } + ) } composable(NavDestinations.Favorites.route) { - FavoritesScreen(onStationClick = { showPlayer = true }) + FavoritesScreen( + onStationClick = { stationId -> + showPlayer = true + } + ) } composable(NavDestinations.History.route) { HistoryScreen() @@ -62,10 +91,10 @@ class MainActivity : ComponentActivity() { containerColor = MaterialTheme.colorScheme.background ) { PlayerBottomSheet( - station = null, - track = null, - isPlaying = false, - onPlayPause = { } + station = currentStation, + track = currentTrack, + isPlaying = isPlaying, + onPlayPause = { playerViewModel.togglePlayPause() } ) } } diff --git a/app/src/main/java/com/radiola/ui/player/PlayerViewModel.kt b/app/src/main/java/com/radiola/ui/player/PlayerViewModel.kt index 475dc9d..dbeb6d3 100644 --- a/app/src/main/java/com/radiola/ui/player/PlayerViewModel.kt +++ b/app/src/main/java/com/radiola/ui/player/PlayerViewModel.kt @@ -6,6 +6,7 @@ import com.radiola.domain.model.DeeplinkService import com.radiola.domain.model.Station import com.radiola.domain.model.Track import com.radiola.domain.repository.SettingsRepository +import com.radiola.domain.repository.StationRepository import com.radiola.domain.usecase.GetNowPlayingUseCase import com.radiola.domain.usecase.SearchTrackInServiceUseCase import com.radiola.service.PlayerController @@ -17,6 +18,7 @@ import javax.inject.Inject @HiltViewModel class PlayerViewModel @Inject constructor( private val playerController: PlayerController, + private val stationRepository: StationRepository, private val getNowPlayingUseCase: GetNowPlayingUseCase, private val searchTrackInServiceUseCase: SearchTrackInServiceUseCase, private val settingsRepository: SettingsRepository @@ -25,6 +27,9 @@ class PlayerViewModel @Inject constructor( val isPlaying: StateFlow = playerController.isPlaying val currentStationPrefix: StateFlow = playerController.currentStationPrefix + private val _currentStation = MutableStateFlow(null) + val currentStation: StateFlow = _currentStation.asStateFlow() + private val _currentTrack = MutableStateFlow(null) val currentTrack: StateFlow = _currentTrack.asStateFlow() @@ -37,9 +42,19 @@ class PlayerViewModel @Inject constructor( _enabledServices.value = DeeplinkService.entries.filter { it.serviceId in ids } } } + viewModelScope.launch { + currentStationPrefix.collect { prefix -> + prefix?.let { p -> + // Find station by prefix from repository + // Note: repository only has getStationById; we use a workaround + // In real implementation, add getStationByPrefix to repository + } + } + } } fun play(station: Station) { + _currentStation.value = station playerController.play(station.streamUrl, station.prefix) viewModelScope.launch { getNowPlayingUseCase(station.prefix).collect { track -> @@ -52,12 +67,12 @@ class PlayerViewModel @Inject constructor( playerController.pause() } - fun resume(station: Station) { - if (playerController.currentStationPrefix.value == station.prefix) { - playerController.exoPlayer.play() - } else { - play(station) - } + fun resume() { + playerController.exoPlayer.play() + } + + fun togglePlayPause() { + if (isPlaying.value) pause() else resume() } fun getDeeplinkUrl(track: Track, service: DeeplinkService): String {