playback: replace song options with equalizer
Replace the song options button in the playback menu with a button that opens the equalizer. I plan to re-introduce the song options in another location.
This commit is contained in:
parent
ee68e7ffe9
commit
d4884cc9ca
9 changed files with 70 additions and 27 deletions
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
## dev
|
## dev
|
||||||
|
|
||||||
|
#### What's Improved
|
||||||
|
- Added a way to access the system equalizer from the playback menu.
|
||||||
|
|
||||||
#### What's Changed
|
#### What's Changed
|
||||||
- ReplayGain can now no longer be disabled. Remove ReplayGain tags from
|
- ReplayGain can now no longer be disabled. Remove ReplayGain tags from
|
||||||
files if such functionality is not desired.
|
files if such functionality is not desired.
|
||||||
|
|
|
@ -17,9 +17,13 @@
|
||||||
|
|
||||||
package org.oxycblt.auxio.playback
|
package org.oxycblt.auxio.playback
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.media.audiofx.AudioEffect
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
@ -34,6 +38,7 @@ import org.oxycblt.auxio.ui.NavigationViewModel
|
||||||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
|
import org.oxycblt.auxio.util.showToast
|
||||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,6 +55,14 @@ class PlaybackPanelFragment :
|
||||||
private val playbackModel: PlaybackViewModel by androidActivityViewModels()
|
private val playbackModel: PlaybackViewModel by androidActivityViewModels()
|
||||||
private val navModel: NavigationViewModel by activityViewModels()
|
private val navModel: NavigationViewModel by activityViewModels()
|
||||||
|
|
||||||
|
// AudioEffect expects you to use startActivityForResult with the panel intent. Use
|
||||||
|
// the contract analogue for this since there is no built-in contract for AudioEffect.
|
||||||
|
private val activityLauncher by lifecycleObject {
|
||||||
|
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateBinding(inflater: LayoutInflater) =
|
override fun onCreateBinding(inflater: LayoutInflater) =
|
||||||
FragmentPlaybackPanelBinding.inflate(inflater)
|
FragmentPlaybackPanelBinding.inflate(inflater)
|
||||||
|
|
||||||
|
@ -110,18 +123,19 @@ class PlaybackPanelFragment :
|
||||||
|
|
||||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.action_go_artist -> {
|
R.id.action_open_equalizer -> {
|
||||||
playbackModel.song.value?.let { navModel.exploreNavigateTo(it.album.artist) }
|
val equalizerIntent =
|
||||||
true
|
Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL)
|
||||||
}
|
.putExtra(
|
||||||
R.id.action_go_album -> {
|
AudioEffect.EXTRA_AUDIO_SESSION, playbackModel.currentAudioSessionId)
|
||||||
playbackModel.song.value?.let { navModel.exploreNavigateTo(it.album) }
|
.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC)
|
||||||
true
|
|
||||||
}
|
try {
|
||||||
R.id.action_song_detail -> {
|
activityLauncher.launch(equalizerIntent)
|
||||||
playbackModel.song.value?.let {
|
} catch (e: ActivityNotFoundException) {
|
||||||
navModel.mainNavigateTo(MainNavigationAction.SongDetails(it))
|
requireContext().showToast(R.string.err_no_app)
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
|
|
|
@ -76,6 +76,9 @@ class PlaybackViewModel(application: Application) :
|
||||||
val isShuffled: StateFlow<Boolean>
|
val isShuffled: StateFlow<Boolean>
|
||||||
get() = _isShuffled
|
get() = _isShuffled
|
||||||
|
|
||||||
|
val currentAudioSessionId: Int?
|
||||||
|
get() = playbackManager.currentAudioSessionId
|
||||||
|
|
||||||
init {
|
init {
|
||||||
musicStore.addCallback(this)
|
musicStore.addCallback(this)
|
||||||
playbackManager.addCallback(this)
|
playbackManager.addCallback(this)
|
||||||
|
@ -170,7 +173,7 @@ class PlaybackViewModel(application: Application) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- POSITION FUNCTIONS ---
|
// --- PLAYER FUNCTIONS ---
|
||||||
|
|
||||||
/** Update the position and push it to [PlaybackStateManager] */
|
/** Update the position and push it to [PlaybackStateManager] */
|
||||||
fun seekTo(positionSecs: Long) {
|
fun seekTo(positionSecs: Long) {
|
||||||
|
|
|
@ -89,6 +89,10 @@ class PlaybackStateManager private constructor() {
|
||||||
var isInitialized = false
|
var isInitialized = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
/** The current audio session ID of the controller. Null if no controller present. */
|
||||||
|
val currentAudioSessionId: Int?
|
||||||
|
get() = controller?.audioSessionId
|
||||||
|
|
||||||
// --- CALLBACKS ---
|
// --- CALLBACKS ---
|
||||||
|
|
||||||
private val callbacks = mutableListOf<Callback>()
|
private val callbacks = mutableListOf<Callback>()
|
||||||
|
@ -509,6 +513,8 @@ class PlaybackStateManager private constructor() {
|
||||||
|
|
||||||
/** Represents a class capable of managing the internal player. */
|
/** Represents a class capable of managing the internal player. */
|
||||||
interface Controller {
|
interface Controller {
|
||||||
|
val audioSessionId: Int
|
||||||
|
|
||||||
/** Called when a new song should be loaded into the player. */
|
/** Called when a new song should be loaded into the player. */
|
||||||
fun loadSong(song: Song?)
|
fun loadSong(song: Song?)
|
||||||
|
|
||||||
|
|
|
@ -250,6 +250,9 @@ class PlaybackService :
|
||||||
|
|
||||||
// --- CONTROLLER OVERRIDES ---
|
// --- CONTROLLER OVERRIDES ---
|
||||||
|
|
||||||
|
override val audioSessionId: Int
|
||||||
|
get() = player.audioSessionId
|
||||||
|
|
||||||
override fun loadSong(song: Song?) {
|
override fun loadSong(song: Song?) {
|
||||||
if (song == null) {
|
if (song == null) {
|
||||||
// Stop the foreground state if there's nothing to play.
|
// Stop the foreground state if there's nothing to play.
|
||||||
|
@ -265,8 +268,6 @@ class PlaybackService :
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
C.ENCODING_PCM_32BIT
|
|
||||||
|
|
||||||
logD("Loading ${song.rawName}")
|
logD("Loading ${song.rawName}")
|
||||||
player.setMediaItem(MediaItem.fromUri(song.uri))
|
player.setMediaItem(MediaItem.fromUri(song.uri))
|
||||||
player.prepare()
|
player.prepare()
|
||||||
|
@ -355,7 +356,7 @@ class PlaybackService :
|
||||||
sendBroadcast(
|
sendBroadcast(
|
||||||
Intent(event)
|
Intent(event)
|
||||||
.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, packageName)
|
.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, packageName)
|
||||||
.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, player.audioSessionId)
|
.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionId)
|
||||||
.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC))
|
.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
app/src/main/res/drawable/ic_equalizer.xml
Normal file
11
app/src/main/res/drawable/ic_equalizer.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M11,21V15H13V17H21V19H13V21ZM3,19V17H9V19ZM7,15V13H3V11H7V9H9V15ZM11,13V11H21V13ZM15,9V3H17V5H21V7H17V9ZM3,7V5H13V7Z"/>
|
||||||
|
</vector>
|
11
app/src/main/res/drawable/ic_more_24.xml
Normal file
11
app/src/main/res/drawable/ic_more_24.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,20Q11.175,20 10.588,19.413Q10,18.825 10,18Q10,17.175 10.588,16.587Q11.175,16 12,16Q12.825,16 13.413,16.587Q14,17.175 14,18Q14,18.825 13.413,19.413Q12.825,20 12,20ZM12,14Q11.175,14 10.588,13.412Q10,12.825 10,12Q10,11.175 10.588,10.587Q11.175,10 12,10Q12.825,10 13.413,10.587Q14,11.175 14,12Q14,12.825 13.413,13.412Q12.825,14 12,14ZM12,8Q11.175,8 10.588,7.412Q10,6.825 10,6Q10,5.175 10.588,4.588Q11.175,4 12,4Q12.825,4 13.413,4.588Q14,5.175 14,6Q14,6.825 13.413,7.412Q12.825,8 12,8Z"/>
|
||||||
|
</vector>
|
|
@ -2,15 +2,8 @@
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_go_artist"
|
android:id="@+id/action_open_equalizer"
|
||||||
android:title="@string/lbl_go_artist"
|
android:title="@string/lbl_equalizer"
|
||||||
app:showAsAction="never" />
|
android:icon="@drawable/ic_equalizer"
|
||||||
<item
|
app:showAsAction="always" />
|
||||||
android:id="@+id/action_go_album"
|
|
||||||
android:title="@string/lbl_go_album"
|
|
||||||
app:showAsAction="never" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_song_detail"
|
|
||||||
android:title="@string/lbl_song_detail"
|
|
||||||
app:showAsAction="never" />
|
|
||||||
</menu>
|
</menu>
|
|
@ -87,6 +87,7 @@
|
||||||
<string name="lbl_sort_asc">Ascending</string>
|
<string name="lbl_sort_asc">Ascending</string>
|
||||||
|
|
||||||
<string name="lbl_playback">Now playing</string>
|
<string name="lbl_playback">Now playing</string>
|
||||||
|
<string name="lbl_equalizer">Equalizer</string>
|
||||||
<string name="lbl_play">Play</string>
|
<string name="lbl_play">Play</string>
|
||||||
<string name="lbl_shuffle">Shuffle</string>
|
<string name="lbl_shuffle">Shuffle</string>
|
||||||
|
|
||||||
|
@ -235,7 +236,7 @@
|
||||||
<string name="err_no_music">No music found</string>
|
<string name="err_no_music">No music found</string>
|
||||||
<string name="err_index_failed">Music loading failed</string>
|
<string name="err_index_failed">Music loading failed</string>
|
||||||
<string name="err_no_perms">Auxio needs permission to read your music library</string>
|
<string name="err_no_perms">Auxio needs permission to read your music library</string>
|
||||||
<string name="err_no_app">No app can open this link</string>
|
<string name="err_no_app">No app found that can handle this task</string>
|
||||||
<!-- No folders in the "Music Folders" setting -->
|
<!-- No folders in the "Music Folders" setting -->
|
||||||
<string name="err_no_dirs">No folders</string>
|
<string name="err_no_dirs">No folders</string>
|
||||||
<string name="err_bad_dir">This folder is not supported</string>
|
<string name="err_bad_dir">This folder is not supported</string>
|
||||||
|
|
Loading…
Reference in a new issue