Reorganize some extension functions
This commit is contained in:
parent
c2dc9f2604
commit
c7a269fe00
5 changed files with 78 additions and 79 deletions
|
@ -45,7 +45,7 @@ abstract class SimpleControllerActivity : SimpleActivity(), Player.Listener {
|
|||
|
||||
fun withPlayer(callback: MediaController.() -> Unit) = controller.withController(callback)
|
||||
|
||||
fun prepareAndPlay(tracks: List<Track>, startIndex: Int = 0, startPosition: Long = 0, startActivity: Boolean = true) {
|
||||
fun prepareAndPlay(tracks: List<Track>, startIndex: Int = 0, startPositionMs: Long = 0, startActivity: Boolean = true) {
|
||||
withPlayer {
|
||||
if (startActivity) {
|
||||
startActivity(
|
||||
|
@ -53,7 +53,7 @@ abstract class SimpleControllerActivity : SimpleActivity(), Player.Listener {
|
|||
)
|
||||
}
|
||||
|
||||
prepareUsingTracks(tracks = tracks, startIndex = startIndex, startPosition = startPosition, play = true) { success ->
|
||||
prepareUsingTracks(tracks = tracks, startIndex = startIndex, startPositionMs = startPositionMs, play = true) { success ->
|
||||
if (success) {
|
||||
updatePlaybackInfo(this)
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ abstract class SimpleControllerActivity : SimpleActivity(), Player.Listener {
|
|||
withPlayer {
|
||||
// it's not yet directly possible to update metadata without interrupting the playback: https://github.com/androidx/media/issues/33
|
||||
if (trackToUpdate == null || currentMediaItem.isSameMedia(trackToUpdate)) {
|
||||
prepareUsingTracks(tracks = queuedTracks, startIndex = currentMediaItemIndex, startPosition = currentPosition, play = isReallyPlaying)
|
||||
prepareUsingTracks(tracks = queuedTracks, startIndex = currentMediaItemIndex, startPositionMs = currentPosition, play = isReallyPlaying)
|
||||
} else {
|
||||
val trackIndex = currentMediaItems.indexOfTrack(trackToUpdate)
|
||||
if (trackIndex > 0) {
|
||||
|
|
|
@ -1,78 +1,7 @@
|
|||
package com.simplemobiletools.musicplayer.extensions
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.media3.session.MediaController
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.musicplayer.models.Track
|
||||
import com.simplemobiletools.musicplayer.models.toMediaItems
|
||||
import com.simplemobiletools.musicplayer.playback.CustomCommands
|
||||
import com.simplemobiletools.musicplayer.playback.PlaybackService.Companion.updatePlaybackInfo
|
||||
|
||||
fun MediaController.sendCommand(command: CustomCommands, extras: Bundle = Bundle.EMPTY) = sendCustomCommand(command.sessionCommand, extras)
|
||||
|
||||
fun MediaController.togglePlayback() {
|
||||
if (isReallyPlaying) {
|
||||
pause()
|
||||
} else {
|
||||
play()
|
||||
}
|
||||
}
|
||||
|
||||
fun MediaController.runOnPlayerThread(callback: MediaController.() -> Unit) =
|
||||
applicationLooper.post {
|
||||
callback(this)
|
||||
}
|
||||
|
||||
fun MediaController.prepareUsingTracks(
|
||||
tracks: List<Track>,
|
||||
startIndex: Int = 0,
|
||||
startPosition: Long = 0,
|
||||
play: Boolean = false,
|
||||
callback: ((success: Boolean) -> Unit)? = null
|
||||
) {
|
||||
if (tracks.isEmpty()) {
|
||||
runOnPlayerThread {
|
||||
callback?.invoke(false)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val mediaItems = tracks.toMediaItems()
|
||||
runOnPlayerThread {
|
||||
setMediaItems(mediaItems, startIndex, startPosition)
|
||||
playWhenReady = play
|
||||
prepare()
|
||||
updatePlaybackInfo(this)
|
||||
callback?.invoke(true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method optimizes player preparation by first starting with the current track and then adding
|
||||
* all queued items using [MediaController.addRemainingMediaItems]. This helps prevent delays, especially with
|
||||
* large queues, and avoids the [android.app.ForegroundServiceStartNotAllowedException] when starting from background.
|
||||
*/
|
||||
fun MediaController.maybePreparePlayer(context: Context, callback: (success: Boolean) -> Unit) {
|
||||
if (currentMediaItem == null) {
|
||||
ensureBackgroundThread {
|
||||
var prepared = false
|
||||
context.audioHelper.getQueuedTracksLazily { tracks, startIndex, startPositionMs ->
|
||||
if (!prepared) {
|
||||
prepareUsingTracks(tracks = tracks, startIndex = startIndex, startPosition = startPositionMs) {
|
||||
callback(it)
|
||||
prepared = it
|
||||
}
|
||||
} else {
|
||||
if (tracks.size == 1) {
|
||||
return@getQueuedTracksLazily
|
||||
}
|
||||
|
||||
addRemainingMediaItems(tracks.toMediaItems(), startIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
callback(false)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package com.simplemobiletools.musicplayer.extensions
|
||||
|
||||
import android.content.Context
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.musicplayer.helpers.PlaybackSetting
|
||||
import com.simplemobiletools.musicplayer.models.Track
|
||||
import com.simplemobiletools.musicplayer.models.toMediaItems
|
||||
|
||||
val Player.isReallyPlaying: Boolean
|
||||
get() = when (playbackState) {
|
||||
|
@ -70,6 +74,19 @@ val Player.shuffledMediaItemsIndices: List<Int>
|
|||
return indices
|
||||
}
|
||||
|
||||
inline fun <T : Player> T.runOnPlayerThread(crossinline callback: T.() -> Unit) =
|
||||
applicationLooper.post {
|
||||
callback()
|
||||
}
|
||||
|
||||
fun Player.togglePlayback() {
|
||||
if (isReallyPlaying) {
|
||||
pause()
|
||||
} else {
|
||||
play()
|
||||
}
|
||||
}
|
||||
|
||||
fun Player.setRepeatMode(playbackSetting: PlaybackSetting) {
|
||||
repeatMode = when (playbackSetting) {
|
||||
PlaybackSetting.REPEAT_TRACK -> Player.REPEAT_MODE_ONE
|
||||
|
@ -118,14 +135,67 @@ fun Player.maybeForcePrevious(): Boolean {
|
|||
}
|
||||
}
|
||||
|
||||
fun Player.prepareUsingTracks(
|
||||
tracks: List<Track>,
|
||||
startIndex: Int = 0,
|
||||
startPositionMs: Long = 0,
|
||||
play: Boolean = false,
|
||||
callback: ((success: Boolean) -> Unit)? = null
|
||||
) {
|
||||
if (tracks.isEmpty()) {
|
||||
runOnPlayerThread {
|
||||
callback?.invoke(false)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val mediaItems = tracks.toMediaItems()
|
||||
runOnPlayerThread {
|
||||
setMediaItems(mediaItems, startIndex, startPositionMs)
|
||||
playWhenReady = play
|
||||
prepare()
|
||||
callback?.invoke(true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes a list of media items and the current index in the playlist. It then
|
||||
* This method prepares the player using queued tracks or in case the queue is empty, initializing
|
||||
* the queue using all tracks. To optimize this, the player is first prepared using the current track and then all queued
|
||||
* items are added using [addRemainingMediaItems]. This helps prevent delays, especially with large queues, and
|
||||
* avoids potential issues like [android.app.ForegroundServiceStartNotAllowedException] when starting from background.
|
||||
*/
|
||||
inline fun Player.maybePreparePlayer(context: Context, crossinline callback: (success: Boolean) -> Unit) {
|
||||
if (currentMediaItem == null) {
|
||||
ensureBackgroundThread {
|
||||
var prepared = false
|
||||
context.audioHelper.getQueuedTracksLazily { tracks, startIndex, startPositionMs ->
|
||||
if (!prepared) {
|
||||
prepareUsingTracks(tracks = tracks, startIndex = startIndex, startPositionMs = startPositionMs) {
|
||||
callback(it)
|
||||
prepared = it
|
||||
}
|
||||
} else {
|
||||
if (tracks.size == 1) {
|
||||
return@getQueuedTracksLazily
|
||||
}
|
||||
|
||||
addRemainingMediaItems(tracks.toMediaItems(), startIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
callback(false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes a list of media items and the current index in the playlist. It then
|
||||
* adds the media items that come before and after the current index to the player's playlist.
|
||||
*/
|
||||
fun Player.addRemainingMediaItems(mediaItems: List<MediaItem>, currentIndex: Int) {
|
||||
val itemsAtStart = mediaItems.take(currentIndex)
|
||||
val itemsAtEnd = mediaItems.takeLast(mediaItems.lastIndex - currentIndex)
|
||||
applicationLooper.post {
|
||||
runOnPlayerThread {
|
||||
addMediaItems(0, itemsAtStart)
|
||||
addMediaItems(currentIndex + 1, itemsAtEnd)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
|
|||
|
||||
abstract fun setupColors(textColor: Int, adjustedPrimaryColor: Int)
|
||||
|
||||
fun prepareAndPlay(tracks: List<Track>, startIndex: Int = 0, startPosition: Long = 0, startActivity: Boolean = true) {
|
||||
(context as SimpleControllerActivity).prepareAndPlay(tracks, startIndex, startPosition, startActivity)
|
||||
fun prepareAndPlay(tracks: List<Track>, startIndex: Int = 0, startPositionMs: Long = 0, startActivity: Boolean = true) {
|
||||
(context as SimpleControllerActivity).prepareAndPlay(tracks, startIndex, startPositionMs, startActivity)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ class TracksFragment(context: Context, attributeSet: AttributeSet) : MyViewPager
|
|||
activity.handleNotificationPermission { granted ->
|
||||
if (granted) {
|
||||
val startIndex = tracks.indexOf(it as Track)
|
||||
prepareAndPlay(tracks, startIndex, 0)
|
||||
prepareAndPlay(tracks, startIndex)
|
||||
} else {
|
||||
if (context is Activity) {
|
||||
PermissionRequiredDialog(activity, R.string.allow_notifications_music_player, { activity.openNotificationSettings() })
|
||||
|
|
Loading…
Reference in a new issue