Add song playback mode option

Add an option to customize whether a song should play from its artist, its album, or all songs when selected.
This commit is contained in:
OxygenCobalt 2020-12-12 13:50:46 -07:00
parent 859391e69b
commit aaa1ad1a3d
14 changed files with 137 additions and 50 deletions

View file

@ -25,7 +25,7 @@ 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.Song
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.applyColor import org.oxycblt.auxio.ui.applyColor
import org.oxycblt.auxio.ui.resolveAttr import org.oxycblt.auxio.ui.resolveAttr
import org.oxycblt.auxio.ui.setupAlbumActions import org.oxycblt.auxio.ui.setupAlbumActions
@ -197,7 +197,9 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
// If the item is a song [That was selected through search], then update the playback // If the item is a song [That was selected through search], then update the playback
// to that song instead of doing any navigation // to that song instead of doing any navigation
if (baseModel is Song) { if (baseModel is Song) {
playbackModel.playSong(baseModel, PlaybackMode.ALL_SONGS) val settingsManager = SettingsManager.getInstance()
playbackModel.playSong(baseModel, settingsManager.songPlaybackMode)
return return
} }

View file

@ -1,23 +1,16 @@
package org.oxycblt.auxio.library.adapters package org.oxycblt.auxio.library.adapters
import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
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.BaseModel
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.recycler.DiffCallback
import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder
import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder
import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder
import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
import org.oxycblt.auxio.recycler.viewholders.SongViewHolder
/** /**
* A near-identical adapter as [SearchAdapter] but this one isn't a [ListAdapter] * A near-identical adapter as [SearchAdapter] but this one isn't a [ListAdapter]

View file

@ -1,9 +1,7 @@
package org.oxycblt.auxio.library.adapters package org.oxycblt.auxio.library.adapters
import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album

View file

@ -1,5 +1,3 @@
@file:Suppress("unused")
package org.oxycblt.auxio.playback package org.oxycblt.auxio.playback
import android.content.Context import android.content.Context
@ -45,7 +43,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
val userQueue: LiveData<MutableList<Song>> get() = mUserQueue val userQueue: LiveData<MutableList<Song>> get() = mUserQueue
private val mIndex = MutableLiveData(0) private val mIndex = MutableLiveData(0)
val index: LiveData<Int> get() = mIndex // val index: LiveData<Int> get() = mIndex
private val mMode = MutableLiveData(PlaybackMode.ALL_SONGS) private val mMode = MutableLiveData(PlaybackMode.ALL_SONGS)
val mode: LiveData<PlaybackMode> get() = mMode val mode: LiveData<PlaybackMode> get() = mMode

View file

@ -1,5 +1,7 @@
package org.oxycblt.auxio.playback.state package org.oxycblt.auxio.playback.state
import java.lang.IllegalArgumentException
// Enum for instruction how the queue should function. // Enum for instruction how the queue should function.
// ALL SONGS -> Play from all songs // ALL SONGS -> Play from all songs
// IN_ARTIST -> Play from the songs of the artist // IN_ARTIST -> Play from the songs of the artist
@ -32,5 +34,17 @@ enum class PlaybackMode {
else -> null else -> null
} }
} }
fun valueOfOrFallback(value: String?): PlaybackMode {
if (value == null) {
return ALL_SONGS
}
return try {
valueOf(value)
} catch (e: IllegalArgumentException) {
ALL_SONGS
}
}
} }
} }

View file

@ -1,5 +1,3 @@
@file:Suppress("unused")
package org.oxycblt.auxio.settings package org.oxycblt.auxio.settings
import android.os.Bundle import android.os.Bundle
@ -21,6 +19,7 @@ import org.oxycblt.auxio.ui.ACCENTS
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.accent
import org.oxycblt.auxio.ui.getDetailedAccentSummary import org.oxycblt.auxio.ui.getDetailedAccentSummary
@Suppress("UNUSED")
class SettingsListFragment : PreferenceFragmentCompat() { class SettingsListFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View file

@ -3,6 +3,7 @@ package org.oxycblt.auxio.settings
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.recycler.DisplayMode import org.oxycblt.auxio.recycler.DisplayMode
import org.oxycblt.auxio.recycler.SortMode import org.oxycblt.auxio.recycler.SortMode
import org.oxycblt.auxio.ui.ACCENTS import org.oxycblt.auxio.ui.ACCENTS
@ -51,14 +52,12 @@ class SettingsManager private constructor(context: Context) :
get() = sharedPrefs.getBoolean(Keys.KEY_USE_ALT_NOTIFICATION_ACTION, false) get() = sharedPrefs.getBoolean(Keys.KEY_USE_ALT_NOTIFICATION_ACTION, false)
val libraryDisplayMode: DisplayMode val libraryDisplayMode: DisplayMode
get() { get() = DisplayMode.valueOfOrFallback(
return DisplayMode.valueOfOrFallback( sharedPrefs.getString(
sharedPrefs.getString( Keys.KEY_LIBRARY_DISPLAY_MODE,
Keys.KEY_LIBRARY_DISPLAY_MODE, DisplayMode.SHOW_ARTISTS.toString()
DisplayMode.SHOW_ARTISTS.toString()
)
) )
} )
val doAudioFocus: Boolean val doAudioFocus: Boolean
get() = sharedPrefs.getBoolean(Keys.KEY_AUDIO_FOCUS, true) get() = sharedPrefs.getBoolean(Keys.KEY_AUDIO_FOCUS, true)
@ -66,6 +65,14 @@ class SettingsManager private constructor(context: Context) :
val doPlugMgt: Boolean val doPlugMgt: Boolean
get() = sharedPrefs.getBoolean(Keys.KEY_PLUG_MANAGEMENT, true) get() = sharedPrefs.getBoolean(Keys.KEY_PLUG_MANAGEMENT, true)
val songPlaybackMode: PlaybackMode
get() = PlaybackMode.valueOfOrFallback(
sharedPrefs.getString(
Keys.KEY_SONG_PLAYBACK_MODE,
PlaybackMode.ALL_SONGS.toString()
)
)
var librarySortMode: SortMode var librarySortMode: SortMode
get() { get() {
return SortMode.fromInt( return SortMode.fromInt(
@ -153,6 +160,7 @@ class SettingsManager private constructor(context: Context) :
const val KEY_LIBRARY_DISPLAY_MODE = "KEY_LIBRARY_DISPLAY_MODE" const val KEY_LIBRARY_DISPLAY_MODE = "KEY_LIBRARY_DISPLAY_MODE"
const val KEY_AUDIO_FOCUS = "KEY_AUDIO_FOCUS" const val KEY_AUDIO_FOCUS = "KEY_AUDIO_FOCUS"
const val KEY_PLUG_MANAGEMENT = "KEY_PLUG_MGT" const val KEY_PLUG_MANAGEMENT = "KEY_PLUG_MGT"
const val KEY_SONG_PLAYBACK_MODE = "KEY_SONG_PLAY_MODE"
const val KEY_LIBRARY_SORT_MODE = "KEY_LIBRARY_SORT_MODE" const val KEY_LIBRARY_SORT_MODE = "KEY_LIBRARY_SORT_MODE"
} }

View file

@ -16,7 +16,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentSongsBinding import org.oxycblt.auxio.databinding.FragmentSongsBinding
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.setupSongActions import org.oxycblt.auxio.ui.setupSongActions
import kotlin.math.ceil import kotlin.math.ceil
@ -42,10 +42,11 @@ class SongsFragment : Fragment() {
val binding = FragmentSongsBinding.inflate(inflater) val binding = FragmentSongsBinding.inflate(inflater)
val musicStore = MusicStore.getInstance() val musicStore = MusicStore.getInstance()
val settingsManager = SettingsManager.getInstance()
val songAdapter = SongsAdapter( val songAdapter = SongsAdapter(
musicStore.songs, musicStore.songs,
doOnClick = { playbackModel.playSong(it, PlaybackMode.ALL_SONGS) }, doOnClick = { playbackModel.playSong(it, settingsManager.songPlaybackMode) },
doOnLongClick = { data, view -> doOnLongClick = { data, view ->
PopupMenu(requireContext(), view).setupSongActions( PopupMenu(requireContext(), view).setupSongActions(
data, requireContext(), playbackModel data, requireContext(), playbackModel

View file

@ -8,6 +8,7 @@ import android.os.Build
import android.text.SpannableString import android.text.SpannableString
import android.text.Spanned import android.text.Spanned
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.util.Log
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.Window import android.view.Window
@ -26,6 +27,7 @@ import org.oxycblt.auxio.music.Genre
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.playback.state.PlaybackMode import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.settings.SettingsManager
// Functions for managing UI elements [Not Colors] // Functions for managing UI elements [Not Colors]
@ -108,6 +110,8 @@ fun Window.handleTransparentSystemBars(config: Configuration) {
* Show actions for a song item, such as the ones found in [org.oxycblt.auxio.songs.SongsFragment] * Show actions for a song item, such as the ones found in [org.oxycblt.auxio.songs.SongsFragment]
*/ */
fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: PlaybackViewModel) { fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: PlaybackViewModel) {
inflateAndShow(R.menu.menu_song_actions)
setOnMenuItemClickListener { setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.action_queue_add -> { R.id.action_queue_add -> {
@ -126,14 +130,34 @@ fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: Play
true true
} }
R.id.action_play_all_songs -> {
playbackModel.playSong(song, PlaybackMode.ALL_SONGS)
true
}
else -> false else -> false
} }
} }
inflateAndShow(R.menu.menu_song_actions)
val settingsManager = SettingsManager.getInstance()
// Find the action that is redundant from the menu and hide it.
val idToRemove = when (settingsManager.songPlaybackMode) {
PlaybackMode.ALL_SONGS -> R.id.action_play_all_songs
PlaybackMode.IN_ARTIST -> R.id.action_play_artist
PlaybackMode.IN_ALBUM -> R.id.action_play_album
else -> -1
}
Log.d(this::class.simpleName, (idToRemove == R.id.action_play_album).toString())
menu.findItem(idToRemove)?.isVisible = false
} }
/** /**
* Show actions for a song item, such as the ones found in [org.oxycblt.auxio.songs.SongsFragment] * Show actions for a album song item, such as the ones found in
* [org.oxycblt.auxio.detail.AlbumDetailFragment]
*/ */
fun PopupMenu.setupAlbumSongActions( fun PopupMenu.setupAlbumSongActions(
song: Song, song: Song,

View file

@ -4,6 +4,10 @@
android:id="@+id/action_queue_add" android:id="@+id/action_queue_add"
android:title="@string/label_queue_add" android:title="@string/label_queue_add"
android:icon="@drawable/ic_queue_add" /> android:icon="@drawable/ic_queue_add" />
<item
android:id="@+id/action_play_all_songs"
android:title="@string/label_play_all_songs"
android:icon="@drawable/ic_song" />
<item <item
android:id="@+id/action_play_artist" android:id="@+id/action_play_artist"
android:title="@string/label_play_artist" android:title="@string/label_play_artist"

View file

@ -23,4 +23,16 @@
<item>SHOW_ARTISTS</item> <item>SHOW_ARTISTS</item>
<item>SHOW_ALBUMS</item> <item>SHOW_ALBUMS</item>
</array> </array>
<array name="song_playback_mode_entries">
<item>@string/label_play_all_songs</item>
<item>@string/label_play_artist</item>
<item>@string/label_play_album</item>
</array>
<array name="song_playback_mode_values">
<item>ALL_SONGS</item>
<item>IN_ARTIST</item>
<item>IN_ALBUM</item>
</array>
</resources> </resources>

View file

@ -23,8 +23,9 @@
<string name="label_play">Play</string> <string name="label_play">Play</string>
<string name="label_shuffle">Shuffle</string> <string name="label_shuffle">Shuffle</string>
<string name="label_play_artist">Play from artist</string> <string name="label_play_all_songs">Play from all songs</string>
<string name="label_play_album">Play from album</string> <string name="label_play_album">Play from album</string>
<string name="label_play_artist">Play from artist</string>
<string name="label_go_artist">Go to artist</string> <string name="label_go_artist">Go to artist</string>
<string name="label_queue">Queue</string> <string name="label_queue">Queue</string>
@ -38,30 +39,37 @@
<!-- Settings namespace | Settings-related labels --> <!-- Settings namespace | Settings-related labels -->
<string name="setting_title">Settings</string> <string name="setting_title">Settings</string>
<string name="setting_ui">Appearance</string> <string name="setting_ui">Appearance</string>
<string name="setting_theme">Theme</string> <string name="setting_theme">Theme</string>
<string name="setting_theme_auto">Automatic</string> <string name="setting_theme_auto">Automatic</string>
<string name="setting_theme_day">Light</string> <string name="setting_theme_day">Light</string>
<string name="setting_theme_night">Dark</string> <string name="setting_theme_night">Dark</string>
<string name="setting_accent">Accent</string> <string name="setting_accent">Accent</string>
<string name="setting_accent_unknown">Unknown Accent</string> <string name="setting_accent_unknown">Unknown Accent</string>
<string name="setting_edge">Edge-To-Edge</string> <string name="setting_edge">Edge-To-Edge</string>
<string name="setting_edge_desc_on">Edge-to-edge is enabled</string> <string name="setting_edge_desc">Enable edge-to-edge</string>
<string name="setting_edge_desc_off">Edge-to-edge is disabled</string>
<string name="setting_display">Display</string> <string name="setting_display">Display</string>
<string name="setting_lib_display">Library Items</string> <string name="setting_lib_display">Library Items</string>
<string name="setting_color_notif">Colorize notification</string>] <string name="setting_color_notif">Colorize notification</string>]
<string name="setting_color_desc_on">Show album art on notification</string> <string name="setting_color_desc">Show album art on notification</string>
<string name="setting_color_desc_off">Hide album art on notification</string>
<string name="setting_use_alt_action">Use alternate notification action</string> <string name="setting_use_alt_action">Use alternate notification action</string>
<string name="setting_use_alt_loop">Prefer repeat mode action</string> <string name="setting_use_alt_loop">Prefer repeat mode action</string>
<string name="setting_use_alt_shuffle">Prefer shuffle action</string> <string name="setting_use_alt_shuffle">Prefer shuffle action</string>
<string name="setting_playback">Audio</string> <string name="setting_playback">Audio</string>
<string name="setting_playback_audio_focus">Audio focus</string> <string name="setting_playback_audio_focus">Audio focus</string>
<string name="setting_playback_focus_desc_on">Audio focus is enabled</string> <string name="setting_playback_focus_desc">Pause when other audio plays [ex. Alarms]</string>
<string name="setting_playback_focus_desc_off">Audio focus is disabled</string>
<string name="setting_playback_plug_mgt">Headset plug management</string> <string name="setting_playback_plug_mgt">Headset plug management</string>
<string name="setting_playback_plug_mgt_desc_on">Headset plug management is enabled</string> <string name="setting_playback_plug_mgt_desc">Play/Pause when headset connection changes</string>
<string name="setting_playback_plug_mgt_desc_off">Headset plug management is disabled</string>
<string name="setting_behavior">Behavior</string>
<string name="setting_behavior_song_play">When a song is selected</string>
<!-- Debug Namespace | Debug labels --> <!-- Debug Namespace | Debug labels -->
<string name="debug_state_saved">State saved</string> <string name="debug_state_saved">State saved</string>

