playback: replace headset focus with headset autoplay

Turn the headset focus setting into the headset autoplay setting.

The way auxio handles headsets is...odd. Sometimes the MediaSession
handles it and Auxio could not care less, and sometimes Auxio actually
needs to handle it. As a result, the idea of being able to disable
headset focus is more or less moot because it will only apply to some
devices and not others.

On the other end, the way Auxio automatically begins playback once a
headset is plugged in is also quite weird. It only works on wired
headsets, and when it does, it overrides all other apps that might
also be playing audio. It's not to say that it's a bad feature, but
it's also one that I don't want to make the defualt. Auxio should
still play along within the confines of Android's expectations, after
all.

Replacing the existing "Headset focus" setting with a new "Headset
autoplay" setting solves both of these issues, as it prevents a
mis-guided disabling of the setting that doesn't actually disable
the feature and it relegates the quirky autoplay behavior to an
setting not enabled by default.
This commit is contained in:
OxygenCobalt 2022-03-05 20:34:12 -07:00
parent 6d003c308b
commit 1b791074ec
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
29 changed files with 140 additions and 166 deletions

View file

@ -19,6 +19,7 @@ from the system theme was used [#80]
#### What's Changed #### What's Changed
- All cover art is now cropped to a 1:1 aspect ratio - All cover art is now cropped to a 1:1 aspect ratio
- Headset focus has been replaced with headset autoplay. It can no longer be disabled.
#### Dev/Meta #### Dev/Meta
- Enabled elevation drop shadows below Android P for consistency - Enabled elevation drop shadows below Android P for consistency

View file

@ -103,7 +103,7 @@ dependencies {
implementation 'io.coil-kt:coil:2.0.0-alpha09' implementation 'io.coil-kt:coil:2.0.0-alpha09'
// Material // Material
implementation 'com.google.android.material:material:1.6.0-alpha02' implementation 'com.google.android.material:material:1.6.0-alpha03'
// --- DEBUG --- // --- DEBUG ---
@ -115,7 +115,6 @@ task ktlint(type: JavaExec, group: "verification") {
description = "Check Kotlin code style." description = "Check Kotlin code style."
mainClass.set("com.pinterest.ktlint.Main") mainClass.set("com.pinterest.ktlint.Main")
classpath = configurations.ktlint classpath = configurations.ktlint
args "src/**/*.kt" args "src/**/*.kt"
} }
check.dependsOn ktlint check.dependsOn ktlint
@ -124,6 +123,5 @@ task ktlintFormat(type: JavaExec, group: "formatting") {
description = "Fix Kotlin code style deviations." description = "Fix Kotlin code style deviations."
mainClass.set("com.pinterest.ktlint.Main") mainClass.set("com.pinterest.ktlint.Main")
classpath = configurations.ktlint classpath = configurations.ktlint
args "-F", "src/**/*.kt" args "-F", "src/**/*.kt"
} }

View file

@ -23,7 +23,6 @@ import androidx.appcompat.widget.PopupMenu
import androidx.core.view.children import androidx.core.view.children
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.Navigation.findNavController
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.R import org.oxycblt.auxio.R

View file

@ -51,6 +51,7 @@ class PlaybackNotification private constructor(
setCategory(NotificationCompat.CATEGORY_SERVICE) setCategory(NotificationCompat.CATEGORY_SERVICE)
setShowWhen(false) setShowWhen(false)
setSilent(true) setSilent(true)
setBadgeIconType(NotificationCompat.BADGE_ICON_NONE)
setContentIntent(context.newMainIntent()) setContentIntent(context.newMainIntent())
setVisibility(NotificationCompat.VISIBILITY_PUBLIC) setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

View file

@ -444,34 +444,38 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
/** /**
* A [BroadcastReceiver] for receiving general playback events from the system. * A [BroadcastReceiver] for receiving general playback events from the system.
* TODO: Don't fire when the service initially starts?
*/ */
private inner class PlaybackReceiver : BroadcastReceiver() { private inner class PlaybackReceiver : BroadcastReceiver() {
private var handledInitialHeadsetPlug = false private var initialHeadsetPlugEventHandled = false
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
when (intent.action) { when (intent.action) {
// --- SYSTEM EVENTS --- // --- SYSTEM EVENTS ---
// Technically the MediaSession seems to handle bluetooth events on their
// own, but keep this around as a fallback in the case that the former fails
// for whatever reason.
AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED -> { AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED -> {
when (intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1)) { when (intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1)) {
AudioManager.SCO_AUDIO_STATE_CONNECTED -> resumeFromPlug()
AudioManager.SCO_AUDIO_STATE_DISCONNECTED -> pauseFromPlug() AudioManager.SCO_AUDIO_STATE_DISCONNECTED -> pauseFromPlug()
AudioManager.SCO_AUDIO_STATE_CONNECTED -> maybeResumeFromPlug()
} }
} }
AudioManager.ACTION_AUDIO_BECOMING_NOISY -> pauseFromPlug() // MediaSession does not handle wired headsets for some reason, so also include
// this. Gotta love Android having two actions for more or less the same thing.
AudioManager.ACTION_HEADSET_PLUG -> { AudioManager.ACTION_HEADSET_PLUG -> {
if (handledInitialHeadsetPlug) { when (intent.getIntExtra("state", -1)) {
when (intent.getIntExtra("state", -1)) { 0 -> pauseFromPlug()
0 -> pauseFromPlug() 1 -> maybeResumeFromPlug()
1 -> resumeFromPlug()
}
} }
handledInitialHeadsetPlug = true initialHeadsetPlugEventHandled = true
} }
// I have never seen this ever happen but it might be useful
AudioManager.ACTION_AUDIO_BECOMING_NOISY -> pauseFromPlug()
// --- AUXIO EVENTS --- // --- AUXIO EVENTS ---
ACTION_PLAY_PAUSE -> playbackManager.setPlaying( ACTION_PLAY_PAUSE -> playbackManager.setPlaying(
!playbackManager.isPlaying !playbackManager.isPlaying
@ -496,25 +500,35 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
WidgetProvider.ACTION_WIDGET_UPDATE -> widgets.update() WidgetProvider.ACTION_WIDGET_UPDATE -> widgets.update()
} }
} }
}
/** /**
* Resume from a headset plug event, as long as its allowed. * Resume from a headset plug event in the case that the quirk is enabled.
*/ * This functionality remains a quirk for two reasons:
private fun resumeFromPlug() { * 1. Automatically resuming more or less overrides all other audio streams, which
if (playbackManager.song != null && settingsManager.doPlugMgt) { * is not that friendly
logD("Device connected, resuming") * 2. There is a bug where playback will always start when this service starts, mostly
playbackManager.setPlaying(true) * due to AudioManager.ACTION_HEADSET_PLUG always firing on startup. This is fixed, but
* I fear that it may not work on OEM skins that for whatever reason don't make this
* action fire.
*/
private fun maybeResumeFromPlug() {
if (playbackManager.song != null &&
settingsManager.headsetAutoplay &&
initialHeadsetPlugEventHandled
) {
logD("Device connected, resuming")
playbackManager.setPlaying(true)
}
} }
}
/** /**
* Pause from a headset plug, as long as its allowed. * Pause from a headset plug.
*/ */
private fun pauseFromPlug() { private fun pauseFromPlug() {
if (playbackManager.song != null && settingsManager.doPlugMgt) { if (playbackManager.song != null) {
logD("Device disconnected, pausing") logD("Device disconnected, pausing")
playbackManager.setPlaying(false) playbackManager.setPlaying(false)
}
} }
} }

