我正在尝试为 Android 汽车构建一个媒体应用程序。我尝试根据官方文档构建应用程序,但我可能错过/误解了一些东西。我创建了一个“可播放”MediaItem,并且可以正确显示它及其元数据。我还为我的“MediaSession”设置了“PlaybackState”,但不知何故,我无法停止/暂停媒体,尽管我可以播放它。
我期望开始播放媒体时应该有“暂停”按钮而不是“停止”。除了这个期望之外,主要的问题是,这个“停止”按钮不起作用。当我按下它时什么也没有发生。
这是我的 MusicService 类的完整代码:
// imports here
class MusicService : MediaBrowserServiceCompat() {
private lateinit var mediaSession: MediaSessionCompat
private lateinit var mediaController: MediaControllerCompat
private var playbackStateBuilder = PlaybackStateCompat.Builder()
override fun onCreate() {
super.onCreate()
mediaSession = MediaSessionCompat(baseContext, "MusicService").apply {
setCallback(MyMediaSessionCallback())
setPlaybackState(
playbackStateBuilder.apply {
setState(PlaybackStateCompat.STATE_NONE, 0L, 1f)
addActionsForPlayback(this)
// addCustomActionsForPlayback(this)
}.build()
)
// setFlags(
// MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
// MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
)
isActive = true
}
sessionToken = mediaSession.sessionToken
mediaController = MediaControllerCompat(baseContext, mediaSession.sessionToken).apply {
registerCallback(MyMediaControllerCallback())
}
}
override fun onGetRoot(
clientPackageName: String, clientUid: Int, rootHints: Bundle?
): BrowserRoot {
val extras = Bundle()
extras.putBoolean(
MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true
)
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
)
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
)
return BrowserRoot("/", extras)
}
override fun onLoadChildren(
parentId: String, result: Result<MutableList<MediaItem>>
) {
val treeResult = BrowseTree(baseContext)[parentId]
if (treeResult.isNullOrEmpty()) {
result.detach()
} else {
result.sendResult(treeResult)
}
}
private fun addActionsForPlayback(playbackStateCompatBuilder: PlaybackStateCompat.Builder) {
playbackStateCompatBuilder.apply {
setActions(PlaybackStateCompat.ACTION_PLAY)
setActions(PlaybackStateCompat.ACTION_PAUSE)
// setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE)
setActions(PlaybackStateCompat.ACTION_STOP)
setActions(PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID)
setActions(PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH)
setActions(PlaybackStateCompat.ACTION_SEEK_TO)
}
}
private fun addCustomActionsForPlayback(playbackStateCompatBuilder: PlaybackStateCompat.Builder) {
// custom actions defined here
}
inner class MyMediaSessionCallback : MediaSessionCompat.Callback() {
private val musicSource = MusicSource(this@MusicService)
private var seekedPos: Long? = null
override fun onPrepare() {
println("Preparing")
super.onPrepare()
}
override fun onPrepareFromMediaId(mediaId: String?, extras: Bundle?) {
println("Preparing from media id")
super.onPrepareFromMediaId(mediaId, extras)
}
override fun onPlay() {
println("Playing")
mediaSession.setPlaybackState(
playbackStateBuilder.apply {
setState(PlaybackStateCompat.STATE_PLAYING, seekedPos ?: 0L, 1f)
setActions(PlaybackStateCompat.ACTION_PAUSE)
setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE)
setActions(PlaybackStateCompat.ACTION_STOP)
setActions(PlaybackStateCompat.ACTION_SEEK_TO)
setActions(PlaybackStateCompat.ACTION_PLAY)
}.build()
)
super.onPlay()
}
override fun onPause() {
println("Paused")
super.onPause()
}
override fun onStop() {
println("Stopped")
mediaSession.release()
super.onStop()
}
override fun onSeekTo(pos: Long) {
println("Seeked to: $pos")
seekedPos = pos
if (mediaController.playbackState.state == 3) {
mediaSession.setPlaybackState(
playbackStateBuilder.apply {
setState(PlaybackStateCompat.STATE_PLAYING, pos, 1f)
addActionsForPlayback(this)
}.build()
)
} else {
mediaSession.setPlaybackState(
playbackStateBuilder.apply {
setState(PlaybackStateCompat.STATE_PAUSED, pos, 1f)
addActionsForPlayback(this)
}.build()
)
}
super.onSeekTo(pos)
}
override fun onRewind() {
println("Rewind command")
super.onRewind()
}
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle) {
seekedPos = null
val defaultArt = BitmapFactory.decodeResource(
[email protected], R.drawable.music_library_icon_2
)
val albumCover = musicSource.getAlbumCover(extras.getString("PATH", ""))
mediaSession.apply {
setPlaybackState(playbackStateBuilder.apply {
setState(PlaybackStateCompat.STATE_STOPPED, 0L, 1f)
setBufferedPosition(extras.getLong(MediaMetadataCompat.METADATA_KEY_DURATION))
setActions(PlaybackStateCompat.ACTION_PLAY)
setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE)
setActions(PlaybackStateCompat.ACTION_STOP)
setActions(PlaybackStateCompat.ACTION_SEEK_TO)
setActions(PlaybackStateCompat.ACTION_PAUSE)
val playbackStateExtras = Bundle().apply {
putString(
MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID,
extras.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID)
)
}
setExtras(playbackStateExtras)
addActionsForPlayback(this)
}.build())
setMetadata(
MediaMetadataCompat.Builder().apply {
putString(
MediaMetadataCompat.METADATA_KEY_MEDIA_ID,
extras.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID)
)
putString(
MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE,
extras.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE)
)
putString(
MediaMetadataCompat.METADATA_KEY_ARTIST,
extras.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE)
)
putString(
MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST,
extras.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE)
)
putString(
MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE,
extras.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE)
)
putLong(
MediaMetadataCompat.METADATA_KEY_DURATION,
extras.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)
)
putString(
MediaMetadataCompat.METADATA_KEY_MEDIA_URI,
extras.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI)
)
if (albumCover != null) {
putBitmap(
MediaMetadataCompat.METADATA_KEY_ART, albumCover
)
} else {
putBitmap(
MediaMetadataCompat.METADATA_KEY_ART, defaultArt
)
}
putLong(
MediaConstants.METADATA_KEY_IS_EXPLICIT,
extras.getLong(MediaConstants.METADATA_KEY_IS_EXPLICIT)
)
}.build()
)
}
println("Playing from Media Id")
super.onPlayFromMediaId(mediaId, extras)
}
override fun onPlayFromSearch(query: String?, extras: Bundle?) {
println("Playing from Search")
super.onPlayFromSearch(query, extras)
}
override fun onPlayFromUri(uri: Uri?, extras: Bundle?) {
println("Playing from Uri")
super.onPlayFromUri(uri, extras)
}
override fun onCommand(command: String?, extras: Bundle?, cb: ResultReceiver?) {
println("Command: $command")
super.onCommand(command, extras, cb)
}
override fun onCustomAction(action: String?, extras: Bundle?) {
println("Action: $action")
super.onCustomAction(action, extras)
}
override fun onMediaButtonEvent(mediaButtonEvent: Intent?): Boolean {
println("Media button event")
return super.onMediaButtonEvent(mediaButtonEvent)
}
}
inner class MyMediaControllerCallback : MediaControllerCompat.Callback() {
override fun onMetadataChanged(metadata: MediaMetadataCompat?) {
println("Controller media id: ${metadata?.description?.mediaId}")
super.onMetadataChanged(metadata)
}
override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {
println("Controller playback state : ${state?.state}")
super.onPlaybackStateChanged(state)
}
override fun onSessionReady() {
println("Controller session is ready")
super.onSessionReady()
}
}
}
我尝试使用以下代码设置 PlaybackState 所需的操作:
setActions(PlaybackStateCompat.ACTION_PLAY)
setActions(PlaybackStateCompat.ACTION_PAUSE)
setActions(PlaybackStateCompat.ACTION_STOP)
setActions(PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID)
setActions(PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH)
setActions(PlaybackStateCompat.ACTION_SEEK_TO)
但是这些没有显示“暂停”按钮,或者没有使“停止”按钮起作用。如果您需要,我可以提供更多从其他类导入的代码块。预先感谢您的帮助。
setActions
引起的。您应该使用支持的操作的位掩码调用一次,而不是每个操作调用一次。
setActions(ACTION_PLAY or ACTION_PAUSE or ACTION_STOP or ACTION_PLAY_FROM_MEDIA_ID or ACTION_PLAY_FROM_SEARCH or ACTION_SEEK_TO)
or
是 Kotlin 中的按位 OR 运算符(类似于其他语言(包括 Java)中的 |
)。这有点令人困惑,但用简单的英语来说,上面的代码翻译为“这支持 ACTION_PLAY 和 ACTION_PAUSE 以及......”