From 383a354670a403ef35e25a02192ab472b384ed79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Sun, 29 Oct 2023 19:16:11 +0100 Subject: [PATCH 1/7] Add compose dependencies Following these instructions: https://developer.android.com/jetpack/compose/setup#kotlin --- android/app/build.gradle.kts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 06cb3a205..08c26c098 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -85,6 +85,13 @@ android { } namespace = "com.simplecityapps.shuttle" + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = rootProject.extra["compose_version"] as String + } + dependencies { implementation(fileTree("dir" to "libs", "include" to listOf("*.jar"))) @@ -126,6 +133,27 @@ android { // AppCompat implementation("androidx.appcompat:appcompat:1.6.1") + val composeBom = platform("androidx.compose:compose-bom:2023.10.01") + + implementation(composeBom) + androidTestImplementation(composeBom) + + // Material Design 3 + implementation("androidx.compose.material3:material3") + + // Android Studio Preview support + implementation("androidx.compose.ui:ui-tooling-preview") + debugImplementation("androidx.compose.ui:ui-tooling") + + // UI Tests + androidTestImplementation("androidx.compose.ui:ui-test-junit4") + debugImplementation("androidx.compose.ui:ui-test-manifest") + + // Optional - Integration with activities + implementation("androidx.activity:activity-compose:1.6.1") + // Optional - Integration with LiveData + implementation("androidx.compose.runtime:runtime-livedata") + // Material implementation("com.google.android.material:material:1.10.0") From dd12dbf34cc0f969aba36326f074ae0270376d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Fri, 1 Dec 2023 19:52:51 +0100 Subject: [PATCH 2/7] Implement list of genres without menu nor fast scroll --- .../ui/screens/library/genres/GenreBinder.kt | 6 +- .../library/genres/GenreListFragment.kt | 96 ++++++++++++------- .../src/main/res/layout/fragment_genres.xml | 9 ++ 3 files changed, 72 insertions(+), 39 deletions(-) diff --git a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreBinder.kt b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreBinder.kt index 699d7ae3c..722110c4a 100644 --- a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreBinder.kt +++ b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreBinder.kt @@ -7,15 +7,13 @@ import android.widget.ImageButton import android.widget.TextView import com.simplecityapps.adapter.ViewBinder import com.simplecityapps.shuttle.R -import com.simplecityapps.shuttle.model.Genre import com.simplecityapps.shuttle.ui.common.recyclerview.ViewTypes import com.squareup.phrase.Phrase class GenreBinder(val genre: com.simplecityapps.shuttle.model.Genre, private val listener: Listener) : ViewBinder { interface Listener { fun onGenreSelected( - genre: com.simplecityapps.shuttle.model.Genre, - viewHolder: ViewHolder + genre: com.simplecityapps.shuttle.model.Genre ) fun onOverflowClicked( @@ -58,7 +56,7 @@ class GenreBinder(val genre: com.simplecityapps.shuttle.model.Genre, private val private val overflowButton: ImageButton = itemView.findViewById(R.id.overflowButton) init { - itemView.setOnClickListener { viewBinder?.listener?.onGenreSelected(viewBinder!!.genre, this) } + itemView.setOnClickListener { viewBinder?.listener?.onGenreSelected(viewBinder!!.genre) } overflowButton.setOnClickListener { viewBinder?.listener?.onOverflowClicked(it, viewBinder!!.genre) } } diff --git a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt index c8bc2aa96..1e1288474 100644 --- a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt +++ b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt @@ -7,21 +7,32 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.appcompat.widget.PopupMenu +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController -import androidx.recyclerview.widget.RecyclerView -import com.simplecityapps.adapter.RecyclerAdapter -import com.simplecityapps.adapter.RecyclerListener -import com.simplecityapps.adapter.ViewBinder import com.simplecityapps.mediaprovider.Progress import com.simplecityapps.shuttle.R +import com.simplecityapps.shuttle.model.Genre import com.simplecityapps.shuttle.ui.common.TagEditorMenuSanitiser import com.simplecityapps.shuttle.ui.common.autoCleared import com.simplecityapps.shuttle.ui.common.dialog.TagEditorAlertDialog import com.simplecityapps.shuttle.ui.common.dialog.showExcludeDialog import com.simplecityapps.shuttle.ui.common.error.userDescription -import com.simplecityapps.shuttle.ui.common.recyclerview.SectionedAdapter import com.simplecityapps.shuttle.ui.common.view.CircularLoadingView import com.simplecityapps.shuttle.ui.common.view.HorizontalLoadingView import com.simplecityapps.shuttle.ui.screens.library.genres.detail.GenreDetailFragmentArgs @@ -31,6 +42,7 @@ import com.simplecityapps.shuttle.ui.screens.playlistmenu.PlaylistMenuPresenter import com.simplecityapps.shuttle.ui.screens.playlistmenu.PlaylistMenuView import com.squareup.phrase.Phrase import dagger.hilt.android.AndroidEntryPoint +import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint @@ -39,9 +51,7 @@ class GenreListFragment : GenreBinder.Listener, GenreListContract.View, CreatePlaylistDialogFragment.Listener { - private var adapter: RecyclerAdapter by autoCleared() - - private var recyclerView: RecyclerView by autoCleared() + private var composeView: ComposeView by autoCleared() private var circularLoadingView: CircularLoadingView by autoCleared() private var horizontalLoadingView: HorizontalLoadingView by autoCleared() @@ -73,17 +83,9 @@ class GenreListFragment : playlistMenuView = PlaylistMenuView(requireContext(), playlistMenuPresenter, childFragmentManager) - adapter = - object : SectionedAdapter(viewLifecycleOwner.lifecycleScope) { - override fun getSectionName(viewBinder: ViewBinder?): String? { - return (viewBinder as? GenreBinder)?.genre?.let { genre -> - presenter.getFastscrollPrefix(genre) - } - } - } - recyclerView = view.findViewById(R.id.recyclerView) - recyclerView.adapter = adapter - recyclerView.setRecyclerListener(RecyclerListener()) + composeView = view.findViewById(R.id.composeView) + presenter.loadGenres(false) + circularLoadingView = view.findViewById(R.id.circularLoadingView) horizontalLoadingView = view.findViewById(R.id.horizontalLoadingView) @@ -102,8 +104,6 @@ class GenreListFragment : override fun onPause() { super.onPause() - - recyclerViewState = recyclerView.layoutManager?.onSaveInstanceState() } override fun onSaveInstanceState(outState: Bundle) { @@ -120,20 +120,47 @@ class GenreListFragment : // GenreListContract.View Implementation - override fun setGenres( - genres: List, - resetPosition: Boolean - ) { - if (resetPosition) { - adapter.clear() + override fun setGenres(genres: List, resetPosition: Boolean) { + composeView.setContent { + GenreList(genres) } + } - val data = genres.map { genre -> GenreBinder(genre, this) }.toMutableList() + @Composable + private fun GenreList(genres: List, modifier: Modifier = Modifier) { + LazyColumn( + modifier = modifier + .fillMaxWidth() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + items(genres) { genre -> + GenreListItem(genre) + } + } + } - adapter.update(data) { - recyclerViewState?.let { - recyclerView.layoutManager?.onRestoreInstanceState(recyclerViewState) - recyclerViewState = null + @Composable + private fun GenreListItem(genre: Genre, modifier: Modifier = Modifier) { + Row(modifier) { + Column( + Modifier + .fillMaxWidth() + .clickable { this@GenreListFragment.onGenreSelected(genre) }, + ) { + Text( + text = genre.name, + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.SemiBold, + ) + Text( + text = Phrase + .fromPlural(LocalContext.current, R.plurals.songsPlural, genre.songCount) + .put("count", genre.songCount) + .format() + .toString(), + style = MaterialTheme.typography.bodySmall, + ) } } } @@ -180,8 +207,7 @@ class GenreListFragment : // GenreBinder.Listener Implementation override fun onGenreSelected( - genre: com.simplecityapps.shuttle.model.Genre, - viewHolder: GenreBinder.ViewHolder + genre: com.simplecityapps.shuttle.model.Genre ) { if (findNavController().currentDestination?.id != R.id.genreDetailFragment) { findNavController().navigate( diff --git a/android/app/src/main/res/layout/fragment_genres.xml b/android/app/src/main/res/layout/fragment_genres.xml index aa64eb374..f407d4941 100644 --- a/android/app/src/main/res/layout/fragment_genres.xml +++ b/android/app/src/main/res/layout/fragment_genres.xml @@ -7,6 +7,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + Date: Tue, 2 Jan 2024 20:10:54 +0100 Subject: [PATCH 3/7] Implement overflow menu on genre items --- .../library/genres/GenreListFragment.kt | 134 +++++++++++++++++- 1 file changed, 131 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt index 1e1288474..c1d706050 100644 --- a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt +++ b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt @@ -15,12 +15,25 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.KeyboardArrowRight +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.fragment.app.Fragment @@ -42,7 +55,6 @@ import com.simplecityapps.shuttle.ui.screens.playlistmenu.PlaylistMenuPresenter import com.simplecityapps.shuttle.ui.screens.playlistmenu.PlaylistMenuView import com.squareup.phrase.Phrase import dagger.hilt.android.AndroidEntryPoint -import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint @@ -142,10 +154,13 @@ class GenreListFragment : @Composable private fun GenreListItem(genre: Genre, modifier: Modifier = Modifier) { - Row(modifier) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { Column( Modifier - .fillMaxWidth() + .weight(1f) .clickable { this@GenreListFragment.onGenreSelected(genre) }, ) { Text( @@ -162,6 +177,119 @@ class GenreListFragment : style = MaterialTheme.typography.bodySmall, ) } + GenreMenu(genre) + } + } + + @Composable + private fun GenreMenu(genre: Genre) { + var isMenuOpened by remember { mutableStateOf(false) } + var isAddToPlaylistSubmenuOpen by remember { mutableStateOf(false) } + + IconButton( + onClick = { isMenuOpened = true }, + ) { + Icon( + Icons.Default.MoreVert, + contentDescription = "Genre menu", + ) + DropdownMenu( + expanded = isMenuOpened, + onDismissRequest = { isMenuOpened = false }, + ) { + DropdownMenuItem( + text = { Text(stringResource(id = R.string.menu_title_play)) }, + onClick = { + presenter.play(genre) + isMenuOpened = false + }, + ) + DropdownMenuItem( + text = { Text(stringResource(id = R.string.menu_title_add_to_queue)) }, + onClick = { + presenter.addToQueue(genre) + isMenuOpened = false + }, + ) + DropdownMenuItem( + text = { Text(stringResource(id = R.string.menu_title_add_to_playlist)) }, + onClick = { + isMenuOpened = false + isAddToPlaylistSubmenuOpen = true + }, + trailingIcon = { + Icon(Icons.Default.KeyboardArrowRight, contentDescription = null) + }, + ) + DropdownMenuItem( + text = { Text(stringResource(id = R.string.menu_title_play_next)) }, + onClick = { + presenter.playNext(genre) + isMenuOpened = false + }, + ) + DropdownMenuItem( + text = { Text(stringResource(id = R.string.menu_title_exclude)) }, + onClick = { + presenter.exclude(genre) + isMenuOpened = false + }, + ) + + val supportsTagEditing = genre.mediaProviders.all { + mediaProvider -> mediaProvider.supportsTagEditing + } + + if (supportsTagEditing) { + DropdownMenuItem( + text = { Text(stringResource(id = R.string.menu_title_edit_tags)) }, + onClick = { + presenter.editTags(genre) + isMenuOpened = false + }, + ) + } + } + AddToPlaylistSubmenu( + genre = genre, + expanded = isAddToPlaylistSubmenuOpen, + onDismiss = { isAddToPlaylistSubmenuOpen = false }, + ) + } + } + + @Composable + private fun AddToPlaylistSubmenu( + genre: Genre, + expanded: Boolean = false, + onDismiss: () -> Unit = {}, + ) { + val playlistData = PlaylistData.Genres(genre) + + DropdownMenu( + expanded = expanded, + onDismissRequest = onDismiss, + ) { + DropdownMenuItem( + text = { Text(stringResource(id = R.string.playlist_menu_create_playlist)) }, + onClick = { + CreatePlaylistDialogFragment.newInstance( + playlistData, + context?.getString(R.string.playlist_create_dialog_playlist_name_hint) + ).show(childFragmentManager) + onDismiss() + }, + ) + + for (playlist in playlistMenuPresenter.playlists) { + DropdownMenuItem( + text = { Text(playlist.name) }, + onClick = { + playlistMenuPresenter.addToPlaylist(playlist, playlistData) + onDismiss() + }, + ) + } } } From 1753b67578aa0cf81769afb20cafba20bd5c4ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Sat, 20 Jan 2024 20:00:25 +0100 Subject: [PATCH 4/7] Replace presenter with view model --- .../ui/screens/library/genres/GenreBinder.kt | 81 -------- .../library/genres/GenreListFragment.kt | 174 ++++++++-------- .../library/genres/GenreListPresenter.kt | 188 ------------------ .../library/genres/GenreListViewModel.kt | 115 +++++++++++ 4 files changed, 209 insertions(+), 349 deletions(-) delete mode 100644 android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreBinder.kt delete mode 100644 android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListPresenter.kt create mode 100644 android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListViewModel.kt diff --git a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreBinder.kt b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreBinder.kt deleted file mode 100644 index 722110c4a..000000000 --- a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreBinder.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.simplecityapps.shuttle.ui.screens.library.genres - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageButton -import android.widget.TextView -import com.simplecityapps.adapter.ViewBinder -import com.simplecityapps.shuttle.R -import com.simplecityapps.shuttle.ui.common.recyclerview.ViewTypes -import com.squareup.phrase.Phrase - -class GenreBinder(val genre: com.simplecityapps.shuttle.model.Genre, private val listener: Listener) : ViewBinder { - interface Listener { - fun onGenreSelected( - genre: com.simplecityapps.shuttle.model.Genre - ) - - fun onOverflowClicked( - view: View, - genre: com.simplecityapps.shuttle.model.Genre - ) {} - } - - override fun createViewHolder(parent: ViewGroup): ViewHolder { - return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.list_item_genre, parent, false)) - } - - override fun viewType(): Int { - return ViewTypes.Genre - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GenreBinder - - if (genre != other.genre) return false - - return true - } - - override fun hashCode(): Int { - return genre.hashCode() - } - - override fun areContentsTheSame(other: Any): Boolean { - return genre.name == (other as? GenreBinder)?.genre?.name && - genre.songCount == (other as? GenreBinder)?.genre?.songCount - } - - class ViewHolder(itemView: View) : ViewBinder.ViewHolder(itemView) { - private val titleTextView: TextView = itemView.findViewById(R.id.title) - private val subtitleTextView: TextView = itemView.findViewById(R.id.subtitle) - private val overflowButton: ImageButton = itemView.findViewById(R.id.overflowButton) - - init { - itemView.setOnClickListener { viewBinder?.listener?.onGenreSelected(viewBinder!!.genre) } - overflowButton.setOnClickListener { viewBinder?.listener?.onOverflowClicked(it, viewBinder!!.genre) } - } - - override fun bind( - viewBinder: GenreBinder, - isPartial: Boolean - ) { - super.bind(viewBinder, isPartial) - - titleTextView.text = viewBinder.genre.name - if (viewBinder.genre.songCount == 0) { - subtitleTextView.text = itemView.resources.getString(R.string.song_list_empty) - } else { - subtitleTextView.text = - Phrase - .fromPlural(itemView.context, R.plurals.songsPlural, viewBinder.genre.songCount) - .put("count", viewBinder.genre.songCount) - .format() - } - } - } -} diff --git a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt index c1d706050..f570ebcc5 100644 --- a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt +++ b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt @@ -6,7 +6,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast -import androidx.appcompat.widget.PopupMenu import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -25,6 +24,7 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -37,14 +37,15 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController +import com.simplecityapps.mediaprovider.MediaImporter import com.simplecityapps.mediaprovider.Progress import com.simplecityapps.shuttle.R import com.simplecityapps.shuttle.model.Genre -import com.simplecityapps.shuttle.ui.common.TagEditorMenuSanitiser +import com.simplecityapps.shuttle.model.MediaProviderType import com.simplecityapps.shuttle.ui.common.autoCleared import com.simplecityapps.shuttle.ui.common.dialog.TagEditorAlertDialog -import com.simplecityapps.shuttle.ui.common.dialog.showExcludeDialog import com.simplecityapps.shuttle.ui.common.error.userDescription import com.simplecityapps.shuttle.ui.common.view.CircularLoadingView import com.simplecityapps.shuttle.ui.common.view.HorizontalLoadingView @@ -60,15 +61,12 @@ import javax.inject.Inject @AndroidEntryPoint class GenreListFragment : Fragment(), - GenreBinder.Listener, - GenreListContract.View, CreatePlaylistDialogFragment.Listener { private var composeView: ComposeView by autoCleared() private var circularLoadingView: CircularLoadingView by autoCleared() private var horizontalLoadingView: HorizontalLoadingView by autoCleared() - @Inject - lateinit var presenter: GenreListPresenter + private val viewModel: GenreListViewModel by viewModels() @Inject lateinit var playlistMenuPresenter: PlaylistMenuPresenter @@ -77,6 +75,17 @@ class GenreListFragment : private var recyclerViewState: Parcelable? = null + private val mediaImporterListener = + object : MediaImporter.Listener { + override fun onSongImportProgress( + providerType: MediaProviderType, + message: String, + progress: Progress?, + ) { + this@GenreListFragment.setLoadingProgress(progress) + } + } + // Lifecycle override fun onCreateView( @@ -96,7 +105,7 @@ class GenreListFragment : playlistMenuView = PlaylistMenuView(requireContext(), playlistMenuPresenter, childFragmentManager) composeView = view.findViewById(R.id.composeView) - presenter.loadGenres(false) + loadGenres() circularLoadingView = view.findViewById(R.id.circularLoadingView) @@ -104,14 +113,51 @@ class GenreListFragment : savedInstanceState?.getParcelable(ARG_RECYCLER_STATE)?.let { recyclerViewState = it } - presenter.bindView(this) playlistMenuPresenter.bindView(playlistMenuView) } + fun loadGenres() { + composeView.setContent { + val viewState by viewModel.viewState.collectAsState() + + Genres(viewState) + } + } + + @Composable + private fun Genres(viewState: GenreListViewModel.ViewState) { + when (viewState) { + is GenreListViewModel.ViewState.Scanning -> { + this.setLoadingState(LoadingState.Scanning) + } + + is GenreListViewModel.ViewState.Loading -> { + this.setLoadingState(LoadingState.Loading) + } + + is GenreListViewModel.ViewState.Ready -> { + if (viewState.genres.isEmpty()) { + if (viewModel.isImportingMedia()) { + viewModel.addMediaImporterListener(mediaImporterListener) + this.setLoadingState(LoadingState.Scanning) + } else { + viewModel.removeMediaImporterListener(mediaImporterListener) + this.setLoadingState(LoadingState.Empty) + } + } else { + viewModel.removeMediaImporterListener(mediaImporterListener) + this.setLoadingState(LoadingState.None) + } + + GenreList(genres = viewState.genres) + } + } + } + override fun onResume() { super.onResume() - presenter.loadGenres(false) + loadGenres() } override fun onPause() { @@ -124,7 +170,6 @@ class GenreListFragment : } override fun onDestroyView() { - presenter.unbindView() playlistMenuPresenter.unbindView() super.onDestroyView() @@ -132,12 +177,6 @@ class GenreListFragment : // GenreListContract.View Implementation - override fun setGenres(genres: List, resetPosition: Boolean) { - composeView.setContent { - GenreList(genres) - } - } - @Composable private fun GenreList(genres: List, modifier: Modifier = Modifier) { LazyColumn( @@ -200,14 +239,20 @@ class GenreListFragment : DropdownMenuItem( text = { Text(stringResource(id = R.string.menu_title_play)) }, onClick = { - presenter.play(genre) + viewModel.play(genre) { result -> + result.onFailure { error -> showLoadError(error as Error) } + } isMenuOpened = false }, ) DropdownMenuItem( text = { Text(stringResource(id = R.string.menu_title_add_to_queue)) }, onClick = { - presenter.addToQueue(genre) + viewModel.addToQueue(genre) { result -> + result.onSuccess { genre -> + onAddedToQueue(genre) + } + } isMenuOpened = false }, ) @@ -224,27 +269,35 @@ class GenreListFragment : DropdownMenuItem( text = { Text(stringResource(id = R.string.menu_title_play_next)) }, onClick = { - presenter.playNext(genre) + viewModel.playNext(genre) { result -> + result.onSuccess { genre -> + onAddedToQueue(genre) + } + } isMenuOpened = false }, ) DropdownMenuItem( text = { Text(stringResource(id = R.string.menu_title_exclude)) }, onClick = { - presenter.exclude(genre) + viewModel.exclude(genre) isMenuOpened = false }, ) - val supportsTagEditing = genre.mediaProviders.all { - mediaProvider -> mediaProvider.supportsTagEditing + val supportsTagEditing = genre.mediaProviders.all { mediaProvider -> + mediaProvider.supportsTagEditing } if (supportsTagEditing) { DropdownMenuItem( text = { Text(stringResource(id = R.string.menu_title_edit_tags)) }, onClick = { - presenter.editTags(genre) + viewModel.editTags(genre) { result -> + result.onSuccess { songs -> + showTagEditor(songs) + } + } isMenuOpened = false }, ) @@ -293,48 +346,46 @@ class GenreListFragment : } } - override fun onAddedToQueue(genre: com.simplecityapps.shuttle.model.Genre) { + fun onAddedToQueue(genre: com.simplecityapps.shuttle.model.Genre) { Toast.makeText(context, Phrase.from(requireContext(), R.string.queue_item_added).put("item_name", genre.name).format(), Toast.LENGTH_SHORT).show() } - override fun setLoadingState(state: GenreListContract.LoadingState) { + fun setLoadingState(state: LoadingState) { when (state) { - is GenreListContract.LoadingState.Scanning -> { + is LoadingState.Scanning -> { horizontalLoadingView.setState(HorizontalLoadingView.State.Loading(getString(R.string.library_scan_in_progress))) circularLoadingView.setState(CircularLoadingView.State.None) } - is GenreListContract.LoadingState.Loading -> { + is LoadingState.Loading -> { horizontalLoadingView.setState(HorizontalLoadingView.State.None) circularLoadingView.setState(CircularLoadingView.State.Loading(getString(R.string.loading))) } - is GenreListContract.LoadingState.Empty -> { + is LoadingState.Empty -> { horizontalLoadingView.setState(HorizontalLoadingView.State.None) circularLoadingView.setState(CircularLoadingView.State.Empty(getString(R.string.genre_list_empty))) } - is GenreListContract.LoadingState.None -> { + is LoadingState.None -> { horizontalLoadingView.setState(HorizontalLoadingView.State.None) circularLoadingView.setState(CircularLoadingView.State.None) } } } - override fun setLoadingProgress(progress: Progress?) { + fun setLoadingProgress(progress: Progress?) { progress?.let { horizontalLoadingView.setProgress(progress.asFloat()) } } - override fun showLoadError(error: Error) { + fun showLoadError(error: Error) { Toast.makeText(context, error.userDescription(resources), Toast.LENGTH_LONG).show() } - override fun showTagEditor(songs: List) { + fun showTagEditor(songs: List) { TagEditorAlertDialog.newInstance(songs).show(childFragmentManager) } - // GenreBinder.Listener Implementation - - override fun onGenreSelected( + fun onGenreSelected( genre: com.simplecityapps.shuttle.model.Genre ) { if (findNavController().currentDestination?.id != R.id.genreDetailFragment) { @@ -345,50 +396,6 @@ class GenreListFragment : } } - override fun onOverflowClicked( - view: View, - genre: com.simplecityapps.shuttle.model.Genre - ) { - val popupMenu = PopupMenu(requireContext(), view) - popupMenu.inflate(R.menu.menu_popup) - TagEditorMenuSanitiser.sanitise(popupMenu.menu, genre.mediaProviders) - - playlistMenuView.createPlaylistMenu(popupMenu.menu) - - popupMenu.setOnMenuItemClickListener { menuItem -> - if (playlistMenuView.handleMenuItem(menuItem, PlaylistData.Genres(genre))) { - return@setOnMenuItemClickListener true - } else { - when (menuItem.itemId) { - R.id.play -> { - presenter.play(genre) - return@setOnMenuItemClickListener true - } - R.id.queue -> { - presenter.addToQueue(genre) - return@setOnMenuItemClickListener true - } - R.id.playNext -> { - presenter.playNext(genre) - return@setOnMenuItemClickListener true - } - R.id.exclude -> { - showExcludeDialog(requireContext(), genre.name) { - presenter.exclude(genre) - } - return@setOnMenuItemClickListener true - } - R.id.editTags -> { - presenter.editTags(genre) - return@setOnMenuItemClickListener true - } - } - } - false - } - popupMenu.show() - } - // CreatePlaylistDialogFragment.Listener Implementation override fun onSave( @@ -407,4 +414,11 @@ class GenreListFragment : fun newInstance() = GenreListFragment() } + + sealed class LoadingState { + object Scanning : LoadingState() + object Loading : LoadingState() + object Empty : LoadingState() + object None : LoadingState() + } } diff --git a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListPresenter.kt b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListPresenter.kt deleted file mode 100644 index 988c22243..000000000 --- a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListPresenter.kt +++ /dev/null @@ -1,188 +0,0 @@ -package com.simplecityapps.shuttle.ui.screens.library.genres - -import com.simplecityapps.mediaprovider.MediaImporter -import com.simplecityapps.mediaprovider.Progress -import com.simplecityapps.mediaprovider.repository.genres.GenreQuery -import com.simplecityapps.mediaprovider.repository.genres.GenreRepository -import com.simplecityapps.mediaprovider.repository.songs.SongRepository -import com.simplecityapps.playback.PlaybackManager -import com.simplecityapps.playback.queue.QueueManager -import com.simplecityapps.shuttle.model.Genre -import com.simplecityapps.shuttle.model.MediaProviderType -import com.simplecityapps.shuttle.query.SongQuery -import com.simplecityapps.shuttle.ui.common.mvp.BasePresenter -import javax.inject.Inject -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.launch - -class GenreListContract { - sealed class LoadingState { - object Scanning : LoadingState() - - object Loading : LoadingState() - - object Empty : LoadingState() - - object None : LoadingState() - } - - interface View { - fun setGenres( - genres: List, - resetPosition: Boolean - ) - - fun onAddedToQueue(genre: Genre) - - fun setLoadingState(state: LoadingState) - - fun setLoadingProgress(progress: Progress?) - - fun showLoadError(error: Error) - - fun showTagEditor(songs: List) - } - - interface Presenter { - fun loadGenres(resetPosition: Boolean) - - fun addToQueue(genre: Genre) - - fun playNext(genre: Genre) - - fun exclude(genre: Genre) - - fun editTags(genre: Genre) - - fun play(genre: Genre) - - fun getFastscrollPrefix(genre: Genre): String? - } -} - -class GenreListPresenter -@Inject -constructor( - private val genreRepository: GenreRepository, - private val songRepository: SongRepository, - private val playbackManager: PlaybackManager, - private val mediaImporter: MediaImporter, - private val queueManager: QueueManager -) : GenreListContract.Presenter, - BasePresenter() { - private var genres: List? = null - - private val mediaImporterListener = - object : MediaImporter.Listener { - override fun onSongImportProgress( - providerType: MediaProviderType, - message: String, - progress: Progress? - ) { - view?.setLoadingProgress(progress) - } - } - - override fun unbindView() { - super.unbindView() - - mediaImporter.listeners.remove(mediaImporterListener) - } - - override fun loadGenres(resetPosition: Boolean) { - if (genres == null) { - if (mediaImporter.isImporting) { - view?.setLoadingState(GenreListContract.LoadingState.Scanning) - } else { - view?.setLoadingState(GenreListContract.LoadingState.Loading) - } - } - launch { - genreRepository.getGenres(GenreQuery.All()) - .distinctUntilChanged() - .flowOn(Dispatchers.IO) - .collect { genres -> - if (genres.isEmpty()) { - if (mediaImporter.isImporting) { - mediaImporter.listeners.add(mediaImporterListener) - view?.setLoadingState(GenreListContract.LoadingState.Scanning) - } else { - mediaImporter.listeners.remove(mediaImporterListener) - view?.setLoadingState(GenreListContract.LoadingState.Empty) - } - } else { - mediaImporter.listeners.remove(mediaImporterListener) - view?.setLoadingState(GenreListContract.LoadingState.None) - } - this@GenreListPresenter.genres = genres - view?.setGenres(genres, resetPosition) - } - } - } - - override fun addToQueue(genre: Genre) { - launch { - val songs = - genreRepository.getSongsForGenre(genre.name, SongQuery.All()) - .firstOrNull() - .orEmpty() - playbackManager.addToQueue(songs) - view?.onAddedToQueue(genre) - } - } - - override fun playNext(genre: Genre) { - launch { - val songs = - genreRepository.getSongsForGenre(genre.name, SongQuery.All()) - .firstOrNull() - .orEmpty() - playbackManager.playNext(songs) - view?.onAddedToQueue(genre) - } - } - - override fun exclude(genre: Genre) { - launch { - val songs = - genreRepository.getSongsForGenre(genre.name, SongQuery.All()) - .firstOrNull() - .orEmpty() - songRepository.setExcluded(songs, true) - queueManager.remove(queueManager.getQueue().filter { queueItem -> songs.contains(queueItem.song) }) - } - } - - override fun editTags(genre: Genre) { - launch { - val songs = - genreRepository.getSongsForGenre(genre.name, SongQuery.All()) - .firstOrNull() - .orEmpty() - view?.showTagEditor(songs) - } - } - - override fun play(genre: Genre) { - launch { - val songs = - genreRepository.getSongsForGenre(genre.name, SongQuery.All()) - .firstOrNull() - .orEmpty() - if (queueManager.setQueue(songs)) { - playbackManager.load { result -> - result.onSuccess { playbackManager.play() } - result.onFailure { error -> view?.showLoadError(error as Error) } - } - } - } - } - - override fun getFastscrollPrefix(genre: Genre): String? { - return genre.name.first().toString() - } -} diff --git a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListViewModel.kt b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListViewModel.kt new file mode 100644 index 000000000..9f25962d9 --- /dev/null +++ b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListViewModel.kt @@ -0,0 +1,115 @@ +package com.simplecityapps.shuttle.ui.screens.library.genres + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.simplecityapps.mediaprovider.MediaImporter +import com.simplecityapps.mediaprovider.repository.genres.GenreQuery +import com.simplecityapps.mediaprovider.repository.genres.GenreRepository +import com.simplecityapps.mediaprovider.repository.songs.SongRepository +import com.simplecityapps.playback.PlaybackManager +import com.simplecityapps.playback.queue.QueueManager +import com.simplecityapps.shuttle.model.Genre +import com.simplecityapps.shuttle.model.Song +import com.simplecityapps.shuttle.query.SongQuery +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class GenreListViewModel @Inject constructor( + private val genreRepository: GenreRepository, + private val songRepository: SongRepository, + private val playbackManager: PlaybackManager, + private val mediaImporter: MediaImporter, + private val queueManager: QueueManager, +) : ViewModel() { + private val _viewState = MutableStateFlow(ViewState.Loading) + val viewState = _viewState.asStateFlow() + + init { + genreRepository.getGenres(GenreQuery.All()) + .onStart { + if (isImportingMedia()) { + _viewState.emit(ViewState.Scanning) + } else { + _viewState.emit(ViewState.Loading) + } + } + .onEach { genres -> + _viewState.emit(ViewState.Ready(genres)) + } + .launchIn(viewModelScope) + } + + fun play(genre: Genre, completion: (Result) -> Unit) { + viewModelScope.launch { + val songs = getSongsForGenreOrEmpty(genre) + if (queueManager.setQueue(songs)) { + playbackManager.load { result -> + result.onSuccess { playbackManager.play() } + completion(result) + } + } + } + } + + fun addToQueue(genre: Genre, completion: (Result) -> Unit) { + viewModelScope.launch { + val songs = getSongsForGenreOrEmpty(genre) + playbackManager.addToQueue(songs) + completion(Result.success(genre)) + } + } + + fun playNext(genre: Genre, completion: (Result) -> Unit) { + viewModelScope.launch { + val songs = getSongsForGenreOrEmpty(genre) + playbackManager.playNext(songs) + completion(Result.success(genre)) + } + } + + fun exclude(genre: Genre) { + viewModelScope.launch { + val songs = getSongsForGenreOrEmpty(genre) + songRepository.setExcluded(songs, true) + queueManager.remove( + queueManager + .getQueue() + .filter { queueItem -> songs.contains(queueItem.song) } + ) + } + } + + fun editTags(genre: Genre, completion: (Result>) -> Unit) { + viewModelScope.launch { + val songs = getSongsForGenreOrEmpty(genre) + completion(Result.success(songs)) + } + } + + private suspend fun getSongsForGenreOrEmpty(genre: Genre) = + genreRepository.getSongsForGenre(genre.name, SongQuery.All()) + .firstOrNull() + .orEmpty() + + fun isImportingMedia() = mediaImporter.isImporting + + fun addMediaImporterListener(listener: MediaImporter.Listener) = + mediaImporter.listeners.add(listener) + + fun removeMediaImporterListener(listener: MediaImporter.Listener) = + mediaImporter.listeners.remove(listener) + + sealed class ViewState { + data object Scanning : ViewState() + data object Loading : ViewState() + data class Ready(val genres: List) : ViewState() + } +} From 657e1d443b6ede328486ee23f0d4ae29beb8b7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Tue, 6 Feb 2024 11:13:14 +0100 Subject: [PATCH 5/7] Clean up GenreListFragment --- .../library/genres/GenreListFragment.kt | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt index f570ebcc5..567698576 100644 --- a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt +++ b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt @@ -82,7 +82,7 @@ class GenreListFragment : message: String, progress: Progress?, ) { - this@GenreListFragment.setLoadingProgress(progress) + setLoadingProgress(progress) } } @@ -116,7 +116,7 @@ class GenreListFragment : playlistMenuPresenter.bindView(playlistMenuView) } - fun loadGenres() { + private fun loadGenres() { composeView.setContent { val viewState by viewModel.viewState.collectAsState() @@ -128,25 +128,25 @@ class GenreListFragment : private fun Genres(viewState: GenreListViewModel.ViewState) { when (viewState) { is GenreListViewModel.ViewState.Scanning -> { - this.setLoadingState(LoadingState.Scanning) + setLoadingState(LoadingState.Scanning) } is GenreListViewModel.ViewState.Loading -> { - this.setLoadingState(LoadingState.Loading) + setLoadingState(LoadingState.Loading) } is GenreListViewModel.ViewState.Ready -> { if (viewState.genres.isEmpty()) { if (viewModel.isImportingMedia()) { viewModel.addMediaImporterListener(mediaImporterListener) - this.setLoadingState(LoadingState.Scanning) + setLoadingState(LoadingState.Scanning) } else { viewModel.removeMediaImporterListener(mediaImporterListener) - this.setLoadingState(LoadingState.Empty) + setLoadingState(LoadingState.Empty) } } else { viewModel.removeMediaImporterListener(mediaImporterListener) - this.setLoadingState(LoadingState.None) + setLoadingState(LoadingState.None) } GenreList(genres = viewState.genres) @@ -156,14 +156,9 @@ class GenreListFragment : override fun onResume() { super.onResume() - loadGenres() } - override fun onPause() { - super.onPause() - } - override fun onSaveInstanceState(outState: Bundle) { outState.putParcelable(ARG_RECYCLER_STATE, recyclerViewState) super.onSaveInstanceState(outState) @@ -175,8 +170,6 @@ class GenreListFragment : super.onDestroyView() } - // GenreListContract.View Implementation - @Composable private fun GenreList(genres: List, modifier: Modifier = Modifier) { LazyColumn( @@ -200,7 +193,7 @@ class GenreListFragment : Column( Modifier .weight(1f) - .clickable { this@GenreListFragment.onGenreSelected(genre) }, + .clickable { onGenreSelected(genre) }, ) { Text( text = genre.name, @@ -346,24 +339,27 @@ class GenreListFragment : } } - fun onAddedToQueue(genre: com.simplecityapps.shuttle.model.Genre) { + fun onAddedToQueue(genre: Genre) { Toast.makeText(context, Phrase.from(requireContext(), R.string.queue_item_added).put("item_name", genre.name).format(), Toast.LENGTH_SHORT).show() } - fun setLoadingState(state: LoadingState) { + private fun setLoadingState(state: LoadingState) { when (state) { is LoadingState.Scanning -> { horizontalLoadingView.setState(HorizontalLoadingView.State.Loading(getString(R.string.library_scan_in_progress))) circularLoadingView.setState(CircularLoadingView.State.None) } + is LoadingState.Loading -> { horizontalLoadingView.setState(HorizontalLoadingView.State.None) circularLoadingView.setState(CircularLoadingView.State.Loading(getString(R.string.loading))) } + is LoadingState.Empty -> { horizontalLoadingView.setState(HorizontalLoadingView.State.None) circularLoadingView.setState(CircularLoadingView.State.Empty(getString(R.string.genre_list_empty))) } + is LoadingState.None -> { horizontalLoadingView.setState(HorizontalLoadingView.State.None) circularLoadingView.setState(CircularLoadingView.State.None) @@ -385,9 +381,7 @@ class GenreListFragment : TagEditorAlertDialog.newInstance(songs).show(childFragmentManager) } - fun onGenreSelected( - genre: com.simplecityapps.shuttle.model.Genre - ) { + private fun onGenreSelected(genre: Genre) { if (findNavController().currentDestination?.id != R.id.genreDetailFragment) { findNavController().navigate( R.id.action_libraryFragment_to_genreDetailFragment, From f4dd88ac967518c15e080aa18fdc4584da0b7838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Tue, 6 Feb 2024 11:35:18 +0100 Subject: [PATCH 6/7] Move GenreListFragment.mediaImporterListener to Genres --- .../library/genres/GenreListFragment.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt index 567698576..7f518c3e5 100644 --- a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt +++ b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt @@ -75,17 +75,6 @@ class GenreListFragment : private var recyclerViewState: Parcelable? = null - private val mediaImporterListener = - object : MediaImporter.Listener { - override fun onSongImportProgress( - providerType: MediaProviderType, - message: String, - progress: Progress?, - ) { - setLoadingProgress(progress) - } - } - // Lifecycle override fun onCreateView( @@ -136,6 +125,17 @@ class GenreListFragment : } is GenreListViewModel.ViewState.Ready -> { + val mediaImporterListener = + object : MediaImporter.Listener { + override fun onSongImportProgress( + providerType: MediaProviderType, + message: String, + progress: Progress?, + ) { + setLoadingProgress(progress) + } + } + if (viewState.genres.isEmpty()) { if (viewModel.isImportingMedia()) { viewModel.addMediaImporterListener(mediaImporterListener) From cde72b059e6190688c48c07fa33e663dc177e872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Tue, 6 Feb 2024 11:40:58 +0100 Subject: [PATCH 7/7] Remove unused GenreListFragment.recyclerViewState --- .../ui/screens/library/genres/GenreListFragment.kt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt index 7f518c3e5..c2d062f7a 100644 --- a/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt +++ b/android/app/src/main/java/com/simplecityapps/shuttle/ui/screens/library/genres/GenreListFragment.kt @@ -1,7 +1,6 @@ package com.simplecityapps.shuttle.ui.screens.library.genres import android.os.Bundle -import android.os.Parcelable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -73,8 +72,6 @@ class GenreListFragment : private lateinit var playlistMenuView: PlaylistMenuView - private var recyclerViewState: Parcelable? = null - // Lifecycle override fun onCreateView( @@ -100,8 +97,6 @@ class GenreListFragment : circularLoadingView = view.findViewById(R.id.circularLoadingView) horizontalLoadingView = view.findViewById(R.id.horizontalLoadingView) - savedInstanceState?.getParcelable(ARG_RECYCLER_STATE)?.let { recyclerViewState = it } - playlistMenuPresenter.bindView(playlistMenuView) } @@ -159,11 +154,6 @@ class GenreListFragment : loadGenres() } - override fun onSaveInstanceState(outState: Bundle) { - outState.putParcelable(ARG_RECYCLER_STATE, recyclerViewState) - super.onSaveInstanceState(outState) - } - override fun onDestroyView() { playlistMenuPresenter.unbindView() @@ -404,8 +394,6 @@ class GenreListFragment : companion object { const val TAG = "GenreListFragment" - const val ARG_RECYCLER_STATE = "recycler_state" - fun newInstance() = GenreListFragment() }