Skip to content

Commit 38645b0

Browse files
Implement playback action buttons
1 parent 48ad123 commit 38645b0

3 files changed

Lines changed: 170 additions & 59 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.schabi.newpipe.ui.components.common
2+
3+
import android.content.res.Configuration
4+
import androidx.annotation.StringRes
5+
import androidx.compose.foundation.layout.Arrangement
6+
import androidx.compose.foundation.layout.Row
7+
import androidx.compose.material.icons.Icons
8+
import androidx.compose.material.icons.filled.Info
9+
import androidx.compose.material3.FilledTonalButton
10+
import androidx.compose.material3.Icon
11+
import androidx.compose.material3.MaterialTheme
12+
import androidx.compose.material3.Surface
13+
import androidx.compose.material3.Text
14+
import androidx.compose.runtime.Composable
15+
import androidx.compose.ui.Alignment
16+
import androidx.compose.ui.graphics.vector.ImageVector
17+
import androidx.compose.ui.res.stringResource
18+
import androidx.compose.ui.tooling.preview.Preview
19+
import androidx.compose.ui.unit.dp
20+
import org.schabi.newpipe.R
21+
import org.schabi.newpipe.ui.theme.AppTheme
22+
23+
@Composable
24+
fun IconButtonWithLabel(
25+
icon: ImageVector,
26+
@StringRes label: Int,
27+
onClick: () -> Unit,
28+
) {
29+
FilledTonalButton(onClick = onClick) {
30+
Row(
31+
horizontalArrangement = Arrangement.spacedBy(4.dp),
32+
verticalAlignment = Alignment.CenterVertically,
33+
) {
34+
Icon(imageVector = icon, contentDescription = null)
35+
Text(text = stringResource(label))
36+
}
37+
}
38+
}
39+
40+
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
41+
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
42+
@Composable
43+
private fun IconButtonWithLabelPreview() {
44+
AppTheme {
45+
Surface(color = MaterialTheme.colorScheme.background) {
46+
IconButtonWithLabel(Icons.Default.Info, R.string.name) {}
47+
}
48+
}
49+
}
Lines changed: 120 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
package org.schabi.newpipe.ui.screens
22

33
import android.content.res.Configuration
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Column
46
import androidx.compose.foundation.layout.Row
7+
import androidx.compose.foundation.layout.Spacer
8+
import androidx.compose.foundation.layout.fillMaxWidth
9+
import androidx.compose.foundation.layout.height
510
import androidx.compose.foundation.layout.padding
611
import androidx.compose.material.icons.Icons
12+
import androidx.compose.material.icons.automirrored.filled.PlaylistPlay
713
import androidx.compose.material.icons.filled.ClearAll
14+
import androidx.compose.material.icons.filled.Headphones
15+
import androidx.compose.material.icons.filled.PictureInPicture
816
import androidx.compose.material3.DropdownMenuItem
917
import androidx.compose.material3.ExperimentalMaterial3Api
1018
import androidx.compose.material3.ExposedDropdownMenuBox
@@ -27,25 +35,48 @@ import androidx.compose.runtime.remember
2735
import androidx.compose.runtime.setValue
2836
import androidx.compose.ui.Alignment
2937
import androidx.compose.ui.Modifier
38+
import androidx.compose.ui.platform.LocalContext
3039
import androidx.compose.ui.res.stringResource
3140
import androidx.compose.ui.tooling.preview.Preview
3241
import androidx.compose.ui.unit.dp
3342
import androidx.lifecycle.compose.collectAsStateWithLifecycle
3443
import androidx.lifecycle.viewmodel.compose.viewModel
3544
import androidx.paging.compose.collectAsLazyPagingItems
3645
import org.schabi.newpipe.R
46+
import org.schabi.newpipe.ktx.findFragmentActivity
3747
import org.schabi.newpipe.local.history.HistoryViewModel
3848
import org.schabi.newpipe.local.history.SortKey
49+
import org.schabi.newpipe.player.playqueue.SinglePlayQueue
50+
import org.schabi.newpipe.ui.components.common.IconButtonWithLabel
3951
import org.schabi.newpipe.ui.components.items.ItemList
4052
import org.schabi.newpipe.ui.theme.AppTheme
53+
import org.schabi.newpipe.util.NavigationHelper
4154

4255
@Composable
4356
fun HistoryScreen(viewModel: HistoryViewModel = viewModel()) {
57+
val context = LocalContext.current
4458
val sortKey by viewModel.sortKey.collectAsStateWithLifecycle()
4559
val historyItems = viewModel.historyItems.collectAsLazyPagingItems()
60+
val queue = SinglePlayQueue(historyItems.itemSnapshotList.map { it!!.toStreamInfoItem() }, 0)
61+
val onClickBackground = {
62+
NavigationHelper.playOnBackgroundPlayer(context, queue, false)
63+
}
64+
val onClickPopup = {
65+
NavigationHelper.playOnPopupPlayer(context, queue, false)
66+
}
67+
val onClickPlayAll = {
68+
NavigationHelper.playOnMainPlayer(context.findFragmentActivity(), queue)
69+
}
4670

4771
ItemList(historyItems, header = {
48-
HistoryHeader(sortKey, viewModel::updateOrder, viewModel::deleteWatchHistory)
72+
HistoryHeader(
73+
sortKey = sortKey,
74+
onSelectSortKey = viewModel::updateOrder,
75+
onClickClear = viewModel::deleteWatchHistory,
76+
onClickBackground = onClickBackground,
77+
onClickPlayAll = onClickPlayAll,
78+
onClickPopup = onClickPopup,
79+
)
4980
})
5081
}
5182

