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:
parent
ec358a13e3
commit
30ad7f99db
9 changed files with 25 additions and 34 deletions
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -127,6 +127,7 @@ class PlaybackFragment : Fragment() {
|
|||
}
|
||||
|
||||
binding.playbackLoop.setImageResource(resId)
|
||||
binding.playbackLoop.isActivated = loopMode != LoopMode.NONE
|
||||
}
|
||||
|
||||
playbackModel.position.observe(viewLifecycleOwner) { pos ->
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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" &&
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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`
|
||||
|
|
Loading…
Reference in a new issue