mirror of
https://github.com/antebudimir/tempus.git
synced 2026-01-01 09:53:33 +00:00
feat: cleaned up MediaService class, added support for Android Auto repository
This commit is contained in:
parent
567350771c
commit
1a407341ec
2 changed files with 25 additions and 174 deletions
|
|
@ -9,7 +9,7 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
|
|
||||||
public class MediaLibraryScanningClient {
|
public class MediaLibraryScanningClient {
|
||||||
private static final String TAG = "SystemClient";
|
private static final String TAG = "MediaLibraryScanningClient";
|
||||||
|
|
||||||
private final Subsonic subsonic;
|
private final Subsonic subsonic;
|
||||||
private final MediaLibraryScanningService mediaLibraryScanningService;
|
private final MediaLibraryScanningService mediaLibraryScanningService;
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,23 @@
|
||||||
package com.cappielloantonio.tempo.service
|
package com.cappielloantonio.tempo.service
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.PendingIntent.FLAG_IMMUTABLE
|
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
import android.app.TaskStackBuilder
|
import android.app.TaskStackBuilder
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.media3.cast.CastPlayer
|
import androidx.media3.cast.CastPlayer
|
||||||
import androidx.media3.cast.SessionAvailabilityListener
|
import androidx.media3.cast.SessionAvailabilityListener
|
||||||
import androidx.media3.common.*
|
import androidx.media3.common.AudioAttributes
|
||||||
|
import androidx.media3.common.C
|
||||||
|
import androidx.media3.common.MediaItem
|
||||||
|
import androidx.media3.common.Player
|
||||||
|
import androidx.media3.common.Tracks
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.exoplayer.DefaultLoadControl
|
import androidx.media3.exoplayer.DefaultLoadControl
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
||||||
import androidx.media3.session.*
|
import androidx.media3.session.MediaLibraryService
|
||||||
import androidx.media3.session.MediaSession.ControllerInfo
|
import androidx.media3.session.MediaSession.ControllerInfo
|
||||||
import com.cappielloantonio.tempo.R
|
import com.cappielloantonio.tempo.repository.AutomotiveRepository
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity
|
import com.cappielloantonio.tempo.ui.activity.MainActivity
|
||||||
import com.cappielloantonio.tempo.util.Constants
|
import com.cappielloantonio.tempo.util.Constants
|
||||||
import com.cappielloantonio.tempo.util.DownloadUtil
|
import com.cappielloantonio.tempo.util.DownloadUtil
|
||||||
|
|
@ -24,33 +26,19 @@ import com.cappielloantonio.tempo.util.ReplayGainUtil
|
||||||
import com.google.android.gms.cast.framework.CastContext
|
import com.google.android.gms.cast.framework.CastContext
|
||||||
import com.google.android.gms.common.ConnectionResult
|
import com.google.android.gms.common.ConnectionResult
|
||||||
import com.google.android.gms.common.GoogleApiAvailability
|
import com.google.android.gms.common.GoogleApiAvailability
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import com.google.common.util.concurrent.Futures
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
|
||||||
|
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
||||||
private val librarySessionCallback = CustomMediaLibrarySessionCallback()
|
|
||||||
|
|
||||||
|
private lateinit var automotiveRepository: AutomotiveRepository
|
||||||
private lateinit var player: ExoPlayer
|
private lateinit var player: ExoPlayer
|
||||||
private lateinit var castPlayer: CastPlayer
|
private lateinit var castPlayer: CastPlayer
|
||||||
private lateinit var mediaLibrarySession: MediaLibrarySession
|
private lateinit var mediaLibrarySession: MediaLibrarySession
|
||||||
private lateinit var customCommands: List<CommandButton>
|
|
||||||
|
|
||||||
private var customLayout = ImmutableList.of<CommandButton>()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON =
|
|
||||||
"android.media3.session.demo.SHUFFLE_ON"
|
|
||||||
private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF =
|
|
||||||
"android.media3.session.demo.SHUFFLE_OFF"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
initializeCustomCommands()
|
initializeRepository()
|
||||||
initializePlayer()
|
initializePlayer()
|
||||||
initializeCastPlayer()
|
initializeCastPlayer()
|
||||||
initializeMediaLibrarySession()
|
initializeMediaLibrarySession()
|
||||||
|
|
@ -66,140 +54,21 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
||||||
return mediaLibrarySession
|
return mediaLibrarySession
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||||
|
val player = mediaLibrarySession.player
|
||||||
|
|
||||||
|
if (!player.playWhenReady || player.mediaItemCount == 0) {
|
||||||
|
stopSelf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
releasePlayer()
|
releasePlayer()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class CustomMediaLibrarySessionCallback : MediaLibrarySession.Callback {
|
private fun initializeRepository() {
|
||||||
|
automotiveRepository = AutomotiveRepository()
|
||||||
override fun onConnect(
|
|
||||||
session: MediaSession,
|
|
||||||
controller: ControllerInfo
|
|
||||||
): MediaSession.ConnectionResult {
|
|
||||||
val connectionResult = super.onConnect(session, controller)
|
|
||||||
val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon()
|
|
||||||
|
|
||||||
customCommands.forEach { commandButton ->
|
|
||||||
// TODO: Aggiungere i comandi personalizzati
|
|
||||||
// commandButton.sessionCommand?.let { availableSessionCommands.add(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
return MediaSession.ConnectionResult.accept(
|
|
||||||
availableSessionCommands.build(),
|
|
||||||
connectionResult.availablePlayerCommands
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPostConnect(session: MediaSession, controller: ControllerInfo) {
|
|
||||||
if (!customLayout.isEmpty() && controller.controllerVersion != 0) {
|
|
||||||
ignoreFuture(mediaLibrarySession.setCustomLayout(controller, customLayout))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCustomCommand(
|
|
||||||
session: MediaSession,
|
|
||||||
controller: ControllerInfo,
|
|
||||||
customCommand: SessionCommand,
|
|
||||||
args: Bundle
|
|
||||||
): ListenableFuture<SessionResult> {
|
|
||||||
if (CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON == customCommand.customAction) {
|
|
||||||
player.shuffleModeEnabled = true
|
|
||||||
customLayout = ImmutableList.of(customCommands[1])
|
|
||||||
session.setCustomLayout(customLayout)
|
|
||||||
} else if (CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF == customCommand.customAction) {
|
|
||||||
player.shuffleModeEnabled = false
|
|
||||||
customLayout = ImmutableList.of(customCommands[0])
|
|
||||||
session.setCustomLayout(customLayout)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
|
|
||||||
}
|
|
||||||
|
|
||||||
/* override fun onGetLibraryRoot(
|
|
||||||
session: MediaLibrarySession,
|
|
||||||
browser: ControllerInfo,
|
|
||||||
params: LibraryParams?
|
|
||||||
): ListenableFuture<LibraryResult<MediaItem>> {
|
|
||||||
if (params != null && params.isRecent) {
|
|
||||||
return Futures.immediateFuture(LibraryResult.ofError(LibraryResult.RESULT_ERROR_NOT_SUPPORTED))
|
|
||||||
}
|
|
||||||
return Futures.immediateFuture(LibraryResult.ofItem(MediaItemTree.getRootItem(), params))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onGetItem(
|
|
||||||
session: MediaLibrarySession,
|
|
||||||
browser: ControllerInfo,
|
|
||||||
mediaId: String
|
|
||||||
): ListenableFuture<LibraryResult<MediaItem>> {
|
|
||||||
val item =
|
|
||||||
MediaItemTree.getItem(mediaId)
|
|
||||||
?: return Futures.immediateFuture(
|
|
||||||
LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)
|
|
||||||
)
|
|
||||||
return Futures.immediateFuture(LibraryResult.ofItem(item, /* params= */ null))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSubscribe(
|
|
||||||
session: MediaLibrarySession,
|
|
||||||
browser: ControllerInfo,
|
|
||||||
parentId: String,
|
|
||||||
params: LibraryParams?
|
|
||||||
): ListenableFuture<LibraryResult<Void>> {
|
|
||||||
val children =
|
|
||||||
MediaItemTree.getChildren(parentId)
|
|
||||||
?: return Futures.immediateFuture(
|
|
||||||
LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)
|
|
||||||
)
|
|
||||||
session.notifyChildrenChanged(browser, parentId, children.size, params)
|
|
||||||
return Futures.immediateFuture(LibraryResult.ofVoid())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onGetChildren(
|
|
||||||
session: MediaLibrarySession,
|
|
||||||
browser: ControllerInfo,
|
|
||||||
parentId: String,
|
|
||||||
page: Int,
|
|
||||||
pageSize: Int,
|
|
||||||
params: LibraryParams?
|
|
||||||
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
|
|
||||||
val children =
|
|
||||||
MediaItemTree.getChildren(parentId)
|
|
||||||
?: return Futures.immediateFuture(
|
|
||||||
LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE)
|
|
||||||
)
|
|
||||||
|
|
||||||
return Futures.immediateFuture(LibraryResult.ofItemList(children, params))
|
|
||||||
}*/
|
|
||||||
|
|
||||||
override fun onAddMediaItems(
|
|
||||||
mediaSession: MediaSession,
|
|
||||||
controller: ControllerInfo,
|
|
||||||
mediaItems: List<MediaItem>
|
|
||||||
): ListenableFuture<List<MediaItem>> {
|
|
||||||
val updatedMediaItems = mediaItems.map {
|
|
||||||
it.buildUpon()
|
|
||||||
.setUri(it.requestMetadata.mediaUri)
|
|
||||||
.setMediaMetadata(it.mediaMetadata)
|
|
||||||
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
return Futures.immediateFuture(updatedMediaItems)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initializeCustomCommands() {
|
|
||||||
customCommands =
|
|
||||||
listOf(
|
|
||||||
getShuffleCommandButton(
|
|
||||||
SessionCommand(CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON, Bundle.EMPTY)
|
|
||||||
),
|
|
||||||
getShuffleCommandButton(
|
|
||||||
SessionCommand(CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF, Bundle.EMPTY)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
customLayout = ImmutableList.of(customCommands[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializePlayer() {
|
private fun initializePlayer() {
|
||||||
|
|
@ -230,13 +99,13 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaLibrarySession =
|
mediaLibrarySession =
|
||||||
MediaLibrarySession.Builder(this, player, librarySessionCallback)
|
MediaLibrarySession.Builder(this, player, createLibrarySessionCallback())
|
||||||
.setSessionActivity(sessionActivityPendingIntent)
|
.setSessionActivity(sessionActivityPendingIntent)
|
||||||
.build()
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
if (!customLayout.isEmpty()) {
|
private fun createLibrarySessionCallback(): MediaLibrarySession.Callback {
|
||||||
mediaLibrarySession.setCustomLayout(customLayout)
|
return MediaLibrarySessionCallback(this, automotiveRepository)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializePlayerListener() {
|
private fun initializePlayerListener() {
|
||||||
|
|
@ -316,25 +185,7 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
||||||
if (this::castPlayer.isInitialized) castPlayer.release()
|
if (this::castPlayer.isInitialized) castPlayer.release()
|
||||||
player.release()
|
player.release()
|
||||||
mediaLibrarySession.release()
|
mediaLibrarySession.release()
|
||||||
}
|
clearListener()
|
||||||
|
|
||||||
@SuppressLint("PrivateResource")
|
|
||||||
private fun getShuffleCommandButton(sessionCommand: SessionCommand): CommandButton {
|
|
||||||
val isOn = sessionCommand.customAction == CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON
|
|
||||||
return CommandButton.Builder()
|
|
||||||
.setDisplayName(
|
|
||||||
getString(
|
|
||||||
if (isOn) R.string.exo_controls_shuffle_on_description
|
|
||||||
else R.string.exo_controls_shuffle_off_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.setSessionCommand(sessionCommand)
|
|
||||||
.setIconResId(if (isOn) R.drawable.exo_icon_shuffle_off else R.drawable.exo_icon_shuffle_on)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ignoreFuture(customLayout: ListenableFuture<SessionResult>) {
|
|
||||||
/* Do nothing. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRenderersFactory() = DownloadUtil.buildRenderersFactory(this, false)
|
private fun getRenderersFactory() = DownloadUtil.buildRenderersFactory(this, false)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue