diff --git a/androidApp/src/main/java/com/m3u/androidApp/ui/AppViewModel.kt b/androidApp/src/main/java/com/m3u/androidApp/ui/AppViewModel.kt index c460c953c..6c87e702a 100644 --- a/androidApp/src/main/java/com/m3u/androidApp/ui/AppViewModel.kt +++ b/androidApp/src/main/java/com/m3u/androidApp/ui/AppViewModel.kt @@ -1,21 +1,26 @@ package com.m3u.androidApp.ui +import android.util.Log import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.work.WorkManager import com.m3u.androidApp.ui.sheet.RemoteControlSheetValue import com.m3u.core.architecture.Publisher import com.m3u.core.architecture.dispatcher.Dispatcher import com.m3u.core.architecture.dispatcher.M3uDispatchers.IO import com.m3u.core.architecture.preferences.Preferences import com.m3u.data.api.LocalPreparedService +import com.m3u.data.repository.playlist.PlaylistRepository +import com.m3u.data.repository.programme.ProgrammeRepository import com.m3u.data.repository.television.ConnectionToTelevisionValue import com.m3u.data.repository.television.TelevisionRepository import com.m3u.data.service.Messager import com.m3u.data.television.model.RemoteDirection +import com.m3u.data.worker.SubscriptionWorker import com.m3u.ui.Destination import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher @@ -37,12 +42,21 @@ import javax.inject.Inject @HiltViewModel class AppViewModel @Inject constructor( messager: Messager, + private val playlistRepository: PlaylistRepository, + private val programmeRepository: ProgrammeRepository, private val televisionRepository: TelevisionRepository, private val localService: LocalPreparedService, + private val workManager: WorkManager, private val preferences: Preferences, private val publisher: Publisher, @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, ) : ViewModel() { + init { + if (preferences.autoRefreshProgrammes) { + refreshAllProgrammes() + } + } + val broadcastCodeOnTelevision: StateFlow = televisionRepository .broadcastCodeOnTelevision .map { code -> code?.let { convertToPaddedString(it) } } @@ -145,6 +159,19 @@ class AppViewModel @Inject constructor( } } + private fun refreshAllProgrammes() { + viewModelScope.launch { + val playlists = playlistRepository.getAll() + playlists.forEach { playlist -> + SubscriptionWorker.epg( + workManager = workManager, + playlistUrl = playlist.url, + ignoreCache = true + ) + } + } + } + var rootDestination: Destination.Root by mutableStateOf( when (preferences.rootDestination) { 0 -> Destination.Root.Foryou diff --git a/core/src/main/java/com/m3u/core/architecture/preferences/Preferences.kt b/core/src/main/java/com/m3u/core/architecture/preferences/Preferences.kt index 5787e7b92..70a52758b 100644 --- a/core/src/main/java/com/m3u/core/architecture/preferences/Preferences.kt +++ b/core/src/main/java/com/m3u/core/architecture/preferences/Preferences.kt @@ -61,8 +61,11 @@ class Preferences @Inject constructor( var clipMode: Int by sharedPreferences.intAsState(DEFAULT_CLIP_MODE, CLIP_MODE) - var autoRefresh: Boolean by - sharedPreferences.booleanAsState(DEFAULT_AUTO_REFRESH, AUTO_REFRESH) + var autoRefreshChannels: Boolean by + sharedPreferences.booleanAsState(DEFAULT_AUTO_REFRESH_CHANNELS, AUTO_REFRESH_CHANNELS) + + var autoRefreshProgrammes: Boolean by + sharedPreferences.booleanAsState(DEFAULT_AUTO_REFRESH_PROGRAMMES, AUTO_REFRESH_PROGRAMMES) var fullInfoPlayer: Boolean by sharedPreferences.booleanAsState(DEFAULT_FULL_INFO_PLAYER, FULL_INFO_PLAYER) @@ -131,7 +134,8 @@ class Preferences @Inject constructor( @ClipMode const val DEFAULT_CLIP_MODE = ClipMode.ADAPTIVE - const val DEFAULT_AUTO_REFRESH = false + const val DEFAULT_AUTO_REFRESH_CHANNELS = false + const val DEFAULT_AUTO_REFRESH_PROGRAMMES = false const val DEFAULT_FULL_INFO_PLAYER = false const val DEFAULT_ROOT_DESTINATION = 0 const val DEFAULT_NO_PICTURE_MODE = false @@ -170,7 +174,8 @@ class Preferences @Inject constructor( const val GOD_MODE = "god-mode" const val CLIP_MODE = "clip-mode" - const val AUTO_REFRESH = "auto-refresh" + const val AUTO_REFRESH_CHANNELS = "auto-refresh-channels" + const val AUTO_REFRESH_PROGRAMMES = "auto-refresh-programmes" const val FULL_INFO_PLAYER = "full-info-player" const val ROOT_DESTINATION = "root-destination" const val NO_PICTURE_MODE = "no-picture-mode" diff --git a/data/src/main/java/com/m3u/data/database/dao/PlaylistDao.kt b/data/src/main/java/com/m3u/data/database/dao/PlaylistDao.kt index 298f19169..d86f15b9e 100644 --- a/data/src/main/java/com/m3u/data/database/dao/PlaylistDao.kt +++ b/data/src/main/java/com/m3u/data/database/dao/PlaylistDao.kt @@ -6,6 +6,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction +import com.m3u.data.database.model.DataSource import com.m3u.data.database.model.Playlist import com.m3u.data.database.model.PlaylistWithCount import com.m3u.data.database.model.PlaylistWithStreams @@ -28,6 +29,9 @@ internal interface PlaylistDao { @Query("SELECT * FROM playlists WHERE url = :url") suspend fun getByUrl(url: String): Playlist? + @Query("SELECT * FROM playlists WHERE source = :source") + suspend fun getBySource(source: DataSource): List + @Query("SELECT * FROM playlists ORDER BY title") fun observeAll(): Flow> diff --git a/data/src/main/java/com/m3u/data/repository/playlist/PlaylistRepository.kt b/data/src/main/java/com/m3u/data/repository/playlist/PlaylistRepository.kt index bf7c52e6c..607e16b8e 100644 --- a/data/src/main/java/com/m3u/data/repository/playlist/PlaylistRepository.kt +++ b/data/src/main/java/com/m3u/data/repository/playlist/PlaylistRepository.kt @@ -2,6 +2,7 @@ package com.m3u.data.repository.playlist import android.net.Uri import androidx.compose.runtime.Immutable +import com.m3u.data.database.model.DataSource import com.m3u.data.database.model.Playlist import com.m3u.data.database.model.PlaylistWithCount import com.m3u.data.database.model.PlaylistWithStreams @@ -14,6 +15,8 @@ interface PlaylistRepository { fun observeAllEpgs(): Flow> fun observePlaylistUrls(): Flow> suspend fun get(url: String): Playlist? + suspend fun getAll(): List + suspend fun getBySource(source: DataSource): List suspend fun getCategoriesByPlaylistUrlIgnoreHidden(url: String, query: String): List fun observeCategoriesByPlaylistUrlIgnoreHidden(url: String, query: String): Flow> fun observe(url: String): Flow diff --git a/data/src/main/java/com/m3u/data/repository/playlist/PlaylistRepositoryImpl.kt b/data/src/main/java/com/m3u/data/repository/playlist/PlaylistRepositoryImpl.kt index fd21c1ec6..803a83ec8 100644 --- a/data/src/main/java/com/m3u/data/repository/playlist/PlaylistRepositoryImpl.kt +++ b/data/src/main/java/com/m3u/data/repository/playlist/PlaylistRepositoryImpl.kt @@ -13,8 +13,8 @@ import com.m3u.core.architecture.logger.execute import com.m3u.core.architecture.logger.install import com.m3u.core.architecture.logger.post import com.m3u.core.architecture.logger.sandBox -import com.m3u.core.architecture.preferences.Preferences import com.m3u.core.architecture.preferences.PlaylistStrategy +import com.m3u.core.architecture.preferences.Preferences import com.m3u.core.util.basic.startsWithAny import com.m3u.core.util.readFileContent import com.m3u.core.util.readFileName @@ -517,6 +517,14 @@ internal class PlaylistRepositoryImpl @Inject constructor( playlistDao.getByUrl(url) } + override suspend fun getAll(): List = logger.execute { + playlistDao.getAll() + } ?: emptyList() + + override suspend fun getBySource(source: DataSource): List = logger.execute { + playlistDao.getBySource(source) + } ?: emptyList() + override suspend fun getCategoriesByPlaylistUrlIgnoreHidden( url: String, query: String diff --git a/data/src/main/java/com/m3u/data/repository/programme/ProgrammeRepository.kt b/data/src/main/java/com/m3u/data/repository/programme/ProgrammeRepository.kt index 34b02d75f..77c8ecc1d 100644 --- a/data/src/main/java/com/m3u/data/repository/programme/ProgrammeRepository.kt +++ b/data/src/main/java/com/m3u/data/repository/programme/ProgrammeRepository.kt @@ -23,7 +23,7 @@ interface ProgrammeRepository { val refreshingEpgUrls: StateFlow> fun checkOrRefreshProgrammesOrThrow( - playlistUrl: String, + vararg playlistUrls: String, ignoreCache: Boolean ): Flow } \ No newline at end of file diff --git a/data/src/main/java/com/m3u/data/repository/programme/ProgrammeRepositoryImpl.kt b/data/src/main/java/com/m3u/data/repository/programme/ProgrammeRepositoryImpl.kt index 4e4f25b19..7b4b2e3c8 100644 --- a/data/src/main/java/com/m3u/data/repository/programme/ProgrammeRepositoryImpl.kt +++ b/data/src/main/java/com/m3u/data/repository/programme/ProgrammeRepositoryImpl.kt @@ -71,11 +71,15 @@ internal class ProgrammeRepositoryImpl @Inject constructor( } override fun checkOrRefreshProgrammesOrThrow( - playlistUrl: String, + vararg playlistUrls: String, ignoreCache: Boolean ): Flow = channelFlow { - val playlist = playlistDao.getByUrl(playlistUrl) ?: return@channelFlow - val epgUrls = playlist.epgUrlsOrXtreamXmlUrl() + val epgUrls = playlistUrls.flatMap { playlistUrl -> + val playlist = playlistDao.getByUrl(playlistUrl) ?: return@flatMap emptyList() + playlist.epgUrlsOrXtreamXmlUrl() + } + .toSet() + .toList() val producer = checkOrRefreshProgrammesOrThrowImpl( epgUrls = epgUrls, ignoreCache = ignoreCache diff --git a/data/src/main/java/com/m3u/data/worker/SubscriptionWorker.kt b/data/src/main/java/com/m3u/data/worker/SubscriptionWorker.kt index ca4fbf745..4fd06827f 100644 --- a/data/src/main/java/com/m3u/data/worker/SubscriptionWorker.kt +++ b/data/src/main/java/com/m3u/data/worker/SubscriptionWorker.kt @@ -116,7 +116,7 @@ class SubscriptionWorker @AssistedInject constructor( val ignoreCache = epgIgnoreCache try { programmeRepository.checkOrRefreshProgrammesOrThrow( - playlistUrl = playlistUrl, + playlistUrl, ignoreCache = ignoreCache ) .onEach { count -> diff --git a/features/foryou/src/main/java/com/m3u/features/foryou/ForyouScreen.kt b/features/foryou/src/main/java/com/m3u/features/foryou/ForyouScreen.kt index 3aa3e3c24..ab8f57fe4 100644 --- a/features/foryou/src/main/java/com/m3u/features/foryou/ForyouScreen.kt +++ b/features/foryou/src/main/java/com/m3u/features/foryou/ForyouScreen.kt @@ -83,6 +83,7 @@ fun ForyouRoute( val series: Stream? by viewModel.series.collectAsStateWithLifecycle() val subscribingPlaylistUrls by viewModel.subscribingPlaylistUrls.collectAsStateWithLifecycle() + val refreshingEpgUrls by viewModel.refreshingEpgUrls.collectAsStateWithLifecycle(emptyList()) if (isPageInfoVisible) { LifecycleResumeEffect(title) { @@ -105,6 +106,7 @@ fun ForyouRoute( ForyouScreen( playlistCountsResource = playlistCountsResource, subscribingPlaylistUrls = subscribingPlaylistUrls, + refreshingEpgUrls = refreshingEpgUrls, recommend = recommend, rowCount = preferences.rowCount, contentPadding = contentPadding, @@ -169,6 +171,7 @@ private fun ForyouScreen( rowCount: Int, playlistCountsResource: Resource>, subscribingPlaylistUrls: List, + refreshingEpgUrls: List, recommend: Recommend, contentPadding: PaddingValues, navigateToPlaylist: (Playlist) -> Unit, @@ -214,6 +217,7 @@ private fun ForyouScreen( rowCount = actualRowCount, playlistCounts = playlistCountsResource.data, subscribingPlaylistUrls = subscribingPlaylistUrls, + refreshingEpgUrls = refreshingEpgUrls, onClick = navigateToPlaylist, onLongClick = { mediaSheetValue = MediaSheetValue.ForyouScreen(it) }, header = header.takeIf { recommend.isNotEmpty() }, diff --git a/features/foryou/src/main/java/com/m3u/features/foryou/ForyouViewModel.kt b/features/foryou/src/main/java/com/m3u/features/foryou/ForyouViewModel.kt index 6b7e2ae82..271589c9d 100644 --- a/features/foryou/src/main/java/com/m3u/features/foryou/ForyouViewModel.kt +++ b/features/foryou/src/main/java/com/m3u/features/foryou/ForyouViewModel.kt @@ -21,11 +21,13 @@ import com.m3u.data.database.model.PlaylistWithCount import com.m3u.data.database.model.Stream import com.m3u.data.parser.xtream.XtreamStreamInfo import com.m3u.data.repository.playlist.PlaylistRepository +import com.m3u.data.repository.programme.ProgrammeRepository import com.m3u.data.repository.stream.StreamRepository import com.m3u.data.worker.SubscriptionWorker import com.m3u.features.foryou.components.recommend.Recommend import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -45,6 +47,7 @@ import kotlin.time.toDuration class ForyouViewModel @Inject constructor( private val playlistRepository: PlaylistRepository, streamRepository: StreamRepository, + programmeRepository: ProgrammeRepository, preferences: Preferences, @Dispatcher(IO) ioDispatcher: CoroutineDispatcher, workManager: WorkManager, @@ -81,6 +84,8 @@ class ForyouViewModel @Inject constructor( started = SharingStarted.WhileSubscribed(5_000L) ) + internal val refreshingEpgUrls: Flow> = programmeRepository.refreshingEpgUrls + private val unseensDuration = snapshotFlow { preferences.unseensMilliseconds } .map { it.toDuration(DurationUnit.MILLISECONDS) } .flowOn(ioDispatcher) diff --git a/features/foryou/src/main/java/com/m3u/features/foryou/components/PlaylistGallery.kt b/features/foryou/src/main/java/com/m3u/features/foryou/components/PlaylistGallery.kt index 053fb2900..d6d86e377 100644 --- a/features/foryou/src/main/java/com/m3u/features/foryou/components/PlaylistGallery.kt +++ b/features/foryou/src/main/java/com/m3u/features/foryou/components/PlaylistGallery.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.unit.dp import com.m3u.data.database.model.DataSource import com.m3u.data.database.model.Playlist import com.m3u.data.database.model.PlaylistWithCount +import com.m3u.data.database.model.epgUrlsOrXtreamXmlUrl import com.m3u.data.database.model.fromLocal import com.m3u.data.database.model.type import com.m3u.i18n.R.string @@ -27,6 +28,7 @@ internal fun PlaylistGallery( rowCount: Int, playlistCounts: List, subscribingPlaylistUrls: List, + refreshingEpgUrls: List, onClick: (Playlist) -> Unit, onLongClick: (Playlist) -> Unit, modifier: Modifier = Modifier, @@ -53,6 +55,9 @@ internal fun PlaylistGallery( val playlist = playlistCount.playlist val count = playlistCount.count val subscribing = playlist.url in subscribingPlaylistUrls + val refreshing = playlist + .epgUrlsOrXtreamXmlUrl() + .any { it in refreshingEpgUrls } PlaylistItem( label = PlaylistGalleryDefaults.calculateUiTitle( title = playlist.title, @@ -66,7 +71,7 @@ internal fun PlaylistGallery( } }, count = count, - subscribing = subscribing, + subscribingOrRefreshing = subscribing || refreshing, local = playlist.fromLocal, onClick = { onClick(playlist) }, onLongClick = { onLongClick(playlist) }, diff --git a/features/foryou/src/main/java/com/m3u/features/foryou/components/PlaylistItem.kt b/features/foryou/src/main/java/com/m3u/features/foryou/components/PlaylistItem.kt index 617075308..2a0ad30f4 100644 --- a/features/foryou/src/main/java/com/m3u/features/foryou/components/PlaylistItem.kt +++ b/features/foryou/src/main/java/com/m3u/features/foryou/components/PlaylistItem.kt @@ -46,7 +46,7 @@ internal fun PlaylistItem( type: String?, count: Int, local: Boolean, - subscribing: Boolean, + subscribingOrRefreshing: Boolean, onClick: () -> Unit, onLongClick: () -> Unit, modifier: Modifier = Modifier @@ -58,7 +58,7 @@ internal fun PlaylistItem( type = type, count = count, local = local, - subscribing = subscribing, + subscribing = subscribingOrRefreshing, onClick = onClick, onLongClick = onLongClick, modifier = modifier @@ -68,7 +68,7 @@ internal fun PlaylistItem( label = label, type = type, count = count, - subscribing = subscribing, + subscribing = subscribingOrRefreshing, onClick = onClick, onLongClick = onLongClick, modifier = modifier diff --git a/features/playlist/src/main/java/com/m3u/features/playlist/PlaylistScreen.kt b/features/playlist/src/main/java/com/m3u/features/playlist/PlaylistScreen.kt index 67ab46c0b..13c5ca73f 100644 --- a/features/playlist/src/main/java/com/m3u/features/playlist/PlaylistScreen.kt +++ b/features/playlist/src/main/java/com/m3u/features/playlist/PlaylistScreen.kt @@ -179,8 +179,8 @@ internal fun PlaylistRoute( } } - LaunchedEffect(preferences.autoRefresh, playlistUrl) { - if (playlistUrl.isNotEmpty() && preferences.autoRefresh) { + LaunchedEffect(preferences.autoRefreshChannels, playlistUrl) { + if (playlistUrl.isNotEmpty() && preferences.autoRefreshChannels) { viewModel.refresh() } } diff --git a/features/playlist/src/main/java/com/m3u/features/playlist/components/SmartphoneStreamGallery.kt b/features/playlist/src/main/java/com/m3u/features/playlist/components/SmartphoneStreamGallery.kt index e6d365770..da253752a 100644 --- a/features/playlist/src/main/java/com/m3u/features/playlist/components/SmartphoneStreamGallery.kt +++ b/features/playlist/src/main/java/com/m3u/features/playlist/components/SmartphoneStreamGallery.kt @@ -10,6 +10,11 @@ import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -58,9 +63,13 @@ internal fun SmartphoneStreamGallery( items(streams?.itemCount ?: 0) { index -> val stream = streams?.get(index) if (stream != null) { + var programme: Programme? by remember { mutableStateOf(null) } + LaunchedEffect(stream.channelId) { + programme = getProgrammeCurrently(stream.channelId.orEmpty()) + } SmartphoneStreamItem( stream = stream, - getProgrammeCurrently = { getProgrammeCurrently(stream.channelId.orEmpty()) }, + programme = programme, recently = recently, zapping = zapping == stream, isVodOrSeriesPlaylist = isVodOrSeriesPlaylist, diff --git a/features/playlist/src/main/java/com/m3u/features/playlist/components/SmartphoneStreamItem.kt b/features/playlist/src/main/java/com/m3u/features/playlist/components/SmartphoneStreamItem.kt index b4fa58ab8..b9a5db986 100644 --- a/features/playlist/src/main/java/com/m3u/features/playlist/components/SmartphoneStreamItem.kt +++ b/features/playlist/src/main/java/com/m3u/features/playlist/components/SmartphoneStreamItem.kt @@ -20,11 +20,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.movableContentOf -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -68,7 +65,7 @@ internal fun SmartphoneStreamItem( zapping: Boolean, onClick: () -> Unit, onLongClick: () -> Unit, - getProgrammeCurrently: suspend () -> Programme?, + programme: Programme?, modifier: Modifier = Modifier, isVodOrSeriesPlaylist: Boolean = true ) { @@ -76,11 +73,6 @@ internal fun SmartphoneStreamItem( val spacing = LocalSpacing.current val preferences = hiltPreferences() - val currentGetProgrammeCurrently by rememberUpdatedState(getProgrammeCurrently) - val programme: Programme? by produceState(null) { - value = currentGetProgrammeCurrently() - } - val favourite = stream.favourite val recentlyString = stringResource(string.ui_sort_recently) @@ -219,7 +211,7 @@ internal fun SmartphoneStreamItem( programme != null -> { Text( - text = programme?.readText() ?: AnnotatedString(""), + text = programme.readText(), style = MaterialTheme.typography.bodySmall, color = LocalContentColor.current.copy(0.56f), maxLines = 1, diff --git a/features/setting/src/main/java/com/m3u/features/setting/fragments/preferences/RegularPreferences.kt b/features/setting/src/main/java/com/m3u/features/setting/fragments/preferences/RegularPreferences.kt index f0999938b..ade65900d 100644 --- a/features/setting/src/main/java/com/m3u/features/setting/fragments/preferences/RegularPreferences.kt +++ b/features/setting/src/main/java/com/m3u/features/setting/fragments/preferences/RegularPreferences.kt @@ -143,11 +143,19 @@ internal fun RegularPreferences( onChanged = { preferences.noPictureMode = !preferences.noPictureMode } ) CheckBoxSharedPreference( - title = string.feat_setting_auto_refresh, - content = string.feat_setting_auto_refresh_description, + title = string.feat_setting_auto_refresh_channels, + content = string.feat_setting_auto_refresh_channels_description, icon = Icons.Rounded.Refresh, - checked = preferences.autoRefresh, - onChanged = { preferences.autoRefresh = !preferences.autoRefresh } + checked = preferences.autoRefreshChannels, + onChanged = { preferences.autoRefreshChannels = !preferences.autoRefreshChannels } + ) + + CheckBoxSharedPreference( + title = string.feat_setting_auto_refresh_programmes, + content = string.feat_setting_auto_refresh_programmes_description, + icon = Icons.Rounded.Refresh, + checked = preferences.autoRefreshProgrammes, + onChanged = { preferences.autoRefreshProgrammes = !preferences.autoRefreshProgrammes } ) CheckBoxSharedPreference( diff --git a/features/stream/src/main/java/com/m3u/features/stream/components/ProgrammeGuide.kt b/features/stream/src/main/java/com/m3u/features/stream/components/ProgrammeGuide.kt index 441768f27..2a84fa78a 100644 --- a/features/stream/src/main/java/com/m3u/features/stream/components/ProgrammeGuide.kt +++ b/features/stream/src/main/java/com/m3u/features/stream/components/ProgrammeGuide.kt @@ -372,7 +372,7 @@ private fun CurrentTimelineCell( ) { val spacing = LocalSpacing.current val preferences = hiltPreferences() - val clockMode = preferences.twelveHourClock + val twelveHourClock = preferences.twelveHourClock val color = MaterialTheme.colorScheme.error val contentColor = MaterialTheme.colorScheme.onError val currentMilliseconds by rememberUpdatedState(milliseconds) @@ -380,7 +380,7 @@ private fun CurrentTimelineCell( Instant .fromEpochMilliseconds(currentMilliseconds) .toLocalDateTime(TimeZone.currentSystemDefault()) - .formatEOrSh(clockMode) + .formatEOrSh(twelveHourClock, ignoreSeconds = false) } Box(contentAlignment = Alignment.CenterEnd) { Canvas( diff --git a/i18n/src/main/res/values-de-rDE/feat_setting.xml b/i18n/src/main/res/values-de-rDE/feat_setting.xml index de8d77710..24f83585d 100644 --- a/i18n/src/main/res/values-de-rDE/feat_setting.xml +++ b/i18n/src/main/res/values-de-rDE/feat_setting.xml @@ -28,8 +28,8 @@ adaptiv Clip gestreckt - autom. Aktualisierung - autom. aktualisierung, wenn die Playlist aufgerufen wird + autom. Aktualisierung + autom. aktualisierung, wenn die Playlist aufgerufen wird immer TV Modus wird zurückgesetzt, wenn die App neu gestartet wird diff --git a/i18n/src/main/res/values-es-rES/feat_setting.xml b/i18n/src/main/res/values-es-rES/feat_setting.xml index 7d5cd4e90..fcd0f45d7 100644 --- a/i18n/src/main/res/values-es-rES/feat_setting.xml +++ b/i18n/src/main/res/values-es-rES/feat_setting.xml @@ -28,8 +28,8 @@ adaptativo centrado estirado - autorefrescar - se actualiza automáticamente cuando se ingresa la lista + autorefrescar + se actualiza automáticamente cuando se ingresa la lista siempre en televisión será revertido cuando se reinicia la app diff --git a/i18n/src/main/res/values-es-rMX/feat_setting.xml b/i18n/src/main/res/values-es-rMX/feat_setting.xml index 405168cb5..f9cfe1590 100644 --- a/i18n/src/main/res/values-es-rMX/feat_setting.xml +++ b/i18n/src/main/res/values-es-rMX/feat_setting.xml @@ -28,8 +28,8 @@ adaptativo centrado estirado - autoactualizar - actualiza automáticamente al ingresar la playlist + autoactualizar + actualiza automáticamente al ingresar la playlist siempre como televisor se revertirá cuando se reinicia la app diff --git a/i18n/src/main/res/values-pt-rBR/feat_setting.xml b/i18n/src/main/res/values-pt-rBR/feat_setting.xml index a33a3544e..3aca9dcfb 100644 --- a/i18n/src/main/res/values-pt-rBR/feat_setting.xml +++ b/i18n/src/main/res/values-pt-rBR/feat_setting.xml @@ -28,8 +28,8 @@ Adaptativo Clip Esticado - Atualização automática - Atualiza automaticamente ao entrar na playlist + Atualização automática + Atualiza automaticamente ao entrar na playlist Sempre no modo TV Será restaurado quando o aplicativo for reiniciado diff --git a/i18n/src/main/res/values-ro-rRO/feat_setting.xml b/i18n/src/main/res/values-ro-rRO/feat_setting.xml index 0320cdbfa..1ffe92960 100644 --- a/i18n/src/main/res/values-ro-rRO/feat_setting.xml +++ b/i18n/src/main/res/values-ro-rRO/feat_setting.xml @@ -26,8 +26,8 @@ adaptiv incadrare tot ecranul - actualizare automata - actualizare automata la deschidere lista + actualizare automata + actualizare automata la deschidere lista mod permanent televizor se va restaura la repornirea aplicatiei diff --git a/i18n/src/main/res/values-zh-rCN/feat_setting.xml b/i18n/src/main/res/values-zh-rCN/feat_setting.xml index e1cef4717..edbcf46f7 100644 --- a/i18n/src/main/res/values-zh-rCN/feat_setting.xml +++ b/i18n/src/main/res/values-zh-rCN/feat_setting.xml @@ -31,8 +31,10 @@ 自适应 裁剪 拉伸 - 自动刷新 - 进入播放列表自动刷新 + 自动刷新频道 + 进入播放列表自动刷新 + 自动刷新节目 + 启动时自动刷新所有节目 电视模式 会在应用重启时恢复 diff --git a/i18n/src/main/res/values/feat_setting.xml b/i18n/src/main/res/values/feat_setting.xml index c91651585..500eb0ba6 100644 --- a/i18n/src/main/res/values/feat_setting.xml +++ b/i18n/src/main/res/values/feat_setting.xml @@ -28,8 +28,10 @@ adaptive clip stretched - auto refresh - automatically refreshed when enter the playlist + auto refresh channels + automatically refreshed when enter the channel + auto refresh programmes + automatically refresh programmes when startup always television will be restored when the app is restarted diff --git a/ui/src/main/java/com/m3u/ui/util/TimeUtils.kt b/ui/src/main/java/com/m3u/ui/util/TimeUtils.kt index ad9ca715d..b16060d73 100644 --- a/ui/src/main/java/com/m3u/ui/util/TimeUtils.kt +++ b/ui/src/main/java/com/m3u/ui/util/TimeUtils.kt @@ -5,21 +5,33 @@ import kotlinx.datetime.LocalDateTime object TimeUtils { fun LocalDateTime.toEOrSh(): Float = run { hour + minute / 60f + second / 3600f } - fun LocalDateTime.formatEOrSh(use12HourFormat: Boolean): String { - return if (use12HourFormat) { + fun LocalDateTime.formatEOrSh( + twelveHourClock: Boolean, + ignoreSeconds: Boolean = true + ): String { + return if (twelveHourClock) { val hour12 = if (hour > 12) hour - 12 else if (hour == 0) 12 else hour -// val amPm = if (hour < 12) "AM" else "PM" val formattedHour = if (hour12 < 10) "0$hour12" else hour12.toString() val formattedMinute = if (minute < 10) "0$minute" else minute.toString() val formattedSecond = if (second < 10) "0$second" else second.toString() -// return "$formattedHour:$formattedMinute:$formattedSecond $amPm" - return "$formattedHour:$formattedMinute:$formattedSecond" + buildString { + append("$formattedHour:") + append(formattedMinute) + if (!ignoreSeconds) { + append(":$formattedSecond") + } + } } else { - "${if (hour < 10) "0$hour" else hour}:" + - "${if (minute < 10) "0$minute" else minute}" + - ":${if (second < 10) "0$second" else second}" + buildString { + append("${if (hour < 10) "0$hour" else hour}:") + append("${if (minute < 10) "0$minute" else minute}") + if (!ignoreSeconds) { + append(":${if (second < 10) "0$second" else second}") + } + } } } + fun Float.formatEOrSh(use12HourFormat: Boolean): String { val hour = (this / 1).toInt() val minute = (this % 1 * 60).toInt()