add close command
This commit is contained in:
parent
e965528011
commit
2e50634b50
|
@ -55,6 +55,8 @@ class NewPlayerImpl(
|
|||
private val repository: MediaRepository
|
||||
) : NewPlayer {
|
||||
|
||||
private var playerScope = CoroutineScope(Dispatchers.Main + Job())
|
||||
|
||||
private var uniqueIdToIdLookup = HashMap<Long, String>()
|
||||
|
||||
// this is used to take care of the NewPlayerService
|
||||
|
@ -74,8 +76,6 @@ class NewPlayerImpl(
|
|||
|
||||
override var fastSeekAmountSec: Int = 10
|
||||
|
||||
private var playerScope = CoroutineScope(Dispatchers.Main + Job())
|
||||
|
||||
override var playBackMode = MutableStateFlow(PlayMode.IDLE)
|
||||
|
||||
override var shuffle: Boolean
|
||||
|
@ -251,7 +251,11 @@ class NewPlayerImpl(
|
|||
}
|
||||
|
||||
override fun release() {
|
||||
mediaController?.release()
|
||||
internalPlayer.release()
|
||||
playBackMode.update {
|
||||
PlayMode.IDLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun internalPlayStream(mediaItem: MediaItem, playMode: PlayMode) {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/* NewPlayer
|
||||
*
|
||||
* @author Christian Schabesberger
|
||||
*
|
||||
* Copyright (C) NewPipe e.V. 2024 <code(at)newpipe-ev.de>
|
||||
*
|
||||
* NewPlayer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package net.newpipe.newplayer.service
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.media3.session.CommandButton
|
||||
import androidx.media3.session.SessionCommand
|
||||
import net.newpipe.newplayer.R
|
||||
|
||||
|
||||
data class CustomCommand(
|
||||
val action: String,
|
||||
val commandButton: CommandButton
|
||||
) {
|
||||
companion object {
|
||||
const val NEW_PLAYER_NOTIFICATION_COMMAND_CLOSE_PLAYBACK = "NEW_PLAYER_CLOSE_PLAYBACK"
|
||||
}
|
||||
}
|
||||
|
||||
fun buildCustomCommandList(context: Context) = listOf(
|
||||
CustomCommand(
|
||||
CustomCommand.NEW_PLAYER_NOTIFICATION_COMMAND_CLOSE_PLAYBACK,
|
||||
CommandButton.Builder()
|
||||
.setDisplayName(context.getString(R.string.close))
|
||||
.setDisplayName("Close")
|
||||
.setSessionCommand(
|
||||
SessionCommand(
|
||||
CustomCommand.NEW_PLAYER_NOTIFICATION_COMMAND_CLOSE_PLAYBACK,
|
||||
Bundle()
|
||||
)
|
||||
)
|
||||
.setIconResId(R.drawable.close_24px)
|
||||
.build()
|
||||
)
|
||||
)
|
||||
|
|
@ -21,32 +21,122 @@
|
|||
|
||||
package net.newpipe.newplayer.service
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.session.MediaSession
|
||||
import androidx.media3.session.MediaSessionService
|
||||
import androidx.media3.session.SessionCommand
|
||||
import androidx.media3.session.SessionError
|
||||
import androidx.media3.session.SessionResult
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import net.newpipe.newplayer.NewPlayer
|
||||
import net.newpipe.newplayer.PlayMode
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val TAG = "NewPlayerService"
|
||||
|
||||
@AndroidEntryPoint
|
||||
class NewPlayerService : MediaSessionService() {
|
||||
|
||||
private var mediaSession: MediaSession? = null
|
||||
private lateinit var mediaSession: MediaSession
|
||||
private lateinit var customCommands: List<CustomCommand>
|
||||
|
||||
@Inject
|
||||
lateinit var newPlayer: NewPlayer
|
||||
|
||||
private var serviceScope = CoroutineScope(Dispatchers.Main + Job())
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
customCommands = buildCustomCommandList(this)
|
||||
|
||||
mediaSession = MediaSession.Builder(this, newPlayer.internalPlayer)
|
||||
.setCallback(object : MediaSession.Callback {
|
||||
override fun onConnect(
|
||||
session: MediaSession,
|
||||
controller: MediaSession.ControllerInfo
|
||||
): MediaSession.ConnectionResult {
|
||||
val connectionResult = super.onConnect(session, controller)
|
||||
val availableSessionCommands =
|
||||
connectionResult.availableSessionCommands.buildUpon()
|
||||
|
||||
customCommands.forEach { command ->
|
||||
command.commandButton.sessionCommand?.let {
|
||||
availableSessionCommands.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
return MediaSession.ConnectionResult.accept(
|
||||
availableSessionCommands.build(),
|
||||
connectionResult.availablePlayerCommands
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPostConnect(
|
||||
session: MediaSession,
|
||||
controller: MediaSession.ControllerInfo
|
||||
) {
|
||||
super.onPostConnect(session, controller)
|
||||
mediaSession.setCustomLayout(customCommands.map{it.commandButton})
|
||||
}
|
||||
|
||||
override fun onCustomCommand(
|
||||
session: MediaSession,
|
||||
controller: MediaSession.ControllerInfo,
|
||||
customCommand: SessionCommand,
|
||||
args: Bundle
|
||||
): ListenableFuture<SessionResult> {
|
||||
when(customCommand.customAction) {
|
||||
CustomCommand.NEW_PLAYER_NOTIFICATION_COMMAND_CLOSE_PLAYBACK -> {
|
||||
newPlayer.release()
|
||||
}
|
||||
else -> {
|
||||
Log.e(TAG, "Unknown custom command: ${customCommand.customAction}")
|
||||
return Futures.immediateFuture(SessionResult(SessionError.ERROR_NOT_SUPPORTED))
|
||||
}
|
||||
}
|
||||
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
|
||||
}
|
||||
|
||||
})
|
||||
.build()
|
||||
|
||||
|
||||
serviceScope.launch {
|
||||
newPlayer.playBackMode.collect { mode ->
|
||||
if(mode == PlayMode.IDLE) {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
newPlayer.release()
|
||||
mediaSession?.release()
|
||||
mediaSession = null
|
||||
mediaSession.release()
|
||||
}
|
||||
|
||||
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
|
||||
println("gurken get session")
|
||||
if (mediaSession == null) {
|
||||
mediaSession = MediaSession.Builder(this, newPlayer.internalPlayer).build()
|
||||
}
|
||||
return mediaSession
|
||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||
// Check if the player is not ready to play or there are no items in the media queue
|
||||
if (!newPlayer.internalPlayer.playWhenReady || newPlayer.playlist.value.size == 0) {
|
||||
// Stop the service
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo) = mediaSession
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M256,760L200,704L424,480L200,256L256,200L480,424L704,200L760,256L536,480L760,704L704,760L480,536L256,760Z"/>
|
||||
</vector>
|
|
@ -52,4 +52,5 @@
|
|||
<string name="shuffle_on">Shuffle playlist enabled</string>
|
||||
<string name="shuffle_off">Shuffle playlist disabled</string>
|
||||
<string name="store_playlist">Save current playlist</string>
|
||||
<string name="close">Close</string>
|
||||
</resources>
|
Loading…
Reference in New Issue