1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-01-10 01:10:33 +00:00

Implement card and list layouts, check for preferred layout from settings

This commit is contained in:
Isira Seneviratne 2024-07-02 08:57:38 +05:30
parent bf1c9ba7b5
commit 72bbe0ea1c
11 changed files with 354 additions and 104 deletions

View File

@ -288,6 +288,7 @@ dependencies {
// Jetpack Compose // Jetpack Compose
implementation(platform('androidx.compose:compose-bom:2024.06.00')) implementation(platform('androidx.compose:compose-bom:2024.06.00'))
implementation 'androidx.compose.material3:material3:1.3.0-beta05' 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.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.ui:ui-text:1.7.0-beta06' // Needed for parsing HTML to AnnotatedString implementation 'androidx.compose.ui:ui-text:1.7.0-beta06' // Needed for parsing HTML to AnnotatedString

View File

@ -1,28 +1,39 @@
package org.schabi.newpipe.compose.playlist package org.schabi.newpipe.compose.playlist
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems
import my.nanihadesuka.compose.LazyColumnScrollbar
import my.nanihadesuka.compose.LazyVerticalGridScrollbar import my.nanihadesuka.compose.LazyVerticalGridScrollbar
import org.schabi.newpipe.DownloaderImpl 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.StreamGridItem
import org.schabi.newpipe.compose.stream.StreamListItem
import org.schabi.newpipe.compose.theme.AppTheme 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.NewPipe
import org.schabi.newpipe.extractor.ServiceList 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_SERVICE_ID
import org.schabi.newpipe.util.KEY_URL import org.schabi.newpipe.util.KEY_URL
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.viewmodels.PlaylistViewModel import org.schabi.newpipe.viewmodels.PlaylistViewModel
@Composable @Composable
@ -31,18 +42,50 @@ fun Playlist(playlistViewModel: PlaylistViewModel = viewModel()) {
val streams = playlistViewModel.streamItems.collectAsLazyPagingItems() val streams = playlistViewModel.streamItems.collectAsLazyPagingItems()
val totalDuration = streams.itemSnapshotList.sumOf { it!!.duration } 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 { playlistInfo?.let {
Surface(color = MaterialTheme.colorScheme.background) { Surface(color = MaterialTheme.colorScheme.background) {
val mode = determineItemViewMode()
if (mode == ItemViewMode.GRID) {
val gridState = rememberLazyGridState() val gridState = rememberLazyGridState()
LazyVerticalGridScrollbar(state = gridState) { LazyVerticalGridScrollbar(state = gridState) {
LazyVerticalGrid(state = gridState, columns = GridCells.Adaptive(164.dp)) { LazyVerticalGrid(state = gridState, columns = GridCells.Adaptive(250.dp)) {
item(span = { GridItemSpan(maxLineSpan) }) { item(span = { GridItemSpan(maxLineSpan) }) {
PlaylistHeader(playlistInfo = it, totalDuration = totalDuration) PlaylistHeader(playlistInfo = it, totalDuration = totalDuration)
} }
items(streams.itemCount) { items(streams.itemCount) {
StreamGridItem(streams[it]!!) StreamGridItem(streams[it]!!, onClick)
}
}
}
} else {
// Card or list views
val listState = rememberLazyListState()
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)
}
}
} }
} }
} }

View File

@ -1,7 +1,9 @@
package org.schabi.newpipe.compose.playlist package org.schabi.newpipe.compose.playlist
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -22,6 +24,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -65,9 +68,9 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp), horizontalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier.apply { modifier = Modifier.let {
if (playlistInfo.uploaderName != null && playlistInfo.uploaderUrl != null) { if (playlistInfo.uploaderName != null && playlistInfo.uploaderUrl != null) {
clickable { it.clickable {
try { try {
NavigationHelper.openChannelFragment( NavigationHelper.openChannelFragment(
(context as FragmentActivity).supportFragmentManager, (context as FragmentActivity).supportFragmentManager,
@ -78,11 +81,13 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) {
ErrorUtil.showUiErrorSnackbar(context, "Opening channel fragment", e) ErrorUtil.showUiErrorSnackbar(context, "Opening channel fragment", e)
} }
} }
} } else it
} }
) { ) {
val imageModifier = Modifier val imageModifier = Modifier
.size(24.dp) .size(24.dp)
.border(BorderStroke(1.dp, Color.White), CircleShape)
.padding(1.dp)
.clip(CircleShape) .clip(CircleShape)
val isMix = YoutubeParsingHelper.isYoutubeMixId(playlistInfo.id) || val isMix = YoutubeParsingHelper.isYoutubeMixId(playlistInfo.id) ||
YoutubeParsingHelper.isYoutubeMusicMixId(playlistInfo.id) YoutubeParsingHelper.isYoutubeMusicMixId(playlistInfo.id)

View File

@ -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) {}
}
}
}

