home: reflect no music state in tabs

This commit is contained in:
Alexander Capehart 2024-11-26 14:52:37 -07:00
parent 4618996fc5
commit 38ed432555
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
11 changed files with 165 additions and 25 deletions

View file

@ -175,6 +175,7 @@ class HomeFragment :
// --- VIEWMODEL SETUP ---
collect(homeModel.recreateTabs.flow, ::handleRecreate)
collect(homeModel.chooseMusicLocations.flow, ::handleChooseFolders)
collectImmediately(homeModel.currentTabType, ::updateCurrentTab)
collect(detailModel.toShow.flow, ::handleShow)
collect(listModel.menu.flow, ::handleMenu)
@ -301,6 +302,16 @@ class HomeFragment :
homeModel.recreateTabs.consume()
}
private fun handleChooseFolders(unit: Unit?) {
if (unit == null) {
return
}
findNavController().navigateSafe(
HomeFragmentDirections.chooseLocations()
)
homeModel.chooseMusicLocations.consume()
}
private fun updateIndexerState(state: IndexingState?) {
// TODO: Make music loading experience a bit more pleasant
// 1. Loading placeholder for item lists

View file

@ -159,6 +159,10 @@ constructor(
val showOuter: Event<Outer>
get() = _showOuter
private val _chooseMusicLocations = MutableEvent<Unit>()
val chooseMusicLocations: Event<Unit>
get() = _chooseMusicLocations
init {
homeGenerator.attach()
}
@ -263,6 +267,10 @@ constructor(
_isFastScrolling.value = isFastScrolling
}
fun startChooseMusicLocations() {
_chooseMusicLocations.put(Unit)
}
fun showSettings() {
_showOuter.put(Outer.Settings)
}

View file

@ -22,6 +22,7 @@ import android.os.Bundle
import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isInvisible
import androidx.fragment.app.activityViewModels
import dagger.hilt.android.AndroidEntryPoint
import java.util.Formatter
@ -37,6 +38,7 @@ import org.oxycblt.auxio.list.recycler.AlbumViewHolder
import org.oxycblt.auxio.list.recycler.FastScrollRecyclerView
import org.oxycblt.auxio.list.sort.Sort
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.IndexingState
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel
@ -79,7 +81,13 @@ class AlbumListFragment :
listener = this@AlbumListFragment
}
collectImmediately(homeModel.albumList, ::updateAlbums)
binding.homeNoMusicMsg.text = getString(R.string.lng_no_albums)
binding.homeChooseMusicSources.setOnClickListener {
homeModel.startChooseMusicLocations()
}
collectImmediately(homeModel.albumList, musicModel.indexingState, ::updateAlbums)
collectImmediately(listModel.selected, ::updateSelection)
collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
@ -143,7 +151,11 @@ class AlbumListFragment :
listModel.openMenu(R.menu.album, item)
}
private fun updateAlbums(albums: List<Album>) {
private fun updateAlbums(albums: List<Album>, indexingState: IndexingState?) {
requireBinding().apply {
homeRecycler.isInvisible = indexingState is IndexingState.Indexing || albums.isEmpty()
homeNoMusic.isInvisible = albums.isEmpty()
}
albumAdapter.update(albums, homeModel.albumInstructions.consume())
}

View file

@ -21,6 +21,7 @@ package org.oxycblt.auxio.home.list
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isInvisible
import androidx.fragment.app.activityViewModels
import dagger.hilt.android.AndroidEntryPoint
import org.oxycblt.auxio.R
@ -35,6 +36,7 @@ import org.oxycblt.auxio.list.recycler.ArtistViewHolder
import org.oxycblt.auxio.list.recycler.FastScrollRecyclerView
import org.oxycblt.auxio.list.sort.Sort
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.IndexingState
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel
@ -74,7 +76,13 @@ class ArtistListFragment :
listener = this@ArtistListFragment
}
collectImmediately(homeModel.artistList, ::updateArtists)
binding.homeNoMusicMsg.text = getString(R.string.lng_no_artists)
binding.homeChooseMusicSources.setOnClickListener {
homeModel.startChooseMusicLocations()
}
collectImmediately(homeModel.artistList, musicModel.indexingState, ::updateArtists)
collectImmediately(listModel.selected, ::updateSelection)
collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
@ -119,7 +127,11 @@ class ArtistListFragment :
listModel.openMenu(R.menu.parent, item)
}
private fun updateArtists(artists: List<Artist>) {
private fun updateArtists(artists: List<Artist>, indexingState: IndexingState?) {
requireBinding().apply {
homeRecycler.isInvisible = indexingState is IndexingState.Indexing || artists.isEmpty()
homeNoMusic.isInvisible = artists.isEmpty()
}
artistAdapter.update(artists, homeModel.artistInstructions.consume())
}

View file

@ -21,6 +21,7 @@ package org.oxycblt.auxio.home.list
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isInvisible
import androidx.fragment.app.activityViewModels
import dagger.hilt.android.AndroidEntryPoint
import org.oxycblt.auxio.R
@ -35,6 +36,7 @@ import org.oxycblt.auxio.list.recycler.FastScrollRecyclerView
import org.oxycblt.auxio.list.recycler.GenreViewHolder
import org.oxycblt.auxio.list.sort.Sort
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.IndexingState
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel
@ -73,7 +75,13 @@ class GenreListFragment :
listener = this@GenreListFragment
}
collectImmediately(homeModel.genreList, ::updateGenres)
binding.homeNoMusicMsg.text = getString(R.string.lng_no_genres)
binding.homeChooseMusicSources.setOnClickListener {
homeModel.startChooseMusicLocations()
}
collectImmediately(homeModel.genreList, musicModel.indexingState, ::updateGenres)
collectImmediately(listModel.selected, ::updateSelection)
collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
@ -118,7 +126,11 @@ class GenreListFragment :
listModel.openMenu(R.menu.parent, item)
}
private fun updateGenres(genres: List<Genre>) {
private fun updateGenres(genres: List<Genre>, indexingState: IndexingState?) {
requireBinding().apply {
homeRecycler.isInvisible = indexingState is IndexingState.Indexing || genres.isEmpty()
homeNoMusic.isInvisible = genres.isEmpty()
}
genreAdapter.update(genres, homeModel.genreInstructions.consume())
}

View file

@ -21,6 +21,7 @@ package org.oxycblt.auxio.home.list
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isInvisible
import androidx.fragment.app.activityViewModels
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
@ -33,6 +34,7 @@ import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.recycler.FastScrollRecyclerView
import org.oxycblt.auxio.list.recycler.PlaylistViewHolder
import org.oxycblt.auxio.list.sort.Sort
import org.oxycblt.auxio.music.IndexingState
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel
@ -71,7 +73,13 @@ class PlaylistListFragment :
listener = this@PlaylistListFragment
}
collectImmediately(homeModel.playlistList, ::updatePlaylists)
binding.homeNoMusicMsg.text = getString(R.string.lng_no_playlists)
binding.homeChooseMusicSources.setOnClickListener {
homeModel.startChooseMusicLocations()
}
collectImmediately(homeModel.playlistList, musicModel.indexingState, ::updatePlaylists)
collectImmediately(listModel.selected, ::updateSelection)
collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
@ -116,7 +124,11 @@ class PlaylistListFragment :
listModel.openMenu(R.menu.playlist, item)
}
private fun updatePlaylists(playlists: List<Playlist>) {
private fun updatePlaylists(playlists: List<Playlist>, indexingState: IndexingState?) {
requireBinding().apply {
homeRecycler.isInvisible = indexingState is IndexingState.Indexing || playlists.isEmpty()
homeNoMusic.isInvisible = playlists.isEmpty()
}
playlistAdapter.update(playlists, homeModel.playlistInstructions.consume())
}

View file

@ -15,13 +15,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.home.list
import android.os.Bundle
import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isInvisible
import androidx.fragment.app.activityViewModels
import dagger.hilt.android.AndroidEntryPoint
import java.util.Formatter
@ -35,6 +36,7 @@ import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.recycler.FastScrollRecyclerView
import org.oxycblt.auxio.list.recycler.SongViewHolder
import org.oxycblt.auxio.list.sort.Sort
import org.oxycblt.auxio.music.IndexingState
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel
@ -59,6 +61,7 @@ class SongListFragment :
override val musicModel: MusicViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
private val songAdapter = SongAdapter(this)
// Save memory by re-using the same formatter and string builder when creating popup text
private val formatterSb = StringBuilder(64)
private val formatter = Formatter(formatterSb)
@ -76,10 +79,17 @@ class SongListFragment :
listener = this@SongListFragment
}
collectImmediately(homeModel.songList, ::updateSongs)
binding.homeNoMusicMsg.text = getString(R.string.lng_no_songs)
binding.homeChooseMusicSources.setOnClickListener {
homeModel.startChooseMusicLocations()
}
collectImmediately(homeModel.songList, musicModel.indexingState, ::updateSongs)
collectImmediately(listModel.selected, ::updateSelection)
collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback
)
}
override fun onDestroyBinding(binding: FragmentHomeListBinding) {
@ -117,11 +127,12 @@ class SongListFragment :
val dateAddedMillis = song.dateAdded.secsToMs()
formatterSb.setLength(0)
DateUtils.formatDateRange(
context,
formatter,
dateAddedMillis,
dateAddedMillis,
DateUtils.FORMAT_ABBREV_ALL)
context,
formatter,
dateAddedMillis,
dateAddedMillis,
DateUtils.FORMAT_ABBREV_ALL
)
.toString()
}
@ -142,8 +153,13 @@ class SongListFragment :
listModel.openMenu(R.menu.song, item, homeModel.playWith)
}
private fun updateSongs(songs: List<Song>) {
private fun updateSongs(songs: List<Song>, indexingState: IndexingState?) {
requireBinding().apply {
homeRecycler.isInvisible = indexingState is IndexingState.Indexing || songs.isEmpty()
homeNoMusic.isInvisible = songs.isEmpty()
}
songAdapter.update(songs, homeModel.songInstructions.consume())
}
private fun updateSelection(selection: List<Music>) {

View file

@ -120,9 +120,7 @@ constructor(
// TODO: Differentiate "hard reloads" (Need the cache) and "Soft reloads"
// (just need to manipulate data)
when (key) {
getString(R.string.set_key_exclude_non_music),
getString(R.string.set_key_music_dirs),
getString(R.string.set_key_music_dirs_include),
getString(R.string.set_key_music_locations),
getString(R.string.set_key_separators),
getString(R.string.set_key_auto_sort_names) -> {
L.d("Dispatching indexing setting change for $key")

View file

@ -1,8 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<org.oxycblt.auxio.list.recycler.FastScrollRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/home_recycler"
style="@style/Widget.Auxio.RecyclerView.Grid.WithAdaptiveFab"
android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_song" />
android:layout_height="match_parent">
<org.oxycblt.auxio.list.recycler.FastScrollRecyclerView
android:id="@+id/home_recycler"
style="@style/Widget.Auxio.RecyclerView.Grid.WithAdaptiveFab"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_song" />
<LinearLayout
android:id="@+id/home_no_music"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_horizontal"
android:visibility="invisible"
android:orientation="vertical"
android:padding="@dimen/spacing_medium">
<ImageView
android:id="@+id/home_no_music_placeholder"
android:layout_width="@dimen/size_icon_huge"
android:layout_height="@dimen/size_icon_huge"
android:layout_marginBottom="@dimen/spacing_small"
android:src="@drawable/ic_song_24"
app:tint="?attr/colorOnSurface" />
<TextView
android:id="@+id/home_no_music_msg"
android:layout_width="256dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_small"
tools:text="@string/lng_no_songs"
android:textAlignment="center"
android:textAppearance="?attr/textAppearanceBodyLarge" />
<org.oxycblt.auxio.ui.RippleFixMaterialButton
android:id="@+id/home_choose_music_sources"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lbl_music_sources" />
</LinearLayout>
</FrameLayout>

View file

@ -87,6 +87,9 @@
<action
android:id="@+id/report_error"
app:destination="@id/error_details_dialog" />
<action
android:id="@+id/choose_locations"
app:destination="@id/music_dirs_dialog" />
</fragment>
<dialog
@ -564,4 +567,10 @@
android:name="parcel"
app:argType="org.oxycblt.auxio.list.menu.Menu$ForSelection$Parcel" />
</dialog>
<dialog
android:id="@+id/music_dirs_dialog"
android:name="org.oxycblt.auxio.music.dirs.MusicDirsDialog"
android:label="music_dirs_dialog"
tools:layout="@layout/dialog_music_dirs" />
</navigation>

View file

@ -16,6 +16,7 @@
<string name="lbl_observing">Monitoring music library</string>
<!-- As in to retry loading music -->
<string name="lbl_retry">Retry</string>
<string name="lbl_music_sources">Pick folders</string>
<!-- As in to show additional information about a music loading error -->
<string name="lbl_show_error_info">More</string>
<!-- As in grant permission -->
@ -214,6 +215,11 @@
Starts Auxio using the previously saved state. If no saved state is available, all songs will be shuffled. Playback will start immediately.
\n\nWARNING: Be careful controlling this service, if you close it and then try to use it again, you will probably crash the app.
</string>
<string name="lng_no_songs">Your songs will show up here once you add some music.</string>
<string name="lng_no_albums">Your albums will show up here once you add some music.</string>
<string name="lng_no_artists">Your artists will show up here once you add some music.</string>
<string name="lng_no_genres">Your genres will show up here once you add some music.</string>
<string name="lng_no_playlists">Your playlists will show up here once you add some music.</string>
<!-- Settings namespace | Settings-related labels -->
<eat-comment />