diff --git a/CHANGELOG.md b/CHANGELOG.md index 90f9ab232..d9bca37d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,12 @@ ## dev #### What's New -- Added basic equalizer support in external apps like Wavelet +- Added basic equalizer support in external apps like Wavelet [#211] - Detail UI now displays the type of item shown (ex. the release type) +#### What's Improved +- Queue now scrolls to currently playing song instead of the song after it + #### What's Fixed - Fixed incorrect font being used in the queue title - Fixed missing fast scroll indicator with date added scrolling diff --git a/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt b/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt index f43bce062..2ea54a26e 100644 --- a/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt +++ b/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt @@ -116,4 +116,11 @@ object IntegerTable { const val REPLAY_GAIN_MODE_ALBUM = 0xA112 /** ReplayGainMode.Dynamic */ const val REPLAY_GAIN_MODE_DYNAMIC = 0xA113 + + /** BarAction.Next */ + const val BAR_ACTION_NEXT = 0xA119 + /** BarAction.Repeat */ + const val BAR_ACTION_REPEAT = 0xA11A + /** BarAction.Shuffle */ + const val BAR_ACTION_SHUFFLE = 0xA11B } diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt index 778682314..025aefe3f 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt @@ -42,7 +42,7 @@ import org.oxycblt.auxio.util.logEOrThrow */ class AlbumListFragment : HomeListFragment() { private val homeAdapter = AlbumAdapter(this) - private val formatterSb = StringBuilder(50) + private val formatterSb = StringBuilder(32) private val formatter = Formatter(formatterSb) override fun onBindingCreated(binding: FragmentHomeListBinding, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarFragment.kt index 83e51b60c..289bd6c23 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarFragment.kt @@ -20,14 +20,18 @@ package org.oxycblt.auxio.playback import android.os.Bundle import android.view.LayoutInflater import androidx.fragment.app.activityViewModels +import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentPlaybackBarBinding import org.oxycblt.auxio.music.Song +import org.oxycblt.auxio.playback.state.RepeatMode +import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.ui.MainNavigationAction import org.oxycblt.auxio.ui.NavigationViewModel import org.oxycblt.auxio.ui.fragment.ViewBindingFragment import org.oxycblt.auxio.util.androidActivityViewModels import org.oxycblt.auxio.util.collectImmediately +import org.oxycblt.auxio.util.getAttrColorCompat import org.oxycblt.auxio.util.getColorCompat /** @@ -46,6 +50,8 @@ class PlaybackBarFragment : ViewBindingFragment() { binding: FragmentPlaybackBarBinding, savedInstanceState: Bundle? ) { + val context = requireContext() + binding.root.apply { setOnClickListener { navModel.mainNavigateTo(MainNavigationAction.Expand) } @@ -58,11 +64,39 @@ class PlaybackBarFragment : ViewBindingFragment() { // Load the track color in manually as it's unclear whether the track actually supports // using a ColorStateList in the resources binding.playbackProgressBar.trackColor = - requireContext().getColorCompat(R.color.sel_track).defaultColor + context.getColorCompat(R.color.sel_track).defaultColor binding.playbackPlayPause.setOnClickListener { playbackModel.invertPlaying() } - binding.playbackSkipNext.setOnClickListener { playbackModel.next() } + // Update the secondary action to match the setting. + + when (Settings(context).barAction) { + BarAction.NEXT -> { + binding.playbackSecondaryAction.apply { + setIconResource(R.drawable.ic_skip_next_24) + contentDescription = getString(R.string.desc_skip_next) + iconTint = context.getAttrColorCompat(R.attr.colorOnSurfaceVariant) + setOnClickListener { playbackModel.next() } + } + } + BarAction.REPEAT -> { + binding.playbackSecondaryAction.apply { + contentDescription = getString(R.string.desc_change_repeat) + iconTint = context.getColorCompat(R.color.sel_accented) + setOnClickListener { playbackModel.incrementRepeatMode() } + collectImmediately(playbackModel.repeatMode, ::updateRepeat) + } + } + BarAction.SHUFFLE -> { + binding.playbackSecondaryAction.apply { + setIconResource(R.drawable.sel_shuffle_state_24) + contentDescription = getString(R.string.desc_shuffle) + iconTint = context.getColorCompat(R.color.sel_accented) + setOnClickListener { playbackModel.invertShuffled() } + collectImmediately(playbackModel.isShuffled, ::updateShuffled) + } + } + } // -- VIEWMODEL SETUP --- @@ -86,7 +120,36 @@ class PlaybackBarFragment : ViewBindingFragment() { requireBinding().playbackPlayPause.isActivated = isPlaying } + private fun updateRepeat(repeatMode: RepeatMode) { + requireBinding().playbackSecondaryAction.apply { + setIconResource(repeatMode.icon) + isActivated = repeatMode != RepeatMode.NONE + } + } + + private fun updateShuffled(isShuffled: Boolean) { + requireBinding().playbackSecondaryAction.isActivated = isShuffled + } + private fun updatePosition(position: Long) { requireBinding().playbackProgressBar.progress = position.toInt() } } + +/** Represents the action that should be shown on the playback bar. */ +enum class BarAction { + NEXT, + REPEAT, + SHUFFLE; + + companion object { + /** Convert an int [code] into an instance, or null if it isn't valid. */ + fun fromIntCode(code: Int) = + when (code) { + IntegerTable.BAR_ACTION_NEXT -> NEXT + IntegerTable.BAR_ACTION_REPEAT -> REPEAT + IntegerTable.BAR_ACTION_SHUFFLE -> SHUFFLE + else -> null + } + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt index f0860e8cc..a1de515c1 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt @@ -34,7 +34,6 @@ import org.oxycblt.auxio.ui.NavigationViewModel import org.oxycblt.auxio.ui.fragment.ViewBindingFragment import org.oxycblt.auxio.util.androidActivityViewModels import org.oxycblt.auxio.util.collectImmediately -import org.oxycblt.auxio.util.getDrawableCompat import org.oxycblt.auxio.util.systemBarInsetsCompat /** @@ -158,15 +157,8 @@ class PlaybackPanelFragment : } private fun updateRepeat(repeatMode: RepeatMode) { - val iconRes = - when (repeatMode) { - RepeatMode.NONE -> R.drawable.ic_repeat_off_24 - RepeatMode.ALL -> R.drawable.ic_repeat_on_24 - RepeatMode.TRACK -> R.drawable.ic_repeat_one_24 - } - requireBinding().playbackRepeat.apply { - icon = requireContext().getDrawableCompat(iconRes) + setIconResource(repeatMode.icon) isActivated = repeatMode != RepeatMode.NONE } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/RepeatMode.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/RepeatMode.kt index 97709897f..6592ae963 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/RepeatMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/RepeatMode.kt @@ -18,6 +18,7 @@ package org.oxycblt.auxio.playback.state import org.oxycblt.auxio.IntegerTable +import org.oxycblt.auxio.R /** * Enum that determines the playback repeat mode. @@ -36,6 +37,15 @@ enum class RepeatMode { TRACK -> NONE } + /** The icon representing this particular mode. */ + val icon: Int + get() = + when (this) { + NONE -> R.drawable.ic_repeat_off_24 + ALL -> R.drawable.ic_repeat_on_24 + TRACK -> R.drawable.ic_repeat_one_24 + } + /** The integer code representing this particular mode. */ val intCode: Int get() = diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/NotificationComponent.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/NotificationComponent.kt index 39f7a6f2c..d25ac9c0a 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/NotificationComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/NotificationComponent.kt @@ -117,14 +117,7 @@ class NotificationComponent(private val context: Context, sessionToken: MediaSes context: Context, repeatMode: RepeatMode ): NotificationCompat.Action { - val drawableRes = - when (repeatMode) { - RepeatMode.NONE -> R.drawable.ic_repeat_off_24 - RepeatMode.ALL -> R.drawable.ic_repeat_on_24 - RepeatMode.TRACK -> R.drawable.ic_repeat_one_24 - } - - return buildAction(context, PlaybackService.ACTION_INC_REPEAT_MODE, drawableRes) + return buildAction(context, PlaybackService.ACTION_INC_REPEAT_MODE, repeatMode.icon) } private fun buildShuffleAction( diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt index 5f0f79f19..02b9c0d71 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt @@ -203,19 +203,15 @@ class PlaybackService : } override fun onPlaybackStateChanged(state: Int) { - when (state) { - Player.STATE_ENDED -> { - logD("State ended") - if (playbackManager.repeatMode == RepeatMode.TRACK) { - playbackManager.rewind() - if (settings.pauseOnRepeat) { - playbackManager.isPlaying = false - } - } else { - playbackManager.next() + if (state == Player.STATE_ENDED) { + if (playbackManager.repeatMode == RepeatMode.TRACK) { + playbackManager.rewind() + if (settings.pauseOnRepeat) { + playbackManager.isPlaying = false } + } else { + playbackManager.next() } - else -> {} } } @@ -274,7 +270,7 @@ class PlaybackService : player.prepare() if (!openAudioEffectSession) { - // Wavelet does not like it if you start an audio effect session without having + // Android does not like it if you start an audio effect session without having // something within your player buffer. Make sure we only start one when we load // a song. broadcastAudioEffectAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) diff --git a/app/src/main/java/org/oxycblt/auxio/settings/Settings.kt b/app/src/main/java/org/oxycblt/auxio/settings/Settings.kt index 6b1658acf..79f3c23e0 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/Settings.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/Settings.kt @@ -27,6 +27,7 @@ import org.oxycblt.auxio.R import org.oxycblt.auxio.home.tabs.Tab import org.oxycblt.auxio.music.Directory import org.oxycblt.auxio.music.dirs.MusicDirs +import org.oxycblt.auxio.playback.BarAction import org.oxycblt.auxio.playback.replaygain.ReplayGainMode import org.oxycblt.auxio.playback.replaygain.ReplayGainPreAmp import org.oxycblt.auxio.playback.state.PlaybackMode @@ -91,13 +92,6 @@ class Settings(private val context: Context, private val callback: Callback? = n } } - /** - * Whether to display the RepeatMode or the shuffle status on the notification. False if repeat, - * true if shuffle. - */ - val useAltNotifAction: Boolean - get() = inner.getBoolean(context.getString(R.string.set_key_alt_notif_action), false) - /** The current library tabs preferred by the user. */ var libTabs: Array get() = @@ -123,6 +117,19 @@ class Settings(private val context: Context, private val callback: Callback? = n val roundMode: Boolean get() = inner.getBoolean(context.getString(R.string.set_key_round_mode), false) + /** Which action to display on the playback bar. */ + val barAction: BarAction + get() = + BarAction.fromIntCode(inner.getInt(context.getString(R.string.set_key_bar_action), -1)) + ?: BarAction.NEXT + + /** + * Whether to display the RepeatMode or the shuffle status on the notification. False if repeat, + * true if shuffle. + */ + val useAltNotifAction: Boolean + get() = inner.getBoolean(context.getString(R.string.set_key_alt_notif_action), false) + /** Whether to resume playback when a headset is connected (may not work well in all cases) */ val headsetAutoplay: Boolean get() = inner.getBoolean(context.getString(R.string.set_key_headset_autoplay), false) @@ -131,7 +138,7 @@ class Settings(private val context: Context, private val callback: Callback? = n val replayGainMode: ReplayGainMode get() = ReplayGainMode.fromIntCode( - inner.getInt(context.getString(R.string.set_key_replay_gain), Int.MIN_VALUE)) + inner.getInt(context.getString(R.string.set_key_replay_gain), -1)) ?: ReplayGainMode.OFF /** The current ReplayGain pre-amp configuration */ @@ -152,8 +159,7 @@ class Settings(private val context: Context, private val callback: Callback? = n val libPlaybackMode: PlaybackMode get() = PlaybackMode.fromInt( - inner.getInt( - context.getString(R.string.set_key_library_song_playback_mode), Int.MIN_VALUE)) + inner.getInt(context.getString(R.string.set_key_library_song_playback_mode), -1)) ?: PlaybackMode.ALL_SONGS /** @@ -163,8 +169,7 @@ class Settings(private val context: Context, private val callback: Callback? = n val detailPlaybackMode: PlaybackMode? get() = PlaybackMode.fromInt( - inner.getInt( - context.getString(R.string.set_key_detail_song_playback_mode), Int.MIN_VALUE)) + inner.getInt(context.getString(R.string.set_key_detail_song_playback_mode), -1)) /** Whether shuffle should stay on when a new song is selected. */ val keepShuffle: Boolean @@ -214,14 +219,11 @@ class Settings(private val context: Context, private val callback: Callback? = n /** The current filter mode of the search tab */ var searchFilterMode: DisplayMode? get() = - DisplayMode.fromInt( - inner.getInt(context.getString(R.string.set_key_search_filter), Int.MIN_VALUE)) + DisplayMode.fromInt(inner.getInt(context.getString(R.string.set_key_search_filter), -1)) set(value) { logD(value) inner.edit { - putInt( - context.getString(R.string.set_key_search_filter), - value?.intCode ?: Int.MIN_VALUE) + putInt(context.getString(R.string.set_key_search_filter), value?.intCode ?: -1) apply() } } @@ -229,8 +231,7 @@ class Settings(private val context: Context, private val callback: Callback? = n /** The song sort mode on HomeFragment */ var libSongSort: Sort get() = - Sort.fromIntCode( - inner.getInt(context.getString(R.string.set_key_lib_songs_sort), Int.MIN_VALUE)) + Sort.fromIntCode(inner.getInt(context.getString(R.string.set_key_lib_songs_sort), -1)) ?: Sort(Sort.Mode.ByName, true) set(value) { inner.edit { @@ -242,8 +243,7 @@ class Settings(private val context: Context, private val callback: Callback? = n /** The album sort mode on HomeFragment */ var libAlbumSort: Sort get() = - Sort.fromIntCode( - inner.getInt(context.getString(R.string.set_key_lib_albums_sort), Int.MIN_VALUE)) + Sort.fromIntCode(inner.getInt(context.getString(R.string.set_key_lib_albums_sort), -1)) ?: Sort(Sort.Mode.ByName, true) set(value) { inner.edit { @@ -255,8 +255,7 @@ class Settings(private val context: Context, private val callback: Callback? = n /** The artist sort mode on HomeFragment */ var libArtistSort: Sort get() = - Sort.fromIntCode( - inner.getInt(context.getString(R.string.set_key_lib_artists_sort), Int.MIN_VALUE)) + Sort.fromIntCode(inner.getInt(context.getString(R.string.set_key_lib_artists_sort), -1)) ?: Sort(Sort.Mode.ByName, true) set(value) { inner.edit { @@ -268,8 +267,7 @@ class Settings(private val context: Context, private val callback: Callback? = n /** The genre sort mode on HomeFragment */ var libGenreSort: Sort get() = - Sort.fromIntCode( - inner.getInt(context.getString(R.string.set_key_lib_genres_sort), Int.MIN_VALUE)) + Sort.fromIntCode(inner.getInt(context.getString(R.string.set_key_lib_genres_sort), -1)) ?: Sort(Sort.Mode.ByName, true) set(value) { inner.edit { @@ -283,8 +281,7 @@ class Settings(private val context: Context, private val callback: Callback? = n get() { var sort = Sort.fromIntCode( - inner.getInt( - context.getString(R.string.set_key_detail_album_sort), Int.MIN_VALUE)) + inner.getInt(context.getString(R.string.set_key_detail_album_sort), -1)) ?: Sort(Sort.Mode.ByDisc, true) // Correct legacy album sort modes to Disc @@ -305,7 +302,7 @@ class Settings(private val context: Context, private val callback: Callback? = n var detailArtistSort: Sort get() = Sort.fromIntCode( - inner.getInt(context.getString(R.string.set_key_detail_artist_sort), Int.MIN_VALUE)) + inner.getInt(context.getString(R.string.set_key_detail_artist_sort), -1)) ?: Sort(Sort.Mode.ByYear, false) set(value) { inner.edit { @@ -318,7 +315,7 @@ class Settings(private val context: Context, private val callback: Callback? = n var detailGenreSort: Sort get() = Sort.fromIntCode( - inner.getInt(context.getString(R.string.set_key_detail_genre_sort), Int.MIN_VALUE)) + inner.getInt(context.getString(R.string.set_key_detail_genre_sort), -1)) ?: Sort(Sort.Mode.ByName, true) set(value) { inner.edit { diff --git a/app/src/main/res/layout/fragment_playback_bar.xml b/app/src/main/res/layout/fragment_playback_bar.xml index 047531c57..f5a2723eb 100644 --- a/app/src/main/res/layout/fragment_playback_bar.xml +++ b/app/src/main/res/layout/fragment_playback_bar.xml @@ -57,7 +57,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> -