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.
This commit is contained in:
OxygenCobalt 2022-02-13 16:12:08 -07:00
parent ec358a13e3
commit 30ad7f99db
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
9 changed files with 25 additions and 34 deletions

View file

@ -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]

View file

@ -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)

View file

@ -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<Song>): List<Genre> {
val genres = mutableListOf<Genre>()
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<Song>()
// 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

View file

@ -127,6 +127,7 @@ class PlaybackFragment : Fragment() {
}
binding.playbackLoop.setImageResource(resId)
binding.playbackLoop.isActivated = loopMode != LoopMode.NONE
}
playbackModel.position.observe(viewLifecycleOwner) { pos ->

View file

@ -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) }
}

View file

@ -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()
}
}

View file

@ -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" &&

View file

@ -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,

View file

@ -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`