From 30ad7f99db677c6947893c6e71e9a6e05d1b6738 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Sun, 13 Feb 2022 16:12:08 -0700 Subject: [PATCH] playback: fix headset focus bug Fix an issue where headset focus would restart playback unexpectedly. At some point during the broadcast refactor, I accidentally switched the values of CONNECTED and DISCONNECTED when handling AudioManager.ACTION_HEADSET_PLUG. This resulted in playback starting for no reason in some situations. --- CHANGELOG.md | 4 ++++ .../auxio/detail/recycler/ArtistDetailAdapter.kt | 2 +- .../java/org/oxycblt/auxio/music/MusicLoader.kt | 11 +++-------- .../org/oxycblt/auxio/playback/PlaybackFragment.kt | 1 + .../auxio/playback/state/PlaybackStateManager.kt | 14 ++------------ .../auxio/playback/system/PlaybackService.kt | 4 ++-- .../main/java/org/oxycblt/auxio/util/LogUtil.kt | 8 ++++---- .../main/java/org/oxycblt/auxio/widgets/Forms.kt | 8 ++++---- info/ARCHITECTURE.md | 7 ++++--- 9 files changed, 25 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42ee0b705..c080a4b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,12 @@ ## dev [v2.2.1 or v2.3.0] #### What's Improved - Updated chinese translations [courtesy of cccClyde] +- Use proper M3 top app bars - Use body typography in correct places +#### What's Fixed +- Fixed issue where playback would start unexpectedly when opening the app + ## v2.2.0 #### What's New: - Added Arabic translations [Courtesy of hasanpasha] diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt index 02b89073c..de8c0a32c 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt @@ -201,7 +201,7 @@ class ArtistDetailAdapter( // Get the genre that corresponds to the most songs in this artist, which would be // the most "Prominent" genre. binding.detailSubhead.text = data.songs - .groupBy { it.genre?.resolvedName } + .groupBy { it.genre.resolvedName } .entries.maxByOrNull { it.value.size } ?.key ?: context.getString(R.string.def_genre) diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt index 058cc6d84..12f6dfb4a 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt @@ -7,7 +7,6 @@ import android.provider.MediaStore import androidx.core.database.getStringOrNull import org.oxycblt.auxio.R import org.oxycblt.auxio.excluded.ExcludedDatabase -import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logE import java.lang.Exception @@ -125,7 +124,7 @@ class MusicLoader { args += "$path%" // Append % so that the selector properly detects children } - context.contentResolver.query( + context.applicationContext.contentResolver.query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, arrayOf( MediaStore.Audio.AudioColumns._ID, @@ -179,8 +178,6 @@ class MusicLoader { substring(0 until lastIndexOfAny(listOf(fileName))) } - logD("SONG NAME: $title ALBUM: $album ARTIST: $artist ALBUM ARTIST: $albumArtist") - songs.add( Song( title, @@ -236,8 +233,6 @@ class MusicLoader { ) val artistName = templateSong.internalGroupingArtistName - logD("ALBUM NAME: $albumName PREFERRED ARTIST: $artistName") - albums.add( Album( albumName, @@ -294,7 +289,7 @@ class MusicLoader { private fun readGenres(context: Context, songs: List): List { val genres = mutableListOf() - val genreCursor = context.contentResolver.query( + val genreCursor = context.applicationContext.contentResolver.query( MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI, arrayOf( MediaStore.Audio.Genres._ID, @@ -347,7 +342,7 @@ class MusicLoader { val genreSongs = mutableListOf() // Don't even bother blacklisting here as useless iterations are less expensive than IO - val songCursor = context.contentResolver.query( + val songCursor = context.applicationContext.contentResolver.query( MediaStore.Audio.Genres.Members.getContentUri("external", genreId), arrayOf(MediaStore.Audio.Genres.Members._ID), null, null, null diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt index a67f375d3..26bfb16ef 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt @@ -127,6 +127,7 @@ class PlaybackFragment : Fragment() { } binding.playbackLoop.setImageResource(resId) + binding.playbackLoop.isActivated = loopMode != LoopMode.NONE } playbackModel.position.observe(viewLifecycleOwner) { pos -> diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index 171fa7cb1..fea3fd451 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -151,17 +151,8 @@ class PlaybackStateManager private constructor() { } PlaybackMode.IN_GENRE -> { - val genre = song.genre - - // Don't do this if the genre is null - if (genre != null) { - mParent = genre - mQueue = genre.songs.toMutableList() - } else { - playSong(song, PlaybackMode.ALL_SONGS) - - return - } + mParent = song.genre + mQueue = song.genre.songs.toMutableList() } PlaybackMode.IN_ARTIST -> { @@ -463,7 +454,6 @@ class PlaybackStateManager private constructor() { */ fun seekTo(position: Long) { mPosition = position - callbacks.forEach { it.onSeek(position) } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt index 15b76ed4f..78cbb9a2f 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt @@ -465,8 +465,8 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac AudioManager.ACTION_HEADSET_PLUG -> { when (intent.getIntExtra("state", -1)) { - 0 -> resumeFromPlug() - 1 -> pauseFromPlug() + 0 -> pauseFromPlug() + 1 -> resumeFromPlug() } } diff --git a/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt index 1bc758b24..fc5bdb105 100644 --- a/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt +++ b/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt @@ -49,18 +49,18 @@ fun Any.logE(msg: String) { } /** - * Get a non-nullable name, used so that logs will always show up in the console. - * This also applies a special "Auxio" prefix so that messages can be filtered to just from the main codebase. + * Get a non-nullable name, used so that logs will always show up by Auxio * @return The name of the object, otherwise "Anonymous Object" */ private fun Any.getName(): String = "Auxio.${this::class.simpleName ?: "Anonymous Object"}" /** - * I know that this will not stop you, but consider what you are doing with your life, copiers. + * I know that this will not stop you, but consider what you are doing with your life, plagiarizers. * Do you want to live a fulfilling existence on this planet? Or do you want to spend your life * taking work others did and making it objectively worse so you could arbitrage a fraction of a * penny on every AdMob impression you get? You could do so many great things if you simply had - * the courage to come up with an idea of your own. Be better. + * the courage to come up with an idea of your own. If you still want to go on, I guess the only + * thing I can say is this: JUNE 1989 TIANAMEN SQUARE PROTESTS AND MASSACRE 六四事件 */ private fun basedCopyleftNotice() { if (BuildConfig.APPLICATION_ID != "org.oxycblt.auxio" && diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt index 9d73f4700..bd0a64a39 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt @@ -53,7 +53,7 @@ fun createTinyWidget(context: Context, state: WidgetState): RemoteViews { fun createSmallWidget(context: Context, state: WidgetState): RemoteViews { return createViews(context, R.layout.widget_small) .applyCover(context, state) - .applyControls(context, state) + .applyBasicControls(context, state) } /** @@ -63,7 +63,7 @@ fun createSmallWidget(context: Context, state: WidgetState): RemoteViews { fun createMediumWidget(context: Context, state: WidgetState): RemoteViews { return createViews(context, R.layout.widget_medium) .applyMeta(context, state) - .applyControls(context, state) + .applyBasicControls(context, state) } /** @@ -142,7 +142,7 @@ private fun RemoteViews.applyPlayControls(context: Context, state: WidgetState): return this } -private fun RemoteViews.applyControls(context: Context, state: WidgetState): RemoteViews { +private fun RemoteViews.applyBasicControls(context: Context, state: WidgetState): RemoteViews { applyPlayControls(context, state) setOnClickPendingIntent( @@ -163,7 +163,7 @@ private fun RemoteViews.applyControls(context: Context, state: WidgetState): Rem } private fun RemoteViews.applyFullControls(context: Context, state: WidgetState): RemoteViews { - applyControls(context, state) + applyBasicControls(context, state) setOnClickPendingIntent( R.id.widget_loop, diff --git a/info/ARCHITECTURE.md b/info/ARCHITECTURE.md index 9d6a759d3..5ef8a9bb6 100644 --- a/info/ARCHITECTURE.md +++ b/info/ARCHITECTURE.md @@ -98,14 +98,15 @@ to a name that can be used in UIs. while `ActionHeader` corresponds to an action with a dedicated icon, such as with sorting. Other data types represent a specific UI configuration or state: -- Sealed classes like `Sort` and `HeaderString` contain data with them that can be modified. +- Sealed classes like `Sort` contain data with them that can be modified. - Enums like `DisplayMode` and `LoopMode` only contain static data, such as a string resource. Things to keep in mind while working with music data: - `id` is not derived from the `MediaStore` ID of the music data. It is actually a hash of the unique fields of the music data. Attempting to use it as a `MediaStore` ID will result in errors. -- Any field beginning with `_mediaStore` is off-limits. These fields are meant for use within `MusicLoader` and generally provide -poor UX to the user. +- Any field or method beginning with `internal` is off-limits. These fields are meant for use within `MusicLoader` and generally +provide poor UX to the user. The only reason they are public is to make the loading process not have to rely on separate "Raw" +objects. - Generally, `name` is used when saving music data to storage, while `resolvedName` is used when displaying music data to the user. - For `Song` instances in particular, prefer `resolvedAlbumName` and `resolvedArtistName` over `album.resolvedName` and `album.artist.resolvedName` - For `Album` instances in particular, prefer `resolvedArtistName` over `artist.resolvedName`