mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-01-23 23:46:57 +00:00
Start implementing stream composable, grid layout
This commit is contained in:
parent
8603b0df6e
commit
bf1c9ba7b5
@ -1,11 +1,12 @@
|
||||
package org.schabi.newpipe.compose.playlist
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
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.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
@ -14,7 +15,9 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import my.nanihadesuka.compose.LazyVerticalGridScrollbar
|
||||
import org.schabi.newpipe.DownloaderImpl
|
||||
import org.schabi.newpipe.compose.stream.StreamGridItem
|
||||
import org.schabi.newpipe.compose.theme.AppTheme
|
||||
import org.schabi.newpipe.extractor.NewPipe
|
||||
import org.schabi.newpipe.extractor.ServiceList
|
||||
@ -30,14 +33,17 @@ fun Playlist(playlistViewModel: PlaylistViewModel = viewModel()) {
|
||||
|
||||
playlistInfo?.let {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
LazyColumn {
|
||||
item {
|
||||
PlaylistHeader(playlistInfo = it, totalDuration = totalDuration)
|
||||
HorizontalDivider(thickness = 1.dp)
|
||||
}
|
||||
val gridState = rememberLazyGridState()
|
||||
|
||||
items(streams.itemCount) {
|
||||
Text(text = streams[it]!!.name)
|
||||
LazyVerticalGridScrollbar(state = gridState) {
|
||||
LazyVerticalGrid(state = gridState, columns = GridCells.Adaptive(164.dp)) {
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
PlaylistHeader(playlistInfo = it, totalDuration = totalDuration)
|
||||
}
|
||||
|
||||
items(streams.itemCount) {
|
||||
StreamGridItem(streams[it]!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,16 +35,12 @@ import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.compose.common.DescriptionText
|
||||
import org.schabi.newpipe.compose.theme.AppTheme
|
||||
import org.schabi.newpipe.error.ErrorUtil
|
||||
import org.schabi.newpipe.extractor.Image
|
||||
import org.schabi.newpipe.extractor.NewPipe
|
||||
import org.schabi.newpipe.extractor.ServiceList
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo
|
||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper
|
||||
import org.schabi.newpipe.extractor.stream.Description
|
||||
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
|
||||
@ -54,7 +50,7 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(4.dp),
|
||||
modifier = Modifier.padding(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
@ -148,22 +144,6 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) {
|
||||
}
|
||||
}
|
||||
|
||||
fun StreamInfoItem(
|
||||
serviceId: Int = NO_SERVICE_ID,
|
||||
url: String,
|
||||
name: String,
|
||||
streamType: StreamType = StreamType.NONE,
|
||||
uploaderName: String? = null,
|
||||
uploaderUrl: String? = null,
|
||||
uploaderAvatars: List<Image> = emptyList(),
|
||||
duration: Long,
|
||||
) = StreamInfoItem(serviceId, url, name, streamType).apply {
|
||||
this.uploaderName = uploaderName
|
||||
this.uploaderUrl = uploaderUrl
|
||||
this.uploaderAvatars = uploaderAvatars
|
||||
this.duration = duration
|
||||
}
|
||||
|
||||
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
@ -0,0 +1,130 @@
|
||||
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
|
||||
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.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
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
NavigationHelper.openVideoDetailFragment(
|
||||
context, (context as FragmentActivity).supportFragmentManager,
|
||||
stream.serviceId, stream.url, stream.name, null, false
|
||||
)
|
||||
}
|
||||
.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)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stream.name,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
maxLines = 2
|
||||
)
|
||||
|
||||
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),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun StreamGridItemPreview(
|
||||
@PreviewParameter(StreamItemPreviewProvider::class) stream: StreamInfoItem
|
||||
) {
|
||||
AppTheme {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
StreamGridItem(stream)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user