Skip to content

Commit ed82565

Browse files
authored
Rework the playlist view on mobile (#1043)
1 parent 81821f2 commit ed82565

File tree

9 files changed

+453
-420
lines changed

9 files changed

+453
-420
lines changed

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ media-maestro = "0.10.0"
3131
mockk = "1.14.2"
3232
okhttp = "4.12.0"
3333
okio = "3.11.0"
34+
reorderable = "2.4.3"
3435
robolectric = "4.14.1"
3536
srg-data-provider = "0.15.1"
3637
tag-commander-core = "5.4.3"
@@ -78,6 +79,7 @@ kotlinx-kover-gradle = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", v
7879
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" }
7980
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
8081
media-maestro = { module = "ch.srgssr.media.maestro:media-maestro", version.ref = "media-maestro" }
82+
reorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "reorderable" }
8183
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
8284
robolectric-annotations = { module = "org.robolectric:annotations", version.ref = "robolectric" }
8385
robolectric-shadows-framework = { module = "org.robolectric:shadows-framework", version.ref = "robolectric" }

pillarbox-demo-tv/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ dependencies {
4949
implementation(libs.kotlin.stdlib)
5050
implementation(libs.kotlinx.coroutines.core)
5151
implementation(libs.kotlinx.datetime)
52+
implementation(libs.kotlinx.serialization.core)
5253
implementation(libs.okhttp)
5354
implementation(libs.srg.data)
5455
implementation(libs.srg.dataprovider.retrofit)

pillarbox-demo/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ dependencies {
9191
implementation(libs.kotlinx.coroutines.core)
9292
implementation(libs.kotlinx.datetime)
9393
implementation(libs.media.maestro)
94+
implementation(libs.reorderable)
9495
implementation(libs.okhttp)
9596
implementation(libs.srg.data)
9697
implementation(libs.srg.dataprovider.retrofit)

pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/player/DemoPlayerView.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.Row
1919
import androidx.compose.foundation.layout.displayCutoutPadding
2020
import androidx.compose.foundation.layout.fillMaxSize
2121
import androidx.compose.foundation.layout.fillMaxWidth
22-
import androidx.compose.foundation.layout.padding
2322
import androidx.compose.material3.ExperimentalMaterial3Api
2423
import androidx.compose.material3.MaterialTheme
2524
import androidx.compose.material3.ModalBottomSheet
@@ -46,7 +45,6 @@ import ch.srgssr.pillarbox.demo.ui.components.ShowSystemUi
4645
import ch.srgssr.pillarbox.demo.ui.player.controls.PlayerBottomToolbar
4746
import ch.srgssr.pillarbox.demo.ui.player.playlist.PlaylistView
4847
import ch.srgssr.pillarbox.demo.ui.player.settings.PlaybackSettingsContent
49-
import ch.srgssr.pillarbox.demo.ui.theme.paddings
5048
import ch.srgssr.pillarbox.ui.ScaleMode
5149

5250
/**
@@ -181,9 +179,8 @@ private fun PlayerContent(
181179
if (displayPlaylist && !pictureInPicture && !fullScreenState) {
182180
PlaylistView(
183181
modifier = Modifier
184-
.weight(1.0f)
185-
.fillMaxWidth()
186-
.padding(horizontal = MaterialTheme.paddings.baseline),
182+
.weight(1f)
183+
.fillMaxWidth(),
187184
player = player,
188185
itemsLibrary = SamplesAll.playlist.items,
189186
)

pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/player/playlist/MediaItemLibraryDialog.kt

Lines changed: 85 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@ package ch.srgssr.pillarbox.demo.ui.player.playlist
66

77
import androidx.compose.foundation.clickable
88
import androidx.compose.foundation.layout.Arrangement
9-
import androidx.compose.foundation.layout.Column
109
import androidx.compose.foundation.layout.Row
1110
import androidx.compose.foundation.layout.fillMaxWidth
12-
import androidx.compose.foundation.layout.padding
13-
import androidx.compose.foundation.layout.wrapContentHeight
14-
import androidx.compose.foundation.layout.wrapContentWidth
1511
import androidx.compose.foundation.lazy.LazyColumn
1612
import androidx.compose.foundation.lazy.items
13+
import androidx.compose.material.icons.Icons
14+
import androidx.compose.material.icons.filled.Close
15+
import androidx.compose.material3.AlertDialog
1716
import androidx.compose.material3.AlertDialogDefaults
18-
import androidx.compose.material3.BasicAlertDialog
1917
import androidx.compose.material3.Checkbox
2018
import androidx.compose.material3.ExperimentalMaterial3Api
21-
import androidx.compose.material3.MaterialTheme
22-
import androidx.compose.material3.Surface
19+
import androidx.compose.material3.Icon
20+
import androidx.compose.material3.IconButton
21+
import androidx.compose.material3.ListItem
22+
import androidx.compose.material3.ListItemDefaults
2323
import androidx.compose.material3.Text
2424
import androidx.compose.material3.TextButton
2525
import androidx.compose.runtime.Composable
@@ -30,12 +30,12 @@ import androidx.compose.runtime.remember
3030
import androidx.compose.ui.Alignment
3131
import androidx.compose.ui.Modifier
3232
import androidx.compose.ui.res.stringResource
33+
import androidx.compose.ui.text.style.TextOverflow
3334
import androidx.compose.ui.tooling.preview.Preview
3435
import ch.srgssr.pillarbox.demo.R
3536
import ch.srgssr.pillarbox.demo.shared.data.DemoItem
3637
import ch.srgssr.pillarbox.demo.shared.data.samples.SamplesAll
3738
import ch.srgssr.pillarbox.demo.ui.theme.PillarboxTheme
38-
import ch.srgssr.pillarbox.demo.ui.theme.paddings
3939

4040
/**
4141
* A dialog allowing the user to add items to the current playlist.
@@ -53,59 +53,56 @@ fun MediaItemLibraryDialog(
5353
onAddClick: (items: List<DemoItem>) -> Unit,
5454
onDismissRequest: () -> Unit,
5555
) {
56-
val selectedItems = remember {
57-
mutableStateListOf<DemoItem>()
58-
}
56+
val selectedItems = remember { mutableStateListOf<DemoItem>() }
5957

60-
BasicAlertDialog(
58+
AlertDialog(
6159
onDismissRequest = onDismissRequest,
60+
confirmButton = {
61+
TextButton(
62+
onClick = {
63+
onAddClick(selectedItems)
64+
onDismissRequest()
65+
},
66+
) {
67+
Text(text = stringResource(android.R.string.ok))
68+
}
69+
},
6270
modifier = modifier,
63-
) {
64-
Surface(
65-
modifier = Modifier
66-
.wrapContentWidth()
67-
.wrapContentHeight(),
68-
shape = AlertDialogDefaults.shape,
69-
color = AlertDialogDefaults.containerColor,
70-
tonalElevation = AlertDialogDefaults.TonalElevation,
71-
) {
72-
Column {
73-
Text(
74-
text = stringResource(R.string.add_to_playlist),
75-
modifier = Modifier.padding(MaterialTheme.paddings.baseline),
76-
color = AlertDialogDefaults.titleContentColor,
77-
style = MaterialTheme.typography.headlineSmall,
78-
)
79-
80-
ItemList(
81-
items = items,
82-
selectedItems = selectedItems,
83-
modifier = Modifier.weight(1f),
84-
onItemClick = { item, checked ->
85-
if (checked) {
86-
selectedItems.add(item)
87-
} else {
88-
selectedItems.remove(item)
89-
}
90-
}
91-
)
71+
dismissButton = {
72+
TextButton(onClick = { selectedItems.addAll(items) }) {
73+
Text(text = stringResource(android.R.string.selectAll))
74+
}
75+
},
76+
title = {
77+
Row(
78+
modifier = Modifier.fillMaxWidth(),
79+
horizontalArrangement = Arrangement.SpaceBetween,
80+
verticalAlignment = Alignment.CenterVertically,
81+
) {
82+
Text(text = stringResource(R.string.add_to_playlist))
9283

93-
ButtonsRow(
94-
modifier = Modifier
95-
.fillMaxWidth()
96-
.padding(MaterialTheme.paddings.small),
97-
onAddClick = {
98-
onAddClick(selectedItems)
99-
onDismissRequest()
100-
},
101-
onCancelClick = onDismissRequest,
102-
onSelectAllClick = {
103-
selectedItems.addAll(items)
104-
}
105-
)
84+
IconButton(onClick = onDismissRequest) {
85+
Icon(
86+
imageVector = Icons.Default.Close,
87+
contentDescription = null,
88+
)
89+
}
10690
}
107-
}
108-
}
91+
},
92+
text = {
93+
ItemList(
94+
items = items,
95+
selectedItems = selectedItems,
96+
onItemClick = { item, checked ->
97+
if (checked) {
98+
selectedItems.add(item)
99+
} else {
100+
selectedItems.remove(item)
101+
}
102+
},
103+
)
104+
},
105+
)
109106
}
110107

111108
@Composable
@@ -118,57 +115,31 @@ private fun ItemList(
118115
LazyColumn(modifier = modifier) {
119116
items(items) { item ->
120117
val checked by remember(item) {
121-
derivedStateOf {
122-
item in selectedItems
123-
}
118+
derivedStateOf { item in selectedItems }
124119
}
125120

126-
Row(
127-
modifier = Modifier
128-
.fillMaxWidth()
129-
.clickable { onItemClick(item, !checked) }
130-
.padding(
131-
horizontal = MaterialTheme.paddings.baseline,
132-
vertical = MaterialTheme.paddings.small,
133-
),
134-
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.paddings.baseline),
135-
verticalAlignment = Alignment.CenterVertically,
136-
) {
137-
Checkbox(
138-
checked = checked,
139-
onCheckedChange = null,
140-
)
141-
142-
Text(
143-
text = item.title ?: "No title",
144-
color = AlertDialogDefaults.textContentColor,
145-
)
146-
}
147-
}
148-
}
149-
}
150-
151-
@Composable
152-
private fun ButtonsRow(
153-
modifier: Modifier = Modifier,
154-
onAddClick: () -> Unit,
155-
onCancelClick: () -> Unit,
156-
onSelectAllClick: () -> Unit,
157-
) {
158-
Row(
159-
modifier = modifier,
160-
horizontalArrangement = Arrangement.SpaceBetween,
161-
) {
162-
TextButton(onClick = onCancelClick) {
163-
Text(stringResource(android.R.string.cancel))
164-
}
165-
166-
TextButton(onClick = onSelectAllClick) {
167-
Text(text = stringResource(android.R.string.selectAll))
168-
}
169-
170-
TextButton(onClick = onAddClick) {
171-
Text(text = stringResource(android.R.string.ok))
121+
ListItem(
122+
headlineContent = {
123+
Text(
124+
text = item.title ?: "No title",
125+
overflow = TextOverflow.Ellipsis,
126+
maxLines = 2,
127+
)
128+
},
129+
modifier = Modifier.clickable {
130+
onItemClick(item, !checked)
131+
},
132+
leadingContent = {
133+
Checkbox(
134+
checked = checked,
135+
onCheckedChange = null,
136+
)
137+
},
138+
colors = ListItemDefaults.colors(
139+
containerColor = AlertDialogDefaults.containerColor,
140+
headlineColor = AlertDialogDefaults.textContentColor,
141+
),
142+
)
172143
}
173144
}
174145
}
@@ -191,25 +162,19 @@ private fun MediaItemLibraryDialogPreview() {
191162
@Composable
192163
private fun ItemListPreview() {
193164
val items = SamplesAll.playlist.items.take(10)
165+
val selectedItems = remember { mutableStateListOf(items[0], items[3], items[4], items[8]) }
194166

195167
PillarboxTheme {
196168
ItemList(
197169
items = items,
198-
selectedItems = listOf(items[0], items[3], items[4], items[8]),
199-
onItemClick = { _, _ -> },
200-
)
201-
}
202-
}
203-
204-
@Preview
205-
@Composable
206-
private fun ButtonsRowPreview() {
207-
PillarboxTheme {
208-
ButtonsRow(
209-
modifier = Modifier.fillMaxWidth(),
210-
onAddClick = {},
211-
onCancelClick = {},
212-
onSelectAllClick = {},
170+
selectedItems = selectedItems,
171+
onItemClick = { item, checked ->
172+
if (checked) {
173+
selectedItems.add(item)
174+
} else {
175+
selectedItems.remove(item)
176+
}
177+
},
213178
)
214179
}
215180
}

0 commit comments

Comments
 (0)