編寫:huanglizhuo - 原文:http://developer.android.com/training/tv/playback/now-playing.html
TV應(yīng)用允許用戶在使用其他應(yīng)用時(shí)后臺播放音樂或其他媒體。如果我們的應(yīng)用程序允許后臺,它必須要為用戶提供返回該應(yīng)用暫停音樂或切換到一個(gè)新的歌曲的方法。 Android框架允許TV應(yīng)用通過在主屏幕上顯示正在播放卡做到這一點(diǎn)。
正在播放卡片是系統(tǒng)的組建,它可以在推薦的行上顯示正在播放的媒體會話它包括了媒體元數(shù)據(jù),如專輯封面,標(biāo)題和應(yīng)用程序圖標(biāo)。當(dāng)用戶選擇它,系統(tǒng)將打開擁有該會話的應(yīng)用程序。
這節(jié)課將演示如何使用 MediaSession 類實(shí)現(xiàn)正在播放卡片。
一個(gè)播放應(yīng)用可以作為 activity 或者 service 運(yùn)行。 service 是當(dāng) activity 結(jié)束時(shí)依然可以后臺播放的。在這節(jié)討論中,媒體播放應(yīng)用是假設(shè)在 MediaBrowserService 下運(yùn)行的。
在service的[onCreate()](http://developer.android.com/reference/android/service/media/MediaBrowserService.html#onCreate())方法中創(chuàng)建一個(gè)新的[ MediaSession ](http://developer.android.com/reference/android/media/session/MediaSession.html#MediaSession(android.content.Context, java.lang.String)),設(shè)置適當(dāng)?shù)幕卣{(diào)函數(shù)和標(biāo)志,并設(shè)置 MediaBrowserService 令牌。
mSession = new MediaSession(this, "MusicService");
mSession.setCallback(new MediaSessionCallback());
mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
// for the MediaBrowserService
setSessionToken(mSession.getSessionToken());
注意:正在播放卡片只有在媒體會話設(shè)置了FLAG_HANDLES_TRANSPORT_CONTROLS標(biāo)志時(shí)在可以顯示。
如果會話是系統(tǒng)最高優(yōu)先級的會話那么正在播放卡片將在setActivity(true)調(diào)用后顯示。同時(shí)我們的應(yīng)用必須像在Managing Audio Focus一節(jié)中那樣請求音頻焦點(diǎn)。
private void handlePlayRequest() {
tryToGetAudioFocus();
if (!mSession.isActive()) {
mSession.setActive(true);
}
...
如果另一個(gè)應(yīng)用發(fā)起媒體播放請求并調(diào)用setActivity(false)后這個(gè)卡片將從主屏上移除。
正如任何媒體的應(yīng)用程序,在 MediaSession 中更新播放狀態(tài),使卡片可以顯示當(dāng)前的元數(shù)據(jù),如在下面的例子:
private void updatePlaybackState() {
long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
position = mMediaPlayer.getCurrentPosition();
}
PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
.setActions(getAvailableActions());
stateBuilder.setState(mState, position, 1.0f);
mSession.setPlaybackState(stateBuilder.build());
}
private long getAvailableActions() {
long actions = PlaybackState.ACTION_PLAY |
PlaybackState.ACTION_PLAY_FROM_MEDIA_ID |
PlaybackState.ACTION_PLAY_FROM_SEARCH;
if (mPlayingQueue == null || mPlayingQueue.isEmpty()) {
return actions;
}
if (mState == PlaybackState.STATE_PLAYING) {
actions |= PlaybackState.ACTION_PAUSE;
}
if (mCurrentIndexOnQueue > 0) {
actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
}
if (mCurrentIndexOnQueue < mPlayingQueue.size() - 1) {
actions |= PlaybackState.ACTION_SKIP_TO_NEXT;
}
return actions;
}
為當(dāng)前正在播放通過setMetadata()方法設(shè)置 MediaMetadata 。.這個(gè)方法可以讓我們?yōu)檎诓シ趴ㄌ峁┯嘘P(guān)軌道,如標(biāo)題,副標(biāo)題,和各種圖標(biāo)等信息。下面的例子假設(shè)我們的播放數(shù)據(jù)存儲在自定義的MediaData類中。
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.putString(MediaMetadata.METADATA_KEY_ART,
myData.artBitmap);
// Add any other fields you have for your data as well
mSession.setMetadata(metadataBuilder.build());
}
當(dāng)用戶選擇正在播放卡片時(shí),系統(tǒng)打開應(yīng)用并擁有會話。如果我們的應(yīng)用在setSessionActivity()有PendingIntent要傳遞,系統(tǒng)將會像下面演示的那樣開啟activity。如果不是,則系統(tǒng)默認(rèn)的Intent打開。您指定的活動必須提供播放控制,允許用戶暫?;蛲V共シ拧?/p>
Intent intent = new Intent(mContext, MyActivity.class);
PendingIntent pi = PendingIntent.getActivity(context, 99 /*request code*/,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
mSession.setSessionActivity(pi);