1 /* 2 * Copyright (C) 2013 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 17 package com.example.android.supportv7.media; 18 19 import android.content.Context; 20 import android.graphics.Bitmap; 21 import android.os.Build; 22 import android.support.v4.media.MediaMetadataCompat; 23 import android.support.v4.media.session.MediaSessionCompat; 24 import android.support.v4.media.session.PlaybackStateCompat; 25 import android.util.Log; 26 27 import androidx.annotation.RequiresApi; 28 import androidx.mediarouter.media.MediaControlIntent; 29 import androidx.mediarouter.media.MediaRouter.RouteInfo; 30 31 /** 32 * Abstraction of common playback operations of media items, such as play, 33 * seek, etc. Used by PlaybackManager as a backend to handle actual playback 34 * of media items. 35 * 36 * TODO: Introduce prepare() method and refactor subclasses accordingly. 37 */ 38 public abstract class Player { 39 private static final String TAG = "SampleMediaRoutePlayer"; 40 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 41 protected static final int STATE_IDLE = 0; 42 protected static final int STATE_PREPARING_FOR_PLAY = 1; 43 protected static final int STATE_PREPARING_FOR_PAUSE = 2; 44 protected static final int STATE_READY = 3; 45 protected static final int STATE_PLAYING = 4; 46 protected static final int STATE_PAUSED = 5; 47 48 private static final long PLAYBACK_ACTIONS = PlaybackStateCompat.ACTION_PAUSE 49 | PlaybackStateCompat.ACTION_PLAY; 50 private static final PlaybackStateCompat INIT_PLAYBACK_STATE = new PlaybackStateCompat.Builder() 51 .setState(PlaybackStateCompat.STATE_NONE, 0, .0f).build(); 52 53 protected Callback mCallback; 54 protected MediaSessionCompat mMediaSession; 55 isRemotePlayback()56 public abstract boolean isRemotePlayback(); isQueuingSupported()57 public abstract boolean isQueuingSupported(); 58 connect(RouteInfo route)59 public abstract void connect(RouteInfo route); release()60 public abstract void release(); 61 62 // basic operations that are always supported play(final PlaylistItem item)63 public abstract void play(final PlaylistItem item); seek(final PlaylistItem item)64 public abstract void seek(final PlaylistItem item); getStatus(final PlaylistItem item, final boolean update)65 public abstract void getStatus(final PlaylistItem item, final boolean update); pause()66 public abstract void pause(); resume()67 public abstract void resume(); stop()68 public abstract void stop(); 69 70 // advanced queuing (enqueue & remove) are only supported 71 // if isQueuingSupported() returns true enqueue(final PlaylistItem item)72 public abstract void enqueue(final PlaylistItem item); remove(String iid)73 public abstract PlaylistItem remove(String iid); 74 takeSnapshot()75 public void takeSnapshot() {} getSnapshot()76 public Bitmap getSnapshot() { return null; } 77 78 /** 79 * presentation display 80 */ 81 @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1) updatePresentation()82 public void updatePresentation() {} 83 setCallback(Callback callback)84 public void setCallback(Callback callback) { 85 mCallback = callback; 86 } 87 create(Context context, RouteInfo route, MediaSessionCompat session)88 public static Player create(Context context, RouteInfo route, MediaSessionCompat session) { 89 Player player; 90 if (route != null && route.supportsControlCategory( 91 MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { 92 player = new RemotePlayer(context); 93 } else if (route != null) { 94 player = new LocalPlayer.SurfaceViewPlayer(context); 95 } else { 96 player = new LocalPlayer.OverlayPlayer(context); 97 } 98 player.setMediaSession(session); 99 player.initMediaSession(); 100 player.connect(route); 101 return player; 102 } 103 initMediaSession()104 protected void initMediaSession() { 105 if (mMediaSession == null) { 106 return; 107 } 108 mMediaSession.setMetadata(null); 109 mMediaSession.setPlaybackState(INIT_PLAYBACK_STATE); 110 } 111 updateMetadata(PlaylistItem currentItem)112 protected void updateMetadata(PlaylistItem currentItem) { 113 if (mMediaSession == null) { 114 return; 115 } 116 if (DEBUG) { 117 Log.d(TAG, "Update metadata: currentItem=" + currentItem); 118 } 119 if (currentItem == null) { 120 mMediaSession.setMetadata(null); 121 return; 122 } 123 MediaMetadataCompat.Builder bob = new MediaMetadataCompat.Builder(); 124 bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, currentItem.getTitle()); 125 bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Subtitle of the thing"); 126 bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, 127 "Description of the thing"); 128 bob.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, getSnapshot()); 129 mMediaSession.setMetadata(bob.build()); 130 } 131 publishState(int state)132 protected void publishState(int state) { 133 if (mMediaSession == null) { 134 return; 135 } 136 PlaybackStateCompat.Builder bob = new PlaybackStateCompat.Builder(); 137 bob.setActions(PLAYBACK_ACTIONS); 138 switch (state) { 139 case STATE_PLAYING: 140 bob.setState(PlaybackStateCompat.STATE_PLAYING, -1, 1); 141 break; 142 case STATE_READY: 143 case STATE_PAUSED: 144 bob.setState(PlaybackStateCompat.STATE_PAUSED, -1, 0); 145 break; 146 case STATE_PREPARING_FOR_PLAY: 147 case STATE_PREPARING_FOR_PAUSE: 148 bob.setState(PlaybackStateCompat.STATE_BUFFERING, -1, 0); 149 break; 150 case STATE_IDLE: 151 bob.setState(PlaybackStateCompat.STATE_STOPPED, -1, 0); 152 break; 153 } 154 PlaybackStateCompat pbState = bob.build(); 155 Log.d(TAG, "Setting state to " + pbState); 156 mMediaSession.setPlaybackState(pbState); 157 if (state != STATE_IDLE) { 158 mMediaSession.setActive(true); 159 } else { 160 mMediaSession.setActive(false); 161 } 162 } 163 setMediaSession(MediaSessionCompat session)164 private void setMediaSession(MediaSessionCompat session) { 165 mMediaSession = session; 166 } 167 168 public interface Callback { onError()169 void onError(); onCompletion()170 void onCompletion(); onPlaylistChanged()171 void onPlaylistChanged(); onPlaylistReady()172 void onPlaylistReady(); 173 } 174 } 175