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:
parent
6d003c308b
commit
1b791074ec
29 changed files with 140 additions and 166 deletions
|
@ -19,6 +19,7 @@ from the system theme was used [#80]
|
|||
|
||||
#### What's Changed
|
||||
- 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
|
||||
- Enabled elevation drop shadows below Android P for consistency
|
||||
|
|
|
@ -103,7 +103,7 @@ dependencies {
|
|||
implementation 'io.coil-kt:coil:2.0.0-alpha09'
|
||||
|
||||
// Material
|
||||
implementation 'com.google.android.material:material:1.6.0-alpha02'
|
||||
implementation 'com.google.android.material:material:1.6.0-alpha03'
|
||||
|
||||
// --- DEBUG ---
|
||||
|
||||
|
@ -115,7 +115,6 @@ task ktlint(type: JavaExec, group: "verification") {
|
|||
description = "Check Kotlin code style."
|
||||
mainClass.set("com.pinterest.ktlint.Main")
|
||||
classpath = configurations.ktlint
|
||||
|
||||
args "src/**/*.kt"
|
||||
}
|
||||
check.dependsOn ktlint
|
||||
|
@ -124,6 +123,5 @@ task ktlintFormat(type: JavaExec, group: "formatting") {
|
|||
description = "Fix Kotlin code style deviations."
|
||||
mainClass.set("com.pinterest.ktlint.Main")
|
||||
classpath = configurations.ktlint
|
||||
|
||||
args "-F", "src/**/*.kt"
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import androidx.appcompat.widget.PopupMenu
|
|||
import androidx.core.view.children
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.Navigation.findNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.R
|
||||
|
|
|
@ -51,6 +51,7 @@ class PlaybackNotification private constructor(
|
|||
setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
setShowWhen(false)
|
||||
setSilent(true)
|
||||
setBadgeIconType(NotificationCompat.BADGE_ICON_NONE)
|
||||
setContentIntent(context.newMainIntent())
|
||||
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
|
||||
|
|
|
@ -444,34 +444,38 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
|
|||
|
||||
/**
|
||||
* 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 var handledInitialHeadsetPlug = false
|
||||
private var initialHeadsetPlugEventHandled = false
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (intent.action) {
|
||||
// --- 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 -> {
|
||||
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_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 -> {
|
||||
if (handledInitialHeadsetPlug) {
|
||||
when (intent.getIntExtra("state", -1)) {
|
||||
0 -> pauseFromPlug()
|
||||
1 -> resumeFromPlug()
|
||||
}
|
||||
when (intent.getIntExtra("state", -1)) {
|
||||
0 -> pauseFromPlug()
|
||||
1 -> maybeResumeFromPlug()
|
||||
}
|
||||
|
||||
handledInitialHeadsetPlug = true
|
||||
initialHeadsetPlugEventHandled = true
|
||||
}
|
||||
|
||||
// I have never seen this ever happen but it might be useful
|
||||
AudioManager.ACTION_AUDIO_BECOMING_NOISY -> pauseFromPlug()
|
||||
|
||||
// --- AUXIO EVENTS ---
|
||||
ACTION_PLAY_PAUSE -> playbackManager.setPlaying(
|
||||
!playbackManager.isPlaying
|
||||
|
@ -496,25 +500,35 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
|
|||
WidgetProvider.ACTION_WIDGET_UPDATE -> widgets.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume from a headset plug event, as long as its allowed.
|
||||
*/
|
||||
private fun resumeFromPlug() {
|
||||
if (playbackManager.song != null && settingsManager.doPlugMgt) {
|
||||
logD("Device connected, resuming")
|
||||
playbackManager.setPlaying(true)
|
||||
/**
|
||||
* Resume from a headset plug event in the case that the quirk is enabled.
|
||||
* This functionality remains a quirk for two reasons:
|
||||
* 1. Automatically resuming more or less overrides all other audio streams, which
|
||||
* is not that friendly
|
||||
* 2. There is a bug where playback will always start when this service starts, mostly
|
||||
* 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.
|
||||
*/
|
||||
private fun pauseFromPlug() {
|
||||
if (playbackManager.song != null && settingsManager.doPlugMgt) {
|
||||
logD("Device disconnected, pausing")
|
||||
playbackManager.setPlaying(false)
|
||||
/**
|
||||
* Pause from a headset plug.
|
||||
*/
|
||||
private fun pauseFromPlug() {
|
||||
if (playbackManager.song != null) {
|
||||
logD("Device disconnected, pausing")
|
||||
playbackManager.setPlaying(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,27 +37,27 @@ import org.oxycblt.auxio.ui.Sort
|
|||
class SettingsManager private constructor(context: Context) :
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
init {
|
||||
sharedPrefs.registerOnSharedPreferenceChangeListener(this)
|
||||
prefs.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
// --- VALUES ---
|
||||
|
||||
/** The current theme */
|
||||
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 */
|
||||
val useBlackTheme: Boolean
|
||||
get() = sharedPrefs.getBoolean(KEY_BLACK_THEME, false)
|
||||
get() = prefs.getBoolean(KEY_BLACK_THEME, false)
|
||||
|
||||
/** The current accent. */
|
||||
var accent: Accent
|
||||
get() = handleAccentCompat(sharedPrefs)
|
||||
get() = handleAccentCompat(prefs)
|
||||
set(value) {
|
||||
sharedPrefs.edit {
|
||||
prefs.edit {
|
||||
putInt(KEY_ACCENT, value.index)
|
||||
apply()
|
||||
}
|
||||
|
@ -68,14 +68,14 @@ class SettingsManager private constructor(context: Context) :
|
|||
* False if loop, true if shuffle.
|
||||
*/
|
||||
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. */
|
||||
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)!!
|
||||
set(value) {
|
||||
sharedPrefs.edit {
|
||||
prefs.edit {
|
||||
putInt(KEY_LIB_TABS, Tab.toSequence(value))
|
||||
apply()
|
||||
}
|
||||
|
@ -83,51 +83,51 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
/** Whether to load embedded covers */
|
||||
val showCovers: Boolean
|
||||
get() = sharedPrefs.getBoolean(KEY_SHOW_COVERS, true)
|
||||
get() = prefs.getBoolean(KEY_SHOW_COVERS, true)
|
||||
|
||||
/** Whether to ignore MediaStore covers */
|
||||
val useQualityCovers: Boolean
|
||||
get() = sharedPrefs.getBoolean(KEY_QUALITY_COVERS, false)
|
||||
get() = prefs.getBoolean(KEY_QUALITY_COVERS, false)
|
||||
|
||||
/** Whether to round album covers */
|
||||
val roundCovers: Boolean
|
||||
get() = sharedPrefs.getBoolean(KEY_ROUND_COVERS, false)
|
||||
get() = prefs.getBoolean(KEY_ROUND_COVERS, false)
|
||||
|
||||
/** Whether to do Audio focus. */
|
||||
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. */
|
||||
val doPlugMgt: Boolean
|
||||
get() = sharedPrefs.getBoolean(KEY_PLUG_MANAGEMENT, true)
|
||||
/** Whether to resume playback when a headset is connected (may not work well in all cases) */
|
||||
val headsetAutoplay: Boolean
|
||||
get() = prefs.getBoolean(KEY_HEADSET_AUTOPLAY, false)
|
||||
|
||||
/** The current ReplayGain configuration */
|
||||
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
|
||||
|
||||
/** What queue to create when a song is selected (ex. From All Songs or Search) */
|
||||
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
|
||||
|
||||
/** Whether shuffle should stay on when a new song is selected. */
|
||||
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. */
|
||||
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 */
|
||||
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 */
|
||||
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) {
|
||||
sharedPrefs.edit {
|
||||
prefs.edit {
|
||||
putInt(KEY_SEARCH_FILTER_MODE, DisplayMode.toFilterInt(value))
|
||||
apply()
|
||||
}
|
||||
|
@ -135,10 +135,10 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
/** The song sort mode on HomeFragment **/
|
||||
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)
|
||||
set(value) {
|
||||
sharedPrefs.edit {
|
||||
prefs.edit {
|
||||
putInt(KEY_LIB_SONGS_SORT, value.toInt())
|
||||
apply()
|
||||
}
|
||||
|
@ -146,10 +146,10 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
/** The album sort mode on HomeFragment **/
|
||||
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)
|
||||
set(value) {
|
||||
sharedPrefs.edit {
|
||||
prefs.edit {
|
||||
putInt(KEY_LIB_ALBUMS_SORT, value.toInt())
|
||||
apply()
|
||||
}
|
||||
|
@ -157,10 +157,10 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
/** The artist sort mode on HomeFragment **/
|
||||
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)
|
||||
set(value) {
|
||||
sharedPrefs.edit {
|
||||
prefs.edit {
|
||||
putInt(KEY_LIB_ARTISTS_SORT, value.toInt())
|
||||
apply()
|
||||
}
|
||||
|
@ -168,10 +168,10 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
/** The genre sort mode on HomeFragment **/
|
||||
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)
|
||||
set(value) {
|
||||
sharedPrefs.edit {
|
||||
prefs.edit {
|
||||
putInt(KEY_LIB_GENRES_SORT, value.toInt())
|
||||
apply()
|
||||
}
|
||||
|
@ -179,10 +179,10 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
/** The detail album sort mode **/
|
||||
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)
|
||||
set(value) {
|
||||
sharedPrefs.edit {
|
||||
prefs.edit {
|
||||
putInt(KEY_DETAIL_ALBUM_SORT, value.toInt())
|
||||
apply()
|
||||
}
|
||||
|
@ -190,10 +190,10 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
/** The detail artist sort mode **/
|
||||
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)
|
||||
set(value) {
|
||||
sharedPrefs.edit {
|
||||
prefs.edit {
|
||||
putInt(KEY_DETAIL_ARTIST_SORT, value.toInt())
|
||||
apply()
|
||||
}
|
||||
|
@ -201,10 +201,10 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
/** The detail genre sort mode **/
|
||||
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)
|
||||
set(value) {
|
||||
sharedPrefs.edit {
|
||||
prefs.edit {
|
||||
putInt(KEY_DETAIL_GENRE_SORT, value.toInt())
|
||||
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_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_SONG_PLAYBACK_MODE = "KEY_SONG_PLAY_MODE2"
|
||||
|
|
|
@ -37,12 +37,3 @@ fun assertBackgroundThread() {
|
|||
"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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -83,10 +83,8 @@
|
|||
<string name="set_audio">صوتيات</string>
|
||||
<string name="set_focus">تركيز الصوت</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_off">اطفاء</string>
|
||||
<string name="set_off">اطفاء</string>
|
||||
<string name="set_replay_gain_track">تفضيل المقطع</string>
|
||||
<string name="set_replay_gain_album">تفضيل الالبوم</string>
|
||||
<string name="set_replay_gain_dynamic">ديناميكي</string>
|
||||
|
|
|
@ -67,8 +67,6 @@
|
|||
<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_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_song_mode">"Když je vybrána skladba"</string>
|
||||
<string name="set_keep_shuffle">"Zapamatovat si náhodné přehrávání"</string>
|
||||
|
|
|
@ -71,10 +71,8 @@
|
|||
<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_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_off">Aus</string>
|
||||
<string name="set_off">Aus</string>
|
||||
<string name="set_replay_gain_track">Titel bevorzugen</string>
|
||||
<string name="set_replay_gain_album">Album bevorzugen</string>
|
||||
|
||||
|
@ -151,7 +149,7 @@
|
|||
<item quantity="other">%d Alben</item>
|
||||
</plurals>
|
||||
<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_album">Album</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_tab_handle">Tab versetzen</string>
|
||||
<string name="def_artist">Unbekannter Künstler</string>
|
||||
|
||||
</resources>
|
|
@ -83,10 +83,8 @@
|
|||
<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_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_off">Desactivado</string>
|
||||
<string name="set_off">Desactivado</string>
|
||||
<string name="set_replay_gain_track">Por pista</string>
|
||||
<string name="set_replay_gain_album">Por álbum</string>
|
||||
<string name="set_replay_gain_dynamic">Dinámico</string>
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
|
||||
<string name="set_audio">Audio</string>
|
||||
<string name="set_focus">Audio Focus</string>
|
||||
<string name="set_plug_mgt">Branchement du casque</string>
|
||||
|
||||
<string name="set_behavior">Comportement</string>
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
|
||||
<string name="set_audio">ऑडियो</string>
|
||||
<string name="set_focus">ऑडियो फोकस</string>
|
||||
<string name="set_plug_mgt">हेडसेट प्लग</string>
|
||||
|
||||
<string name="set_behavior">चाल चलन</string>
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
|
||||
<string name="set_audio">Hang</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>
|
||||
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
|
||||
<string name="set_audio">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_keep_shuffle">Ricorda casuale</string>
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
|
||||
<string name="set_audio">오디오</string>
|
||||
<string name="set_focus">오디오 포커스</string>
|
||||
<string name="set_plug_mgt">헤드셋 연결</string>
|
||||
|
||||
<string name="set_behavior">동작</string>
|
||||
|
||||
|
|
|
@ -72,8 +72,6 @@
|
|||
<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_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_song_mode">Wanneer een liedje is geselecteerd</string>
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
|
||||
<string name="set_audio">Dźwięk</string>
|
||||
<string name="set_focus">Wyciszanie otoczenia</string>
|
||||
<string name="set_plug_mgt">Podłączanie słuchawek</string>
|
||||
|
||||
<string name="set_behavior">Zachowanie</string>
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
|
||||
<string name="set_audio">Á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_keep_shuffle">Memorizar aleatorização</string>
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
|
||||
<string name="set_audio">Á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_keep_shuffle">Memorizar aleatorização</string>
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
|
||||
<string name="set_audio">Audio</string>
|
||||
<string name="set_focus">Concentrare audio</string>
|
||||
<string name="set_plug_mgt">Conexiune cu cască</string>
|
||||
|
||||
<string name="set_behavior">Comportament</string>
|
||||
|
||||
|
|
|
@ -83,10 +83,8 @@
|
|||
<string name="set_audio">Звук</string>
|
||||
<string name="set_focus">Аудио-фокус</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_off">Выкл.</string>
|
||||
<string name="set_off">Выкл.</string>
|
||||
<string name="set_replay_gain_track">По треку</string>
|
||||
<string name="set_replay_gain_album">По альбому</string>
|
||||
<string name="set_replay_gain_dynamic">Динамический</string>
|
||||
|
|
|
@ -83,10 +83,8 @@
|
|||
<string name="set_audio">音频</string>
|
||||
<string name="set_focus">音频焦点</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_off">关</string>
|
||||
<string name="set_off">关</string>
|
||||
<string name="set_replay_gain_track">偏好曲目</string>
|
||||
<string name="set_replay_gain_album">偏好专辑</string>
|
||||
<string name="set_replay_gain_dynamic">动态</string>
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
|
||||
<string name="set_audio">音訊</string>
|
||||
<string name="set_focus">音頻焦點</string>
|
||||
<string name="set_plug_mgt">耳機插頭</string>
|
||||
|
||||
<string name="set_behavior">行為</string>
|
||||
<string name="set_keep_shuffle">記住隨機播放</string>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
</string-array>
|
||||
|
||||
<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_album</item>
|
||||
<item>@string/set_replay_gain_dynamic</item>
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
<!-- Info namespace | App labels -->
|
||||
<string name="info_app_desc">A simple, rational music player for android.</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 -->
|
||||
<string name="lbl_retry">Retry</string>
|
||||
|
@ -83,10 +82,9 @@
|
|||
<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_plug_mgt">Headset focus</string>
|
||||
<string name="set_plug_mgt_desc">Play/Pause when the headset connection changes</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_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_album">Prefer album</string>
|
||||
<string name="set_replay_gain_dynamic">Dynamic</string>
|
||||
|
@ -108,6 +106,8 @@
|
|||
<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_off">Off</string>
|
||||
|
||||
<!-- Error Namespace | Error Labels -->
|
||||
<string name="err_no_music">No music found</string>
|
||||
<string name="err_load_failed">Music loading failed</string>
|
||||
|
|
|
@ -87,11 +87,11 @@
|
|||
app:title="@string/set_focus" />
|
||||
|
||||
<org.oxycblt.auxio.settings.pref.M3SwitchPreference
|
||||
app:defaultValue="true"
|
||||
app:defaultValue="false"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_PLUG_MGT"
|
||||
app:summary="@string/set_plug_mgt_desc"
|
||||
app:title="@string/set_plug_mgt" />
|
||||
app:key="auxio_headset_autoplay"
|
||||
app:summary="@string/set_headset_autoplay_desc"
|
||||
app:title="@string/set_headset_autoplay" />
|
||||
|
||||
<org.oxycblt.auxio.settings.pref.IntListPreference
|
||||
app:defaultValue="@integer/replay_gain_off"
|
||||
|
|
70
prebuild.py
70
prebuild.py
|
@ -1,15 +1,24 @@
|
|||
#!/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
|
||||
# deal with the insanity of bash.
|
||||
|
||||
# This script automatically assembles any required ExoPlayer extensions or components as
|
||||
# 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 platform
|
||||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
# WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE FLAC EXTENSION.
|
||||
# IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
|
||||
# WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE FLAC EXTENSION AND
|
||||
# THE GRADLE DEPENDENCY. IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
|
||||
EXO_VERSION = "2.17.0"
|
||||
FLAC_VERSION = "1.3.2"
|
||||
|
||||
|
@ -19,32 +28,38 @@ INFO="\033[1;94m"
|
|||
OK="\033[1;92m"
|
||||
NC="\033[0m"
|
||||
|
||||
system = platform.system()
|
||||
|
||||
# We do some shell scripting later on, so we can't support windows.
|
||||
system = platform.system()
|
||||
if system not in ["Linux", "Darwin"]:
|
||||
print("fatal: unsupported platform " + system)
|
||||
sys.exit(1)
|
||||
|
||||
def sh(cmd):
|
||||
print(INFO + "execute: " + NC + cmd)
|
||||
code = subprocess.call(["sh", "-c", "set -e; " + cmd])
|
||||
|
||||
if code != 0:
|
||||
print(FATAL + "fatal:" + NC + " command failed with exit code " + str(code))
|
||||
sys.exit(1)
|
||||
|
||||
start_path = os.path.join(os.path.abspath(os.curdir))
|
||||
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):
|
||||
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):
|
||||
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")):
|
||||
# We don't have a proper path. Do some digging on the Android SDK directory
|
||||
# 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")
|
||||
|
||||
candidates = []
|
||||
|
||||
for entry in os.scandir(ndk_root):
|
||||
if entry.is_dir():
|
||||
candidates.append(entry.path)
|
||||
|
||||
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):
|
||||
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:
|
||||
ndk_path = candidates[0]
|
||||
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)
|
||||
|
||||
ndk_build_path = os.path.join(ndk_path, "ndk-build")
|
||||
|
||||
# Now try to install ExoPlayer.
|
||||
sh("rm -rf " + exoplayer_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)
|
||||
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")
|
||||
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")
|
||||
flac_ext_aar_path = os.path.join(
|
||||
exoplayer_path, "extensions", "flac", "buildout",
|
||||
"outputs", "aar", "extension-flac-release.aar"
|
||||
)
|
||||
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")
|
||||
|
||||
os.chdir(exoplayer_path)
|
||||
sh("./gradlew extension-flac:bundleReleaseAar")
|
||||
|
@ -102,4 +116,4 @@ os.chdir(start_path)
|
|||
sh("mkdir " + libs_path)
|
||||
sh("cp " + flac_ext_aar_path + " " + libs_path)
|
||||
|
||||
print(OK + "success:" + NC + " completed pre-build.")
|
||||
print(OK + "success:" + NC + " completed pre-build")
|
||||
|
|
Loading…
Reference in a new issue