Reorganize some extension functions

This commit is contained in:
Naveen 2023-08-29 18:51:43 +05:30
parent c2dc9f2604
commit c7a269fe00
No known key found for this signature in database
GPG key ID: 0E155DAD31671DA3
5 changed files with 78 additions and 79 deletions

View file

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

View file

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

View file

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

View file

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

View file

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