Skip to content

Commit c38fa89

Browse files
committed
refactor: rewrites sync metrics dialog in compose
1 parent 0d4d3d2 commit c38fa89

11 files changed

Lines changed: 160 additions & 633 deletions

File tree

app/src/main/java/com/kelsos/mbrc/AppModule.kt

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import com.kelsos.mbrc.features.library.presentation.ArtistAdapter
2323
import com.kelsos.mbrc.features.library.presentation.ArtistViewModel
2424
import com.kelsos.mbrc.features.library.presentation.GenreAdapter
2525
import com.kelsos.mbrc.features.library.presentation.GenreViewModel
26-
import com.kelsos.mbrc.features.library.presentation.LibraryFragment
2726
import com.kelsos.mbrc.features.library.presentation.LibrarySearchModel
2827
import com.kelsos.mbrc.features.library.presentation.LibraryViewModel
2928
import com.kelsos.mbrc.features.library.presentation.TrackAdapter
@@ -34,10 +33,6 @@ import com.kelsos.mbrc.features.library.presentation.details.LibraryGenreArtists
3433
import com.kelsos.mbrc.features.library.presentation.details.viemodels.AlbumTrackViewModel
3534
import com.kelsos.mbrc.features.library.presentation.details.viemodels.ArtistAlbumViewModel
3635
import com.kelsos.mbrc.features.library.presentation.details.viemodels.GenreArtistViewModel
37-
import com.kelsos.mbrc.features.library.presentation.screens.AlbumScreen
38-
import com.kelsos.mbrc.features.library.presentation.screens.ArtistScreen
39-
import com.kelsos.mbrc.features.library.presentation.screens.GenreScreen
40-
import com.kelsos.mbrc.features.library.presentation.screens.TrackScreen
4136
import com.kelsos.mbrc.features.library.repositories.AlbumRepository
4237
import com.kelsos.mbrc.features.library.repositories.AlbumRepositoryImpl
4338
import com.kelsos.mbrc.features.library.repositories.ArtistRepository
@@ -281,7 +276,6 @@ val appModule = module {
281276

282277
worker { QueueWorker(get(), get(), get()) }
283278
worker { SyncWorker(get(), get(), get(), get()) }
284-
fragment { LibraryFragment(get()) }
285279
fragment { LibraryAlbumTracksFragment(get(), get(), get()) }
286280
fragment { LibraryArtistAlbumsFragment(get(), get(), get()) }
287281
fragment { LibraryGenreArtistsFragment(get(), get(), get()) }
@@ -313,10 +307,6 @@ val uiModule = module {
313307
viewModel<TrackViewModel>()
314308

315309
single<LibrarySearchModel>()
316-
factory<GenreScreen>()
317-
factory<AlbumScreen>()
318-
factory<ArtistScreen>()
319-
factory<TrackScreen>()
320310

321311
viewModel<GenreArtistViewModel>()
322312
viewModel<ArtistAlbumViewModel>()

app/src/main/java/com/kelsos/mbrc/features/library/LibraryScreen.kt

Lines changed: 159 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
package com.kelsos.mbrc.features.library
22

3+
import androidx.annotation.StringRes
34
import androidx.compose.animation.core.animateFloatAsState
5+
import androidx.compose.foundation.layout.Arrangement
46
import androidx.compose.foundation.layout.Column
57
import androidx.compose.foundation.layout.Row
8+
import androidx.compose.foundation.layout.Spacer
69
import androidx.compose.foundation.layout.fillMaxSize
710
import androidx.compose.foundation.layout.fillMaxWidth
811
import androidx.compose.foundation.layout.height
12+
import androidx.compose.foundation.layout.padding
13+
import androidx.compose.foundation.shape.RoundedCornerShape
14+
import androidx.compose.material.Checkbox
15+
import androidx.compose.material.DropdownMenu
16+
import androidx.compose.material.DropdownMenuItem
17+
import androidx.compose.material.Icon
18+
import androidx.compose.material.IconButton
919
import androidx.compose.material.LinearProgressIndicator
1020
import androidx.compose.material.MaterialTheme
1121
import androidx.compose.material.ProgressIndicatorDefaults
@@ -14,15 +24,26 @@ import androidx.compose.material.Tab
1424
import androidx.compose.material.TabRow
1525
import androidx.compose.material.TabRowDefaults
1626
import androidx.compose.material.Text
27+
import androidx.compose.material.TextButton
28+
import androidx.compose.material.icons.Icons
29+
import androidx.compose.material.icons.filled.MoreVert
30+
import androidx.compose.material.icons.filled.Refresh
31+
import androidx.compose.material.icons.filled.Search
1732
import androidx.compose.runtime.Composable
33+
import androidx.compose.runtime.collectAsState
1834
import androidx.compose.runtime.getValue
1935
import androidx.compose.runtime.livedata.observeAsState
36+
import androidx.compose.runtime.mutableStateOf
37+
import androidx.compose.runtime.remember
38+
import androidx.compose.runtime.setValue
2039
import androidx.compose.ui.Alignment
2140
import androidx.compose.ui.Modifier
41+
import androidx.compose.ui.draw.clip
2242
import androidx.compose.ui.res.stringResource
2343
import androidx.compose.ui.tooling.preview.Devices
2444
import androidx.compose.ui.tooling.preview.Preview
2545
import androidx.compose.ui.unit.dp
46+
import androidx.compose.ui.window.Dialog
2647
import androidx.lifecycle.LiveData
2748
import com.google.accompanist.pager.ExperimentalPagerApi
2849
import com.google.accompanist.pager.HorizontalPager
@@ -35,27 +56,103 @@ import com.kelsos.mbrc.features.library.presentation.LibraryViewModel
3556
import com.kelsos.mbrc.features.library.sync.LibrarySyncProgress
3657
import com.kelsos.mbrc.features.library.sync.SyncCategory
3758
import com.kelsos.mbrc.features.queue.Queue
59+
import com.kelsos.mbrc.metrics.SyncedData
3860
import com.kelsos.mbrc.theme.Accent
3961
import com.kelsos.mbrc.theme.DarkBackground
4062
import com.kelsos.mbrc.theme.RemoteTheme
4163
import kotlinx.coroutines.CoroutineScope
4264
import kotlinx.coroutines.MainScope
65+
import kotlinx.coroutines.flow.Flow
4366
import kotlinx.coroutines.launch
4467
import org.koin.androidx.compose.getViewModel
4568

4669
@Composable
47-
fun LibraryScreen(openDrawer: () -> Unit, coroutineScope: CoroutineScope) {
48-
val vm = getViewModel<LibraryViewModel>()
70+
fun LibraryScreen(
71+
openDrawer: () -> Unit,
72+
coroutineScope: CoroutineScope,
73+
vm: LibraryViewModel = getViewModel()
74+
) {
4975
LibraryScreen(
5076
openDrawer = openDrawer, sync = { vm.refresh() },
5177
action = { queue, meta, id ->
5278
vm.queue(id, meta, queue)
5379
},
5480
syncProgress = vm.syncProgress,
81+
albumArtistsOnly = vm.albumArtistOnly,
82+
setAlbumArtistOnly = { vm.setAlbumArtistOnly(it) },
83+
metrics = vm.syncState,
5584
coroutineScope
5685
)
5786
}
5887

88+
@Composable
89+
fun SyncMetricsDialog(metrics: Flow<SyncedData>, showDialog: Boolean, dismiss: () -> Unit) {
90+
if (showDialog) {
91+
Dialog(
92+
onDismissRequest = { dismiss() },
93+
) {
94+
Surface(
95+
modifier = Modifier.clip(RoundedCornerShape(8.dp))
96+
) {
97+
val syncedData by metrics.collectAsState(initial = SyncedData(0, 0, 0, 0, 0))
98+
SyncMetricsContent(syncedData, dismiss)
99+
}
100+
}
101+
}
102+
}
103+
104+
@Composable
105+
fun SyncMetricsContent(metrics: SyncedData, dismiss: () -> Unit) =
106+
Column(modifier = Modifier.padding(16.dp)) {
107+
Row(modifier = Modifier.fillMaxWidth()) {
108+
Text(
109+
text = stringResource(id = R.string.library_stats__title),
110+
style = MaterialTheme.typography.h6
111+
)
112+
}
113+
Row(
114+
modifier = Modifier
115+
.padding(vertical = 16.dp)
116+
.fillMaxWidth()
117+
) {
118+
Text(
119+
text = stringResource(id = R.string.library_stats__description),
120+
style = MaterialTheme.typography.caption,
121+
color = MaterialTheme.colors.onSurface.copy(0.7f)
122+
)
123+
}
124+
125+
MetricsRow(R.string.media__genres, metrics.genres)
126+
MetricsRow(R.string.media__artists, metrics.artists)
127+
MetricsRow(R.string.media__albums, metrics.albums)
128+
MetricsRow(R.string.media__tracks, metrics.tracks)
129+
MetricsRow(R.string.media__playlists, metrics.playlists)
130+
Row(modifier = Modifier.padding(top = 16.dp)) {
131+
Spacer(modifier = Modifier.weight(1f))
132+
TextButton(onClick = dismiss) {
133+
Text(text = stringResource(id = android.R.string.ok))
134+
}
135+
}
136+
}
137+
138+
@Composable
139+
private fun MetricsRow(@StringRes resId: Int, items: Long) {
140+
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
141+
Column {
142+
Text(text = stringResource(id = resId), color = MaterialTheme.colors.primary)
143+
}
144+
Column {
145+
Text(text = items.toString())
146+
}
147+
}
148+
}
149+
150+
@Preview
151+
@Composable
152+
fun SyncMetricsContentPreview() {
153+
SyncMetricsContent(metrics = SyncedData(10, 40, 100, 1000, 4)) {}
154+
}
155+
59156
@Composable
60157
fun LibrarySyncIndicator(syncProgress: LiveData<LibrarySyncProgress>) {
61158
val progress by syncProgress.observeAsState(initial = LibrarySyncProgress(0, 0, 0, false))
@@ -81,7 +178,7 @@ fun LibrarySyncIndicator(syncProgress: LibrarySyncProgress) = Surface(
81178
SyncCategory.TRACKS -> R.string.library__category_tracks
82179
SyncCategory.PLAYLISTS -> R.string.library__category_playlists
83180
SyncCategory.COVERS -> R.string.library__category_covers
84-
else -> throw IllegalArgumentException()
181+
else -> R.string.media__genres
85182
}
86183
Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
87184
Text(
@@ -115,13 +212,62 @@ fun LibrarySyncIndicatorPreview() {
115212
LibrarySyncIndicator(syncProgress = LibrarySyncProgress(12000, 120000, 2, true))
116213
}
117214

215+
@Composable
216+
fun SyncButton(sync: () -> Unit) {
217+
IconButton(onClick = { sync() }) {
218+
Icon(
219+
imageVector = Icons.Filled.Refresh,
220+
contentDescription = stringResource(id = R.string.press_to_sync)
221+
)
222+
}
223+
}
224+
225+
@Composable
226+
fun LibraryScreenOverflow(
227+
showAlbumArtistsOnly: Flow<Boolean>,
228+
setAlbumArtistOnly: (enabled: Boolean) -> Unit,
229+
metrics: Flow<SyncedData>
230+
) {
231+
var showMenu by remember { mutableStateOf(false) }
232+
var showMetricsDialog by remember { mutableStateOf(false) }
233+
IconButton(onClick = { showMenu = !showMenu }) {
234+
Icon(
235+
imageVector = Icons.Filled.MoreVert,
236+
contentDescription = stringResource(id = R.string.press_to_sync)
237+
)
238+
}
239+
DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
240+
val albumArtistsOnly by showAlbumArtistsOnly.collectAsState(initial = false)
241+
DropdownMenuItem(onClick = { setAlbumArtistOnly(!albumArtistsOnly) }) {
242+
Checkbox(checked = albumArtistsOnly, onCheckedChange = setAlbumArtistOnly)
243+
Text(
244+
text = stringResource(id = R.string.library__action_only_album_artists),
245+
modifier = Modifier.padding(start = 16.dp)
246+
)
247+
}
248+
249+
DropdownMenuItem(onClick = {
250+
showMenu = false
251+
showMetricsDialog = true
252+
}) {
253+
Text(
254+
text = stringResource(id = R.string.library_menu__sync_state),
255+
)
256+
}
257+
}
258+
SyncMetricsDialog(metrics, showDialog = showMetricsDialog) { showMetricsDialog = false }
259+
}
260+
118261
@OptIn(ExperimentalPagerApi::class)
119262
@Composable
120263
fun LibraryScreen(
121264
openDrawer: () -> Unit,
122265
sync: () -> Unit,
123266
action: (queue: Queue, meta: Meta, id: Long) -> Unit,
124267
syncProgress: LiveData<LibrarySyncProgress>,
268+
albumArtistsOnly: Flow<Boolean>,
269+
setAlbumArtistOnly: (enabled: Boolean) -> Unit,
270+
metrics: Flow<SyncedData>,
125271
coroutineScope: CoroutineScope
126272
) = Surface {
127273
val tabs = listOf(
@@ -135,6 +281,16 @@ fun LibraryScreen(
135281

136282
Column(modifier = Modifier.fillMaxSize()) {
137283
RemoteTopAppBar(openDrawer = openDrawer) {
284+
Row {
285+
IconButton(onClick = { /*TODO*/ }) {
286+
Icon(
287+
imageVector = Icons.Filled.Search,
288+
contentDescription = stringResource(id = R.string.library_search_hint)
289+
)
290+
}
291+
SyncButton(sync = sync)
292+
LibraryScreenOverflow(albumArtistsOnly, setAlbumArtistOnly, metrics)
293+
}
138294
}
139295
TabRow(
140296
selectedTabIndex = pagerState.currentPage,

0 commit comments

Comments
 (0)