home: add play/shuffle to song list
Re-add the play/shuffle options to the song list, now as a header. This seems to be the best option UX-wise, but the implementation is really I think the best option regarding that is to extend this idiom to all lists or split these fragments up. Both are reasonable.
This commit is contained in:
parent
3ab425839c
commit
b3156941d4
11 changed files with 242 additions and 75 deletions
|
@ -164,15 +164,11 @@ class HomeFragment : Fragment() {
|
||||||
binding.homeAppbar.liftOnScrollTargetViewId = when (requireNotNull(tab)) {
|
binding.homeAppbar.liftOnScrollTargetViewId = when (requireNotNull(tab)) {
|
||||||
DisplayMode.SHOW_SONGS -> {
|
DisplayMode.SHOW_SONGS -> {
|
||||||
updateSortMenu(sortItem, tab)
|
updateSortMenu(sortItem, tab)
|
||||||
|
|
||||||
R.id.home_song_list
|
R.id.home_song_list
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayMode.SHOW_ALBUMS -> {
|
DisplayMode.SHOW_ALBUMS -> {
|
||||||
updateSortMenu(sortItem, tab) { id ->
|
updateSortMenu(sortItem, tab) { id -> id != R.id.option_sort_album }
|
||||||
id != R.id.option_sort_album
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.home_album_list
|
R.id.home_album_list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,10 +30,14 @@ import androidx.navigation.fragment.findNavController
|
||||||
import org.oxycblt.auxio.BuildConfig
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||||
|
import org.oxycblt.auxio.home.recycler.HomeAdapter
|
||||||
|
import org.oxycblt.auxio.home.recycler.ParentAdapter
|
||||||
|
import org.oxycblt.auxio.home.recycler.SongsAdapter
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
|
import org.oxycblt.auxio.music.Parent
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.DisplayMode
|
import org.oxycblt.auxio.ui.DisplayMode
|
||||||
|
@ -56,53 +60,34 @@ class HomeListFragment : Fragment() {
|
||||||
): View {
|
): View {
|
||||||
val binding = FragmentHomeListBinding.inflate(inflater)
|
val binding = FragmentHomeListBinding.inflate(inflater)
|
||||||
|
|
||||||
val homeAdapter = HomeAdapter(
|
|
||||||
doOnClick = { item ->
|
|
||||||
when (item) {
|
|
||||||
is Song -> playbackModel.playSong(item)
|
|
||||||
|
|
||||||
is Album -> findNavController().navigate(
|
|
||||||
HomeFragmentDirections.actionShowAlbum(item.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
is Artist -> findNavController().navigate(
|
|
||||||
HomeFragmentDirections.actionShowArtist(item.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
is Genre -> findNavController().navigate(
|
|
||||||
HomeFragmentDirections.actionShowGenre(item.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
::newMenu
|
|
||||||
)
|
|
||||||
|
|
||||||
// Get some tab-specific values before we go ahead. More specifically, the data to use
|
// Get some tab-specific values before we go ahead. More specifically, the data to use
|
||||||
// and the unique ID that HomeFragment's AppBarLayout uses to determine lift state.
|
// and the unique ID that HomeFragment's AppBarLayout uses to determine lift state.
|
||||||
val pos = requireNotNull(arguments).getInt(ARG_POS)
|
val pos = requireNotNull(arguments).getInt(ARG_POS)
|
||||||
|
|
||||||
@IdRes val customId: Int
|
@IdRes val customId: Int
|
||||||
|
val homeAdapter: HomeAdapter<out BaseModel>
|
||||||
val homeData: LiveData<out List<BaseModel>>
|
val homeData: LiveData<out List<BaseModel>>
|
||||||
|
|
||||||
when (homeModel.tabs.value!![pos]) {
|
when (homeModel.tabs.value!![pos]) {
|
||||||
DisplayMode.SHOW_SONGS -> {
|
DisplayMode.SHOW_SONGS -> {
|
||||||
customId = R.id.home_song_list
|
customId = R.id.home_song_list
|
||||||
homeData = homeModel.songs
|
homeData = homeModel.songs
|
||||||
|
homeAdapter = SongsAdapter(::onSongClick, ::newMenu, playbackModel)
|
||||||
}
|
}
|
||||||
DisplayMode.SHOW_ALBUMS -> {
|
DisplayMode.SHOW_ALBUMS -> {
|
||||||
customId = R.id.home_album_list
|
customId = R.id.home_album_list
|
||||||
homeData = homeModel.albums
|
homeData = homeModel.albums
|
||||||
|
homeAdapter = ParentAdapter(::onParentClick, ::newMenu)
|
||||||
}
|
}
|
||||||
DisplayMode.SHOW_ARTISTS -> {
|
DisplayMode.SHOW_ARTISTS -> {
|
||||||
customId = R.id.home_artist_list
|
customId = R.id.home_artist_list
|
||||||
homeData = homeModel.artists
|
homeData = homeModel.artists
|
||||||
|
homeAdapter = ParentAdapter(::onParentClick, ::newMenu)
|
||||||
}
|
}
|
||||||
DisplayMode.SHOW_GENRES -> {
|
DisplayMode.SHOW_GENRES -> {
|
||||||
customId = R.id.home_genre_list
|
customId = R.id.home_genre_list
|
||||||
homeData = homeModel.genres
|
homeData = homeModel.genres
|
||||||
|
homeAdapter = ParentAdapter(::onParentClick, ::newMenu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +114,26 @@ class HomeListFragment : Fragment() {
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onSongClick(song: Song) {
|
||||||
|
playbackModel.playSong(song)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onParentClick(parent: Parent) {
|
||||||
|
when (parent) {
|
||||||
|
is Album -> findNavController().navigate(
|
||||||
|
HomeFragmentDirections.actionShowAlbum(parent.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
is Artist -> findNavController().navigate(
|
||||||
|
HomeFragmentDirections.actionShowArtist(parent.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
is Genre -> findNavController().navigate(
|
||||||
|
HomeFragmentDirections.actionShowGenre(parent.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARG_POS = BuildConfig.APPLICATION_ID + ".key.POS"
|
private const val ARG_POS = BuildConfig.APPLICATION_ID + ".key.POS"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Auxio Project
|
||||||
|
* HomeAdapter.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* 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.recycler
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base class that implements an [updateData] that is required across [SongsAdapter] and [ParentAdapter]
|
||||||
|
*/
|
||||||
|
abstract class HomeAdapter<T : BaseModel> : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
protected var data = listOf<BaseModel>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the data with [newData]. [notifyDataSetChanged] will be called.
|
||||||
|
*/
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun updateData(newData: List<BaseModel>) {
|
||||||
|
data = newData
|
||||||
|
|
||||||
|
// I would use ListAdapter instead of this inefficient invalidate call, but they still
|
||||||
|
// haven't fixed the issue where ListAdapter's calculations will cause wild scrolling
|
||||||
|
// for basically no reason.
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 Auxio Project
|
* Copyright (c) 2021 Auxio Project
|
||||||
* HomeAdapter.kt is part of Auxio.
|
* ParentAdapter.kt is part of Auxio.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,32 +16,26 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.home
|
package org.oxycblt.auxio.home.recycler
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Parent
|
||||||
import org.oxycblt.auxio.ui.AlbumViewHolder
|
import org.oxycblt.auxio.ui.AlbumViewHolder
|
||||||
import org.oxycblt.auxio.ui.ArtistViewHolder
|
import org.oxycblt.auxio.ui.ArtistViewHolder
|
||||||
import org.oxycblt.auxio.ui.GenreViewHolder
|
import org.oxycblt.auxio.ui.GenreViewHolder
|
||||||
import org.oxycblt.auxio.ui.SongViewHolder
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A universal adapter for displaying data in [HomeFragment].
|
* A universal adapter for displaying [Parent] data.
|
||||||
*/
|
*/
|
||||||
class HomeAdapter(
|
class ParentAdapter(
|
||||||
private val doOnClick: (data: BaseModel) -> Unit,
|
private val doOnClick: (data: Parent) -> Unit,
|
||||||
private val doOnLongClick: (view: View, data: BaseModel) -> Unit
|
private val doOnLongClick: (view: View, data: Parent) -> Unit,
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : HomeAdapter<Parent>() {
|
||||||
|
|
||||||
private var data = listOf<BaseModel>()
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = data.size
|
override fun getItemCount(): Int = data.size
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
@ -49,7 +43,6 @@ class HomeAdapter(
|
||||||
is Genre -> GenreViewHolder.ITEM_TYPE
|
is Genre -> GenreViewHolder.ITEM_TYPE
|
||||||
is Artist -> ArtistViewHolder.ITEM_TYPE
|
is Artist -> ArtistViewHolder.ITEM_TYPE
|
||||||
is Album -> AlbumViewHolder.ITEM_TYPE
|
is Album -> AlbumViewHolder.ITEM_TYPE
|
||||||
is Song -> SongViewHolder.ITEM_TYPE
|
|
||||||
else -> error("Unsupported item ${data[position]::class.simpleName}")
|
else -> error("Unsupported item ${data[position]::class.simpleName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,10 +61,6 @@ class HomeAdapter(
|
||||||
parent.context, doOnClick, doOnLongClick
|
parent.context, doOnClick, doOnLongClick
|
||||||
)
|
)
|
||||||
|
|
||||||
SongViewHolder.ITEM_TYPE -> SongViewHolder.from(
|
|
||||||
parent.context, doOnClick, doOnLongClick
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> error("Invalid viewholder item type.")
|
else -> error("Invalid viewholder item type.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,20 +70,6 @@ class HomeAdapter(
|
||||||
is Genre -> (holder as GenreViewHolder).bind(item)
|
is Genre -> (holder as GenreViewHolder).bind(item)
|
||||||
is Artist -> (holder as ArtistViewHolder).bind(item)
|
is Artist -> (holder as ArtistViewHolder).bind(item)
|
||||||
is Album -> (holder as AlbumViewHolder).bind(item)
|
is Album -> (holder as AlbumViewHolder).bind(item)
|
||||||
is Song -> (holder as SongViewHolder).bind(item)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the data with [newData]. [notifyDataSetChanged] will be called.
|
|
||||||
*/
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
|
||||||
fun updateData(newData: List<BaseModel>) {
|
|
||||||
data = newData
|
|
||||||
|
|
||||||
// I would use ListAdapter instead of this inefficient invalidate call, but they still
|
|
||||||
// haven't fixed the issue where ListAdapter's calculations will cause wild scrolling
|
|
||||||
// for basically no reason.
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Auxio Project
|
||||||
|
* HomeAdapter.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* 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.recycler
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import org.oxycblt.auxio.databinding.ItemPlayShuffleBinding
|
||||||
|
import org.oxycblt.auxio.music.Song
|
||||||
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
|
import org.oxycblt.auxio.ui.SongViewHolder
|
||||||
|
import org.oxycblt.auxio.util.inflater
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An adapter for displaying a song list with a special play/shuffle header.
|
||||||
|
* Note that the data for the play/pause icon does not need to be included with the data
|
||||||
|
* you are submitting. It is automatically handled by the adapter.
|
||||||
|
* TODO: Maybe extend play/shuffle to all items?
|
||||||
|
*/
|
||||||
|
class SongsAdapter(
|
||||||
|
private val doOnClick: (data: Song) -> Unit,
|
||||||
|
private val doOnLongClick: (view: View, data: Song) -> Unit,
|
||||||
|
private val playbackModel: PlaybackViewModel
|
||||||
|
) : HomeAdapter<Song>() {
|
||||||
|
override fun getItemCount(): Int = if
|
||||||
|
(data.isEmpty()) 0 // Account for the play/shuffle viewholder
|
||||||
|
else
|
||||||
|
data.size + 1
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return if (position == 0) {
|
||||||
|
PLAY_ITEM_TYPE
|
||||||
|
} else {
|
||||||
|
SongViewHolder.ITEM_TYPE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
return when (viewType) {
|
||||||
|
PLAY_ITEM_TYPE -> PlayViewHolder(
|
||||||
|
ItemPlayShuffleBinding.inflate(parent.context.inflater)
|
||||||
|
)
|
||||||
|
|
||||||
|
SongViewHolder.ITEM_TYPE -> SongViewHolder.from(
|
||||||
|
parent.context, doOnClick, doOnLongClick
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> error("Invalid viewholder item type.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
if (holder is SongViewHolder) {
|
||||||
|
holder.bind(data[position - 1] as Song)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class PlayViewHolder(
|
||||||
|
binding: ItemPlayShuffleBinding
|
||||||
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
init {
|
||||||
|
// Force the layout to *actually* be the screen width.
|
||||||
|
// We can't inherit BaseViewHolder here since this ViewHolder isn't really connected
|
||||||
|
// to an item.
|
||||||
|
binding.root.layoutParams = RecyclerView.LayoutParams(
|
||||||
|
RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.playButton.setOnClickListener {
|
||||||
|
playbackModel.playAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.shuffleButton.setOnClickListener {
|
||||||
|
playbackModel.shuffleAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PLAY_ITEM_TYPE = 0xA00E
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,13 +40,14 @@ import org.oxycblt.auxio.util.logD
|
||||||
*
|
*
|
||||||
* You think that if you wanted to query a song's genre from a media database, you could just
|
* You think that if you wanted to query a song's genre from a media database, you could just
|
||||||
* put "genre" in the query and it would return it, right? But not with MediaStore! No, that's
|
* put "genre" in the query and it would return it, right? But not with MediaStore! No, that's
|
||||||
* to straightfoward for this platform. So instead, you have to query for each genre, query all
|
* to straightfoward for this platform that was dropped on it's head as a baby. So instead, you
|
||||||
* the songs in each genre, and then iterate through those songs to link every song with their
|
* have to query for each genre, query all the songs in each genre, and then iterate through those
|
||||||
* genre. This is not documented anywhere in MediaStore's documentation, and the O(mom im scared)
|
* songs to link every song with their genre. This is not documented anywhere in MediaStore's
|
||||||
* algorithm you have to run to get it working single-handedly DOUBLES Auxio's loading times. At
|
* documentation, and the O(mom im scared) algorithm you have to run to get it working
|
||||||
* no point have the devs considered that this column is absolutely busted, and instead focused on
|
* single-handedly DOUBLES Auxio's loading times. At no point have the devs considered that this
|
||||||
* adding infuriat- I mean nice proprietary extensions to MediaStore for their own Google Play Music,
|
* column is absolutely busted, and instead focused on adding infuriat- I mean nice proprietary
|
||||||
* and we all know how great that worked out!
|
* extensions to MediaStore for their own Google Play Music, and we all know how great that worked
|
||||||
|
* out!
|
||||||
*
|
*
|
||||||
* It's not even ergonomics that makes this API bad. It's base implementation is completely borked
|
* It's not even ergonomics that makes this API bad. It's base implementation is completely borked
|
||||||
* as well. Did you know that MediaStore doesn't accept dates that aren't from ID3v2.3 MP3 files?
|
* as well. Did you know that MediaStore doesn't accept dates that aren't from ID3v2.3 MP3 files?
|
||||||
|
@ -55,21 +56,21 @@ import org.oxycblt.auxio.util.logD
|
||||||
* DATE tag. Once again, this is because internally android uses an ancient in-house metadata
|
* DATE tag. Once again, this is because internally android uses an ancient in-house metadata
|
||||||
* parser to get everything indexed, and so far they have not bothered to modernize this parser
|
* parser to get everything indexed, and so far they have not bothered to modernize this parser
|
||||||
* or even switch it to something more powerful like Taglib, not even in Android 12. ID3v2.4 is
|
* or even switch it to something more powerful like Taglib, not even in Android 12. ID3v2.4 is
|
||||||
* 21 years old. It can drink now. All my what.
|
* 21 years old. It can drink now. All of my what.
|
||||||
*
|
*
|
||||||
* Not to mention all the other infuriating quirks. Album artists can't be accessed from the albums
|
* Not to mention all the other infuriating quirks. Album artists can't be accessed from the albums
|
||||||
* table, so we have to go for the less efficent "make a big query on all the songs lol" method
|
* table, so we have to go for the less efficent "make a big query on all the songs lol" method
|
||||||
* so that songs don't end up fragmented across artists. Pretty much every OEM has added some
|
* so that songs don't end up fragmented across artists. Pretty much every OEM has added some
|
||||||
* extension or quirk to MediaStore that I cannot determine, with some OEMs (COUGHSAMSUNGCOUGH)
|
* extension or quirk to MediaStore that I cannot reproduce, with some OEMs (COUGHSAMSUNGCOUGH)
|
||||||
* crippling the normal tables so that you're railroaded into their ad-infested music app.
|
* crippling the normal tables so that you're railroaded into their ad-infested music app.
|
||||||
* The way I do blacklisting relies on a deprecated method, and the supposedly "modern" method
|
* The way I do blacklisting relies on a deprecated method, and the supposedly "modern" method
|
||||||
* is SLOWER and causes even more problems since I have to manage databases across version
|
* is SLOWER and causes even more problems since I have to manage databases across version
|
||||||
* boundaries. Sometimes music will have a deformed clone that I can't filter out, sometimes
|
* boundaries. Sometimes music will have a deformed clone that I can't filter out, sometimes
|
||||||
* Genre's will just break for no reason, sometimes this plate of spaghetti just completely breaks
|
* Genres will just break for no reason, sometimes this spaghetti parser just completely breaks
|
||||||
* down and is unable to get any metadata. Everything is broken in it's own special unique way and
|
* down and is unable to get any metadata. Everything is broken in it's own special unique way and
|
||||||
* I absolutely hate it.
|
* I absolutely hate it.
|
||||||
*
|
*
|
||||||
* Is there anything we can do about it? No. Google has routinely shut down issues that beg google
|
* Is there anything we can do about it? No. Google has routinely shut down issues that begged google
|
||||||
* to fix glaring issues with MediaStore or to just take the API behind the woodshed and shoot it.
|
* to fix glaring issues with MediaStore or to just take the API behind the woodshed and shoot it.
|
||||||
* Largely because they have zero incentive to improve it, especially for such obscure things
|
* Largely because they have zero incentive to improve it, especially for such obscure things
|
||||||
* as indexing music. As a result, some players like Vanilla and VLC just hack their own pidgin
|
* as indexing music. As a result, some players like Vanilla and VLC just hack their own pidgin
|
||||||
|
@ -84,7 +85,7 @@ import org.oxycblt.auxio.util.logD
|
||||||
* I'm pretty sure nothing is going to happen and MediaStore will continue to be neglected and
|
* I'm pretty sure nothing is going to happen and MediaStore will continue to be neglected and
|
||||||
* probably deprecated eventually for a "new" API that just coincidentally excludes music indexing.
|
* probably deprecated eventually for a "new" API that just coincidentally excludes music indexing.
|
||||||
* Because go **** yourself for wanting to listen to music you own. Be a good consoomer and listen
|
* Because go **** yourself for wanting to listen to music you own. Be a good consoomer and listen
|
||||||
* to your AlgoMix MusikStream™.
|
* to your AlgoPop StreamMix™.
|
||||||
*
|
*
|
||||||
* I hate this platform so much.
|
* I hate this platform so much.
|
||||||
*
|
*
|
||||||
|
|
|
@ -204,6 +204,13 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play all songs
|
||||||
|
*/
|
||||||
|
fun playAll() {
|
||||||
|
playbackManager.playAll()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuffle all songs
|
* Shuffle all songs
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -232,6 +232,18 @@ class PlaybackStateManager private constructor() {
|
||||||
updatePlayback(mQueue[0])
|
updatePlayback(mQueue[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play all songs.
|
||||||
|
*/
|
||||||
|
fun playAll() {
|
||||||
|
mMode = PlaybackMode.ALL_SONGS
|
||||||
|
mQueue = musicStore.songs.toMutableList()
|
||||||
|
mParent = null
|
||||||
|
|
||||||
|
setShuffling(false, keepSong = false)
|
||||||
|
updatePlayback(mQueue[0])
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuffle all songs.
|
* Shuffle all songs.
|
||||||
*/
|
*/
|
||||||
|
|
29
app/src/main/res/layout/item_play_shuffle.xml
Normal file
29
app/src/main/res/layout/item_play_shuffle.xml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="@dimen/spacing_medium"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/play_button"
|
||||||
|
style="@style/Widget.Auxio.Button.Secondary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginEnd="@dimen/spacing_small"
|
||||||
|
android:text="@string/lbl_play" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/shuffle_button"
|
||||||
|
style="@style/Widget.Auxio.Button.Primary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="@dimen/spacing_small"
|
||||||
|
android:text="@string/lbl_shuffle" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
|
@ -56,6 +56,7 @@ To prevent any strange bugs, all integer representations must be unique. A table
|
||||||
0xA00C | GenreSongViewHolder
|
0xA00C | GenreSongViewHolder
|
||||||
|
|
||||||
0xA00D | QueueSongViewHolder
|
0xA00D | QueueSongViewHolder
|
||||||
|
0xA00E | PlayPauseViewHolder
|
||||||
|
|
||||||
0xA0A0 | Auxio notification code
|
0xA0A0 | Auxio notification code
|
||||||
0xA0C0 | Auxio request code
|
0xA0C0 | Auxio request code
|
||||||
|
|
Loading…
Reference in a new issue