Skip to content

Commit 5abc03c

Browse files
Extract thumbnail into common composable
1 parent 21caa6c commit 5abc03c

4 files changed

Lines changed: 100 additions & 80 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.schabi.newpipe.ui.components.items.common
2+
3+
import androidx.annotation.DrawableRes
4+
import androidx.compose.foundation.background
5+
import androidx.compose.foundation.layout.Arrangement
6+
import androidx.compose.foundation.layout.Box
7+
import androidx.compose.foundation.layout.Row
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.foundation.layout.size
10+
import androidx.compose.material3.Icon
11+
import androidx.compose.material3.MaterialTheme
12+
import androidx.compose.material3.Text
13+
import androidx.compose.runtime.Composable
14+
import androidx.compose.ui.Alignment
15+
import androidx.compose.ui.Modifier
16+
import androidx.compose.ui.graphics.Color
17+
import androidx.compose.ui.graphics.vector.ImageVector
18+
import androidx.compose.ui.layout.ContentScale
19+
import androidx.compose.ui.res.painterResource
20+
import androidx.compose.ui.unit.dp
21+
import coil3.compose.AsyncImage
22+
import org.schabi.newpipe.extractor.Image
23+
import org.schabi.newpipe.util.image.ImageStrategy
24+
25+
@Composable
26+
fun Thumbnail(
27+
images: List<Image>,
28+
imageDescription: String,
29+
@DrawableRes imagePlaceholder: Int,
30+
cornerBackgroundColor: Color,
31+
cornerIcon: ImageVector?,
32+
cornerText: String,
33+
contentScale: ContentScale,
34+
modifier: Modifier = Modifier
35+
) {
36+
Box(contentAlignment = Alignment.BottomEnd) {
37+
AsyncImage(
38+
model = ImageStrategy.choosePreferredImage(images),
39+
contentDescription = imageDescription,
40+
placeholder = painterResource(imagePlaceholder),
41+
error = painterResource(imagePlaceholder),
42+
contentScale = contentScale,
43+
modifier = modifier
44+
)
45+
46+
Row(
47+
modifier = Modifier
48+
.padding(2.dp)
49+
.background(cornerBackgroundColor)
50+
.padding(2.dp),
51+
verticalAlignment = Alignment.CenterVertically,
52+
horizontalArrangement = Arrangement.spacedBy(4.dp)
53+
) {
54+
if (cornerIcon != null) {
55+
Icon(
56+
imageVector = cornerIcon,
57+
contentDescription = null,
58+
tint = Color.White,
59+
modifier = Modifier.size(18.dp)
60+
)
61+
}
62+
63+
Text(
64+
text = cornerText,
65+
color = Color.White,
66+
style = MaterialTheme.typography.bodySmall
67+
)
68+
}
69+
}
70+
}
Lines changed: 12 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,32 @@
11
package org.schabi.newpipe.ui.components.items.playlist
22

3-
import androidx.compose.foundation.background
4-
import androidx.compose.foundation.layout.Box
5-
import androidx.compose.foundation.layout.Row
6-
import androidx.compose.foundation.layout.padding
7-
import androidx.compose.foundation.layout.size
83
import androidx.compose.material.icons.Icons
94
import androidx.compose.material.icons.automirrored.filled.PlaylistPlay
10-
import androidx.compose.material3.Icon
11-
import androidx.compose.material3.MaterialTheme
12-
import androidx.compose.material3.Text
135
import androidx.compose.runtime.Composable
14-
import androidx.compose.ui.Alignment
156
import androidx.compose.ui.Modifier
167
import androidx.compose.ui.graphics.Color
178
import androidx.compose.ui.layout.ContentScale
189
import androidx.compose.ui.platform.LocalContext
19-
import androidx.compose.ui.res.painterResource
20-
import androidx.compose.ui.unit.dp
21-
import coil3.compose.AsyncImage
10+
import androidx.compose.ui.res.stringResource
2211
import org.schabi.newpipe.R
2312
import org.schabi.newpipe.ui.components.items.Playlist
13+
import org.schabi.newpipe.ui.components.items.common.Thumbnail
2414
import org.schabi.newpipe.util.Localization
25-
import org.schabi.newpipe.util.image.ImageStrategy
2615

2716
@Composable
2817
fun PlaylistThumbnail(
2918
playlist: Playlist,
3019
modifier: Modifier = Modifier,
3120
contentScale: ContentScale = ContentScale.Fit
3221
) {
33-
Box(contentAlignment = Alignment.BottomEnd) {
34-
AsyncImage(
35-
model = ImageStrategy.choosePreferredImage(playlist.thumbnails),
36-
contentDescription = null,
37-
placeholder = painterResource(R.drawable.placeholder_thumbnail_playlist),
38-
error = painterResource(R.drawable.placeholder_thumbnail_playlist),
39-
contentScale = contentScale,
40-
modifier = modifier
41-
)
42-
43-
Row(
44-
modifier = Modifier
45-
.padding(2.dp)
46-
.background(Color.Black.copy(alpha = 0.5f))
47-
.padding(2.dp),
48-
verticalAlignment = Alignment.CenterVertically
49-
) {
50-
Icon(
51-
imageVector = Icons.AutoMirrored.Default.PlaylistPlay,
52-
contentDescription = null,
53-
tint = Color.White,
54-
modifier = Modifier.size(18.dp)
55-
)
56-
57-
val context = LocalContext.current
58-
Text(
59-
text = Localization.localizeStreamCountMini(context, playlist.streamCount),
60-
color = Color.White,
61-
style = MaterialTheme.typography.bodySmall,
62-
modifier = Modifier.padding(start = 4.dp)
63-
)
64-
}
65-
}
22+
Thumbnail(
23+
images = playlist.thumbnails,
24+
imageDescription = stringResource(R.string.playlist_content_description, playlist.name),
25+
imagePlaceholder = R.drawable.placeholder_thumbnail_playlist,
26+
cornerBackgroundColor = Color.Black.copy(alpha = 0.5f),
27+
cornerIcon = Icons.AutoMirrored.Default.PlaylistPlay,
28+
cornerText = Localization.localizeStreamCountMini(LocalContext.current, playlist.streamCount),
29+
contentScale = contentScale,
30+
modifier = modifier
31+
)
6632
}

