diff --git a/CHANGELOG.md b/CHANGELOG.md index 131f6c3f..f0913606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed +- Fixed another crash when clearing app from recents ([#298]) ## [1.5.1] - 2025-11-05 ### Fixed diff --git a/app/src/main/kotlin/org/fossify/musicplayer/activities/TrackActivity.kt b/app/src/main/kotlin/org/fossify/musicplayer/activities/TrackActivity.kt index b47658b9..2371a632 100644 --- a/app/src/main/kotlin/org/fossify/musicplayer/activities/TrackActivity.kt +++ b/app/src/main/kotlin/org/fossify/musicplayer/activities/TrackActivity.kt @@ -11,9 +11,7 @@ import android.provider.MediaStore import android.util.Size import android.view.GestureDetector import android.view.MotionEvent -import android.view.View import android.widget.SeekBar -import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.scale import androidx.core.os.postDelayed @@ -22,11 +20,37 @@ import androidx.media3.common.MediaItem import com.bumptech.glide.load.resource.bitmap.CenterCrop import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.request.RequestOptions -import org.fossify.commons.extensions.* +import org.fossify.commons.extensions.applyColorFilter +import org.fossify.commons.extensions.beGone +import org.fossify.commons.extensions.beInvisibleIf +import org.fossify.commons.extensions.beVisible +import org.fossify.commons.extensions.copyToClipboard +import org.fossify.commons.extensions.getColoredDrawableWithColor +import org.fossify.commons.extensions.getFormattedDuration +import org.fossify.commons.extensions.getProperBackgroundColor +import org.fossify.commons.extensions.getProperPrimaryColor +import org.fossify.commons.extensions.getProperTextColor +import org.fossify.commons.extensions.realScreenSize +import org.fossify.commons.extensions.toast +import org.fossify.commons.extensions.updateTextColors +import org.fossify.commons.extensions.value +import org.fossify.commons.extensions.viewBinding import org.fossify.commons.helpers.MEDIUM_ALPHA +import org.fossify.commons.helpers.mydebug import org.fossify.musicplayer.R import org.fossify.musicplayer.databinding.ActivityTrackBinding -import org.fossify.musicplayer.extensions.* +import org.fossify.musicplayer.extensions.config +import org.fossify.musicplayer.extensions.getCoverArtHeight +import org.fossify.musicplayer.extensions.getPlaybackSetting +import org.fossify.musicplayer.extensions.getTrackCoverArt +import org.fossify.musicplayer.extensions.getTrackFromUri +import org.fossify.musicplayer.extensions.isReallyPlaying +import org.fossify.musicplayer.extensions.loadGlideResource +import org.fossify.musicplayer.extensions.nextMediaItem +import org.fossify.musicplayer.extensions.sendCommand +import org.fossify.musicplayer.extensions.setRepeatMode +import org.fossify.musicplayer.extensions.toTrack +import org.fossify.musicplayer.extensions.updatePlayPauseIcon import org.fossify.musicplayer.fragments.PlaybackSpeedFragment import org.fossify.musicplayer.helpers.PlaybackSetting import org.fossify.musicplayer.helpers.SEEK_INTERVAL_S @@ -153,9 +177,9 @@ class TrackActivity : SimpleControllerActivity(), PlaybackSpeedListener { private fun setupButtons() = binding.apply { activityTrackToggleShuffle.setOnClickListener { withPlayer { toggleShuffle() } } - activityTrackPrevious.setOnClickListener { withPlayer { forceSeekToPrevious() } } + activityTrackPrevious.setOnClickListener { withPlayer { seekToPrevious() } } activityTrackPlayPause.setOnClickListener { togglePlayback() } - activityTrackNext.setOnClickListener { withPlayer { forceSeekToNext() } } + activityTrackNext.setOnClickListener { withPlayer { seekToNext() } } activityTrackProgressCurrent.setOnClickListener { seekBack() } activityTrackProgressMax.setOnClickListener { seekForward() } activityTrackPlaybackSetting.setOnClickListener { togglePlaybackSetting() } diff --git a/app/src/main/kotlin/org/fossify/musicplayer/extensions/Player.kt b/app/src/main/kotlin/org/fossify/musicplayer/extensions/Player.kt index 24e04eb3..0a958380 100644 --- a/app/src/main/kotlin/org/fossify/musicplayer/extensions/Player.kt +++ b/app/src/main/kotlin/org/fossify/musicplayer/extensions/Player.kt @@ -99,18 +99,6 @@ fun Player.setRepeatMode(playbackSetting: PlaybackSetting) { } } -fun Player.forceSeekToNext() { - if (!maybeForceNext()) { - seekToNext() - } -} - -fun Player.forceSeekToPrevious() { - if (!maybeForcePrevious()) { - seekToPrevious() - } -} - /** * Force seek to the next media item regardless of the current [Player.RepeatMode]. Returns true on success. */ diff --git a/app/src/main/kotlin/org/fossify/musicplayer/playback/PlaybackService.kt b/app/src/main/kotlin/org/fossify/musicplayer/playback/PlaybackService.kt index f36d7e34..6cb5cf01 100644 --- a/app/src/main/kotlin/org/fossify/musicplayer/playback/PlaybackService.kt +++ b/app/src/main/kotlin/org/fossify/musicplayer/playback/PlaybackService.kt @@ -1,8 +1,6 @@ package org.fossify.musicplayer.playback -import android.content.Intent import android.os.Handler -import android.os.HandlerThread import android.os.Looper import androidx.annotation.OptIn import androidx.core.os.postDelayed @@ -16,6 +14,7 @@ import org.fossify.commons.extensions.hasPermission import org.fossify.commons.extensions.showErrorToast import org.fossify.musicplayer.extensions.isReallyPlaying import org.fossify.musicplayer.extensions.nextMediaItem +import org.fossify.musicplayer.extensions.runOnPlayerThread import org.fossify.musicplayer.helpers.NotificationHelper import org.fossify.musicplayer.helpers.getPermissionToRequest import org.fossify.musicplayer.playback.library.MediaItemProvider @@ -25,9 +24,7 @@ import org.fossify.musicplayer.playback.player.initializeSessionAndPlayer @OptIn(UnstableApi::class) class PlaybackService : MediaLibraryService(), MediaSessionService.Listener { internal lateinit var player: SimpleMusicPlayer - internal lateinit var playerThread: HandlerThread internal lateinit var playerListener: Player.Listener - internal lateinit var playerHandler: Handler internal lateinit var mediaSession: MediaLibrarySession internal lateinit var mediaItemProvider: MediaItemProvider @@ -77,7 +74,7 @@ class PlaybackService : MediaLibraryService(), MediaSessionService.Listener { } internal fun withPlayer(callback: SimpleMusicPlayer.() -> Unit) { - playerHandler.post { callback(player) } + player.runOnPlayerThread { callback(this) } } private fun showNoPermissionNotification() { @@ -102,12 +99,6 @@ class PlaybackService : MediaLibraryService(), MediaSessionService.Listener { // todo: show a notification instead. } - override fun onTaskRemoved(rootIntent: Intent?) { - playerHandler.post { - super.onTaskRemoved(rootIntent) - } - } - companion object { // Initializing a media controller might take a noticeable amount of time thus we expose current playback info here to keep things as quick as possible. var isPlaying: Boolean = false diff --git a/app/src/main/kotlin/org/fossify/musicplayer/playback/player/Player.kt b/app/src/main/kotlin/org/fossify/musicplayer/playback/player/Player.kt index d833df4a..746f9c82 100644 --- a/app/src/main/kotlin/org/fossify/musicplayer/playback/player/Player.kt +++ b/app/src/main/kotlin/org/fossify/musicplayer/playback/player/Player.kt @@ -5,8 +5,6 @@ package org.fossify.musicplayer.playback.player import android.app.PendingIntent import android.content.Context import android.content.Intent -import android.os.Handler -import android.os.HandlerThread import androidx.media3.common.AudioAttributes import androidx.media3.common.C import androidx.media3.common.util.UnstableApi @@ -24,22 +22,19 @@ import org.fossify.musicplayer.playback.SimpleEqualizer import org.fossify.musicplayer.playback.getCustomLayout import org.fossify.musicplayer.playback.getMediaSessionCallback -private const val PLAYER_THREAD = "PlayerThread" - /** * Initializes player and media session. - * - * All player operations are handled on a separate handler thread to avoid slowing down the main thread. - * See https://developer.android.com/guide/topics/media/exoplayer/hello-world#a-note-on-threading for more info. */ -internal fun PlaybackService.initializeSessionAndPlayer(handleAudioFocus: Boolean, handleAudioBecomingNoisy: Boolean) { - playerThread = HandlerThread(PLAYER_THREAD).also { it.start() } - playerHandler = Handler(playerThread.looper) +internal fun PlaybackService.initializeSessionAndPlayer( + handleAudioFocus: Boolean, + handleAudioBecomingNoisy: Boolean +) { player = initializePlayer(handleAudioFocus, handleAudioBecomingNoisy) playerListener = getPlayerListener() - mediaSession = MediaLibraryService.MediaLibrarySession.Builder(this, player, getMediaSessionCallback()) - .setSessionActivity(getSessionActivityIntent()) - .build() + mediaSession = + MediaLibraryService.MediaLibrarySession.Builder(this, player, getMediaSessionCallback()) + .setSessionActivity(getSessionActivityIntent()) + .build() withPlayer { addListener(playerListener) @@ -51,7 +46,10 @@ internal fun PlaybackService.initializeSessionAndPlayer(handleAudioFocus: Boolea } } -private fun PlaybackService.initializePlayer(handleAudioFocus: Boolean, handleAudioBecomingNoisy: Boolean): SimpleMusicPlayer { +private fun PlaybackService.initializePlayer( + handleAudioFocus: Boolean, + handleAudioBecomingNoisy: Boolean +): SimpleMusicPlayer { val renderersFactory = AudioOnlyRenderersFactory(context = this) return SimpleMusicPlayer( ExoPlayer.Builder(this, renderersFactory) @@ -66,7 +64,6 @@ private fun PlaybackService.initializePlayer(handleAudioFocus: Boolean, handleAu ) .setSeekBackIncrementMs(SEEK_INTERVAL_MS) .setSeekForwardIncrementMs(SEEK_INTERVAL_MS) - .setLooper(playerThread.looper) .build() ) } diff --git a/app/src/main/kotlin/org/fossify/musicplayer/playback/player/SimpleMusicPlayer.kt b/app/src/main/kotlin/org/fossify/musicplayer/playback/player/SimpleMusicPlayer.kt index 8bddd88f..70f32bc7 100644 --- a/app/src/main/kotlin/org/fossify/musicplayer/playback/player/SimpleMusicPlayer.kt +++ b/app/src/main/kotlin/org/fossify/musicplayer/playback/player/SimpleMusicPlayer.kt @@ -6,8 +6,17 @@ import androidx.media3.common.Player import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.ShuffleOrder.DefaultShuffleOrder -import kotlinx.coroutines.* -import org.fossify.musicplayer.extensions.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.fossify.musicplayer.extensions.currentMediaItems +import org.fossify.musicplayer.extensions.maybeForceNext +import org.fossify.musicplayer.extensions.maybeForcePrevious +import org.fossify.musicplayer.extensions.move +import org.fossify.musicplayer.extensions.runOnPlayerThread +import org.fossify.musicplayer.extensions.shuffledMediaItemsIndices import org.fossify.musicplayer.inlines.indexOfFirstOrNull private const val DEFAULT_SHUFFLE_ORDER_SEED = 42L @@ -30,10 +39,10 @@ class SimpleMusicPlayer(private val exoPlayer: ExoPlayer) : ForwardingPlayer(exo return super.getAvailableCommands() .buildUpon() .addAll( - Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, - Player.COMMAND_SEEK_TO_PREVIOUS, - Player.COMMAND_SEEK_TO_NEXT, - Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, + COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, + COMMAND_SEEK_TO_PREVIOUS, + COMMAND_SEEK_TO_NEXT, + COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, ) .build() } @@ -106,7 +115,7 @@ class SimpleMusicPlayer(private val exoPlayer: ExoPlayer) : ForwardingPlayer(exo @Deprecated("Should be rewritten when https://github.com/androidx/media/issues/325 is implemented.") fun setShuffleIndices(indices: IntArray) { val shuffleOrder = DefaultShuffleOrder(indices, DEFAULT_SHUFFLE_ORDER_SEED) - exoPlayer.setShuffleOrder(shuffleOrder) + exoPlayer.shuffleOrder = shuffleOrder } @Deprecated("Should be rewritten when https://github.com/androidx/media/issues/325 is implemented.") @@ -132,7 +141,10 @@ class SimpleMusicPlayer(private val exoPlayer: ExoPlayer) : ForwardingPlayer(exo val shuffledCurrentIndex = shuffledIndices.indexOf(itemIndex) val shuffledNewIndex = shuffledIndices.indexOf(nextMediaItemIndex) shuffledIndices.move(currentIndex = shuffledCurrentIndex, newIndex = shuffledNewIndex) - exoPlayer.setShuffleOrder(DefaultShuffleOrder(shuffledIndices.toIntArray(), DEFAULT_SHUFFLE_ORDER_SEED)) + exoPlayer.shuffleOrder = DefaultShuffleOrder( + shuffledIndices.toIntArray(), + DEFAULT_SHUFFLE_ORDER_SEED + ) } }