1
0
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:
Isira Seneviratne 2024-07-01 07:33:49 +05:30
parent 8603b0df6e
commit bf1c9ba7b5
3 changed files with 147 additions and 31 deletions

View File

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

View File

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

View File

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