View file

@ -22,8 +22,7 @@
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:key="KEY_EDGE" app:key="KEY_EDGE"
android:title="@string/setting_edge" android:title="@string/setting_edge"
app:summaryOn="@string/setting_edge_desc_on" app:summary="@string/setting_edge_desc"
app:summaryOff="@string/setting_edge_desc_off"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:allowDividerBelow="false" app:allowDividerBelow="false"
app:defaultValue="false" /> app:defaultValue="false" />
@ -45,8 +44,7 @@
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:key="KEY_COLOR_NOTIF" app:key="KEY_COLOR_NOTIF"
android:title="@string/setting_color_notif" android:title="@string/setting_color_notif"
app:summaryOn="@string/setting_color_desc_on" app:summary="@string/setting_color_desc"
app:summaryOff="@string/setting_color_desc_off"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:defaultValue="true" /> app:defaultValue="true" />
@ -70,15 +68,30 @@
android:title="@string/setting_playback_audio_focus" android:title="@string/setting_playback_audio_focus"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:defaultValue="true" app:defaultValue="true"
app:summaryOn="@string/setting_playback_focus_desc_on" app:summary="@string/setting_playback_focus_desc" />
app:summaryOff="@string/setting_playback_focus_desc_off" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:key="KEY_PLUG_MGT" app:key="KEY_PLUG_MGT"
android:title="@string/setting_playback_plug_mgt" android:title="@string/setting_playback_plug_mgt"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:defaultValue="true" app:defaultValue="true"
app:summaryOn="@string/setting_playback_plug_mgt_desc_on" app:allowDividerBelow="false"
app:summaryOff="@string/setting_playback_plug_mgt_desc_off" /> app:summary="@string/setting_playback_plug_mgt_desc" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/setting_behavior"
android:layout="@layout/item_header">
<ListPreference
app:key="KEY_SONG_PLAY_MODE"
android:title="@string/setting_behavior_song_play"
app:defaultValue="ALL_SONGS"
app:entries="@array/song_playback_mode_entries"
app:entryValues="@array/song_playback_mode_values"
app:iconSpaceReserved="false"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View file