View file

@ -37,27 +37,27 @@ import org.oxycblt.auxio.ui.Sort
class SettingsManager private constructor(context: Context) : class SettingsManager private constructor(context: Context) :
SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener {
private val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context) private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
init { init {
sharedPrefs.registerOnSharedPreferenceChangeListener(this) prefs.registerOnSharedPreferenceChangeListener(this)
} }
// --- VALUES --- // --- VALUES ---
/** The current theme */ /** The current theme */
val theme: Int val theme: Int
get() = sharedPrefs.getInt(KEY_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) get() = prefs.getInt(KEY_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
/** Whether the dark theme should be black or not */ /** Whether the dark theme should be black or not */
val useBlackTheme: Boolean val useBlackTheme: Boolean
get() = sharedPrefs.getBoolean(KEY_BLACK_THEME, false) get() = prefs.getBoolean(KEY_BLACK_THEME, false)
/** The current accent. */ /** The current accent. */
var accent: Accent var accent: Accent
get() = handleAccentCompat(sharedPrefs) get() = handleAccentCompat(prefs)
set(value) { set(value) {
sharedPrefs.edit { prefs.edit {
putInt(KEY_ACCENT, value.index) putInt(KEY_ACCENT, value.index)
apply() apply()
} }
@ -68,14 +68,14 @@ class SettingsManager private constructor(context: Context) :
* False if loop, true if shuffle. * False if loop, true if shuffle.
*/ */
val useAltNotifAction: Boolean val useAltNotifAction: Boolean
get() = sharedPrefs.getBoolean(KEY_USE_ALT_NOTIFICATION_ACTION, false) get() = prefs.getBoolean(KEY_USE_ALT_NOTIFICATION_ACTION, false)
/** The current library tabs preferred by the user. */ /** The current library tabs preferred by the user. */
var libTabs: Array<Tab> var libTabs: Array<Tab>
get() = Tab.fromSequence(sharedPrefs.getInt(KEY_LIB_TABS, Tab.SEQUENCE_DEFAULT)) get() = Tab.fromSequence(prefs.getInt(KEY_LIB_TABS, Tab.SEQUENCE_DEFAULT))
?: Tab.fromSequence(Tab.SEQUENCE_DEFAULT)!! ?: Tab.fromSequence(Tab.SEQUENCE_DEFAULT)!!
set(value) { set(value) {
sharedPrefs.edit { prefs.edit {
putInt(KEY_LIB_TABS, Tab.toSequence(value)) putInt(KEY_LIB_TABS, Tab.toSequence(value))
apply() apply()
} }
@ -83,51 +83,51 @@ class SettingsManager private constructor(context: Context) :
/** Whether to load embedded covers */ /** Whether to load embedded covers */
val showCovers: Boolean val showCovers: Boolean
get() = sharedPrefs.getBoolean(KEY_SHOW_COVERS, true) get() = prefs.getBoolean(KEY_SHOW_COVERS, true)
/** Whether to ignore MediaStore covers */ /** Whether to ignore MediaStore covers */
val useQualityCovers: Boolean val useQualityCovers: Boolean
get() = sharedPrefs.getBoolean(KEY_QUALITY_COVERS, false) get() = prefs.getBoolean(KEY_QUALITY_COVERS, false)
/** Whether to round album covers */ /** Whether to round album covers */
val roundCovers: Boolean val roundCovers: Boolean
get() = sharedPrefs.getBoolean(KEY_ROUND_COVERS, false) get() = prefs.getBoolean(KEY_ROUND_COVERS, false)
/** Whether to do Audio focus. */ /** Whether to do Audio focus. */
val doAudioFocus: Boolean val doAudioFocus: Boolean
get() = sharedPrefs.getBoolean(KEY_AUDIO_FOCUS, true) get() = prefs.getBoolean(KEY_AUDIO_FOCUS, true)
/** Whether to resume/stop playback when a headset is connected/disconnected. */ /** Whether to resume playback when a headset is connected (may not work well in all cases) */
val doPlugMgt: Boolean val headsetAutoplay: Boolean
get() = sharedPrefs.getBoolean(KEY_PLUG_MANAGEMENT, true) get() = prefs.getBoolean(KEY_HEADSET_AUTOPLAY, false)
/** The current ReplayGain configuration */ /** The current ReplayGain configuration */
val replayGainMode: ReplayGainMode val replayGainMode: ReplayGainMode
get() = ReplayGainMode.fromInt(sharedPrefs.getInt(KEY_REPLAY_GAIN, Int.MIN_VALUE)) get() = ReplayGainMode.fromInt(prefs.getInt(KEY_REPLAY_GAIN, Int.MIN_VALUE))
?: ReplayGainMode.OFF ?: ReplayGainMode.OFF
/** What queue to create when a song is selected (ex. From All Songs or Search) */ /** What queue to create when a song is selected (ex. From All Songs or Search) */
val songPlaybackMode: PlaybackMode val songPlaybackMode: PlaybackMode
get() = PlaybackMode.fromInt(sharedPrefs.getInt(KEY_SONG_PLAYBACK_MODE, Int.MIN_VALUE)) get() = PlaybackMode.fromInt(prefs.getInt(KEY_SONG_PLAYBACK_MODE, Int.MIN_VALUE))
?: PlaybackMode.ALL_SONGS ?: PlaybackMode.ALL_SONGS
/** Whether shuffle should stay on when a new song is selected. */ /** Whether shuffle should stay on when a new song is selected. */
val keepShuffle: Boolean val keepShuffle: Boolean
get() = sharedPrefs.getBoolean(KEY_KEEP_SHUFFLE, true) get() = prefs.getBoolean(KEY_KEEP_SHUFFLE, true)
/** Whether to rewind when the back button is pressed. */ /** Whether to rewind when the back button is pressed. */
val rewindWithPrev: Boolean val rewindWithPrev: Boolean
get() = sharedPrefs.getBoolean(KEY_PREV_REWIND, true) get() = prefs.getBoolean(KEY_PREV_REWIND, true)
/** Whether [org.oxycblt.auxio.playback.state.LoopMode.TRACK] should pause when the track repeats */ /** Whether [org.oxycblt.auxio.playback.state.LoopMode.TRACK] should pause when the track repeats */
val pauseOnLoop: Boolean val pauseOnLoop: Boolean
get() = sharedPrefs.getBoolean(KEY_LOOP_PAUSE, false) get() = prefs.getBoolean(KEY_LOOP_PAUSE, false)
/** The current filter mode of the search tab */ /** The current filter mode of the search tab */
var searchFilterMode: DisplayMode? var searchFilterMode: DisplayMode?
get() = DisplayMode.fromFilterInt(sharedPrefs.getInt(KEY_SEARCH_FILTER_MODE, Int.MIN_VALUE)) get() = DisplayMode.fromFilterInt(prefs.getInt(KEY_SEARCH_FILTER_MODE, Int.MIN_VALUE))
set(value) { set(value) {
sharedPrefs.edit { prefs.edit {
putInt(KEY_SEARCH_FILTER_MODE, DisplayMode.toFilterInt(value)) putInt(KEY_SEARCH_FILTER_MODE, DisplayMode.toFilterInt(value))
apply() apply()
} }
@ -135,10 +135,10 @@ class SettingsManager private constructor(context: Context) :
/** The song sort mode on HomeFragment **/ /** The song sort mode on HomeFragment **/
var libSongSort: Sort var libSongSort: Sort
get() = Sort.fromInt(sharedPrefs.getInt(KEY_LIB_SONGS_SORT, Int.MIN_VALUE)) get() = Sort.fromInt(prefs.getInt(KEY_LIB_SONGS_SORT, Int.MIN_VALUE))
?: Sort.ByName(true) ?: Sort.ByName(true)
set(value) { set(value) {
sharedPrefs.edit { prefs.edit {
putInt(KEY_LIB_SONGS_SORT, value.toInt()) putInt(KEY_LIB_SONGS_SORT, value.toInt())
apply() apply()
} }
@ -146,10 +146,10 @@ class SettingsManager private constructor(context: Context) :
/** The album sort mode on HomeFragment **/ /** The album sort mode on HomeFragment **/
var libAlbumSort: Sort var libAlbumSort: Sort
get() = Sort.fromInt(sharedPrefs.getInt(KEY_LIB_ALBUMS_SORT, Int.MIN_VALUE)) get() = Sort.fromInt(prefs.getInt(KEY_LIB_ALBUMS_SORT, Int.MIN_VALUE))
?: Sort.ByName(true) ?: Sort.ByName(true)
set(value) { set(value) {
sharedPrefs.edit { prefs.edit {
putInt(KEY_LIB_ALBUMS_SORT, value.toInt()) putInt(KEY_LIB_ALBUMS_SORT, value.toInt())
apply() apply()
} }
@ -157,10 +157,10 @@ class SettingsManager private constructor(context: Context) :
/** The artist sort mode on HomeFragment **/ /** The artist sort mode on HomeFragment **/
var libArtistSort: Sort var libArtistSort: Sort
get() = Sort.fromInt(sharedPrefs.getInt(KEY_LIB_ARTISTS_SORT, Int.MIN_VALUE)) get() = Sort.fromInt(prefs.getInt(KEY_LIB_ARTISTS_SORT, Int.MIN_VALUE))
?: Sort.ByName(true) ?: Sort.ByName(true)
set(value) { set(value) {
sharedPrefs.edit { prefs.edit {
putInt(KEY_LIB_ARTISTS_SORT, value.toInt()) putInt(KEY_LIB_ARTISTS_SORT, value.toInt())
apply() apply()
} }
@ -168,10 +168,10 @@ class SettingsManager private constructor(context: Context) :
/** The genre sort mode on HomeFragment **/ /** The genre sort mode on HomeFragment **/
var libGenreSort: Sort var libGenreSort: Sort
get() = Sort.fromInt(sharedPrefs.getInt(KEY_LIB_GENRES_SORT, Int.MIN_VALUE)) get() = Sort.fromInt(prefs.getInt(KEY_LIB_GENRES_SORT, Int.MIN_VALUE))
?: Sort.ByName(true) ?: Sort.ByName(true)
set(value) { set(value) {
sharedPrefs.edit { prefs.edit {
putInt(KEY_LIB_GENRES_SORT, value.toInt()) putInt(KEY_LIB_GENRES_SORT, value.toInt())
apply() apply()
} }
@ -179,10 +179,10 @@ class SettingsManager private constructor(context: Context) :
/** The detail album sort mode **/ /** The detail album sort mode **/
var detailAlbumSort: Sort var detailAlbumSort: Sort
get() = Sort.fromInt(sharedPrefs.getInt(KEY_DETAIL_ALBUM_SORT, Int.MIN_VALUE)) get() = Sort.fromInt(prefs.getInt(KEY_DETAIL_ALBUM_SORT, Int.MIN_VALUE))
?: Sort.ByName(true) ?: Sort.ByName(true)
set(value) { set(value) {
sharedPrefs.edit { prefs.edit {
putInt(KEY_DETAIL_ALBUM_SORT, value.toInt()) putInt(KEY_DETAIL_ALBUM_SORT, value.toInt())
apply() apply()
} }
@ -190,10 +190,10 @@ class SettingsManager private constructor(context: Context) :
/** The detail artist sort mode **/ /** The detail artist sort mode **/
var detailArtistSort: Sort var detailArtistSort: Sort
get() = Sort.fromInt(sharedPrefs.getInt(KEY_DETAIL_ARTIST_SORT, Int.MIN_VALUE)) get() = Sort.fromInt(prefs.getInt(KEY_DETAIL_ARTIST_SORT, Int.MIN_VALUE))
?: Sort.ByYear(false) ?: Sort.ByYear(false)
set(value) { set(value) {
sharedPrefs.edit { prefs.edit {
putInt(KEY_DETAIL_ARTIST_SORT, value.toInt()) putInt(KEY_DETAIL_ARTIST_SORT, value.toInt())
apply() apply()
} }
@ -201,10 +201,10 @@ class SettingsManager private constructor(context: Context) :
/** The detail genre sort mode **/ /** The detail genre sort mode **/
var detailGenreSort: Sort var detailGenreSort: Sort
get() = Sort.fromInt(sharedPrefs.getInt(KEY_DETAIL_GENRE_SORT, Int.MIN_VALUE)) get() = Sort.fromInt(prefs.getInt(KEY_DETAIL_GENRE_SORT, Int.MIN_VALUE))
?: Sort.ByName(true) ?: Sort.ByName(true)
set(value) { set(value) {
sharedPrefs.edit { prefs.edit {
putInt(KEY_DETAIL_GENRE_SORT, value.toInt()) putInt(KEY_DETAIL_GENRE_SORT, value.toInt())
apply() apply()
} }
@ -281,7 +281,7 @@ class SettingsManager private constructor(context: Context) :
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_AUDIO_FOCUS = "KEY_AUDIO_FOCUS"
const val KEY_PLUG_MANAGEMENT = "KEY_PLUG_MGT" const val KEY_HEADSET_AUTOPLAY = "auxio_headset_autoplay"
const val KEY_REPLAY_GAIN = "auxio_replay_gain" const val KEY_REPLAY_GAIN = "auxio_replay_gain"
const val KEY_SONG_PLAYBACK_MODE = "KEY_SONG_PLAY_MODE2" const val KEY_SONG_PLAYBACK_MODE = "KEY_SONG_PLAY_MODE2"

View file

@ -37,12 +37,3 @@ fun assertBackgroundThread() {
"This operation must be ran on a background thread" "This operation must be ran on a background thread"
} }
} }
/**
* Assert that we are on a foreground thread.
*/
fun assertMainThread() {
check(Looper.myLooper() == Looper.getMainLooper()) {
"This operation must be ran on the main thread"
}
}

View file

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:parentTag="android.widget.FrameLayout">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/shuffle_fab"
style="@style/Widget.Auxio.FloatingActionButton.Adaptive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_medium"
android:contentDescription="@string/desc_shuffle_all"
android:src="@drawable/ic_shuffle"
app:icon="@drawable/ic_shuffle" />
</merge>
</layout>

View file

@ -83,10 +83,8 @@
<string name="set_audio">صوتيات</string> <string name="set_audio">صوتيات</string>
<string name="set_focus">تركيز الصوت</string> <string name="set_focus">تركيز الصوت</string>
<string name="set_focus_desc">ايقاف مؤقت عند تشغيل صوت آخر (كالمكالمات)</string> <string name="set_focus_desc">ايقاف مؤقت عند تشغيل صوت آخر (كالمكالمات)</string>
<string name="set_plug_mgt">تركيز السماعة</string>
<string name="set_plug_mgt_desc">تشغيل/ايقاف مؤقت عند حدوث تغيير في اتصال السماعة</string>
<string name="set_replay_gain">صخب الصوت (تجريبي)</string> <string name="set_replay_gain">صخب الصوت (تجريبي)</string>
<string name="set_replay_gain_off">اطفاء</string> <string name="set_off">اطفاء</string>
<string name="set_replay_gain_track">تفضيل المقطع</string> <string name="set_replay_gain_track">تفضيل المقطع</string>
<string name="set_replay_gain_album">تفضيل الالبوم</string> <string name="set_replay_gain_album">تفضيل الالبوم</string>
<string name="set_replay_gain_dynamic">ديناميكي</string> <string name="set_replay_gain_dynamic">ديناميكي</string>

View file

@ -67,8 +67,6 @@
<string name="set_audio">"Zvuk"</string> <string name="set_audio">"Zvuk"</string>
<string name="set_focus">"Zaměření zvuku"</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_focus_desc">Pozastavit při přehrávání jiného zvuku (např. hovor)</string>
<string name="set_plug_mgt">"Zaměření sluchátek"</string>
<string name="set_plug_mgt_desc">"Přehrát/pozastavit při změně připojení sluchátek"</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>

View file

@ -71,10 +71,8 @@
<string name="set_audio">Audio</string> <string name="set_audio">Audio</string>
<string name="set_focus">Audiofokus</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_focus_desc">Pausieren wenn andere Töne abspielt wird [Bsp. Anrufe]</string>
<string name="set_plug_mgt">Kopfhörerfokus</string>
<string name="set_plug_mgt_desc">Abspielen/Pausieren wenn sich die Kopfhörerverbindung ändert</string>
<string name="set_replay_gain">ReplayGain (Experimentell)</string> <string name="set_replay_gain">ReplayGain (Experimentell)</string>
<string name="set_replay_gain_off">Aus</string> <string name="set_off">Aus</string>
<string name="set_replay_gain_track">Titel bevorzugen</string> <string name="set_replay_gain_track">Titel bevorzugen</string>
<string name="set_replay_gain_album">Album bevorzugen</string> <string name="set_replay_gain_album">Album bevorzugen</string>
@ -151,7 +149,7 @@
<item quantity="other">%d Alben</item> <item quantity="other">%d Alben</item>
</plurals> </plurals>
<string name="info_app_desc">Ein einfacher, rationaler Musikplayer für Android.</string> <string name="info_app_desc">Ein einfacher, rationaler Musikplayer für Android.</string>
<string name="info_widget_desc">Spielende Musik anzeigen und kontrollieren</string> <string name="info_widget_desc">Musikwiedergabe anzeigen und kontrollieren</string>
<string name="lbl_sort_artist">Künstler</string> <string name="lbl_sort_artist">Künstler</string>
<string name="lbl_sort_album">Album</string> <string name="lbl_sort_album">Album</string>
<string name="lbl_sort_year">Jahr</string> <string name="lbl_sort_year">Jahr</string>
@ -173,5 +171,4 @@
<string name="desc_clear_queue_item">Lied in der Warteschlange löschen</string> <string name="desc_clear_queue_item">Lied in der Warteschlange löschen</string>
<string name="desc_tab_handle">Tab versetzen</string> <string name="desc_tab_handle">Tab versetzen</string>
<string name="def_artist">Unbekannter Künstler</string> <string name="def_artist">Unbekannter Künstler</string>
</resources> </resources>

View file

@ -83,10 +83,8 @@
<string name="set_audio">Sonido</string> <string name="set_audio">Sonido</string>
<string name="set_focus">Enfoque de 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_focus_desc">Pausar cuando se reproduce otro sonido (Ej: llamadas)</string>
<string name="set_plug_mgt">Enfoque de auriculares</string>
<string name="set_plug_mgt_desc">Reproducir/Pausar dependiendo de la conexión de auriculares</string>
<string name="set_replay_gain">ReplayGain (Experimental)</string> <string name="set_replay_gain">ReplayGain (Experimental)</string>
<string name="set_replay_gain_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>
<string name="set_replay_gain_album">Por álbum</string> <string name="set_replay_gain_album">Por álbum</string>
<string name="set_replay_gain_dynamic">Dinámico</string> <string name="set_replay_gain_dynamic">Dinámico</string>

View file

@ -50,7 +50,6 @@
<string name="set_audio">Audio</string> <string name="set_audio">Audio</string>
<string name="set_focus">Audio Focus</string> <string name="set_focus">Audio Focus</string>
<string name="set_plug_mgt">Branchement du casque</string>
<string name="set_behavior">Comportement</string> <string name="set_behavior">Comportement</string>

View file

@ -41,7 +41,6 @@
<string name="set_audio">ऑडियो</string> <string name="set_audio">ऑडियो</string>
<string name="set_focus">ऑडियो फोकस</string> <string name="set_focus">ऑडियो फोकस</string>
<string name="set_plug_mgt">हेडसेट प्लग</string>
<string name="set_behavior">चाल चलन</string> <string name="set_behavior">चाल चलन</string>

View file

@ -49,7 +49,6 @@
<string name="set_audio">Hang</string> <string name="set_audio">Hang</string>
<string name="set_focus">Hangfókusz</string> <string name="set_focus">Hangfókusz</string>
<string name="set_plug_mgt">Fejhallgató csatlakozó</string>
<string name="set_behavior">Működés</string> <string name="set_behavior">Működés</string>

View file

@ -50,7 +50,6 @@
<string name="set_audio">Audio</string> <string name="set_audio">Audio</string>
<string name="set_focus">Focus audio</string> <string name="set_focus">Focus audio</string>
<string name="set_plug_mgt">Inserimento cuffie</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>

View file

@ -48,7 +48,6 @@
<string name="set_audio">오디오</string> <string name="set_audio">오디오</string>
<string name="set_focus">오디오 포커스</string> <string name="set_focus">오디오 포커스</string>
<string name="set_plug_mgt">헤드셋 연결</string>
<string name="set_behavior">동작</string> <string name="set_behavior">동작</string>

View file

@ -72,8 +72,6 @@
<string name="set_audio">Audio</string> <string name="set_audio">Audio</string>
<string name="set_focus">Audiofocus</string> <string name="set_focus">Audiofocus</string>
<string name="set_focus_desc">Pauze wanneer andere audio speelt (ex. Gesprekken)</string> <string name="set_focus_desc">Pauze wanneer andere audio speelt (ex. Gesprekken)</string>
<string name="set_plug_mgt">Headset-pluggen</string>
<string name="set_plug_mgt_desc">Afspelen/Pauzeren wanneer de headsetaansluiting verandert</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>

View file

@ -49,7 +49,6 @@
<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_focus">Wyciszanie otoczenia</string>
<string name="set_plug_mgt">Podłączanie słuchawek</string>
<string name="set_behavior">Zachowanie</string> <string name="set_behavior">Zachowanie</string>

View file

@ -49,7 +49,6 @@
<string name="set_audio">Áudio</string> <string name="set_audio">Áudio</string>
<string name="set_focus">Foco do áudio</string> <string name="set_focus">Foco do áudio</string>
<string name="set_plug_mgt">Entrada do fone de ouvido</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>

View file

@ -50,7 +50,6 @@
<string name="set_audio">Áudio</string> <string name="set_audio">Áudio</string>
<string name="set_focus">Foco de áudio</string> <string name="set_focus">Foco de áudio</string>
<string name="set_plug_mgt">Entrada do fone de ouvido</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>

View file

@ -50,7 +50,6 @@
<string name="set_audio">Audio</string> <string name="set_audio">Audio</string>
<string name="set_focus">Concentrare audio</string> <string name="set_focus">Concentrare audio</string>
<string name="set_plug_mgt">Conexiune cu cască</string>
<string name="set_behavior">Comportament</string> <string name="set_behavior">Comportament</string>

View file

@ -83,10 +83,8 @@
<string name="set_audio">Звук</string> <string name="set_audio">Звук</string>
<string name="set_focus">Аудио-фокус</string> <string name="set_focus">Аудио-фокус</string>
<string name="set_focus_desc">Ставить на паузу при звонках</string> <string name="set_focus_desc">Ставить на паузу при звонках</string>
<string name="set_plug_mgt">Гарнитурный фокус</string>
<string name="set_plug_mgt_desc">Ставить на паузу при отключении гарнитуры</string>
<string name="set_replay_gain">ReplayGain (экспериментально)</string> <string name="set_replay_gain">ReplayGain (экспериментально)</string>
<string name="set_replay_gain_off">Выкл.</string> <string name="set_off">Выкл.</string>
<string name="set_replay_gain_track">По треку</string> <string name="set_replay_gain_track">По треку</string>
<string name="set_replay_gain_album">По альбому</string> <string name="set_replay_gain_album">По альбому</string>
<string name="set_replay_gain_dynamic">Динамический</string> <string name="set_replay_gain_dynamic">Динамический</string>

View file

@ -83,10 +83,8 @@
<string name="set_audio">音频</string> <string name="set_audio">音频</string>
<string name="set_focus">音频焦点</string> <string name="set_focus">音频焦点</string>
<string name="set_focus_desc">有其它音频播放(比如电话)时暂停</string> <string name="set_focus_desc">有其它音频播放(比如电话)时暂停</string>
<string name="set_plug_mgt">设备焦点</string>
<string name="set_plug_mgt_desc">设备连接状态改变时播放/暂停</string>
<string name="set_replay_gain">回放增益</string> <string name="set_replay_gain">回放增益</string>
<string name="set_replay_gain_off"></string> <string name="set_off"></string>
<string name="set_replay_gain_track">偏好曲目</string> <string name="set_replay_gain_track">偏好曲目</string>
<string name="set_replay_gain_album">偏好专辑</string> <string name="set_replay_gain_album">偏好专辑</string>
<string name="set_replay_gain_dynamic">动态</string> <string name="set_replay_gain_dynamic">动态</string>

View file

@ -48,7 +48,6 @@
<string name="set_audio">音訊</string> <string name="set_audio">音訊</string>
<string name="set_focus">音頻焦點</string> <string name="set_focus">音頻焦點</string>
<string name="set_plug_mgt">耳機插頭</string>
<string name="set_behavior">行為</string> <string name="set_behavior">行為</string>
<string name="set_keep_shuffle">記住隨機播放</string> <string name="set_keep_shuffle">記住隨機播放</string>

View file

@ -30,7 +30,7 @@
</string-array> </string-array>
<array name="entries_replay_gain"> <array name="entries_replay_gain">
<item>@string/set_replay_gain_off</item> <item>@string/set_off</item>
<item>@string/set_replay_gain_track</item> <item>@string/set_replay_gain_track</item>
<item>@string/set_replay_gain_album</item> <item>@string/set_replay_gain_album</item>
<item>@string/set_replay_gain_dynamic</item> <item>@string/set_replay_gain_dynamic</item>

View file

@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<!-- Info namespace | App labels --> <!-- Info namespace | App labels -->
<string name="info_app_desc">A simple, rational music player for android.</string> <string name="info_app_desc">A simple, rational music player for android.</string>
<string name="info_channel_name">Music Playback</string> <string name="info_channel_name">Music Playback</string>
<string name="info_widget_desc">View and control playing music</string> <string name="info_widget_desc">View and control music playback</string>
<!-- Label Namespace | Static Labels --> <!-- Label Namespace | Static Labels -->
<string name="lbl_retry">Retry</string> <string name="lbl_retry">Retry</string>
@ -83,10 +82,9 @@
<string name="set_audio">Audio</string> <string name="set_audio">Audio</string>
<string name="set_focus">Audio focus</string> <string name="set_focus">Audio focus</string>
<string name="set_focus_desc">Pause when other audio plays (ex. Calls)</string> <string name="set_focus_desc">Pause when other audio plays (ex. Calls)</string>
<string name="set_plug_mgt">Headset focus</string> <string name="set_headset_autoplay">Headset autoplay</string>
<string name="set_plug_mgt_desc">Play/Pause when the headset connection changes</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>
<string name="set_replay_gain_off">Off</string>
<string name="set_replay_gain_track">Prefer track</string> <string name="set_replay_gain_track">Prefer track</string>
<string name="set_replay_gain_album">Prefer album</string> <string name="set_replay_gain_album">Prefer album</string>
<string name="set_replay_gain_dynamic">Dynamic</string> <string name="set_replay_gain_dynamic">Dynamic</string>
@ -108,6 +106,8 @@
<string name="set_excluded">Excluded folders</string> <string name="set_excluded">Excluded folders</string>
<string name="set_excluded_desc">The content of excluded folders is hidden from your library</string> <string name="set_excluded_desc">The content of excluded folders is hidden from your library</string>
<string name="set_off">Off</string>
<!-- Error Namespace | Error Labels --> <!-- Error Namespace | Error Labels -->
<string name="err_no_music">No music found</string> <string name="err_no_music">No music found</string>
<string name="err_load_failed">Music loading failed</string> <string name="err_load_failed">Music loading failed</string>

View file

@ -87,11 +87,11 @@
app:title="@string/set_focus" /> app:title="@string/set_focus" />
<org.oxycblt.auxio.settings.pref.M3SwitchPreference <org.oxycblt.auxio.settings.pref.M3SwitchPreference
app:defaultValue="true" app:defaultValue="false"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="KEY_PLUG_MGT" app:key="auxio_headset_autoplay"
app:summary="@string/set_plug_mgt_desc" app:summary="@string/set_headset_autoplay_desc"
app:title="@string/set_plug_mgt" /> app:title="@string/set_headset_autoplay" />
<org.oxycblt.auxio.settings.pref.IntListPreference <org.oxycblt.auxio.settings.pref.IntListPreference
app:defaultValue="@integer/replay_gain_off" app:defaultValue="@integer/replay_gain_off"

View file

@ -1,15 +1,24 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# This script automatically installs exoplayer with the necessary components.
# This is written in version-agnostic python 3, because I'd rather not have to # This script automatically assembles any required ExoPlayer extensions or components as
# deal with the insanity of bash. # an AAR blob. This method is not only faster than depending on ExoPlayer outright as we
# only need to build our components once, it's also easier to use with Android Studio, which
# tends to get bogged down when we include a massive source repository as part of the gradle
# project. This script may change from time to time depending on the components or extensions
# that I leverage. It's recommended to re-run it after every release to ensure consistent
# behavior.
# As for why I wrote this in Python and not Bash, it's because Bash really does not have
# the capabilities for a nice, seamless pre-build process.
import os import os
import platform import platform
import sys import sys
import subprocess import subprocess
import re import re
# WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE FLAC EXTENSION. # WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE FLAC EXTENSION AND
# IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE. # THE GRADLE DEPENDENCY. IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
EXO_VERSION = "2.17.0" EXO_VERSION = "2.17.0"
FLAC_VERSION = "1.3.2" FLAC_VERSION = "1.3.2"
@ -19,32 +28,38 @@ INFO="\033[1;94m"
OK="\033[1;92m" OK="\033[1;92m"
NC="\033[0m" NC="\033[0m"
system = platform.system()
# We do some shell scripting later on, so we can't support windows. # We do some shell scripting later on, so we can't support windows.
system = platform.system()
if system not in ["Linux", "Darwin"]: if system not in ["Linux", "Darwin"]:
print("fatal: unsupported platform " + system) print("fatal: unsupported platform " + system)
sys.exit(1) sys.exit(1)
def sh(cmd): def sh(cmd):
print(INFO + "execute: " + NC + cmd)
code = subprocess.call(["sh", "-c", "set -e; " + cmd]) code = subprocess.call(["sh", "-c", "set -e; " + cmd])
if code != 0: if code != 0:
print(FATAL + "fatal:" + NC + " command failed with exit code " + str(code)) print(FATAL + "fatal:" + NC + " command failed with exit code " + str(code))
sys.exit(1) sys.exit(1)
start_path = os.path.join(os.path.abspath(os.curdir)) start_path = os.path.join(os.path.abspath(os.curdir))
libs_path = os.path.join(start_path, "app", "libs") libs_path = os.path.join(start_path, "app", "libs")
exoplayer_path = os.path.join(start_path, "app", "build", "srclibs", "exoplayer")
if os.path.exists(libs_path): if os.path.exists(libs_path):
reinstall = input(INFO + "info:" + NC + " exoplayer is already installed. would you like to reinstall it? [y/n] ") reinstall = input(INFO + "info:" + NC + " exoplayer is already installed. " +
"would you like to reinstall it? [y/n] ")
if not re.match("[yY][eE][sS]|[yY]", reinstall): if not re.match("[yY][eE][sS]|[yY]", reinstall):
sys.exit(0) sys.exit(0)
ndk_path = os.getenv("NDK_PATH") exoplayer_path = os.path.join(start_path, "app", "build", "srclibs", "exoplayer")
# Ensure that there is always an SDK environment variable.
# Technically there is also an sdk.dir field in local.properties, but that does
# not work when you clone a project without a local.properties.
if os.getenv("ANDROID_HOME") is None and os.getenv("ANDROID_SDK_ROOT") is None:
print(FATAL + "fatal:" + NC + " sdk location not found. please define " +
"ANDROID_HOME/ANDROID_SDK_ROOT before continuing.")
sys.exit(1)
ndk_path = os.getenv("NDK_PATH")
if ndk_path is None or not os.path.isfile(os.path.join(ndk_path, "ndk-build")): if ndk_path is None or not os.path.isfile(os.path.join(ndk_path, "ndk-build")):
# We don't have a proper path. Do some digging on the Android SDK directory # We don't have a proper path. Do some digging on the Android SDK directory
# to see if we can find it. # to see if we can find it.
@ -54,14 +69,13 @@ if ndk_path is None or not os.path.isfile(os.path.join(ndk_path, "ndk-build")):
ndk_root = os.path.join(os.getenv("HOME"), "Library", "Android", "sdk", "ndk") ndk_root = os.path.join(os.getenv("HOME"), "Library", "Android", "sdk", "ndk")
candidates = [] candidates = []
for entry in os.scandir(ndk_root): for entry in os.scandir(ndk_root):
if entry.is_dir(): if entry.is_dir():
candidates.append(entry.path) candidates.append(entry.path)
if len(candidates) > 0: if len(candidates) > 0:
print(WARN + "warn:" + NC + " NDK_PATH was not set or invalid. multiple candidates were found however:") print(WARN + "warn:" + NC + " NDK_PATH was not set or invalid. multiple " +
"candidates were found however:")
for i, candidate in enumerate(candidates): for i, candidate in enumerate(candidates):
print("[" + str(i) + "] " + candidate) print("[" + str(i) + "] " + candidate)
@ -70,9 +84,12 @@ if ndk_path is None or not os.path.isfile(os.path.join(ndk_path, "ndk-build")):
except: except:
ndk_path = candidates[0] ndk_path = candidates[0]
else: else:
print(FATAL + "fatal:" + NC + " the android ndk was not installed at a recognized location.") print(FATAL + "fatal:" + NC + " the android ndk was not installed at a " +
"recognized location.")
system.exit(1) system.exit(1)
ndk_build_path = os.path.join(ndk_path, "ndk-build")
# Now try to install ExoPlayer. # Now try to install ExoPlayer.
sh("rm -rf " + exoplayer_path) sh("rm -rf " + exoplayer_path)
sh("rm -rf " + libs_path) sh("rm -rf " + libs_path)
@ -82,18 +99,15 @@ sh("git clone https://github.com/google/ExoPlayer.git " + exoplayer_path)
os.chdir(exoplayer_path) os.chdir(exoplayer_path)
sh("git checkout r" + EXO_VERSION) sh("git checkout r" + EXO_VERSION)
print(INFO + "info:" + NC + " installing flac extension...") print(INFO + "info:" + NC + " assembling flac extension...")
flac_ext_aar_path = os.path.join(exoplayer_path, "extensions", "flac",
"buildout", "outputs", "aar", "extension-flac-release.aar")
flac_ext_jni_path = os.path.join("extensions", "flac", "src", "main", "jni") flac_ext_jni_path = os.path.join("extensions", "flac", "src", "main", "jni")
ndk_build_path = os.path.join(ndk_path, "ndk-build")
os.chdir(flac_ext_jni_path)
sh('curl "https://ftp.osuosl.org/pub/xiph/releases/flac/flac-' + FLAC_VERSION + '.tar.xz" | tar xJ && mv "flac-' + FLAC_VERSION + '" flac')
sh(ndk_build_path + " APP_ABI=all -j4")
print(INFO + "info:" + NC + " assembling libraries") os.chdir(flac_ext_jni_path)
flac_ext_aar_path = os.path.join( sh('curl "https://ftp.osuosl.org/pub/xiph/releases/flac/flac-' + FLAC_VERSION +
exoplayer_path, "extensions", "flac", "buildout", '.tar.xz" | tar xJ && mv "flac-' + FLAC_VERSION + '" flac')
"outputs", "aar", "extension-flac-release.aar" sh(ndk_build_path + " APP_ABI=all -j4")
)
os.chdir(exoplayer_path) os.chdir(exoplayer_path)
sh("./gradlew extension-flac:bundleReleaseAar") sh("./gradlew extension-flac:bundleReleaseAar")
@ -102,4 +116,4 @@ os.chdir(start_path)
sh("mkdir " + libs_path) sh("mkdir " + libs_path)
sh("cp " + flac_ext_aar_path + " " + libs_path) sh("cp " + flac_ext_aar_path + " " + libs_path)
print(OK + "success:" + NC + " completed pre-build.") print(OK + "success:" + NC + " completed pre-build")