app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamThumbnail.kt

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,25 @@
11
package org.schabi.newpipe.ui.components.items.stream
22

3-
import androidx.compose.foundation.background
4-
import androidx.compose.foundation.layout.Box
53
import androidx.compose.foundation.layout.Column
6-
import androidx.compose.foundation.layout.padding
74
import androidx.compose.foundation.layout.requiredHeight
85
import androidx.compose.material3.LinearProgressIndicator
9-
import androidx.compose.material3.MaterialTheme
10-
import androidx.compose.material3.Text
116
import androidx.compose.runtime.Composable
127
import androidx.compose.runtime.LaunchedEffect
138
import androidx.compose.runtime.getValue
149
import androidx.compose.runtime.mutableLongStateOf
1510
import androidx.compose.runtime.saveable.rememberSaveable
1611
import androidx.compose.runtime.setValue
17-
import androidx.compose.ui.Alignment
1812
import androidx.compose.ui.Modifier
1913
import androidx.compose.ui.graphics.Color
2014
import androidx.compose.ui.layout.ContentScale
21-
import androidx.compose.ui.res.painterResource
2215
import androidx.compose.ui.res.stringResource
2316
import androidx.compose.ui.unit.dp
2417
import androidx.lifecycle.viewmodel.compose.viewModel
25-
import coil3.compose.AsyncImage
2618
import org.schabi.newpipe.R
2719
import org.schabi.newpipe.ui.components.items.Stream
20+
import org.schabi.newpipe.ui.components.items.common.Thumbnail
2821
import org.schabi.newpipe.util.Localization
2922
import org.schabi.newpipe.util.StreamTypeUtil
30-
import org.schabi.newpipe.util.image.ImageStrategy
3123
import org.schabi.newpipe.viewmodels.StreamViewModel
3224
import kotlin.time.Duration.Companion.milliseconds
3325
import kotlin.time.Duration.Companion.seconds
@@ -40,31 +32,21 @@ fun StreamThumbnail(
4032
contentScale: ContentScale = ContentScale.Fit
4133
) {
4234
Column(modifier = modifier) {
43-
Box(contentAlignment = Alignment.BottomEnd) {
44-
AsyncImage(
45-
model = ImageStrategy.choosePreferredImage(stream.thumbnails),
46-
contentDescription = null,
47-
placeholder = painterResource(R.drawable.placeholder_thumbnail_video),
48-
error = painterResource(R.drawable.placeholder_thumbnail_video),
49-
contentScale = contentScale,
50-
modifier = modifier
51-
)
52-
53-
val isLive = StreamTypeUtil.isLiveStream(stream.type)
54-
Text(
55-
modifier = Modifier
56-
.padding(2.dp)
57-
.background(if (isLive) Color.Red else Color.Black.copy(alpha = 0.5f))
58-
.padding(2.dp),
59-
text = if (isLive) {
60-
stringResource(R.string.duration_live)
61-
} else {
62-
Localization.getDurationString(stream.duration)
63-
},
64-
color = Color.White,
65-
style = MaterialTheme.typography.bodySmall
66-
)
67-
}
35+
val isLive = StreamTypeUtil.isLiveStream(stream.type)
36+
Thumbnail(
37+
images = stream.thumbnails,
38+
imageDescription = stringResource(R.string.stream_content_description, stream.name),
39+
imagePlaceholder = R.drawable.placeholder_thumbnail_video,
40+
cornerBackgroundColor = if (isLive) Color.Red else Color.Black.copy(alpha = 0.5f),
41+
cornerIcon = null,
42+
cornerText = if (isLive) {
43+
stringResource(R.string.duration_live)
44+
} else {
45+
Localization.getDurationString(stream.duration)
46+
},
47+
contentScale = contentScale,
48+
modifier = modifier
49+
)
6850

6951
if (showProgress) {
7052
val streamViewModel = viewModel<StreamViewModel>()

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,8 @@
861861
<string name="newpipe_extractor_description">NewPipeExtractor is a library for extracting things from streaming sites. It is a core component of NewPipe, but could be used independently.</string>
862862
<string name="history_sort_label">Sort by</string>
863863
<string name="clear_history_description">Button to clear watch history</string>
864+
<string name="playlist_content_description">Thumbnail for playlist %1$s</string>
865+
<string name="stream_content_description">Thumbnail for stream %1$s</string>
864866
<plurals name="comments">
865867
<item quantity="one">%d comment</item>
866868
<item quantity="other">%d comments</item>

0 commit comments

Comments
 (0)