@@ -55,75 +86,107 @@ private fun HistoryHeader(
5586
sortKey: SortKey,
5687
onSelectSortKey: (SortKey) -> Unit,
5788
onClickClear: () -> Unit,
89+
onClickBackground: () -> Unit,
90+
onClickPlayAll: () -> Unit,
91+
onClickPopup: () -> Unit
5892
) {
5993
var expanded by remember { mutableStateOf(false) }
6094
val selected = when (sortKey) {
6195
SortKey.MOST_PLAYED -> R.string.title_most_played
6296
SortKey.LAST_PLAYED -> R.string.title_last_played
6397
}
6498

65-
Row(verticalAlignment = Alignment.CenterVertically) {
66-
ExposedDropdownMenuBox(
67-
modifier = Modifier.padding(top = 12.dp, start = 12.dp),
68-
expanded = expanded,
69-
onExpandedChange = { expanded = it },
70-
) {
71-
TextField(
72-
enabled = true,
73-
modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable),
74-
value = stringResource(selected),
75-
readOnly = true,
76-
onValueChange = {},
77-
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
78-
colors = ExposedDropdownMenuDefaults.textFieldColors(),
79-
label = { Text(text = stringResource(R.string.history_sort_label)) }
80-
)
81-
82-
ExposedDropdownMenu(
99+
Column(
100+
modifier = Modifier
101+
.fillMaxWidth()
102+
.padding(12.dp),
103+
verticalArrangement = Arrangement.Center,
104+
horizontalAlignment = Alignment.CenterHorizontally,
105+
) {
106+
Row(verticalAlignment = Alignment.CenterVertically) {
107+
ExposedDropdownMenuBox(
83108
expanded = expanded,
84-
onDismissRequest = { expanded = false },
109+
onExpandedChange = { expanded = it },
85110
) {
86-
DropdownMenuItem(
87-
text = {
88-
Text(
89-
text = stringResource(R.string.title_most_played),
90-
color = MaterialTheme.colorScheme.onBackground,
91-
)
92-
},
93-
onClick = {
94-
expanded = false
95-
onSelectSortKey(SortKey.MOST_PLAYED)
96-
}
97-
)
98-
DropdownMenuItem(
99-
text = {
100-
Text(
101-
text = stringResource(R.string.title_last_played),
102-
color = MaterialTheme.colorScheme.onBackground,
103-
)
104-
},
105-
onClick = {
106-
expanded = false
107-
onSelectSortKey(SortKey.LAST_PLAYED)
108-
}
111+
TextField(
112+
enabled = true,
113+
modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable),
114+
value = stringResource(selected),
115+
readOnly = true,
116+
onValueChange = {},
117+
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
118+
colors = ExposedDropdownMenuDefaults.textFieldColors(),
119+
label = { Text(text = stringResource(R.string.history_sort_label)) }
109120
)
121+
122+
ExposedDropdownMenu(
123+
expanded = expanded,
124+
onDismissRequest = { expanded = false },
125+
) {
126+
DropdownMenuItem(
127+
text = {
128+
Text(
129+
text = stringResource(R.string.title_most_played),
130+
color = MaterialTheme.colorScheme.onBackground,
131+
)
132+
},
133+
onClick = {
134+
expanded = false
135+
onSelectSortKey(SortKey.MOST_PLAYED)
136+
}
137+
)
138+
DropdownMenuItem(
139+
text = {
140+
Text(
141+
text = stringResource(R.string.title_last_played),
142+
color = MaterialTheme.colorScheme.onBackground,
143+
)
144+
},
145+
onClick = {
146+
expanded = false
147+
onSelectSortKey(SortKey.LAST_PLAYED)
148+
}
149+
)
150+
}
110151
}
111-
}
112152

113-
TooltipBox(
114-
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
115-
tooltip = {
116-
PlainTooltip { Text(text = stringResource(R.string.clear_views_history_title)) }
117-
},
118-
state = rememberTooltipState(),
119-
) {
120-
IconButton(onClick = onClickClear) {
121-
Icon(
122-
imageVector = Icons.Default.ClearAll,
123-
contentDescription = stringResource(R.string.clear_history_description),
124-
)
153+
TooltipBox(
154+
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
155+
tooltip = {
156+
PlainTooltip { Text(text = stringResource(R.string.clear_views_history_title)) }
157+
},
158+
state = rememberTooltipState(),
159+
) {
160+
IconButton(onClick = onClickClear) {
161+
Icon(
162+
imageVector = Icons.Default.ClearAll,
163+
contentDescription = stringResource(R.string.clear_history_description),
164+
)
165+
}
125166
}
126167
}
168+
169+
Spacer(Modifier.height(12.dp))
170+
171+
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
172+
IconButtonWithLabel(
173+
icon = Icons.Default.Headphones,
174+
label = R.string.controls_background_title,
175+
onClick = onClickBackground
176+
)
177+
178+
IconButtonWithLabel(
179+
icon = Icons.AutoMirrored.Filled.PlaylistPlay,
180+
label = R.string.play_all,
181+
onClick = onClickPlayAll
182+
)
183+
184+
IconButtonWithLabel(
185+
icon = Icons.Default.PictureInPicture,
186+
label = R.string.controls_popup_title,
187+
onClick = onClickPopup
188+
)
189+
}
127190
}
128191
}
129192

@@ -133,7 +196,7 @@ private fun HistoryHeader(
133196
private fun HistoryHeaderPreview() {
134197
AppTheme {
135198
Surface(color = MaterialTheme.colorScheme.background) {
136-
HistoryHeader(SortKey.MOST_PLAYED, {}, {})
199+
HistoryHeader(SortKey.MOST_PLAYED, {}, {}, {}, {}, {})
137200
}
138201
}
139202
}

app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import androidx.annotation.NonNull;
1616
import androidx.annotation.Nullable;
1717
import androidx.appcompat.app.AlertDialog;
18-
import androidx.appcompat.app.AppCompatActivity;
1918
import androidx.core.content.ContextCompat;
2019
import androidx.fragment.app.Fragment;
2120
import androidx.fragment.app.FragmentActivity;
@@ -137,7 +136,7 @@ public static <T> Intent getPlayerEnqueueNextIntent(@NonNull final Context conte
137136
}
138137

139138
/* PLAY */
140-
public static void playOnMainPlayer(final AppCompatActivity activity,
139+
public static void playOnMainPlayer(final FragmentActivity activity,
141140
@NonNull final PlayQueue playQueue) {
142141
final PlayQueueItem item = playQueue.getItem();
143142
if (item != null) {

0 commit comments

Comments
 (0)