TV アプリでは、ランチャーの背後またはバックグラウンドでメディアを再生するときに [再生しています] カードを表示する必要があります。このカードにより、ユーザーは現在メディアを再生しているアプリに戻ることができます。
アクティブな MediaSession
が存在するとき、Android フレームワークはホーム画面に [再生しています] カードを表示します。カードにはアルバムアート、タイトル、アプリアイコンといったメディアのメタデータが表示されます。
ユーザーがカードを選択すると、アプリが開きます。
このレッスンでは、MediaSession
クラスを使用して [再生しています] カードを実装する方法を説明します。

図 1. バックグラウンドでメディアを再生しているときに表示される [再生しています] カード
メディア セッションを開始する
アプリがメディアの再生を準備しているときに、MediaSession
を作成します。次のコード スニペットは、適切なコールバックとフラグを設定する方法の例を示しています。
Kotlin
session = MediaSession(this, "MusicService").apply { setCallback(MediaSessionCallback()) setFlags( MediaSession.FLAG_HANDLES_MEDIA_BUTTONS or MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS ) }
Java
session = new MediaSession(this, "MusicService"); session.setCallback(new MediaSessionCallback()); session.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
注: [再生しています] カードは、FLAG_HANDLES_TRANSPORT_CONTROLS
フラグが設定されたメディア セッションに対してのみ表示されます。
[再生しています] カードを表示する
[再生しています] カードは、アクティブなセッションに対してのみ表示されます。再生が開始されたら、setActive(true)
を呼び出す必要があります。また、音声フォーカスの管理で説明しているように、アプリでは音声フォーカスをリクエストする必要があります。
Kotlin
private fun handlePlayRequest() { tryToGetAudioFocus() if (!session.isActive) { session.isActive = true } ... }
Java
private void handlePlayRequest() { tryToGetAudioFocus(); if (!session.isActive()) { session.setActive(true); } ... }
setActive(false)
の呼び出しによりメディア セッションが無効になった場合、または別のアプリがメディアの再生を開始した場合、このカードはランチャー画面から削除されます。再生が完全に停止した場合は、アクティブなメディアがなければ、アプリはメディア セッションを直ちに無効にする必要があります。再生が一時停止した場合は、しばらく待機してから(通常は 5~30 分後)メディア セッションを無効にします。
再生ステータスを更新する
カードが現在のメディアの状態を表示できるように、MediaSession
で再生ステータスを更新します。
Kotlin
private fun updatePlaybackState() { val position: Long = mediaPlayer ?.takeIf { it.isPlaying } ?.currentPosition?.toLong() ?: PlaybackState.PLAYBACK_POSITION_UNKNOWN val stateBuilder = PlaybackState.Builder() .setActions(getAvailableActions()).apply { setState(mState, position, 1.0f) } session.setPlaybackState(stateBuilder.build()) } private fun getAvailableActions(): Long { var actions = (PlaybackState.ACTION_PLAY_PAUSE or PlaybackState.ACTION_PLAY_FROM_MEDIA_ID or PlaybackState.ACTION_PLAY_FROM_SEARCH) playingQueue?.takeIf { it.isNotEmpty() }?.apply { actions = if (mState == PlaybackState.STATE_PLAYING) { actions or PlaybackState.ACTION_PAUSE } else { actions or PlaybackState.ACTION_PLAY } if (currentIndexOnQueue > 0) { actions = actions or PlaybackState.ACTION_SKIP_TO_PREVIOUS } if (currentIndexOnQueue < size - 1) { actions = actions or PlaybackState.ACTION_SKIP_TO_NEXT } } return actions }
Java
private void updatePlaybackState() { long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN; if (mediaPlayer != null && mediaPlayer.isPlaying()) { position = mediaPlayer.getCurrentPosition(); } PlaybackState.Builder stateBuilder = new PlaybackState.Builder() .setActions(getAvailableActions()); stateBuilder.setState(mState, position, 1.0f); session.setPlaybackState(stateBuilder.build()); } private long getAvailableActions() { long actions = PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_PLAY_FROM_MEDIA_ID | PlaybackState.ACTION_PLAY_FROM_SEARCH; if (playingQueue == null || playingQueue.isEmpty()) { return actions; } if (mState == PlaybackState.STATE_PLAYING) { actions |= PlaybackState.ACTION_PAUSE; } else { actions |= PlaybackState.ACTION_PLAY; } if (currentIndexOnQueue > 0) { actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS; } if (currentIndexOnQueue < playingQueue.size() - 1) { actions |= PlaybackState.ACTION_SKIP_TO_NEXT; } return actions; }
メディアのメタデータを表示する
setMetadata()
メソッドで MediaMetadata
を設定します。メディア セッション オブジェクトのこのメソッドを使用すると、トラックに関する情報(タイトル、サブタイトル、各種のアイコンなど)を [再生しています] カードに表示できます。次の例は、トラックのデータがカスタム データクラス MediaData
に格納されていることを前提としています。
Kotlin
private fun updateMetadata(myData: MediaData) { val metadataBuilder = MediaMetadata.Builder().apply { // To provide most control over how an item is displayed set the // display fields in the metadata putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, myData.displayTitle) putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, myData.displaySubtitle) putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, myData.artUri) // And at minimum the title and artist for legacy support putString(MediaMetadata.METADATA_KEY_TITLE, myData.title) putString(MediaMetadata.METADATA_KEY_ARTIST, myData.artist) // A small bitmap for the artwork is also recommended putBitmap(MediaMetadata.METADATA_KEY_ART, myData.artBitmap) // Add any other fields you have for your data as well } session.setMetadata(metadataBuilder.build()) }
Java
private void updateMetadata(MediaData myData) { MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder(); // To provide most control over how an item is displayed set the // display fields in the metadata metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, myData.displayTitle); metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, myData.displaySubtitle); metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, myData.artUri); // And at minimum the title and artist for legacy support metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, myData.title); metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, myData.artist); // A small bitmap for the artwork is also recommended metadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_ART, myData.artBitmap); // Add any other fields you have for your data as well session.setMetadata(metadataBuilder.build()); }
ユーザー アクションに応答する
ユーザーが [再生しています] カードを選択すると、セッション中のアプリが開きます。
アプリが PendingIntent
を setSessionActivity()
に提供した場合は、次に示すように、指定したアクティビティが開始されます。提供しなかった場合は、デフォルトのシステム インテントが開きます。指定したアクティビティは、ユーザーが再生を一時停止または停止できるように再生コントロールを提供する必要があります。
Kotlin
val pi: PendingIntent = Intent(context, MyActivity::class.java).let { intent -> PendingIntent.getActivity( context, 99 /*request code*/, intent, PendingIntent.FLAG_UPDATE_CURRENT ) } session.setSessionActivity(pi)
Java
Intent intent = new Intent(context, MyActivity.class); PendingIntent pi = PendingIntent.getActivity(context, 99 /*request code*/, intent, PendingIntent.FLAG_UPDATE_CURRENT); session.setSessionActivity(pi);