diff --git a/app/src/main/java/com/radiola/MainActivity.kt b/app/src/main/java/com/radiola/MainActivity.kt index 61773ee..28fc9dc 100644 --- a/app/src/main/java/com/radiola/MainActivity.kt +++ b/app/src/main/java/com/radiola/MainActivity.kt @@ -109,6 +109,19 @@ class MainActivity : ComponentActivity() { val isRecording by playerViewModel.isRecording.collectAsState() val isLoggedIn by tokenDataStore.isLoggedIn.collectAsState(initial = false) + // Возврат на передний план → мгновенно освежаем now-playing + // (фоновая заморозка ColorOS может останавливать опрос эфира). + val lifecycleOwner = androidx.compose.ui.platform.LocalLifecycleOwner.current + DisposableEffect(lifecycleOwner) { + val obs = androidx.lifecycle.LifecycleEventObserver { _, event -> + if (event == androidx.lifecycle.Lifecycle.Event.ON_RESUME) { + playerViewModel.onAppForeground() + } + } + lifecycleOwner.lifecycle.addObserver(obs) + onDispose { lifecycleOwner.lifecycle.removeObserver(obs) } + } + // --- Авто-обновление: проверяем версию на старте, показываем диалог --- var pendingUpdate by remember { mutableStateOf(null) } var updateState by remember { mutableStateOf(com.radiola.ui.update.UpdateState.IDLE) } 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 cabf2b8..65a5202 100644 --- a/app/src/main/java/com/radiola/ui/player/PlayerViewModel.kt +++ b/app/src/main/java/com/radiola/ui/player/PlayerViewModel.kt @@ -118,6 +118,20 @@ class PlayerViewModel @Inject constructor( viewModelScope.launch { settingsRepository.getPreferredBitrate().collect { preferredBitrate = it } } + // Восстановление сессии: если процесс/Activity пересоздались, а станция уже + // играет в фоновом сервисе (PlayerController помнит id) — заново привязываемся + // и запускаем опрос now-playing. Иначе мини-плеер/эфир «застывают». + viewModelScope.launch { + combine(playerController.currentStationId, _stations) { id, list -> + id?.let { sid -> list.firstOrNull { it.id == sid } } + }.collect { station -> + if (station != null && _currentStation.value == null) { + _currentStation.value = station + _playlist.value = _stations.value + startNowPlaying(station) + } + } + } viewModelScope.launch { _currentTrack .filterNotNull() @@ -150,8 +164,20 @@ class PlayerViewModel @Inject constructor( playerController.play(url, station.prefix, station.name, station.id) } viewModelScope.launch { pushHistoryUseCase(station.id) } + startNowPlaying(station) + } + + /** + * Запускает опрос now-playing для станции: мгновенный рефреш + цикл раз в 5с + * (пока играем) + сбор трека из API (приоритет) и ICY (фолбэк). Вынесено из + * play(), чтобы переиспользовать при восстановлении сессии (возврат из фона / + * пересоздание ViewModel) — иначе эфир «застывает» на последнем значении. + */ + private fun startNowPlaying(station: Station) { nowPlayingJob?.cancel() nowPlayingJob = viewModelScope.launch { + // Сразу тянем свежий эфир — не ждём первые 5с цикла. + launch { nowPlayingRepository.refreshNowPlaying() } // Поллинг now-playing — ТОЛЬКО пока играем. collectLatest отменяет // внутренний цикл при паузе (иначе на паузе радио зря дёргали сеть // каждые 5с → батарея + лишняя нагрузка на бэкенд). @@ -208,6 +234,17 @@ class PlayerViewModel @Inject constructor( } } + /** + * Возврат приложения на передний план: мгновенно освежаем эфир (чтобы юзер не + * видел залипший трек после фоновой заморозки) и, если опрос почему-то не идёт, + * перезапускаем его для текущей станции. + */ + fun onAppForeground() { + val station = _currentStation.value ?: return + viewModelScope.launch { nowPlayingRepository.refreshNowPlaying() } + if (nowPlayingJob?.isActive != true) startNowPlaying(station) + } + /** Стартовое качество станции с учётом предпочтения пользователя. */ private fun pickInitialQuality(station: Station): StreamQuality? { val list = station.qualities