@ -36,8 +36,7 @@
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:key="KEY_COLOR_NOTIF" app:key="KEY_COLOR_NOTIF"
android:title="@string/setting_color_notif" android:title="@string/setting_color_notif"
app:summaryOn="@string/setting_color_desc_on" app:summary="@string/setting_color_desc"
app:summaryOff="@string/setting_color_desc_off"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:defaultValue="true" /> app:defaultValue="true" />
@ -61,15 +60,29 @@
android:title="@string/setting_playback_audio_focus" android:title="@string/setting_playback_audio_focus"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:defaultValue="true" app:defaultValue="true"
app:summaryOn="@string/setting_playback_focus_desc_on" app:summary="@string/setting_playback_focus_desc" />
app:summaryOff="@string/setting_playback_focus_desc_off" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:key="KEY_PLUG_MGT" app:key="KEY_PLUG_MGT"
android:title="@string/setting_playback_plug_mgt" android:title="@string/setting_playback_plug_mgt"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:defaultValue="true" app:defaultValue="true"
app:summaryOn="@string/setting_playback_plug_mgt_desc_on" app:allowDividerBelow="false"
app:summaryOff="@string/setting_playback_plug_mgt_desc_off" /> app:summary="@string/setting_playback_plug_mgt_desc" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/setting_behavior"
android:layout="@layout/item_header">
<ListPreference
app:key="KEY_SONG_PLAY_MODE"
android:title="@string/setting_behavior_song_play"
app:defaultValue="ALL_SONGS"
app:entries="@array/song_playback_mode_entries"
app:entryValues="@array/song_playback_mode_values"
app:iconSpaceReserved="false"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>