Skip to content

Commit

Permalink
Add DescriptionText composable, remove playlist preview dependency on…
Browse files Browse the repository at this point in the history
… external HTTP calls
  • Loading branch information
Isira-Seneviratne committed Jul 22, 2024
1 parent 0c2afe9 commit 8049fa5
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import coil.compose.AsyncImage
import org.schabi.newpipe.R
import org.schabi.newpipe.compose.common.DescriptionText
import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.compose.util.rememberParsedDescription
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
Expand Down Expand Up @@ -101,8 +101,8 @@ fun Comment(comment: CommentsInfoItem) {
Text(text = nameAndDate, color = MaterialTheme.colorScheme.secondary)
}

Text(
text = rememberParsedDescription(comment.commentText),
DescriptionText(
description = comment.commentText,
// If the comment is expanded, we display all its content
// otherwise we only display the first two lines
maxLines = if (isExpanded) Int.MAX_VALUE else 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ 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.common.DescriptionText
import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.compose.util.rememberParsedDescription
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.util.Localization
Expand Down Expand Up @@ -102,8 +102,8 @@ fun CommentRepliesHeader(comment: CommentsInfoItem) {
}
}

Text(
text = rememberParsedDescription(comment.commentText),
DescriptionText(
description = comment.commentText,
style = MaterialTheme.typography.bodyMedium
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import my.nanihadesuka.compose.LazyColumnScrollbar
import org.schabi.newpipe.R
import org.schabi.newpipe.compose.status.LoadingIndicator
import org.schabi.newpipe.compose.common.LoadingIndicator
import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.extractor.stream.Description
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.schabi.newpipe.compose.common

import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ParagraphStyle
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import org.schabi.newpipe.extractor.stream.Description

@Composable
fun DescriptionText(
description: Description,
modifier: Modifier = Modifier,
overflow: TextOverflow = TextOverflow.Clip,
maxLines: Int = Int.MAX_VALUE,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) {
// TODO: Handle links and hashtags, Markdown.
val parsedDescription = remember(description) {
if (description.type == Description.HTML) {
val styles = TextLinkStyles(SpanStyle(textDecoration = TextDecoration.Underline))
AnnotatedString.fromHtml(description.content, styles)
} else {
AnnotatedString(description.content, ParagraphStyle())
}
}

Text(
modifier = modifier,
text = parsedDescription,
maxLines = maxLines,
style = style,
overflow = overflow,
onTextLayout = onTextLayout
)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.schabi.newpipe.compose.status
package org.schabi.newpipe.compose.common

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
Expand All @@ -11,7 +11,9 @@ import androidx.compose.ui.Modifier
@Composable
fun LoadingIndicator(modifier: Modifier = Modifier) {
CircularProgressIndicator(
modifier = modifier.fillMaxSize().wrapContentSize(Alignment.Center),
modifier = modifier
.fillMaxSize()
.wrapContentSize(Alignment.Center),
color = MaterialTheme.colorScheme.primary,
trackColor = MaterialTheme.colorScheme.surfaceVariant,
)
Expand Down
76 changes: 43 additions & 33 deletions app/src/main/java/org/schabi/newpipe/compose/playlist/Playlist.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,62 +10,72 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import org.schabi.newpipe.DownloaderImpl
import org.schabi.newpipe.compose.status.LoadingIndicator
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import org.schabi.newpipe.compose.common.LoadingIndicator
import org.schabi.newpipe.compose.stream.StreamInfoItem
import org.schabi.newpipe.compose.stream.StreamList
import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.extractor.ServiceList
import org.schabi.newpipe.util.KEY_SERVICE_ID
import org.schabi.newpipe.util.KEY_URL
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.viewmodels.PlaylistViewModel

@Composable
fun Playlist(playlistViewModel: PlaylistViewModel = viewModel()) {
Surface(color = MaterialTheme.colorScheme.background) {
val playlistInfo by playlistViewModel.playlistInfo.collectAsState()
Playlist(playlistInfo, playlistViewModel.streamItems)
}
}

playlistInfo?.let {
val streams = playlistViewModel.streamItems.collectAsLazyPagingItems()
val totalDuration by remember {
derivedStateOf {
streams.itemSnapshotList.sumOf { it!!.duration }
}
@Composable
private fun Playlist(
playlistInfo: PlaylistInfo?,
streamFlow: Flow<PagingData<StreamInfoItem>>
) {
playlistInfo?.let {
val streams = streamFlow.collectAsLazyPagingItems()
val totalDuration by remember {
derivedStateOf {
streams.itemSnapshotList.sumOf { it!!.duration }
}
}

StreamList(
streams = streams,
gridHeader = {
item(span = { GridItemSpan(maxLineSpan) }) {
PlaylistHeader(it, totalDuration)
}
},
listHeader = {
item {
PlaylistHeader(it, totalDuration)
}
StreamList(
streams = streams,
gridHeader = {
item(span = { GridItemSpan(maxLineSpan) }) {
PlaylistHeader(it, totalDuration)
}
)
} ?: LoadingIndicator()
}
},
listHeader = {
item {
PlaylistHeader(it, totalDuration)
}
}
)
} ?: LoadingIndicator()
}

@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PlaylistPreview() {
NewPipe.init(DownloaderImpl.init(null))

val params = mapOf(
KEY_SERVICE_ID to ServiceList.YouTube.serviceId,
KEY_URL to "https://www.youtube.com/playlist?list=PLAIcZs9N4171hRrG_4v32Ca2hLvSuQ6QI"
val description = Description("Example description", Description.PLAIN_TEXT)
val playlistInfo = PlaylistInfo(
"", 1, "", "Example playlist", description, listOf(), 1L,
null, "Uploader", listOf(), null
)
val stream = StreamInfoItem(streamType = StreamType.VIDEO_STREAM)
val streamFlow = flowOf(PagingData.from(listOf(stream)))

AppTheme {
Surface(color = MaterialTheme.colorScheme.background) {
Playlist(PlaylistViewModel(SavedStateHandle(params)))
Playlist(playlistInfo, streamFlow)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,11 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import coil.compose.AsyncImage
import org.schabi.newpipe.DownloaderImpl
import org.schabi.newpipe.R
import org.schabi.newpipe.compose.common.DescriptionText
import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.compose.util.rememberParsedDescription
import org.schabi.newpipe.error.ErrorUtil
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.util.Localization
Expand All @@ -67,7 +64,7 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) {
NavigationHelper.openChannelFragment(
(context as FragmentActivity).supportFragmentManager,
playlistInfo.serviceId, playlistInfo.uploaderUrl,
playlistInfo.uploaderName
playlistInfo.uploaderName!!
)
} catch (e: Exception) {
ErrorUtil.showUiErrorSnackbar(context, "Opening channel fragment", e)
Expand Down Expand Up @@ -108,14 +105,13 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) {
Text(text = "$count$formattedDuration", style = MaterialTheme.typography.bodySmall)
}

val description = playlistInfo.description ?: Description.EMPTY_DESCRIPTION
if (description != Description.EMPTY_DESCRIPTION) {
if (playlistInfo.description != Description.EMPTY_DESCRIPTION) {
var isExpanded by rememberSaveable { mutableStateOf(false) }
var isExpandable by rememberSaveable { mutableStateOf(false) }

Text(
DescriptionText(
modifier = Modifier.animateContentSize(),
text = rememberParsedDescription(description),
description = playlistInfo.description,
maxLines = if (isExpanded) Int.MAX_VALUE else 5,
style = MaterialTheme.typography.bodyMedium,
overflow = TextOverflow.Ellipsis,
Expand Down Expand Up @@ -144,10 +140,10 @@ fun PlaylistHeader(playlistInfo: PlaylistInfo, totalDuration: Long) {
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PlaylistHeaderPreview() {
NewPipe.init(DownloaderImpl.init(null))
val playlistInfo = PlaylistInfo.getInfo(
ServiceList.YouTube,
"https://www.youtube.com/playlist?list=PLAIcZs9N4171hRrG_4v32Ca2hLvSuQ6QI"
val description = Description("Example description", Description.PLAIN_TEXT)
val playlistInfo = PlaylistInfo(
"", 1, "", "Example playlist", description, listOf(), 1L,
null, "Uploader", listOf(), null
)

AppTheme {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.schabi.newpipe.compose.playlist

import androidx.compose.runtime.Immutable
import org.schabi.newpipe.extractor.Image
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.extractor.stream.StreamInfoItem

@Immutable
class PlaylistInfo(
val id: String,
val serviceId: Int,
val url: String,
val name: String,
val description: Description,
val relatedItems: List<StreamInfoItem>,
val streamCount: Long,
val uploaderUrl: String?,
val uploaderName: String?,
val uploaderAvatars: List<Image>,
val nextPage: Page?
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ import org.schabi.newpipe.util.NavigationHelper
@Composable
fun StreamList(
streams: LazyPagingItems<StreamInfoItem>,
itemViewMode: ItemViewMode = determineItemViewMode(),
gridHeader: LazyGridScope.() -> Unit = {},
listHeader: LazyListScope.() -> Unit = {}
) {
val mode = determineItemViewMode()
val context = LocalContext.current
val onClick = remember {
{ stream: StreamInfoItem ->
Expand All @@ -54,7 +54,7 @@ fun StreamList(
}
}

if (mode == ItemViewMode.GRID) {
if (itemViewMode == ItemViewMode.GRID) {
val gridState = rememberLazyGridState()

LazyVerticalGridScrollbar(state = gridState) {
Expand Down Expand Up @@ -82,7 +82,7 @@ fun StreamList(
val stream = streams[it]!!
val isSelected = selectedStream == stream

if (mode == ItemViewMode.CARD) {
if (itemViewMode == ItemViewMode.CARD) {
StreamCardItem(stream, isSelected, onClick, onLongClick, onDismissPopup)
} else {
StreamListItem(stream, isSelected, onClick, onLongClick, onDismissPopup)
Expand Down
21 changes: 0 additions & 21 deletions app/src/main/java/org/schabi/newpipe/compose/util/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,12 @@ package org.schabi.newpipe.compose.util

import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ParagraphStyle
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.text.style.TextDecoration
import androidx.preference.PreferenceManager
import androidx.window.core.layout.WindowWidthSizeClass
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.stream.Description
import org.schabi.newpipe.info_list.ItemViewMode

@Composable
fun rememberParsedDescription(description: Description): AnnotatedString {
// TODO: Handle links and hashtags, Markdown.
return remember(description) {
if (description.type == Description.HTML) {
val styles = TextLinkStyles(SpanStyle(textDecoration = TextDecoration.Underline))
AnnotatedString.fromHtml(description.content, styles)
} else {
AnnotatedString(description.content, ParagraphStyle())
}
}
}

@Composable
fun determineItemViewMode(): ItemViewMode {
val context = LocalContext.current
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import androidx.paging.PagingSource
import androidx.paging.PagingState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.schabi.newpipe.compose.playlist.PlaylistInfo
import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.playlist.PlaylistInfo
import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.extractor.playlist.PlaylistInfo as ExtractorPlaylistInfo

class PlaylistItemsSource(
private val playlistInfo: PlaylistInfo,
Expand All @@ -17,7 +18,8 @@ class PlaylistItemsSource(
override suspend fun load(params: LoadParams<Page>): LoadResult<Page, StreamInfoItem> {
return params.key?.let {
withContext(Dispatchers.IO) {
val response = PlaylistInfo.getMoreItems(service, playlistInfo.url, playlistInfo.nextPage)
val response = ExtractorPlaylistInfo
.getMoreItems(service, playlistInfo.url, playlistInfo.nextPage)
LoadResult.Page(response.items, null, response.nextPage)
}
} ?: LoadResult.Page(playlistInfo.relatedItems, null, playlistInfo.nextPage)
Expand Down
Loading

0 comments on commit 8049fa5

Please sign in to comment.