MediaSessionWrapper.java revision 633eb826b8c97731dfc5ef12c7bf78a63734275d
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.tv; 18 19import android.content.Context; 20import android.content.Intent; 21import android.graphics.Bitmap; 22import android.graphics.BitmapFactory; 23import android.media.MediaMetadata; 24import android.media.session.MediaSession; 25import android.media.session.PlaybackState; 26import android.media.tv.TvContract; 27import android.media.tv.TvInputInfo; 28import android.os.AsyncTask; 29import android.support.annotation.NonNull; 30import android.support.annotation.Nullable; 31import android.text.TextUtils; 32 33import com.android.tv.data.Channel; 34import com.android.tv.data.Program; 35import com.android.tv.util.ImageLoader; 36import com.android.tv.util.Utils; 37 38/** 39 * A wrapper class for {@link MediaSession} to support common operations on media sessions for 40 * {@link MainActivity}. 41 */ 42class MediaSessionWrapper { 43 private static final String MEDIA_SESSION_TAG = "com.android.tv.mediasession"; 44 private static PlaybackState MEDIA_SESSION_STATE_PLAYING = new PlaybackState.Builder() 45 .setState(PlaybackState.STATE_PLAYING, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f) 46 .build(); 47 private static PlaybackState MEDIA_SESSION_STATE_STOPPED = new PlaybackState.Builder() 48 .setState(PlaybackState.STATE_STOPPED, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f) 49 .build(); 50 51 private final Context mContext; 52 private final MediaSession mMediaSession; 53 private int mNowPlayingCardWidth; 54 private int mNowPlayingCardHeight; 55 56 MediaSessionWrapper(Context context) { 57 mContext = context; 58 mMediaSession = new MediaSession(context, MEDIA_SESSION_TAG); 59 mMediaSession.setCallback(new MediaSession.Callback() { 60 @Override 61 public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) { 62 // Consume the media button event here. Should not send it to other apps. 63 return true; 64 } 65 }); 66 mMediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | 67 MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); 68 mNowPlayingCardWidth = mContext.getResources().getDimensionPixelSize( 69 R.dimen.notif_card_img_max_width); 70 mNowPlayingCardHeight = mContext.getResources().getDimensionPixelSize( 71 R.dimen.notif_card_img_height); 72 } 73 74 /** 75 * Sets playback state. 76 * 77 * @param isPlaying {@code true} if TV is playing, otherwise {@code false}. 78 */ 79 void setPlaybackState(boolean isPlaying) { 80 if (isPlaying) { 81 mMediaSession.setActive(true); 82 // setPlaybackState() has to be called after calling setActive(). b/31933276 83 mMediaSession.setPlaybackState(MEDIA_SESSION_STATE_PLAYING); 84 } else if (mMediaSession.isActive()) { 85 mMediaSession.setPlaybackState(MEDIA_SESSION_STATE_STOPPED); 86 mMediaSession.setActive(false); 87 } 88 } 89 90 /** 91 * Updates media session according to the current TV playback status. 92 * 93 * @param blocked {@code true} if the current channel is blocked, either by user settings or 94 * the current program's content ratings. 95 * @param currentChannel The currently playing channel. 96 * @param currentProgram The currently playing program. 97 */ 98 void update(boolean blocked, Channel currentChannel, Program currentProgram) { 99 if (currentChannel == null) { 100 setPlaybackState(false); 101 return; 102 } 103 104 // If the channel is blocked, display a lock and a short text on the Now Playing Card 105 if (blocked) { 106 Bitmap art = BitmapFactory.decodeResource(mContext.getResources(), 107 R.drawable.ic_message_lock_preview); 108 updateMediaMetadata(mContext.getResources() 109 .getString(R.string.channel_banner_locked_channel_title), art); 110 setPlaybackState(true); 111 return; 112 } 113 114 String cardTitleText = null; 115 String posterArtUri = null; 116 if (currentProgram != null) { 117 cardTitleText = currentProgram.getTitle(); 118 posterArtUri = currentProgram.getPosterArtUri(); 119 } 120 if (TextUtils.isEmpty(cardTitleText)) { 121 cardTitleText = getChannelName(currentChannel); 122 } 123 updateMediaMetadata(cardTitleText, null); 124 if (posterArtUri == null) { 125 posterArtUri = TvContract.buildChannelLogoUri(currentChannel.getId()).toString(); 126 } 127 updatePosterArt(currentChannel, currentProgram, cardTitleText, null, posterArtUri); 128 setPlaybackState(true); 129 } 130 131 /** 132 * Releases the media session. 133 * 134 * @see MediaSession#release() 135 */ 136 void release() { 137 mMediaSession.release(); 138 } 139 140 private String getChannelName(Channel channel) { 141 if (channel.isPassthrough()) { 142 TvInputInfo input = TvApplication.getSingletons(mContext).getTvInputManagerHelper() 143 .getTvInputInfo(channel.getInputId()); 144 return Utils.loadLabel(mContext, input); 145 } else { 146 return channel.getDisplayName(); 147 } 148 } 149 150 private void updatePosterArt(Channel currentChannel, Program currentProgram, 151 String cardTitleText, @Nullable Bitmap posterArt, @Nullable String posterArtUri) { 152 if (posterArt != null) { 153 updateMediaMetadata(cardTitleText, posterArt); 154 } else if (posterArtUri != null) { 155 ImageLoader.loadBitmap(mContext, posterArtUri, mNowPlayingCardWidth, 156 mNowPlayingCardHeight, new ProgramPosterArtCallback(this, currentChannel, 157 currentProgram, cardTitleText)); 158 } else { 159 updateMediaMetadata(cardTitleText, R.drawable.default_now_card); 160 } 161 } 162 163 private void updateMediaMetadata(final String title, final Bitmap posterArt) { 164 new AsyncTask<Void, Void, Void>() { 165 @Override 166 protected Void doInBackground(Void... arg0) { 167 MediaMetadata.Builder builder = new MediaMetadata.Builder(); 168 builder.putString(MediaMetadata.METADATA_KEY_TITLE, title); 169 if (posterArt != null) { 170 builder.putBitmap(MediaMetadata.METADATA_KEY_ART, posterArt); 171 } 172 mMediaSession.setMetadata(builder.build()); 173 return null; 174 } 175 }.execute(); 176 } 177 178 private void updateMediaMetadata(final String title, final int imageResId) { 179 new AsyncTask<Void, Void, Void> () { 180 @Override 181 protected Void doInBackground(Void... arg0) { 182 MediaMetadata.Builder builder = new MediaMetadata.Builder(); 183 builder.putString(MediaMetadata.METADATA_KEY_TITLE, title); 184 Bitmap posterArt = 185 BitmapFactory.decodeResource(mContext.getResources(), imageResId); 186 if (posterArt != null) { 187 builder.putBitmap(MediaMetadata.METADATA_KEY_ART, posterArt); 188 } 189 mMediaSession.setMetadata(builder.build()); 190 return null; 191 } 192 }.execute(); 193 } 194 195 private static class ProgramPosterArtCallback extends 196 ImageLoader.ImageLoaderCallback<MediaSessionWrapper> { 197 private final Channel mChannel; 198 private final Program mProgram; 199 private final String mCardTitleText; 200 201 ProgramPosterArtCallback(MediaSessionWrapper sessionWrapper, Channel channel, 202 Program program, String cardTitleText) { 203 super(sessionWrapper); 204 mChannel = channel; 205 mProgram = program; 206 mCardTitleText = cardTitleText; 207 } 208 209 @Override 210 public void onBitmapLoaded(MediaSessionWrapper sessionWrapper, @Nullable Bitmap posterArt) { 211 if (((MainActivity) sessionWrapper.mContext).isNowPlayingProgram(mChannel, mProgram)) { 212 sessionWrapper.updatePosterArt(mChannel, mProgram, mCardTitleText, posterArt, null); 213 } 214 } 215 } 216} 217