- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在使用 Android 的 MediaSession 时遇到了一些困难。
我一直在研究应该从 url 流式传输的原型(prototype) radio 应用程序。
到目前为止,我已经将它与前台服务一起使用,该服务可以通过主屏幕中的按钮进行控制。音频按预期继续超出应用程序的宽度,但是我有一个通知,根据播放状态显示播放或停止按钮。
我的问题是这个按钮不起作用。
我检测到 onStartCommand
被媒体按钮 Intent 调用,但调用 MediaButtonReceiver.handleIntent(mediaSession, intent)
结果什么也没发生。我的注册MediaCallback
永远不会被调用。
我已经为此阅读了文档,观看了 Google 的 youtube 系列,将其与一些演示应用程序进行了比较,并浏览了 StackOverflow,到目前为止,我一直无法找到任何适用于我的应用程序的解决方案。
我可以将媒体回调按钮换成通知上的自定义按钮,但我宁愿不这样做,我更愿意让它与 MediaSession 一起工作,这样我就可以获得 watch 、自动和锁屏集成。
这就是我的服务:
import android.app.*
import android.content.Context
import android.content.Intent
import android.os.IBinder
import android.support.v4.app.NotificationManagerCompat
import android.support.v4.content.ContextCompat
import project.base.App
import project.dagger.FeatureDagger
import javax.inject.Inject
import android.graphics.BitmapFactory
import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaSessionCompat
import android.media.AudioManager
import android.os.Build
import android.support.v4.media.session.MediaButtonReceiver
import android.support.v4.media.session.PlaybackStateCompat
import android.util.Log
import project.dagger.holder.FeatureHolder
import project.extensions.toActivityPendingIntent
import project.story.listen.*
private const val NOTIFICATION_ID = 1
class PlaybackService : Service(), PlaybackInteraction, ListenView {
@Inject lateinit var interactor: PlaybackInteractor
@Inject lateinit var presenter: ListenPresenter
@Inject lateinit var notificationFactory: NotificationFactory
private lateinit var mediaSession: MediaSessionCompat
override fun onBind(intent: Intent?): IBinder? = null
override fun onCreate() {
super.onCreate()
FeatureDagger.create(application as App).component.inject(this)
FeatureHolder.create(application as App)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) notificationFactory.createChannel()
mediaSession = MediaSessionCompat(this, "PlayerService")
mediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
mediaSession.setCallback(MediaCallback(
presenter::playTapped,
presenter::stopTapped,
presenter::terminatePlayback))
mediaSession.setSessionActivity(launchIntent())
mediaSession.setMetadata(metadata())
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.requestAudioFocus({
// Ignore
}, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
mediaSession.isActive = true
presenter.onViewCreated(this)
presenter.onStart()
interactor.onInteractionCreated(this)
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
MediaButtonReceiver.handleIntent(mediaSession, intent)
return START_NOT_STICKY
}
override fun showState(state: State) =
when (state) {
State.BUFFERING -> buffering()
State.PLAYING -> playing()
State.STOPPED -> stopped()
}
private fun buffering() =
startForeground(NOTIFICATION_ID, notificationFactory.bufferingNotification())
private fun playing() {
mediaSession.setPlaybackState(playingState())
startForeground(NOTIFICATION_ID, notificationFactory.playingNotification(mediaSession))
}
private fun stopped() {
mediaSession.setPlaybackState(stoppedState())
stopForeground(false)
NotificationManagerCompat
.from(this)
.notify(NOTIFICATION_ID, notificationFactory.stoppedNotification(mediaSession))
}
override fun dismiss() {
mediaSession.release()
stopSelf()
}
private fun playingState() =
PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PLAYING, 0, 0f)
.setActions(PlaybackStateCompat.ACTION_STOP)
.build()
private fun stoppedState() =
PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_STOPPED, 0, 0f)
.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE)
.build()
private fun metadata() =
MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "Test Artist")
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, "Test Album")
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Test Track Name")
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, 10000)
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART,
BitmapFactory.decodeResource(resources,
R.mipmap.ic_launcher))
.build()
private fun launchIntent() =
ListenActivity.buildIntent(this)
.toActivityPendingIntent(this)
companion object {
fun launch(context: Context) =
ContextCompat.startForegroundService(context, Intent(context, PlaybackService::class.java))
}
}
<service android:name="project.story.playback.PlaybackService">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</service>
<receiver android:name="android.support.v4.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>
MediaCallback
被设计为可重用,它的来源:
import android.support.v4.media.session.MediaSessionCompat
class MediaCallback(
private val onPlay: () -> Unit,
private val onPause: () -> Unit,
private val onStop: () -> Unit)
: MediaSessionCompat.Callback() {
override fun onPlay() {
super.onPlay()
onPlay.invoke()
}
override fun onPause() {
super.onPause()
onPause.invoke()
}
override fun onStop() {
super.onStop()
onStop.invoke()
}
}
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import android.support.annotation.RequiresApi
import android.support.v4.app.NotificationCompat
import android.support.v4.content.ContextCompat
import android.support.v4.media.session.MediaButtonReceiver
import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import project.extensions.toActivityPendingIntent
import project.feature.listen.R
import project.story.listen.ListenActivity
private const val CHANNEL_ID = "playback"
class NotificationFactory(private val context: Context) {
private fun baseNotification() =
NotificationCompat
.Builder(context, CHANNEL_ID)
.setContentTitle(context.getString(R.string.app_name))
.setSmallIcon(uk.co.keithkirk.cuillinfm.R.drawable.ic_notification)
.setColor(ContextCompat.getColor(context, uk.co.keithkirk.cuillinfm.R.color.accent))
.setAutoCancel(false)
.setContentIntent(launchIntent())
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
fun bufferingNotification() =
baseNotification()
.setOngoing(true)
.setContentText(context.getString(R.string.buffering))
.setProgress(0, 0, true)
.build()
fun playingNotification(session: MediaSessionCompat) =
baseNotification()
.setOngoing(true)
.setContentText(context.getString(R.string.playing))
.setStyle(android.support.v4.media.app.NotificationCompat.MediaStyle()
.setMediaSession(session.sessionToken)
.setShowCancelButton(true)
.setCancelButtonIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(
context,
PlaybackStateCompat.ACTION_STOP)))
.addAction(stopAction())
.build()
fun stoppedNotification(session: MediaSessionCompat) =
baseNotification()
.setOngoing(false)
.setContentText(context.getString(R.string.stopped))
.setDeleteIntent(terminateIntent())
.setStyle(android.support.v4.media.app.NotificationCompat.MediaStyle()
.setMediaSession(session.sessionToken)
.setShowCancelButton(false))
.addAction(playAction())
.build()
@RequiresApi(Build.VERSION_CODES.O)
fun createChannel() {
val channel = NotificationChannel(CHANNEL_ID,
context.getString(R.string.media_playback),
NotificationManager.IMPORTANCE_LOW)
channel.description = context.getString(R.string.media_playback_controls)
channel.setShowBadge(false)
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
(context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
.createNotificationChannel(channel)
}
private fun launchIntent() =
ListenActivity.buildIntent(context)
.toActivityPendingIntent(context)
private fun playAction() = NotificationCompat.Action(
R.drawable.ic_play_arrow_white,
context.getString(R.string.play),
playIntent())
private fun stopAction() = NotificationCompat.Action(
R.drawable.ic_stop_white,
context.getString(R.string.stop),
stopIntent())
private fun playIntent() =
MediaButtonReceiver.buildMediaButtonPendingIntent(
context,
PlaybackStateCompat.ACTION_PLAY)
private fun stopIntent() =
MediaButtonReceiver.buildMediaButtonPendingIntent(
context,
PlaybackStateCompat.ACTION_PAUSE)
private fun terminateIntent() =
MediaButtonReceiver.buildMediaButtonPendingIntent(
context,
PlaybackStateCompat.ACTION_STOP)
}
PlaybackInteractor
和
ListenPresenter
是架构的表示层,因此它们通过事件总线与更广泛的系统进行通信。我将对其进行总结,但除非有必要,否则我会避免发布源代码,因为这篇文章已经足够大了。
ListenPresenter
当播放、停止或终止被点击/需要时被告知,它在事件总线上发布这些指令,它还从总线读取当前播放状态并通知 View 更新(在这种情况下,服务更新通知)。此演示者的另一个实例连接到主屏幕上的按钮。
PlaybackInteractor
监听开始、停止和终止事件,并调用 Player 对象的包装类要求。当播放器通过状态更改进行回调时,它会更新事件总线上的播放状态。它还调用
dismiss
在需要终止服务时。
最佳答案
我无法让 MediaCallbacks 被调用,但我确实找到了另一种解决方案。
这并不理想,但不是依靠媒体框架来通知状态更改的回调,而是让服务拦截 Intent 并自行解决。
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
handleIntent(intent)
MediaButtonReceiver.handleIntent(mediaSession, intent)
return START_NOT_STICKY
}
private fun handleIntent(intent: Intent) =
(intent.extras?.get(Intent.EXTRA_KEY_EVENT) as KeyEvent?)?.keyCode.also {
when (it) {
KeyEvent.KEYCODE_MEDIA_PAUSE -> presenter.stopTapped()
KeyEvent.KEYCODE_MEDIA_PLAY -> presenter.playTapped()
KeyEvent.KEYCODE_MEDIA_STOP -> presenter.terminatePlayback()
}
}
关于android - 如何使用 Android MediaSessionCompat 获取通知回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52017261/
我无法导入 MediaSessionCompat。用于导入整个 android.support.v4.media 库的导入语句不会出现在 Android Studio“IntelliSense”中,并
如何使用 MediaSessionCompat?有人可以举一个简单的工作示例吗? 我找到了一些,但他们使用: MediaSessionCompat _mediaSession = new MediaS
将支持库升级到 25.3.1 后遇到此崩溃: Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual me
mediaSessionCompat.setMetadata(new MediaMetadataCompat.Builder() .putStr
我很好奇是否有人遇到过这个问题或有任何建议。我正在编写一个使用 MediaSessionCompat API 的媒体播放器。每隔一段时间,我都会确定播放媒体的当前位置并引发更改事件。我重新设置了播放状
我在使用 Android 的 MediaSession 时遇到了一些困难。 我一直在研究应该从 url 流式传输的原型(prototype) radio 应用程序。 到目前为止,我已经将它与前台服务一
我对 MediaControllerCompat 和 MediaSessionCompat 的导入 import android.support.v4.media.session.MediaContr
社区同胞! 所以我的应用程序遇到问题,那就是使用 MediaSessionCompat。 该崩溃问题仅出现在 Android 8 版本(8.1 也有)上,但主要出现在不同设备上的 8.0.0 上。 致
对于 Lollipop ,我们被告知要更换 registerMediaButtonEventReceiver() 与 setMediaButtonReceiver(PendingIntent)。 这在
我想用一个使用 MediaSession 的新类替换我应用程序中的 android RemoteControlClient 类,但它不适用于 API 级别 < 21,MediaSessionCompa
我想将我的 exoplayer 实现与媒体 session 对象连接起来。我设置了一个 SimpleExoPlayerView 来显示视频。每次单击按钮时,我都希望触发媒体 session 回调。我只
当我尝试在运行 Android 5.0.1 的 HUAWEI P8 lite 设备上显示通知时,我的应用程序崩溃(它在 Nexus 和三星设备上运行良好)。我的大部分代码取自 Ian Lake 的视频
我有一个应用程序可以从服务中复制网络广播(使用 AACDecoder 库)并在 Activity 未恢复时在简单的持续通知中显示正在播放的内容。我想要的是通过 NotificationCompat.M
我正在尝试在 Android 中实现通知播放器。我关注了this实现这一目标的指南。但是,作为我的要求,我需要在后台通知播放器中实现“跳到下一个”和“跳到上一个”。 到目前为止,MediaSessio
我正在尝试将我的应用程序更新到 Android SDK 31,但我遇到了 MediaSessionCompat 的问题。 我有一个扩展 MediaBrowserServiceCompat() 的 Me
我是一名优秀的程序员,十分优秀!