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