ui: split off selection from home
Split off selection functionality from the home fragment to a new selection viewmodel. It's going to be used elsewhere, so may as well.
This commit is contained in:
parent
f1ae870eea
commit
761dab9239
14 changed files with 42 additions and 30 deletions
|
|
@ -48,7 +48,7 @@ I primarily built Auxio for myself, but you can use it too, I guess.
|
||||||
- Customizable behavior
|
- Customizable behavior
|
||||||
- Seamless artist system that unifies album artist and artist tags
|
- Seamless artist system that unifies album artist and artist tags
|
||||||
- Advanced media indexer with support for multiple artists, release types,
|
- Advanced media indexer with support for multiple artists, release types,
|
||||||
precise/original dates, sort tags, and more
|
precise/original dates, sort tags, and more
|
||||||
- SD Card-aware folder management
|
- SD Card-aware folder management
|
||||||
- Reliable playback state persistence
|
- Reliable playback state persistence
|
||||||
- Full ReplayGain support (On MP3, MP4, FLAC, OGG, and OPUS)
|
- Full ReplayGain support (On MP3, MP4, FLAC, OGG, and OPUS)
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ abstract class DetailAdapter<L : DetailAdapter.Listener>(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) =
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) =
|
||||||
throw IllegalStateException()
|
throw NotImplementedError()
|
||||||
|
|
||||||
override fun onBindViewHolder(
|
override fun onBindViewHolder(
|
||||||
holder: RecyclerView.ViewHolder,
|
holder: RecyclerView.ViewHolder,
|
||||||
|
|
|
||||||
|
|
@ -60,10 +60,6 @@ class HomeViewModel(application: Application) :
|
||||||
val genres: StateFlow<List<Genre>>
|
val genres: StateFlow<List<Genre>>
|
||||||
get() = _genres
|
get() = _genres
|
||||||
|
|
||||||
private val _selected = MutableStateFlow(listOf<Music>())
|
|
||||||
val selected: StateFlow<List<Music>>
|
|
||||||
get() = _selected
|
|
||||||
|
|
||||||
var tabs: List<MusicMode> = visibleTabs
|
var tabs: List<MusicMode> = visibleTabs
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
|
@ -88,19 +84,6 @@ class HomeViewModel(application: Application) :
|
||||||
musicStore.addCallback(this)
|
musicStore.addCallback(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Select a music item. */
|
|
||||||
fun select(item: Music) {
|
|
||||||
val items = _selected.value.toMutableList()
|
|
||||||
if (items.remove(item)) {
|
|
||||||
logD("Unselecting item $item")
|
|
||||||
_selected.value = items
|
|
||||||
} else {
|
|
||||||
logD("Selecting item $item")
|
|
||||||
items.add(item)
|
|
||||||
_selected.value = items
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Update the current tab based off of the new ViewPager position. */
|
/** Update the current tab based off of the new ViewPager position. */
|
||||||
fun updateCurrentTab(pos: Int) {
|
fun updateCurrentTab(pos: Int) {
|
||||||
logD("Updating current tab to ${tabs[pos]}")
|
logD("Updating current tab to ${tabs[pos]}")
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ class AlbumListFragment : HomeListFragment<Album>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
collectImmediately(homeModel.albums, homeAdapter::replaceList)
|
collectImmediately(homeModel.albums, homeAdapter::replaceList)
|
||||||
collectImmediately(homeModel.selected, homeAdapter::updateSelection)
|
collectImmediately(selectionModel.selected, homeAdapter::updateSelection)
|
||||||
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::handleParent)
|
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::handleParent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ class ArtistListFragment : HomeListFragment<Artist>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
collectImmediately(homeModel.artists, homeAdapter::replaceList)
|
collectImmediately(homeModel.artists, homeAdapter::replaceList)
|
||||||
collectImmediately(homeModel.selected, homeAdapter::updateSelection)
|
collectImmediately(selectionModel.selected, homeAdapter::updateSelection)
|
||||||
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::handleParent)
|
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::handleParent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ class GenreListFragment : HomeListFragment<Genre>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
collectImmediately(homeModel.genres, homeAdapter::replaceList)
|
collectImmediately(homeModel.genres, homeAdapter::replaceList)
|
||||||
collectImmediately(homeModel.selected, homeAdapter::updateSelection)
|
collectImmediately(selectionModel.selected, homeAdapter::updateSelection)
|
||||||
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::handlePlayback)
|
collectImmediately(playbackModel.parent, playbackModel.isPlaying, ::handlePlayback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,11 @@ package org.oxycblt.auxio.home.list
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||||
import org.oxycblt.auxio.home.HomeViewModel
|
import org.oxycblt.auxio.home.HomeViewModel
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
|
import org.oxycblt.auxio.ui.SelectionViewModel
|
||||||
import org.oxycblt.auxio.ui.fastscroll.FastScrollRecyclerView
|
import org.oxycblt.auxio.ui.fastscroll.FastScrollRecyclerView
|
||||||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
||||||
import org.oxycblt.auxio.ui.recycler.Item
|
import org.oxycblt.auxio.ui.recycler.Item
|
||||||
|
|
@ -39,6 +41,7 @@ abstract class HomeListFragment<T : Item> :
|
||||||
FastScrollRecyclerView.PopupProvider,
|
FastScrollRecyclerView.PopupProvider,
|
||||||
FastScrollRecyclerView.OnFastScrollListener {
|
FastScrollRecyclerView.OnFastScrollListener {
|
||||||
protected val homeModel: HomeViewModel by androidActivityViewModels()
|
protected val homeModel: HomeViewModel by androidActivityViewModels()
|
||||||
|
protected val selectionModel: SelectionViewModel by activityViewModels()
|
||||||
|
|
||||||
override fun onCreateBinding(inflater: LayoutInflater) =
|
override fun onCreateBinding(inflater: LayoutInflater) =
|
||||||
FragmentHomeListBinding.inflate(inflater)
|
FragmentHomeListBinding.inflate(inflater)
|
||||||
|
|
@ -69,7 +72,7 @@ abstract class HomeListFragment<T : Item> :
|
||||||
|
|
||||||
override fun onItemClick(item: Item) {
|
override fun onItemClick(item: Item) {
|
||||||
check(item is Music) { "Unexpected datatype: ${item::class.java}" }
|
check(item is Music) { "Unexpected datatype: ${item::class.java}" }
|
||||||
if (homeModel.selected.value.isEmpty()) {
|
if (selectionModel.selected.value.isEmpty()) {
|
||||||
onItemClick(item)
|
onItemClick(item)
|
||||||
} else {
|
} else {
|
||||||
onSelect(item)
|
onSelect(item)
|
||||||
|
|
@ -78,6 +81,6 @@ abstract class HomeListFragment<T : Item> :
|
||||||
|
|
||||||
override fun onSelect(item: Item) {
|
override fun onSelect(item: Item) {
|
||||||
check(item is Music) { "Unexpected datatype: ${item::class.java}" }
|
check(item is Music) { "Unexpected datatype: ${item::class.java}" }
|
||||||
homeModel.select(item)
|
selectionModel.select(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class SongListFragment : HomeListFragment<Song>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
collectImmediately(homeModel.songs, homeAdapter::replaceList)
|
collectImmediately(homeModel.songs, homeAdapter::replaceList)
|
||||||
collectImmediately(homeModel.selected, homeAdapter::updateSelection)
|
collectImmediately(selectionModel.selected, homeAdapter::updateSelection)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::handlePlayback)
|
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::handlePlayback)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
app/src/main/java/org/oxycblt/auxio/ui/SelectionViewModel.kt
Normal file
26
app/src/main/java/org/oxycblt/auxio/ui/SelectionViewModel.kt
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.oxycblt.auxio.ui
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import org.oxycblt.auxio.music.Music
|
||||||
|
import org.oxycblt.auxio.util.logD
|
||||||
|
|
||||||
|
class SelectionViewModel : ViewModel() {
|
||||||
|
private val _selected = MutableStateFlow(listOf<Music>())
|
||||||
|
val selected: StateFlow<List<Music>>
|
||||||
|
get() = _selected
|
||||||
|
|
||||||
|
/** Select a music item. */
|
||||||
|
fun select(item: Music) {
|
||||||
|
val items = _selected.value.toMutableList()
|
||||||
|
if (items.remove(item)) {
|
||||||
|
logD("Unselecting item $item")
|
||||||
|
_selected.value = items
|
||||||
|
} else {
|
||||||
|
logD("Selecting item $item")
|
||||||
|
items.add(item)
|
||||||
|
_selected.value = items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
style="@style/Widget.Auxio.Button.Secondary"
|
style="@style/Widget.Auxio.Button.Secondary"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
android:layout_marginTop="@dimen/spacing_mid_medium"
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
android:layout_marginEnd="@dimen/spacing_small"
|
||||||
android:text="@string/lbl_play"
|
android:text="@string/lbl_play"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/detail_shuffle_button"
|
app:layout_constraintEnd_toStartOf="@+id/detail_shuffle_button"
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
style="@style/Widget.Auxio.Button.Secondary"
|
style="@style/Widget.Auxio.Button.Secondary"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
android:layout_marginTop="@dimen/spacing_mid_medium"
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
android:layout_marginEnd="@dimen/spacing_small"
|
||||||
android:text="@string/lbl_play"
|
android:text="@string/lbl_play"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/detail_shuffle_button"
|
app:layout_constraintEnd_toStartOf="@+id/detail_shuffle_button"
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
android:layout_marginStart="@dimen/spacing_medium"
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
android:layout_marginTop="@dimen/spacing_mid_medium"
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
android:layout_marginEnd="@dimen/spacing_small"
|
||||||
android:text="@string/lbl_play"
|
android:text="@string/lbl_play"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/detail_cover"
|
app:layout_constraintBottom_toBottomOf="@+id/detail_cover"
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/spacing_medium"
|
android:layout_marginStart="@dimen/spacing_medium"
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
android:layout_marginTop="@dimen/spacing_mid_medium"
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
android:layout_marginEnd="@dimen/spacing_small"
|
||||||
android:text="@string/lbl_play"
|
android:text="@string/lbl_play"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/detail_cover"
|
app:layout_constraintBottom_toBottomOf="@+id/detail_cover"
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
style="@style/Widget.Auxio.Button.Secondary"
|
style="@style/Widget.Auxio.Button.Secondary"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/spacing_medium"
|
android:layout_marginTop="@dimen/spacing_mid_medium"
|
||||||
android:layout_marginEnd="@dimen/spacing_small"
|
android:layout_marginEnd="@dimen/spacing_small"
|
||||||
android:text="@string/lbl_play"
|
android:text="@string/lbl_play"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/detail_shuffle_button"
|
app:layout_constraintEnd_toStartOf="@+id/detail_shuffle_button"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue