playback: rework audio focus
Rework audio focus to rely on the native ExoPlayer implementation instead of a custom implementation. Previously, we avoided ExoPlayer's AudioFocus system as it never played after a transient lost. A few versions later now through, now it does, so we may as well switch to it. This does introduce a bug where ReplayGain functionality will conflict with audio focus, but I hope to eliminate this with #115 as I switch to an AudioProcessor instead of a callback.
This commit is contained in:
parent
e54a58c612
commit
b748d73abb
37 changed files with 33 additions and 175 deletions
|
@ -5,10 +5,15 @@
|
||||||
#### What's Fixed
|
#### What's Fixed
|
||||||
- Fixed incorrect ellipsizing on song items
|
- Fixed incorrect ellipsizing on song items
|
||||||
|
|
||||||
|
#### What's Changed
|
||||||
|
- Audio focus is no longer configurable
|
||||||
|
|
||||||
#### Dev/Meta
|
#### Dev/Meta
|
||||||
- Updated translations [Konstantin Tutsch -> German, cccClyde -> Chinese ]
|
- Updated translations [Konstantin Tutsch -> German, cccClyde -> Chinese ]
|
||||||
- Switched to spotless and ktfmt instead of ktlint
|
- Switched to spotless and ktfmt instead of ktlint
|
||||||
- Migrated constants to centralized table
|
- Migrated constants to centralized table
|
||||||
|
- Introduced new RecyclerView framework
|
||||||
|
- Use native ExoPlayer AudioFocus implementation
|
||||||
- Removed databinding [Greatly reduces compile times]
|
- Removed databinding [Greatly reduces compile times]
|
||||||
- A bunch of internal view implementation improvements
|
- A bunch of internal view implementation improvements
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.oxycblt.auxio.coil.GenreImageFetcher
|
||||||
import org.oxycblt.auxio.coil.MusicKeyer
|
import org.oxycblt.auxio.coil.MusicKeyer
|
||||||
import org.oxycblt.auxio.settings.SettingsManager
|
import org.oxycblt.auxio.settings.SettingsManager
|
||||||
|
|
||||||
/** TODO: Rework null-safety/usage of requireNotNull */
|
|
||||||
@Suppress("UNUSED")
|
@Suppress("UNUSED")
|
||||||
class AuxioApp : Application(), ImageLoaderFactory {
|
class AuxioApp : Application(), ImageLoaderFactory {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
|
|
@ -100,7 +100,7 @@ class DetailViewModel : ViewModel() {
|
||||||
if (mCurrentAlbum.value?.id == id) return
|
if (mCurrentAlbum.value?.id == id) return
|
||||||
val musicStore = MusicStore.requireInstance()
|
val musicStore = MusicStore.requireInstance()
|
||||||
val album =
|
val album =
|
||||||
requireNotNull(musicStore.albums.find { it.id == id }) { "Invalid album ID provided " }
|
requireNotNull(musicStore.albums.find { it.id == id }) { "Invalid album id provided " }
|
||||||
|
|
||||||
mCurrentAlbum.value = album
|
mCurrentAlbum.value = album
|
||||||
refreshAlbumData(album)
|
refreshAlbumData(album)
|
||||||
|
@ -109,9 +109,7 @@ class DetailViewModel : ViewModel() {
|
||||||
fun setArtistId(id: Long) {
|
fun setArtistId(id: Long) {
|
||||||
if (mCurrentArtist.value?.id == id) return
|
if (mCurrentArtist.value?.id == id) return
|
||||||
val musicStore = MusicStore.requireInstance()
|
val musicStore = MusicStore.requireInstance()
|
||||||
val artist =
|
val artist = requireNotNull(musicStore.artists.find { it.id == id }) {}
|
||||||
requireNotNull(musicStore.artists.find { it.id == id }) { "Invalid artist ID provided" }
|
|
||||||
|
|
||||||
mCurrentArtist.value = artist
|
mCurrentArtist.value = artist
|
||||||
refreshArtistData(artist)
|
refreshArtistData(artist)
|
||||||
}
|
}
|
||||||
|
@ -119,8 +117,7 @@ class DetailViewModel : ViewModel() {
|
||||||
fun setGenreId(id: Long) {
|
fun setGenreId(id: Long) {
|
||||||
if (mCurrentGenre.value?.id == id) return
|
if (mCurrentGenre.value?.id == id) return
|
||||||
val musicStore = MusicStore.requireInstance()
|
val musicStore = MusicStore.requireInstance()
|
||||||
val genre =
|
val genre = requireNotNull(musicStore.genres.find { it.id == id })
|
||||||
requireNotNull(musicStore.genres.find { it.id == id }) { "Invalid genre ID provided" }
|
|
||||||
mCurrentGenre.value = genre
|
mCurrentGenre.value = genre
|
||||||
refreshGenreData(genre)
|
refreshGenreData(genre)
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,7 +179,8 @@ private constructor(
|
||||||
override fun bind(item: Album, listener: MenuItemListener) {
|
override fun bind(item: Album, listener: MenuItemListener) {
|
||||||
binding.parentImage.bindAlbumCover(item)
|
binding.parentImage.bindAlbumCover(item)
|
||||||
binding.parentName.textSafe = item.resolvedName
|
binding.parentName.textSafe = item.resolvedName
|
||||||
binding.parentInfo.textSafe = if (item.year != null) {
|
binding.parentInfo.textSafe =
|
||||||
|
if (item.year != null) {
|
||||||
binding.context.getString(R.string.fmt_number, item.year)
|
binding.context.getString(R.string.fmt_number, item.year)
|
||||||
} else {
|
} else {
|
||||||
binding.context.getString(R.string.def_date)
|
binding.context.getString(R.string.def_date)
|
||||||
|
|
|
@ -49,7 +49,7 @@ sealed class Tab(open val mode: DisplayMode) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** The length a well-formed tab sequence should be */
|
/** The length a well-formed tab sequence should be */
|
||||||
const val SEQUENCE_LEN = 4
|
private const val SEQUENCE_LEN = 4
|
||||||
/** The default tab sequence, represented in integer form */
|
/** The default tab sequence, represented in integer form */
|
||||||
const val SEQUENCE_DEFAULT = 0b1000_1001_1010_1011_0100
|
const val SEQUENCE_DEFAULT = 0b1000_1001_1010_1011_0100
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ class PlaybackService :
|
||||||
private lateinit var notificationManager: NotificationManager
|
private lateinit var notificationManager: NotificationManager
|
||||||
|
|
||||||
// System backend components
|
// System backend components
|
||||||
private lateinit var audioReactor: AudioReactor
|
private lateinit var audioReactor: VolumeReactor
|
||||||
private lateinit var widgets: WidgetController
|
private lateinit var widgets: WidgetController
|
||||||
private val systemReceiver = PlaybackReceiver()
|
private val systemReceiver = PlaybackReceiver()
|
||||||
|
|
||||||
|
@ -130,10 +130,10 @@ class PlaybackService :
|
||||||
.setUsage(C.USAGE_MEDIA)
|
.setUsage(C.USAGE_MEDIA)
|
||||||
.setContentType(C.CONTENT_TYPE_MUSIC)
|
.setContentType(C.CONTENT_TYPE_MUSIC)
|
||||||
.build(),
|
.build(),
|
||||||
false)
|
true)
|
||||||
|
|
||||||
audioReactor =
|
audioReactor =
|
||||||
AudioReactor(this) { volume ->
|
VolumeReactor { volume ->
|
||||||
logD("Updating player volume to $volume")
|
logD("Updating player volume to $volume")
|
||||||
player.volume = volume
|
player.volume = volume
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,6 @@ class PlaybackService :
|
||||||
player.release()
|
player.release()
|
||||||
connector.release()
|
connector.release()
|
||||||
mediaSession.release()
|
mediaSession.release()
|
||||||
audioReactor.release()
|
|
||||||
widgets.release()
|
widgets.release()
|
||||||
|
|
||||||
playbackManager.removeCallback(this)
|
playbackManager.removeCallback(this)
|
||||||
|
@ -214,6 +213,13 @@ class PlaybackService :
|
||||||
|
|
||||||
// --- PLAYER EVENT LISTENER OVERRIDES ---
|
// --- PLAYER EVENT LISTENER OVERRIDES ---
|
||||||
|
|
||||||
|
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
|
||||||
|
super.onPlayWhenReadyChanged(playWhenReady, reason)
|
||||||
|
if (playbackManager.isPlaying != playWhenReady) {
|
||||||
|
playbackManager.setPlaying(playWhenReady)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPlaybackStateChanged(state: Int) {
|
override fun onPlaybackStateChanged(state: Int) {
|
||||||
when (state) {
|
when (state) {
|
||||||
Player.STATE_READY -> startPolling()
|
Player.STATE_READY -> startPolling()
|
||||||
|
@ -281,7 +287,6 @@ class PlaybackService :
|
||||||
override fun onPlayingUpdate(isPlaying: Boolean) {
|
override fun onPlayingUpdate(isPlaying: Boolean) {
|
||||||
if (isPlaying && !player.isPlaying) {
|
if (isPlaying && !player.isPlaying) {
|
||||||
player.play()
|
player.play()
|
||||||
audioReactor.requestFocus()
|
|
||||||
startPolling()
|
startPolling()
|
||||||
} else {
|
} else {
|
||||||
player.pause()
|
player.pause()
|
||||||
|
|
|
@ -17,13 +17,7 @@
|
||||||
|
|
||||||
package org.oxycblt.auxio.playback.system
|
package org.oxycblt.auxio.playback.system
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.media.AudioManager
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.core.math.MathUtils
|
import androidx.core.math.MathUtils
|
||||||
import androidx.media.AudioAttributesCompat
|
|
||||||
import androidx.media.AudioFocusRequestCompat
|
|
||||||
import androidx.media.AudioManagerCompat
|
|
||||||
import com.google.android.exoplayer2.metadata.Metadata
|
import com.google.android.exoplayer2.metadata.Metadata
|
||||||
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame
|
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame
|
||||||
import com.google.android.exoplayer2.metadata.vorbis.VorbisComment
|
import com.google.android.exoplayer2.metadata.vorbis.VorbisComment
|
||||||
|
@ -31,36 +25,24 @@ import kotlin.math.pow
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||||
import org.oxycblt.auxio.settings.SettingsManager
|
import org.oxycblt.auxio.settings.SettingsManager
|
||||||
import org.oxycblt.auxio.util.getSystemServiceSafe
|
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
import org.oxycblt.auxio.util.logW
|
import org.oxycblt.auxio.util.logW
|
||||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the current volume and playback state across ReplayGain and AudioFocus events.
|
* Manages the current volume across ReplayGain and AudioFocus events.
|
||||||
|
*
|
||||||
|
* TODO: Add ReplayGain pre-amp
|
||||||
|
*
|
||||||
|
* TODO: Add positive ReplayGain
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class AudioReactor(context: Context, private val callback: (Float) -> Unit) :
|
class VolumeReactor(private val callback: (Float) -> Unit) {
|
||||||
AudioManager.OnAudioFocusChangeListener, SettingsManager.Callback {
|
|
||||||
private data class Gain(val track: Float, val album: Float)
|
private data class Gain(val track: Float, val album: Float)
|
||||||
private data class GainTag(val key: String, val value: Float)
|
private data class GainTag(val key: String, val value: Float)
|
||||||
|
|
||||||
private val playbackManager = PlaybackStateManager.getInstance()
|
private val playbackManager = PlaybackStateManager.getInstance()
|
||||||
private val settingsManager = SettingsManager.getInstance()
|
private val settingsManager = SettingsManager.getInstance()
|
||||||
private val audioManager = context.getSystemServiceSafe(AudioManager::class)
|
|
||||||
|
|
||||||
private val request =
|
|
||||||
AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN)
|
|
||||||
.setWillPauseWhenDucked(false)
|
|
||||||
.setAudioAttributes(
|
|
||||||
AudioAttributesCompat.Builder()
|
|
||||||
.setContentType(AudioAttributesCompat.CONTENT_TYPE_MUSIC)
|
|
||||||
.setUsage(AudioAttributesCompat.USAGE_MEDIA)
|
|
||||||
.build())
|
|
||||||
.setOnAudioFocusChangeListener(this)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
private var pauseWasTransient = false
|
|
||||||
|
|
||||||
// It's good to keep the volume and the ducking multiplier separate so that we don't
|
// It's good to keep the volume and the ducking multiplier separate so that we don't
|
||||||
// lose information
|
// lose information
|
||||||
|
@ -77,23 +59,9 @@ class AudioReactor(context: Context, private val callback: (Float) -> Unit) :
|
||||||
callback(volume)
|
callback(volume)
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
|
||||||
settingsManager.addCallback(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Request the android system for audio focus */
|
|
||||||
fun requestFocus() {
|
|
||||||
logD("Requesting audio focus")
|
|
||||||
AudioManagerCompat.requestAudioFocus(audioManager, request)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the rough volume adjustment for [Metadata] with ReplayGain tags. This is based off
|
* Updates the rough volume adjustment for [Metadata] with ReplayGain tags. This is based off
|
||||||
* Vanilla Music's implementation.
|
* Vanilla Music's implementation.
|
||||||
*
|
|
||||||
* TODO: Add ReplayGain pre-amp
|
|
||||||
*
|
|
||||||
* TODO: Add positive ReplayGain
|
|
||||||
*/
|
*/
|
||||||
fun applyReplayGain(metadata: Metadata?) {
|
fun applyReplayGain(metadata: Metadata?) {
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
|
@ -218,75 +186,9 @@ class AudioReactor(context: Context, private val callback: (Float) -> Unit) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Abandon the current focus request and any callbacks */
|
|
||||||
fun release() {
|
|
||||||
AudioManagerCompat.abandonAudioFocusRequest(audioManager, request)
|
|
||||||
settingsManager.removeCallback(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- INTERNAL AUDIO FOCUS ---
|
|
||||||
|
|
||||||
override fun onAudioFocusChange(focusChange: Int) {
|
|
||||||
if (!settingsManager.doAudioFocus && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
|
||||||
// Don't do audio focus if its not enabled
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
when (focusChange) {
|
|
||||||
AudioManager.AUDIOFOCUS_GAIN -> onGain()
|
|
||||||
AudioManager.AUDIOFOCUS_LOSS -> onLossPermanent()
|
|
||||||
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> onLossTransient()
|
|
||||||
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> onDuck()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onGain() {
|
|
||||||
if (multiplier == MULTIPLIER_DUCK) {
|
|
||||||
unduck()
|
|
||||||
} else if (pauseWasTransient) {
|
|
||||||
logD("Gained focus after transient loss")
|
|
||||||
|
|
||||||
// Play again if the pause was only temporary [AudioManager.AUDIOFOCUS_LOSS_TRANSIENT]
|
|
||||||
playbackManager.setPlaying(true)
|
|
||||||
pauseWasTransient = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onLossTransient() {
|
|
||||||
// Since this loss is only temporary, mark it as such if we had to pause playback.
|
|
||||||
if (playbackManager.isPlaying) {
|
|
||||||
logD("Pausing for transient loss")
|
|
||||||
playbackManager.setPlaying(false)
|
|
||||||
pauseWasTransient = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onLossPermanent() {
|
|
||||||
logD("Pausing for permanent loss")
|
|
||||||
playbackManager.setPlaying(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onDuck() {
|
|
||||||
multiplier = MULTIPLIER_DUCK
|
|
||||||
logD("Ducked volume, now $volume")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unduck() {
|
|
||||||
multiplier = 1f
|
|
||||||
logD("Unducked volume, now $volume")
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- SETTINGS MANAGEMENT ---
|
// --- SETTINGS MANAGEMENT ---
|
||||||
|
|
||||||
override fun onAudioFocusUpdate(focus: Boolean) {
|
|
||||||
if (!focus) {
|
|
||||||
onGain()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val MULTIPLIER_DUCK = 0.2f
|
|
||||||
|
|
||||||
const val RG_TRACK = "REPLAYGAIN_TRACK_GAIN"
|
const val RG_TRACK = "REPLAYGAIN_TRACK_GAIN"
|
||||||
const val RG_ALBUM = "REPLAYGAIN_ALBUM_GAIN"
|
const val RG_ALBUM = "REPLAYGAIN_ALBUM_GAIN"
|
||||||
const val R128_TRACK = "R128_TRACK_GAIN"
|
const val R128_TRACK = "R128_TRACK_GAIN"
|
|
@ -94,10 +94,6 @@ class SettingsManager private constructor(context: Context) :
|
||||||
val roundCovers: Boolean
|
val roundCovers: Boolean
|
||||||
get() = prefs.getBoolean(KEY_ROUND_COVERS, false)
|
get() = prefs.getBoolean(KEY_ROUND_COVERS, false)
|
||||||
|
|
||||||
/** Whether to do Audio focus. */
|
|
||||||
val doAudioFocus: Boolean
|
|
||||||
get() = prefs.getBoolean(KEY_AUDIO_FOCUS, true)
|
|
||||||
|
|
||||||
/** Whether to resume playback when a headset is connected (may not work well in all cases) */
|
/** Whether to resume playback when a headset is connected (may not work well in all cases) */
|
||||||
val headsetAutoplay: Boolean
|
val headsetAutoplay: Boolean
|
||||||
get() = prefs.getBoolean(KEY_HEADSET_AUTOPLAY, false)
|
get() = prefs.getBoolean(KEY_HEADSET_AUTOPLAY, false)
|
||||||
|
@ -239,7 +235,6 @@ class SettingsManager private constructor(context: Context) :
|
||||||
KEY_SHOW_COVERS -> callbacks.forEach { it.onShowCoverUpdate(showCovers) }
|
KEY_SHOW_COVERS -> callbacks.forEach { it.onShowCoverUpdate(showCovers) }
|
||||||
KEY_QUALITY_COVERS -> callbacks.forEach { it.onQualityCoverUpdate(useQualityCovers) }
|
KEY_QUALITY_COVERS -> callbacks.forEach { it.onQualityCoverUpdate(useQualityCovers) }
|
||||||
KEY_LIB_TABS -> callbacks.forEach { it.onLibTabsUpdate(libTabs) }
|
KEY_LIB_TABS -> callbacks.forEach { it.onLibTabsUpdate(libTabs) }
|
||||||
KEY_AUDIO_FOCUS -> callbacks.forEach { it.onAudioFocusUpdate(doAudioFocus) }
|
|
||||||
KEY_REPLAY_GAIN -> callbacks.forEach { it.onReplayGainUpdate(replayGainMode) }
|
KEY_REPLAY_GAIN -> callbacks.forEach { it.onReplayGainUpdate(replayGainMode) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,7 +250,6 @@ class SettingsManager private constructor(context: Context) :
|
||||||
fun onNotifActionUpdate(useAltAction: Boolean) {}
|
fun onNotifActionUpdate(useAltAction: Boolean) {}
|
||||||
fun onShowCoverUpdate(showCovers: Boolean) {}
|
fun onShowCoverUpdate(showCovers: Boolean) {}
|
||||||
fun onQualityCoverUpdate(doQualityCovers: Boolean) {}
|
fun onQualityCoverUpdate(doQualityCovers: Boolean) {}
|
||||||
fun onAudioFocusUpdate(focus: Boolean) {}
|
|
||||||
fun onReplayGainUpdate(mode: ReplayGainMode) {}
|
fun onReplayGainUpdate(mode: ReplayGainMode) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +266,6 @@ class SettingsManager private constructor(context: Context) :
|
||||||
const val KEY_ROUND_COVERS = "auxio_round_covers"
|
const val KEY_ROUND_COVERS = "auxio_round_covers"
|
||||||
const val KEY_USE_ALT_NOTIFICATION_ACTION = "KEY_ALT_NOTIF_ACTION"
|
const val KEY_USE_ALT_NOTIFICATION_ACTION = "KEY_ALT_NOTIF_ACTION"
|
||||||
|
|
||||||
const val KEY_AUDIO_FOCUS = "KEY_AUDIO_FOCUS"
|
|
||||||
const val KEY_HEADSET_AUTOPLAY = "auxio_headset_autoplay"
|
const val KEY_HEADSET_AUTOPLAY = "auxio_headset_autoplay"
|
||||||
const val KEY_REPLAY_GAIN = "auxio_replay_gain"
|
const val KEY_REPLAY_GAIN = "auxio_replay_gain"
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ val Drawable.isRtl: Boolean
|
||||||
val ViewBinding.context: Context
|
val ViewBinding.context: Context
|
||||||
get() = root.context
|
get() = root.context
|
||||||
|
|
||||||
var TextView.textSafe
|
var TextView.textSafe: CharSequence
|
||||||
get() = text
|
get() = text
|
||||||
set(value) {
|
set(value) {
|
||||||
text = value
|
text = value
|
||||||
|
|
|
@ -68,7 +68,6 @@ class WidgetProvider : AppWidgetProvider() {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadWidgetBitmap(context, song) { bitmap ->
|
loadWidgetBitmap(context, song) { bitmap ->
|
||||||
logD(bitmap == null)
|
|
||||||
val state =
|
val state =
|
||||||
WidgetState(
|
WidgetState(
|
||||||
song,
|
song,
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/colorSurface"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context=".settings.AboutFragment">
|
tools:context=".settings.AboutFragment">
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
android:id="@+id/queue_coordinator"
|
android:id="@+id/queue_coordinator"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/colorSurface"
|
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<org.oxycblt.auxio.ui.EdgeAppBarLayout
|
<org.oxycblt.auxio.ui.EdgeAppBarLayout
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
android:id="@+id/settings_coordinator"
|
android:id="@+id/settings_coordinator"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/colorSurface"
|
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<org.oxycblt.auxio.ui.EdgeAppBarLayout
|
<org.oxycblt.auxio.ui.EdgeAppBarLayout
|
||||||
|
|
|
@ -2,10 +2,8 @@
|
||||||
<FrameLayout 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:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:background="?attr/colorSurface">
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/background"
|
android:id="@+id/background"
|
||||||
|
|
|
@ -81,8 +81,6 @@
|
||||||
<string name="set_alt_shuffle">تفضيل نشاط الخلط</string>
|
<string name="set_alt_shuffle">تفضيل نشاط الخلط</string>
|
||||||
|
|
||||||
<string name="set_audio">صوتيات</string>
|
<string name="set_audio">صوتيات</string>
|
||||||
<string name="set_focus">تركيز الصوت</string>
|
|
||||||
<string name="set_focus_desc">ايقاف مؤقت عند تشغيل صوت آخر (كالمكالمات)</string>
|
|
||||||
<string name="set_replay_gain">صخب الصوت (تجريبي)</string>
|
<string name="set_replay_gain">صخب الصوت (تجريبي)</string>
|
||||||
<string name="set_off">اطفاء</string>
|
<string name="set_off">اطفاء</string>
|
||||||
<string name="set_replay_gain_track">تفضيل المقطع</string>
|
<string name="set_replay_gain_track">تفضيل المقطع</string>
|
||||||
|
|
|
@ -65,8 +65,6 @@
|
||||||
<string name="set_alt_loop">"Preferovat akci režimu opakování"</string>
|
<string name="set_alt_loop">"Preferovat akci režimu opakování"</string>
|
||||||
<string name="set_alt_shuffle">"Preferovat akci náhodného přehrávání"</string>
|
<string name="set_alt_shuffle">"Preferovat akci náhodného přehrávání"</string>
|
||||||
<string name="set_audio">"Zvuk"</string>
|
<string name="set_audio">"Zvuk"</string>
|
||||||
<string name="set_focus">"Zaměření zvuku"</string>
|
|
||||||
<string name="set_focus_desc">Pozastavit při přehrávání jiného zvuku (např. hovor)</string>
|
|
||||||
<string name="set_behavior">"Chování"</string>
|
<string name="set_behavior">"Chování"</string>
|
||||||
<string name="set_song_mode">"Když je vybrána skladba"</string>
|
<string name="set_song_mode">"Když je vybrána skladba"</string>
|
||||||
<string name="set_keep_shuffle">"Zapamatovat si náhodné přehrávání"</string>
|
<string name="set_keep_shuffle">"Zapamatovat si náhodné přehrávání"</string>
|
||||||
|
|
|
@ -71,8 +71,6 @@
|
||||||
<string name="set_alt_shuffle">Zufällig-Aktionstaste bevorzugen</string>
|
<string name="set_alt_shuffle">Zufällig-Aktionstaste bevorzugen</string>
|
||||||
|
|
||||||
<string name="set_audio">Audio</string>
|
<string name="set_audio">Audio</string>
|
||||||
<string name="set_focus">Audiofokus</string>
|
|
||||||
<string name="set_focus_desc">Pausieren wenn andere Töne abspielt wird [Bsp. Anrufe]</string>
|
|
||||||
<string name="set_headset_autoplay">Kopfhörer automatische Wiedergabe</string>
|
<string name="set_headset_autoplay">Kopfhörer automatische Wiedergabe</string>
|
||||||
<string name="set_headset_autoplay_desc">Beginne die Wiedergabe immer, wenn Kopfhörer verbunden sind (funktioniert nicht auf allen Geräten)</string>
|
<string name="set_headset_autoplay_desc">Beginne die Wiedergabe immer, wenn Kopfhörer verbunden sind (funktioniert nicht auf allen Geräten)</string>
|
||||||
<string name="set_replay_gain">ReplayGain (Experimentell)</string>
|
<string name="set_replay_gain">ReplayGain (Experimentell)</string>
|
||||||
|
|
|
@ -81,8 +81,6 @@
|
||||||
<string name="set_alt_shuffle">Preferir acción de mezcla</string>
|
<string name="set_alt_shuffle">Preferir acción de mezcla</string>
|
||||||
|
|
||||||
<string name="set_audio">Sonido</string>
|
<string name="set_audio">Sonido</string>
|
||||||
<string name="set_focus">Enfoque de sonido</string>
|
|
||||||
<string name="set_focus_desc">Pausar cuando se reproduce otro sonido (Ej: llamadas)</string>
|
|
||||||
<string name="set_replay_gain">ReplayGain (Experimental)</string>
|
<string name="set_replay_gain">ReplayGain (Experimental)</string>
|
||||||
<string name="set_off">Desactivado</string>
|
<string name="set_off">Desactivado</string>
|
||||||
<string name="set_replay_gain_track">Por pista</string>
|
<string name="set_replay_gain_track">Por pista</string>
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
<string name="set_quality_covers">Ignorer le stockage des pochettes</string>
|
<string name="set_quality_covers">Ignorer le stockage des pochettes</string>
|
||||||
|
|
||||||
<string name="set_audio">Audio</string>
|
<string name="set_audio">Audio</string>
|
||||||
<string name="set_focus">Audio Focus</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">Comportement</string>
|
<string name="set_behavior">Comportement</string>
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
<string name="set_accent">एक्सेंट</string>
|
<string name="set_accent">एक्सेंट</string>
|
||||||
|
|
||||||
<string name="set_audio">ऑडियो</string>
|
<string name="set_audio">ऑडियो</string>
|
||||||
<string name="set_focus">ऑडियो फोकस</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">चाल चलन</string>
|
<string name="set_behavior">चाल चलन</string>
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
<string name="set_quality_covers">A médiatár albumborítók figyelmen kívül hagyása</string>
|
<string name="set_quality_covers">A médiatár albumborítók figyelmen kívül hagyása</string>
|
||||||
|
|
||||||
<string name="set_audio">Hang</string>
|
<string name="set_audio">Hang</string>
|
||||||
<string name="set_focus">Hangfókusz</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">Működés</string>
|
<string name="set_behavior">Működés</string>
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
<string name="set_quality_covers">Abaikan sampul-sampul pada Media Penyimpanan</string>
|
<string name="set_quality_covers">Abaikan sampul-sampul pada Media Penyimpanan</string>
|
||||||
|
|
||||||
<string name="set_audio">Audio</string>
|
<string name="set_audio">Audio</string>
|
||||||
<string name="set_focus">Fokus audio</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">Perilaku</string>
|
<string name="set_behavior">Perilaku</string>
|
||||||
<string name="set_keep_shuffle">Ingat putar acak</string>
|
<string name="set_keep_shuffle">Ingat putar acak</string>
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
<string name="set_quality_covers">Ignora le copertine del Media Store</string>
|
<string name="set_quality_covers">Ignora le copertine del Media Store</string>
|
||||||
|
|
||||||
<string name="set_audio">Audio</string>
|
<string name="set_audio">Audio</string>
|
||||||
<string name="set_focus">Focus audio</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">Comportamento</string>
|
<string name="set_behavior">Comportamento</string>
|
||||||
<string name="set_keep_shuffle">Ricorda casuale</string>
|
<string name="set_keep_shuffle">Ricorda casuale</string>
|
||||||
|
|
|
@ -47,7 +47,6 @@
|
||||||
<string name="set_quality_covers">미디어 스토어 앨범 커버 무시</string>
|
<string name="set_quality_covers">미디어 스토어 앨범 커버 무시</string>
|
||||||
|
|
||||||
<string name="set_audio">오디오</string>
|
<string name="set_audio">오디오</string>
|
||||||
<string name="set_focus">오디오 포커스</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">동작</string>
|
<string name="set_behavior">동작</string>
|
||||||
|
|
||||||
|
|
|
@ -70,8 +70,6 @@
|
||||||
<string name="set_alt_shuffle">Voorkeur aan shuffle actie</string>
|
<string name="set_alt_shuffle">Voorkeur aan shuffle actie</string>
|
||||||
|
|
||||||
<string name="set_audio">Audio</string>
|
<string name="set_audio">Audio</string>
|
||||||
<string name="set_focus">Audiofocus</string>
|
|
||||||
<string name="set_focus_desc">Pauze wanneer andere audio speelt (ex. Gesprekken)</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">Gedrag</string>
|
<string name="set_behavior">Gedrag</string>
|
||||||
<string name="set_song_mode">Wanneer een liedje is geselecteerd</string>
|
<string name="set_song_mode">Wanneer een liedje is geselecteerd</string>
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
<string name="set_quality_covers">Ignoruj okładki z Media Store</string>
|
<string name="set_quality_covers">Ignoruj okładki z Media Store</string>
|
||||||
|
|
||||||
<string name="set_audio">Dźwięk</string>
|
<string name="set_audio">Dźwięk</string>
|
||||||
<string name="set_focus">Wyciszanie otoczenia</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">Zachowanie</string>
|
<string name="set_behavior">Zachowanie</string>
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
<string name="set_quality_covers">Ignorar capas Media Store</string>
|
<string name="set_quality_covers">Ignorar capas Media Store</string>
|
||||||
|
|
||||||
<string name="set_audio">Áudio</string>
|
<string name="set_audio">Áudio</string>
|
||||||
<string name="set_focus">Foco do áudio</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">Comportamento</string>
|
<string name="set_behavior">Comportamento</string>
|
||||||
<string name="set_keep_shuffle">Memorizar aleatorização</string>
|
<string name="set_keep_shuffle">Memorizar aleatorização</string>
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
<string name="set_quality_covers">Ignorar capas Media Store</string>
|
<string name="set_quality_covers">Ignorar capas Media Store</string>
|
||||||
|
|
||||||
<string name="set_audio">Áudio</string>
|
<string name="set_audio">Áudio</string>
|
||||||
<string name="set_focus">Foco de áudio</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">Comportamento</string>
|
<string name="set_behavior">Comportamento</string>
|
||||||
<string name="set_keep_shuffle">Memorizar aleatorização</string>
|
<string name="set_keep_shuffle">Memorizar aleatorização</string>
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
<string name="set_quality_covers">Ignoră coperțile Media Store</string>
|
<string name="set_quality_covers">Ignoră coperțile Media Store</string>
|
||||||
|
|
||||||
<string name="set_audio">Audio</string>
|
<string name="set_audio">Audio</string>
|
||||||
<string name="set_focus">Concentrare audio</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">Comportament</string>
|
<string name="set_behavior">Comportament</string>
|
||||||
|
|
||||||
|
|
|
@ -81,8 +81,6 @@
|
||||||
<string name="set_alt_shuffle">Режим перемешивания</string>
|
<string name="set_alt_shuffle">Режим перемешивания</string>
|
||||||
|
|
||||||
<string name="set_audio">Звук</string>
|
<string name="set_audio">Звук</string>
|
||||||
<string name="set_focus">Аудио-фокус</string>
|
|
||||||
<string name="set_focus_desc">Ставить на паузу при звонках</string>
|
|
||||||
<string name="set_replay_gain">ReplayGain (экспериментально)</string>
|
<string name="set_replay_gain">ReplayGain (экспериментально)</string>
|
||||||
<string name="set_off">Выкл.</string>
|
<string name="set_off">Выкл.</string>
|
||||||
<string name="set_replay_gain_track">По треку</string>
|
<string name="set_replay_gain_track">По треку</string>
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
<string name="set_quality_covers">Medya Deposu albüm kapağını yoksay</string>
|
<string name="set_quality_covers">Medya Deposu albüm kapağını yoksay</string>
|
||||||
|
|
||||||
<string name="set_audio">Ses</string>
|
<string name="set_audio">Ses</string>
|
||||||
<string name="set_focus">Ses odaklama</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">Tercihler</string>
|
<string name="set_behavior">Tercihler</string>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<bool name="enable_theme_settings">false</bool>
|
<bool name="enable_theme_settings">false</bool>
|
||||||
<bool name="enable_audio_focus_setting">false</bool>
|
|
||||||
</resources>
|
</resources>
|
|
@ -80,8 +80,6 @@
|
||||||
<string name="set_alt_shuffle">偏好随机播放操作</string>
|
<string name="set_alt_shuffle">偏好随机播放操作</string>
|
||||||
|
|
||||||
<string name="set_audio">音频</string>
|
<string name="set_audio">音频</string>
|
||||||
<string name="set_focus">音频焦点</string>
|
|
||||||
<string name="set_focus_desc">有其它音频播放(比如电话)时暂停</string>
|
|
||||||
<string name="set_headset_autoplay">自动播放</string>
|
<string name="set_headset_autoplay">自动播放</string>
|
||||||
<string name="set_headset_autoplay_desc">连接至耳机时总是自动播放(并非在所有设备上都有用)</string>
|
<string name="set_headset_autoplay_desc">连接至耳机时总是自动播放(并非在所有设备上都有用)</string>
|
||||||
<string name="set_replay_gain">回放增益(实验性)</string>
|
<string name="set_replay_gain">回放增益(实验性)</string>
|
||||||
|
|
|
@ -47,7 +47,6 @@
|
||||||
<string name="set_quality_covers">忽略音訊檔內嵌的專輯封面</string>
|
<string name="set_quality_covers">忽略音訊檔內嵌的專輯封面</string>
|
||||||
|
|
||||||
<string name="set_audio">音訊</string>
|
<string name="set_audio">音訊</string>
|
||||||
<string name="set_focus">音頻焦點</string>
|
|
||||||
|
|
||||||
<string name="set_behavior">行為</string>
|
<string name="set_behavior">行為</string>
|
||||||
<string name="set_keep_shuffle">記住隨機播放</string>
|
<string name="set_keep_shuffle">記住隨機播放</string>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<bool name="enable_theme_settings">true</bool>
|
<bool name="enable_theme_settings">true</bool>
|
||||||
<bool name="enable_audio_focus_setting">true</bool>
|
|
||||||
<integer name="recycler_spans">1</integer>
|
<integer name="recycler_spans">1</integer>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -80,8 +80,6 @@
|
||||||
<string name="set_alt_shuffle">Prefer shuffle action</string>
|
<string name="set_alt_shuffle">Prefer shuffle action</string>
|
||||||
|
|
||||||
<string name="set_audio">Audio</string>
|
<string name="set_audio">Audio</string>
|
||||||
<string name="set_focus">Audio focus</string>
|
|
||||||
<string name="set_focus_desc">Pause when other audio plays (ex. Calls)</string>
|
|
||||||
<string name="set_headset_autoplay">Headset autoplay</string>
|
<string name="set_headset_autoplay">Headset autoplay</string>
|
||||||
<string name="set_headset_autoplay_desc">Always start playing when a headset is connected (may not work on all devices)</string>
|
<string name="set_headset_autoplay_desc">Always start playing when a headset is connected (may not work on all devices)</string>
|
||||||
<string name="set_replay_gain">ReplayGain (Experimental)</string>
|
<string name="set_replay_gain">ReplayGain (Experimental)</string>
|
||||||
|
|
|
@ -78,14 +78,6 @@
|
||||||
app:layout="@layout/item_header"
|
app:layout="@layout/item_header"
|
||||||
app:title="@string/set_audio">
|
app:title="@string/set_audio">
|
||||||
|
|
||||||
<org.oxycblt.auxio.settings.pref.M3SwitchPreference
|
|
||||||
app:defaultValue="true"
|
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
app:isPreferenceVisible="@bool/enable_audio_focus_setting"
|
|
||||||
app:key="KEY_AUDIO_FOCUS"
|
|
||||||
app:summary="@string/set_focus_desc"
|
|
||||||
app:title="@string/set_focus" />
|
|
||||||
|
|
||||||
<org.oxycblt.auxio.settings.pref.M3SwitchPreference
|
<org.oxycblt.auxio.settings.pref.M3SwitchPreference
|
||||||
app:defaultValue="false"
|
app:defaultValue="false"
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
|
|
Loading…
Reference in a new issue