diff --git a/app/build.gradle b/app/build.gradle index e57d9c531..3804a7217 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -288,6 +288,7 @@ dependencies { // Jetpack Compose implementation(platform('androidx.compose:compose-bom:2024.06.00')) implementation 'androidx.compose.material3:material3:1.3.0-beta05' + implementation 'androidx.compose.material3.adaptive:adaptive:1.0.0-beta04' implementation 'androidx.activity:activity-compose' implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.compose.ui:ui-text:1.7.0-beta06' // Needed for parsing HTML to AnnotatedString diff --git a/app/src/main/java/org/schabi/newpipe/compose/playlist/Playlist.kt b/app/src/main/java/org/schabi/newpipe/compose/playlist/Playlist.kt index 3d73d55a2..9dceda9d8 100644 --- a/app/src/main/java/org/schabi/newpipe/compose/playlist/Playlist.kt +++ b/app/src/main/java/org/schabi/newpipe/compose/playlist/Playlist.kt @@ -1,28 +1,39 @@ package org.schabi.newpipe.compose.playlist import android.content.res.Configuration +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.rememberLazyGridState +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.fragment.app.FragmentActivity import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.paging.compose.collectAsLazyPagingItems +import my.nanihadesuka.compose.LazyColumnScrollbar import my.nanihadesuka.compose.LazyVerticalGridScrollbar import org.schabi.newpipe.DownloaderImpl +import org.schabi.newpipe.compose.stream.StreamCardItem import org.schabi.newpipe.compose.stream.StreamGridItem +import org.schabi.newpipe.compose.stream.StreamListItem import org.schabi.newpipe.compose.theme.AppTheme +import org.schabi.newpipe.compose.util.determineItemViewMode import org.schabi.newpipe.extractor.NewPipe import org.schabi.newpipe.extractor.ServiceList +import org.schabi.newpipe.extractor.stream.StreamInfoItem +import org.schabi.newpipe.info_list.ItemViewMode import org.schabi.newpipe.util.KEY_SERVICE_ID import org.schabi.newpipe.util.KEY_URL +import org.schabi.newpipe.util.NavigationHelper import org.schabi.newpipe.viewmodels.PlaylistViewModel @Composable @@ -31,18 +42,50 @@ fun Playlist(playlistViewModel: PlaylistViewModel = viewModel()) { val streams = playlistViewModel.streamItems.collectAsLazyPagingItems() val totalDuration = streams.itemSnapshotList.sumOf { it!!.duration } + val context = LocalContext.current + val onClick = { stream: StreamInfoItem -> + NavigationHelper.openVideoDetailFragment( + context, (context as FragmentActivity).supportFragmentManager, + stream.serviceId, stream.url, stream.name, null, false + ) + } + playlistInfo?.let { Surface(color = MaterialTheme.colorScheme.background) { - val gridState = rememberLazyGridState() + val mode = determineItemViewMode() - LazyVerticalGridScrollbar(state = gridState) { - LazyVerticalGrid(state = gridState, columns = GridCells.Adaptive(164.dp)) { - item(span = { GridItemSpan(maxLineSpan) }) { - PlaylistHeader(playlistInfo = it, totalDuration = totalDuration) + if (mode == ItemViewMode.GRID) { + val gridState = rememberLazyGridState() + + LazyVerticalGridScrollbar(state = gridState) { + LazyVerticalGrid(state = gridState, columns = GridCells.Adaptive(250.dp)) { + item(span = { GridItemSpan(maxLineSpan) }) { + PlaylistHeader(playlistInfo = it, totalDuration = totalDuration) + } + + items(streams.itemCount) { + StreamGridItem(streams[it]!!, onClick) + } } + } + } else { + // Card or list views + val listState = rememberLazyListState() - items(streams.itemCount) { - StreamGridItem(streams[it]!!) + LazyColumnScrollbar(state = listState) { + LazyColumn(state = listState) { + item { + PlaylistHeader(playlistInfo = it, totalDuration = totalDuration) + } + + items(streams.itemCount) { + val stream = streams[it]!! + if (mode == ItemViewMode.CARD) { + StreamCardItem(stream, onClick) + } else { + StreamListItem(stream, onClick) + } + } } } } diff --git a/app/src/main/java/org/schabi/newpipe/compose/playlist/PlaylistHeader.kt b/app/src/main/java/org/schabi/newpipe/compose/playlist/PlaylistHeader.kt index 9130ef5a8..fe7367c53 100644 --- a/app/src/main/java/org/schabi/newpipe/compose/playlist/PlaylistHeader.kt +++ b/app/src/main/java/org/schabi/newpipe/compose/playlist/PlaylistHeader.kt @@ -1,7 +1,9 @@ package org.schabi.newpipe.compose.playlist import android.content.res.Configuration +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image +import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -22,6 +24,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -65,9 +68,9 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp), - modifier = Modifier.apply { + modifier = Modifier.let { if (playlistInfo.uploaderName != null && playlistInfo.uploaderUrl != null) { - clickable { + it.clickable { try { NavigationHelper.openChannelFragment( (context as FragmentActivity).supportFragmentManager, @@ -78,11 +81,13 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) { ErrorUtil.showUiErrorSnackbar(context, "Opening channel fragment", e) } } - } + } else it } ) { val imageModifier = Modifier .size(24.dp) + .border(BorderStroke(1.dp, Color.White), CircleShape) + .padding(1.dp) .clip(CircleShape) val isMix = YoutubeParsingHelper.isYoutubeMixId(playlistInfo.id) || YoutubeParsingHelper.isYoutubeMusicMixId(playlistInfo.id) diff --git a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamCardItem.kt b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamCardItem.kt new file mode 100644 index 000000000..0a69330ac --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamCardItem.kt @@ -0,0 +1,70 @@ +package org.schabi.newpipe.compose.stream + +import android.content.res.Configuration +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import org.schabi.newpipe.compose.theme.AppTheme +import org.schabi.newpipe.extractor.stream.StreamInfoItem + +@Composable +fun StreamCardItem(stream: StreamInfoItem, onClick: (StreamInfoItem) -> Unit) { + Column( + modifier = Modifier + .clickable(onClick = { onClick(stream) }) + .padding(top = 12.dp, start = 2.dp, end = 2.dp) + ) { + StreamThumbnail( + stream = stream, + modifier = Modifier.fillMaxWidth(), + contentScale = ContentScale.FillWidth + ) + + Column(modifier = Modifier.padding(10.dp)) { + Text( + text = stream.name, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleSmall, + maxLines = 2 + ) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text(text = stream.uploaderName.orEmpty(), style = MaterialTheme.typography.bodySmall) + + Text( + text = getStreamInfoDetail(stream), + style = MaterialTheme.typography.bodySmall + ) + } + } + } +} + +@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun StreamCardItemPreview( + @PreviewParameter(StreamItemPreviewProvider::class) stream: StreamInfoItem +) { + AppTheme { + Surface(color = MaterialTheme.colorScheme.background) { + StreamCardItem(stream) {} + } + } +} diff --git a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamGridItem.kt b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamGridItem.kt index 696300a35..432cc1594 100644 --- a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamGridItem.kt +++ b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamGridItem.kt @@ -1,6 +1,5 @@ package org.schabi.newpipe.compose.stream -import android.content.Context import android.content.res.Configuration import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column @@ -11,47 +10,21 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp -import androidx.fragment.app.FragmentActivity -import coil.compose.AsyncImage -import org.schabi.newpipe.R import org.schabi.newpipe.compose.theme.AppTheme -import org.schabi.newpipe.extractor.Image import org.schabi.newpipe.extractor.stream.StreamInfoItem -import org.schabi.newpipe.extractor.stream.StreamType -import org.schabi.newpipe.util.Localization -import org.schabi.newpipe.util.NO_SERVICE_ID -import org.schabi.newpipe.util.NavigationHelper -import org.schabi.newpipe.util.image.ImageStrategy -import java.util.concurrent.TimeUnit @Composable -fun StreamGridItem(stream: StreamInfoItem) { - val context = LocalContext.current - +fun StreamGridItem(stream: StreamInfoItem, onClick: (StreamInfoItem) -> Unit) { Column( modifier = Modifier - .clickable { - NavigationHelper.openVideoDetailFragment( - context, (context as FragmentActivity).supportFragmentManager, - stream.serviceId, stream.url, stream.name, null, false - ) - } + .clickable(onClick = { onClick(stream) }) .padding(12.dp) ) { - AsyncImage( - model = ImageStrategy.choosePreferredImage(stream.thumbnails), - contentDescription = null, - placeholder = painterResource(R.drawable.placeholder_thumbnail_video), - error = painterResource(R.drawable.placeholder_thumbnail_video), - modifier = Modifier.size(width = 164.dp, height = 92.dp) - ) + StreamThumbnail(stream = stream, modifier = Modifier.size(width = 246.dp, height = 138.dp)) Text( text = stream.name, @@ -62,60 +35,13 @@ fun StreamGridItem(stream: StreamInfoItem) { Text(text = stream.uploaderName.orEmpty(), style = MaterialTheme.typography.bodySmall) - Text(text = getStreamInfoDetail(context, stream), style = MaterialTheme.typography.bodySmall) + Text( + text = getStreamInfoDetail(stream), + style = MaterialTheme.typography.bodySmall + ) } } -private fun getStreamInfoDetail(context: Context, stream: StreamInfoItem): String { - val views = if (stream.viewCount >= 0) { - when (stream.streamType) { - StreamType.AUDIO_LIVE_STREAM -> Localization.listeningCount(context, stream.viewCount) - StreamType.LIVE_STREAM -> Localization.shortWatchingCount(context, stream.viewCount) - else -> Localization.shortViewCount(context, stream.viewCount) - } - } else { - "" - } - val date = - Localization.relativeTimeOrTextual(context, stream.uploadDate, stream.textualUploadDate) - - return if (views.isEmpty()) { - date - } else if (date.isNullOrEmpty()) { - views - } else { - "$views • $date" - } -} - -fun StreamInfoItem( - serviceId: Int = NO_SERVICE_ID, - url: String = "", - name: String = "Stream", - streamType: StreamType, - uploaderName: String? = "Uploader", - uploaderUrl: String? = null, - uploaderAvatars: List = emptyList(), - duration: Long = TimeUnit.HOURS.toSeconds(1), - viewCount: Long = 10, - textualUploadDate: String = "1 month ago" -) = StreamInfoItem(serviceId, url, name, streamType).apply { - this.uploaderName = uploaderName - this.uploaderUrl = uploaderUrl - this.uploaderAvatars = uploaderAvatars - this.duration = duration - this.viewCount = viewCount - this.textualUploadDate = textualUploadDate -} - -private class StreamItemPreviewProvider : PreviewParameterProvider { - override val values = sequenceOf( - StreamInfoItem(streamType = StreamType.NONE), - StreamInfoItem(streamType = StreamType.LIVE_STREAM), - StreamInfoItem(streamType = StreamType.AUDIO_LIVE_STREAM), - ) -} - @Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO) @Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable @@ -124,7 +50,7 @@ private fun StreamGridItemPreview( ) { AppTheme { Surface(color = MaterialTheme.colorScheme.background) { - StreamGridItem(stream) + StreamGridItem(stream, onClick = {}) } } } diff --git a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamListItem.kt b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamListItem.kt new file mode 100644 index 000000000..fa85c025e --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamListItem.kt @@ -0,0 +1,65 @@ +package org.schabi.newpipe.compose.stream + +import android.content.res.Configuration +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import org.schabi.newpipe.compose.theme.AppTheme +import org.schabi.newpipe.extractor.stream.StreamInfoItem + +@Composable +fun StreamListItem(stream: StreamInfoItem, onClick: (StreamInfoItem) -> Unit) { + Row( + modifier = Modifier + .clickable(onClick = { onClick(stream) }) + .fillMaxWidth() + .padding(12.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + StreamThumbnail(stream = stream, modifier = Modifier.size(width = 98.dp, height = 55.dp)) + + Column { + Text( + text = stream.name, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleSmall, + maxLines = 1 + ) + + Text(text = stream.uploaderName.orEmpty(), style = MaterialTheme.typography.bodySmall) + + Text( + text = getStreamInfoDetail(stream), + style = MaterialTheme.typography.bodySmall + ) + } + } +} + +@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun StreamListItemPreview( + @PreviewParameter(StreamItemPreviewProvider::class) stream: StreamInfoItem +) { + AppTheme { + Surface(color = MaterialTheme.colorScheme.background) { + StreamListItem(stream, onClick = {}) + } + } +} diff --git a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamThumbnail.kt b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamThumbnail.kt new file mode 100644 index 000000000..f9228da3c --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamThumbnail.kt @@ -0,0 +1,44 @@ +package org.schabi.newpipe.compose.stream + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import org.schabi.newpipe.R +import org.schabi.newpipe.extractor.stream.StreamInfoItem +import org.schabi.newpipe.util.Localization +import org.schabi.newpipe.util.image.ImageStrategy + +@Composable +fun StreamThumbnail( + stream: StreamInfoItem, + modifier: Modifier = Modifier, + contentScale: ContentScale = ContentScale.Fit, +) { + Box(modifier = modifier, contentAlignment = Alignment.BottomEnd) { + AsyncImage( + model = ImageStrategy.choosePreferredImage(stream.thumbnails), + contentDescription = null, + placeholder = painterResource(R.drawable.placeholder_thumbnail_video), + error = painterResource(R.drawable.placeholder_thumbnail_video), + contentScale = contentScale, + modifier = modifier + ) + + Text( + text = Localization.getDurationString(stream.duration), + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(2.dp) + .background(Color.Black.copy(alpha = 0.5f)) + ) + } +} diff --git a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamUtils.kt b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamUtils.kt new file mode 100644 index 000000000..06284dd2d --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamUtils.kt @@ -0,0 +1,68 @@ +package org.schabi.newpipe.compose.stream + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import org.schabi.newpipe.extractor.Image +import org.schabi.newpipe.extractor.stream.StreamInfoItem +import org.schabi.newpipe.extractor.stream.StreamType +import org.schabi.newpipe.util.Localization +import org.schabi.newpipe.util.NO_SERVICE_ID +import java.util.concurrent.TimeUnit + +fun StreamInfoItem( + serviceId: Int = NO_SERVICE_ID, + url: String = "", + name: String = "Stream", + streamType: StreamType, + uploaderName: String? = "Uploader", + uploaderUrl: String? = null, + uploaderAvatars: List = emptyList(), + duration: Long = TimeUnit.HOURS.toSeconds(1), + viewCount: Long = 10, + textualUploadDate: String = "1 month ago" +) = StreamInfoItem(serviceId, url, name, streamType).apply { + this.uploaderName = uploaderName + this.uploaderUrl = uploaderUrl + this.uploaderAvatars = uploaderAvatars + this.duration = duration + this.viewCount = viewCount + this.textualUploadDate = textualUploadDate +} + +@Composable +internal fun getStreamInfoDetail(stream: StreamInfoItem): String { + val context = LocalContext.current + + return rememberSaveable(stream) { + val count = stream.viewCount + val views = if (count >= 0) { + when (stream.streamType) { + StreamType.AUDIO_LIVE_STREAM -> Localization.listeningCount(context, count) + StreamType.LIVE_STREAM -> Localization.shortWatchingCount(context, count) + else -> Localization.shortViewCount(context, count) + } + } else { + "" + } + val date = + Localization.relativeTimeOrTextual(context, stream.uploadDate, stream.textualUploadDate) + + if (views.isEmpty()) { + date + } else if (date.isNullOrEmpty()) { + views + } else { + "$views • $date" + } + } +} + +internal class StreamItemPreviewProvider : PreviewParameterProvider { + override val values = sequenceOf( + StreamInfoItem(streamType = StreamType.NONE), + StreamInfoItem(streamType = StreamType.LIVE_STREAM), + StreamInfoItem(streamType = StreamType.AUDIO_LIVE_STREAM), + ) +} diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment2.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment2.kt index a0c42fd0c..1106c9b40 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment2.kt +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment2.kt @@ -2,7 +2,6 @@ package org.schabi.newpipe.fragments.list.playlist import android.os.Bundle import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy @@ -15,13 +14,12 @@ class PlaylistFragment2 : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, - ): View = - ComposeView(requireContext()).apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - AppTheme { - Playlist() - } + ) = ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + AppTheme { + Playlist() } } + } } diff --git a/app/src/main/java/org/schabi/newpipe/info_list/ItemViewMode.java b/app/src/main/java/org/schabi/newpipe/info_list/ItemViewMode.java index 447c540a0..5d5650b92 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/ItemViewMode.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/ItemViewMode.java @@ -4,10 +4,6 @@ package org.schabi.newpipe.info_list; * Item view mode for streams & playlist listing screens. */ public enum ItemViewMode { - /** - * Default mode. - */ - AUTO, /** * Full width list item with thumb on the left and two line title & uploader in right. */ diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/common/Utils.kt b/app/src/main/java/org/schabi/newpipe/ui/components/common/Utils.kt new file mode 100644 index 000000000..90a513eef --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/ui/components/common/Utils.kt @@ -0,0 +1,34 @@ +package org.schabi.newpipe.compose.util + +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.preference.PreferenceManager +import androidx.window.core.layout.WindowWidthSizeClass +import org.schabi.newpipe.R +import org.schabi.newpipe.info_list.ItemViewMode + +@Composable +fun determineItemViewMode(): ItemViewMode { + val context = LocalContext.current + val listMode = PreferenceManager.getDefaultSharedPreferences(context) + .getString( + context.getString(R.string.list_view_mode_key), + context.getString(R.string.list_view_mode_value) + ) + + return when (listMode) { + context.getString(R.string.list_view_mode_list_key) -> ItemViewMode.LIST + context.getString(R.string.list_view_mode_grid_key) -> ItemViewMode.GRID + context.getString(R.string.list_view_mode_card_key) -> ItemViewMode.CARD + else -> { + // Auto mode - evaluate whether to use Grid based on screen real estate. + val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass + if (windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.EXPANDED) { + ItemViewMode.GRID + } else { + ItemViewMode.LIST + } + } + } +}