View File

@ -1,6 +1,5 @@
package org.schabi.newpipe.compose.stream package org.schabi.newpipe.compose.stream
import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -11,47 +10,21 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier 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.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp 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.compose.theme.AppTheme
import org.schabi.newpipe.extractor.Image
import org.schabi.newpipe.extractor.stream.StreamInfoItem 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 @Composable
fun StreamGridItem(stream: StreamInfoItem) { fun StreamGridItem(stream: StreamInfoItem, onClick: (StreamInfoItem) -> Unit) {
val context = LocalContext.current
Column( Column(
modifier = Modifier modifier = Modifier
.clickable { .clickable(onClick = { onClick(stream) })
NavigationHelper.openVideoDetailFragment(
context, (context as FragmentActivity).supportFragmentManager,
stream.serviceId, stream.url, stream.name, null, false
)
}
.padding(12.dp) .padding(12.dp)
) { ) {
AsyncImage( StreamThumbnail(stream = stream, modifier = Modifier.size(width = 246.dp, height = 138.dp))
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)
)
Text( Text(
text = stream.name, text = stream.name,
@ -62,58 +35,11 @@ fun StreamGridItem(stream: StreamInfoItem) {
Text(text = stream.uploaderName.orEmpty(), style = MaterialTheme.typography.bodySmall) 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<Image> = 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<StreamInfoItem> {
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 = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@ -124,7 +50,7 @@ private fun StreamGridItemPreview(
) { ) {
AppTheme { AppTheme {
Surface(color = MaterialTheme.colorScheme.background) { Surface(color = MaterialTheme.colorScheme.background) {
StreamGridItem(stream) StreamGridItem(stream, onClick = {})
} }
} }
} }

View File

@ -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 = {})
}
}
}

View File

@ -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))
)
}
}

View File

@ -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<Image> = 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<StreamInfoItem> {
override val values = sequenceOf(
StreamInfoItem(streamType = StreamType.NONE),
StreamInfoItem(streamType = StreamType.LIVE_STREAM),
StreamInfoItem(streamType = StreamType.AUDIO_LIVE_STREAM),
)
}

View File

@ -2,7 +2,6 @@ package org.schabi.newpipe.fragments.list.playlist
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.platform.ViewCompositionStrategy
@ -15,8 +14,7 @@ class PlaylistFragment2 : Fragment() {
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle?, savedInstanceState: Bundle?,
): View = ) = ComposeView(requireContext()).apply {
ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent { setContent {
AppTheme { AppTheme {

View File

@ -4,10 +4,6 @@ package org.schabi.newpipe.info_list;
* Item view mode for streams & playlist listing screens. * Item view mode for streams & playlist listing screens.
*/ */
public enum ItemViewMode { public enum ItemViewMode {
/**
* Default mode.
*/
AUTO,
/** /**
* Full width list item with thumb on the left and two line title & uploader in right. * Full width list item with thumb on the left and two line title & uploader in right.
*/ */

View File

@ -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
}
}
}
}