mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-01-09 17:00:32 +00:00
Implement card and list layouts, check for preferred layout from settings
This commit is contained in:
parent
bf1c9ba7b5
commit
72bbe0ea1c
@ -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
|
||||
|
@ -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 mode = determineItemViewMode()
|
||||
|
||||
if (mode == ItemViewMode.GRID) {
|
||||
val gridState = rememberLazyGridState()
|
||||
|
||||
LazyVerticalGridScrollbar(state = gridState) {
|
||||
LazyVerticalGrid(state = gridState, columns = GridCells.Adaptive(164.dp)) {
|
||||
LazyVerticalGrid(state = gridState, columns = GridCells.Adaptive(250.dp)) {
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
PlaylistHeader(playlistInfo = it, totalDuration = totalDuration)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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) {}
|
||||
}
|
||||
}
|
||||
}
|
@ -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,58 +35,11 @@ fun StreamGridItem(stream: StreamInfoItem) {
|
||||
|
||||
Text(text = stream.uploaderName.orEmpty(), style = MaterialTheme.typography.bodySmall)
|
||||
|
||||
Text(text = getStreamInfoDetail(context, 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),
|
||||
Text(
|
||||
text = getStreamInfoDetail(stream),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@ -124,7 +50,7 @@ private fun StreamGridItemPreview(
|
||||
) {
|
||||
AppTheme {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
StreamGridItem(stream)
|
||||
StreamGridItem(stream, onClick = {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 = {})
|
||||
}
|
||||
}
|
||||
}
|
@ -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))
|
||||
)
|
||||
}
|
||||
}
|
@ -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),
|
||||
)
|
||||
}
|
@ -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,8 +14,7 @@ class PlaylistFragment2 : Fragment() {
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View =
|
||||
ComposeView(requireContext()).apply {
|
||||
) = ComposeView(requireContext()).apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
AppTheme {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user