1 /* 2 * Copyright (C) 2016 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 androidx.leanback.media; 18 19 import android.content.Context; 20 21 import androidx.annotation.CallSuper; 22 23 import java.util.ArrayList; 24 import java.util.List; 25 26 /** 27 * Base class for abstraction of media play/pause feature. A subclass of PlaybackGlue will contain 28 * implementation of Media Player or a connection to playback Service. App initializes 29 * PlaybackGlue subclass, associated it with a {@link PlaybackGlueHost}. {@link PlaybackGlueHost} 30 * is typically implemented by a Fragment or an Activity, it provides the environment to render UI 31 * for PlaybackGlue object, it optionally provides SurfaceHolder via {@link SurfaceHolderGlueHost} 32 * to render video. A typical PlaybackGlue should release resources (e.g. MediaPlayer or connection 33 * to playback Service) in {@link #onDetachedFromHost()}. 34 * {@link #onDetachedFromHost()} is called in two cases: 35 * <ul> 36 * <li> app manually change it using {@link #setHost(PlaybackGlueHost)} call</li> 37 * <li> When host (fragment or activity) is destroyed </li> 38 * </ul> 39 * In rare case if an PlaybackGlue wants to live outside fragment / activity life cycle, it may 40 * manages resource release by itself. 41 * 42 * @see PlaybackGlueHost 43 */ 44 public abstract class PlaybackGlue { 45 private final Context mContext; 46 private PlaybackGlueHost mPlaybackGlueHost; 47 48 /** 49 * Interface to allow clients to take action once the video is ready to play and start stop. 50 */ 51 public abstract static class PlayerCallback { 52 /** 53 * Event for {@link #isPrepared()} changed. 54 * @param glue The PlaybackGlue that has changed {@link #isPrepared()}. 55 */ onPreparedStateChanged(PlaybackGlue glue)56 public void onPreparedStateChanged(PlaybackGlue glue) { 57 } 58 59 /** 60 * Event for Play/Pause state change. See {@link #isPlaying()}}. 61 * @param glue The PlaybackGlue that has changed playing or pausing state. 62 */ onPlayStateChanged(PlaybackGlue glue)63 public void onPlayStateChanged(PlaybackGlue glue) { 64 } 65 66 /** 67 * Event of the current media is finished. 68 * @param glue The PlaybackGlue that has finished current media playing. 69 */ onPlayCompleted(PlaybackGlue glue)70 public void onPlayCompleted(PlaybackGlue glue) { 71 } 72 } 73 74 ArrayList<PlayerCallback> mPlayerCallbacks; 75 76 /** 77 * Constructor. 78 */ PlaybackGlue(Context context)79 public PlaybackGlue(Context context) { 80 this.mContext = context; 81 } 82 83 /** 84 * Returns the context. 85 */ getContext()86 public Context getContext() { 87 return mContext; 88 } 89 90 /** 91 * Returns true when the media player is prepared to start media playback. When returning false, 92 * app may listen to {@link PlayerCallback#onPreparedStateChanged(PlaybackGlue)} event. 93 * @return True if prepared, false otherwise. 94 */ isPrepared()95 public boolean isPrepared() { 96 return true; 97 } 98 99 /** 100 * Add a PlayerCallback. 101 * @param playerCallback The callback to add. 102 */ addPlayerCallback(PlayerCallback playerCallback)103 public void addPlayerCallback(PlayerCallback playerCallback) { 104 if (mPlayerCallbacks == null) { 105 mPlayerCallbacks = new ArrayList(); 106 } 107 mPlayerCallbacks.add(playerCallback); 108 } 109 110 /** 111 * Remove a PlayerCallback. 112 * @param callback The callback to remove. 113 */ removePlayerCallback(PlayerCallback callback)114 public void removePlayerCallback(PlayerCallback callback) { 115 if (mPlayerCallbacks != null) { 116 mPlayerCallbacks.remove(callback); 117 } 118 } 119 120 /** 121 * @return A snapshot of list of PlayerCallbacks set on the Glue. 122 */ getPlayerCallbacks()123 protected List<PlayerCallback> getPlayerCallbacks() { 124 if (mPlayerCallbacks == null) { 125 return null; 126 } 127 return new ArrayList(mPlayerCallbacks); 128 } 129 130 /** 131 * Returns true if media is currently playing. 132 */ isPlaying()133 public boolean isPlaying() { 134 return false; 135 } 136 137 /** 138 * Starts the media player. Does nothing if {@link #isPrepared()} is false. To wait 139 * {@link #isPrepared()} to be true before playing, use {@link #playWhenPrepared()}. 140 */ play()141 public void play() { 142 } 143 144 /** 145 * Starts play when {@link #isPrepared()} becomes true. 146 */ playWhenPrepared()147 public void playWhenPrepared() { 148 if (isPrepared()) { 149 play(); 150 } else { 151 addPlayerCallback(new PlayerCallback() { 152 @Override 153 public void onPreparedStateChanged(PlaybackGlue glue) { 154 if (glue.isPrepared()) { 155 removePlayerCallback(this); 156 play(); 157 } 158 } 159 }); 160 } 161 } 162 163 /** 164 * Pauses the media player. 165 */ pause()166 public void pause() { 167 } 168 169 /** 170 * Goes to the next media item. This method is optional. 171 */ next()172 public void next() { 173 } 174 175 /** 176 * Goes to the previous media item. This method is optional. 177 */ previous()178 public void previous() { 179 } 180 181 /** 182 * This method is used to associate a PlaybackGlue with the {@link PlaybackGlueHost} which 183 * provides UI and optional {@link SurfaceHolderGlueHost}. 184 * 185 * @param host The host for the PlaybackGlue. Set to null to detach from the host. 186 */ setHost(PlaybackGlueHost host)187 public final void setHost(PlaybackGlueHost host) { 188 if (mPlaybackGlueHost == host) { 189 return; 190 } 191 if (mPlaybackGlueHost != null) { 192 mPlaybackGlueHost.attachToGlue(null); 193 } 194 mPlaybackGlueHost = host; 195 if (mPlaybackGlueHost != null) { 196 mPlaybackGlueHost.attachToGlue(this); 197 } 198 } 199 200 /** 201 * This method is called when {@link PlaybackGlueHost is started. Subclass may override. 202 */ onHostStart()203 protected void onHostStart() { 204 } 205 206 /** 207 * This method is called when {@link PlaybackGlueHost is stopped. Subclass may override. 208 */ onHostStop()209 protected void onHostStop() { 210 } 211 212 /** 213 * This method is called when {@link PlaybackGlueHost is resumed. Subclass may override. 214 */ onHostResume()215 protected void onHostResume() { 216 } 217 218 /** 219 * This method is called when {@link PlaybackGlueHost is paused. Subclass may override. 220 */ onHostPause()221 protected void onHostPause() { 222 } 223 224 /** 225 * This method is called attached to associated {@link PlaybackGlueHost}. Subclass may override 226 * and call super.onAttachedToHost(). 227 */ 228 @CallSuper onAttachedToHost(PlaybackGlueHost host)229 protected void onAttachedToHost(PlaybackGlueHost host) { 230 mPlaybackGlueHost = host; 231 mPlaybackGlueHost.setHostCallback(new PlaybackGlueHost.HostCallback() { 232 @Override 233 public void onHostStart() { 234 PlaybackGlue.this.onHostStart(); 235 } 236 237 @Override 238 public void onHostStop() { 239 PlaybackGlue.this.onHostStop(); 240 } 241 242 @Override 243 public void onHostResume() { 244 PlaybackGlue.this.onHostResume(); 245 } 246 247 @Override 248 public void onHostPause() { 249 PlaybackGlue.this.onHostPause(); 250 } 251 252 @Override 253 public void onHostDestroy() { 254 setHost(null); 255 } 256 }); 257 } 258 259 /** 260 * This method is called when current associated {@link PlaybackGlueHost} is attached to a 261 * different {@link PlaybackGlue} or {@link PlaybackGlueHost} is destroyed . Subclass may 262 * override and call super.onDetachedFromHost() at last. A typical PlaybackGlue will release 263 * resources (e.g. MediaPlayer or connection to playback service) in this method. 264 */ 265 @CallSuper onDetachedFromHost()266 protected void onDetachedFromHost() { 267 if (mPlaybackGlueHost != null) { 268 mPlaybackGlueHost.setHostCallback(null); 269 mPlaybackGlueHost = null; 270 } 271 } 272 273 /** 274 * @return Associated {@link PlaybackGlueHost} or null if not attached to host. 275 */ getHost()276 public PlaybackGlueHost getHost() { 277 return mPlaybackGlueHost; 278 } 279 } 280