playback: fix headset focus autoplay issue

Fix an issue where headset focus would result in unexpected playback
whenever the service started.

AudioManager.ACTION_HEADSET_PLUG seems to always fire when the initial
BroadcastReceiver is set up, which results in a weird bug where if a
wired headset is connected while PlaybackService is started, playback
will start immediately, which was not user friendly.

I fear that this may result in an edge case that results in headset
focus not firing in an unrelated situation, which in that case I would
be forced to remove headset autoplay entirely (or at least relegate it
to a quirk option).
This commit is contained in:
OxygenCobalt 2022-03-03 18:47:55 -07:00
parent 1e39ceb9fc
commit 6d003c308b
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 20 additions and 8 deletions

View file

@ -15,6 +15,7 @@ from the system theme was used [#80]
- Fixed music loading failure that would occur when certain paths were parsed [#84] - Fixed music loading failure that would occur when certain paths were parsed [#84]
- Fixed incorrect track numbers when the tag was formatted as NN/TT [#88] - Fixed incorrect track numbers when the tag was formatted as NN/TT [#88]
- Fixed years deliberately set as "0" showing up as "No Date" - Fixed years deliberately set as "0" showing up as "No Date"
- Fixed headset management unexpectedly starting audio when the app initially opens
#### 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

View file

@ -29,6 +29,10 @@ 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: Phase out databinding
* TODO: Rework sealed classes to minimize whens and maximize overrides
*/
@Suppress("UNUSED") @Suppress("UNUSED")
class AuxioApp : Application(), ImageLoaderFactory { class AuxioApp : Application(), ImageLoaderFactory {
override fun onCreate() { override fun onCreate() {

View file

@ -43,7 +43,6 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
* TODO: Add a new view for crashes with a stack trace * TODO: Add a new view for crashes with a stack trace
* TODO: Custom language support * TODO: Custom language support
* TODO: Rework menus [perhaps add multi-select] * TODO: Rework menus [perhaps add multi-select]
* TODO: Phase out databinding
*/ */
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private val playbackModel: PlaybackViewModel by viewModels() private val playbackModel: PlaybackViewModel by viewModels()

View file

@ -56,6 +56,7 @@ import org.oxycblt.auxio.util.logTraceOrThrow
* views for each respective item. * views for each respective item.
* @author OxygenCobalt * @author OxygenCobalt
* TODO: Make tabs invisible when there is only one * TODO: Make tabs invisible when there is only one
* TODO: Add duration and song count sorts
*/ */
class HomeFragment : Fragment() { class HomeFragment : Fragment() {
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()

View file

@ -149,7 +149,7 @@ data class Song(
*/ */
data class Album( data class Album(
override val name: String, override val name: String,
/** The latest year of the songs in this album. */ /** The latest year of the songs in this album. Null if none of the songs had metadata. */
val year: Int?, val year: Int?,
/** The URI for the cover art corresponding to this album. */ /** The URI for the cover art corresponding to this album. */
val albumCoverUri: Uri, val albumCoverUri: Uri,

View file

@ -241,6 +241,7 @@ class MusicLoader {
// Use the song with the latest year as our metadata song. // Use the song with the latest year as our metadata song.
// This allows us to replicate the LAST_YEAR field, which is useful as it means that // This allows us to replicate the LAST_YEAR field, which is useful as it means that
// weird years like "0" wont show up if there are alternatives. // weird years like "0" wont show up if there are alternatives.
// TODO: Weigh songs with null years lower than songs with zero years
val templateSong = requireNotNull( val templateSong = requireNotNull(
albumSongs.maxByOrNull { it.internalMediaStoreYear ?: 0 } albumSongs.maxByOrNull { it.internalMediaStoreYear ?: 0 }
) )

View file

@ -69,7 +69,8 @@ class MusicStore private constructor() {
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
val loader = MusicLoader() val loader = MusicLoader()
val library = loader.load(context) ?: return Response.Err(ErrorKind.NO_MUSIC) val library = loader.load(context)
?: return Response.Err(ErrorKind.NO_MUSIC)
mSongs = library.songs mSongs = library.songs
mAlbums = library.albums mAlbums = library.albums
@ -78,7 +79,7 @@ class MusicStore private constructor() {
logD("Music load completed successfully in ${System.currentTimeMillis() - start}ms") logD("Music load completed successfully in ${System.currentTimeMillis() - start}ms")
} catch (e: Exception) { } catch (e: Exception) {
logE("Something went horribly wrong") logE("Music loading failed.")
logE(e.stackTraceToString()) logE(e.stackTraceToString())
return Response.Err(ErrorKind.FAILED) return Response.Err(ErrorKind.FAILED)
} }

View file

@ -439,7 +439,6 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
private fun stopForegroundAndNotification() { private fun stopForegroundAndNotification() {
stopForeground(true) stopForeground(true)
notificationManager.cancel(PlaybackNotification.NOTIFICATION_ID) notificationManager.cancel(PlaybackNotification.NOTIFICATION_ID)
isForeground = false isForeground = false
} }
@ -448,6 +447,8 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
* TODO: Don't fire when the service initially starts? * TODO: Don't fire when the service initially starts?
*/ */
private inner class PlaybackReceiver : BroadcastReceiver() { private inner class PlaybackReceiver : BroadcastReceiver() {
private var handledInitialHeadsetPlug = 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 ---
@ -461,12 +462,16 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
AudioManager.ACTION_AUDIO_BECOMING_NOISY -> pauseFromPlug() AudioManager.ACTION_AUDIO_BECOMING_NOISY -> pauseFromPlug()
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 -> resumeFromPlug() 1 -> resumeFromPlug()
} }
} }
handledInitialHeadsetPlug = true
}
// --- AUXIO EVENTS --- // --- AUXIO EVENTS ---
ACTION_PLAY_PAUSE -> playbackManager.setPlaying( ACTION_PLAY_PAUSE -> playbackManager.setPlaying(
!playbackManager.isPlaying !playbackManager.isPlaying