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