1 package android.support.v17.leanback.app;
2 
3 import android.content.Context;
4 import android.graphics.Bitmap;
5 import android.graphics.drawable.BitmapDrawable;
6 import android.graphics.drawable.Drawable;
7 import android.os.Bundle;
8 import android.support.v4.media.MediaMetadataCompat;
9 import android.support.v4.media.session.MediaControllerCompat;
10 import android.support.v4.media.session.PlaybackStateCompat;
11 import android.util.Log;
12 
13 /**
14  * A helper class for implementing a glue layer between a
15  * {@link PlaybackOverlayFragment} and a
16  * {@link android.support.v4.media.session.MediaControllerCompat}.
17  */
18 public abstract class MediaControllerGlue extends PlaybackControlGlue {
19     private static final String TAG = "MediaControllerGlue";
20     private static final boolean DEBUG = false;
21 
22     private MediaControllerCompat mMediaController;
23 
24     private final MediaControllerCompat.Callback mCallback = new MediaControllerCompat.Callback() {
25         @Override
26         public void onMetadataChanged(MediaMetadataCompat metadata) {
27             if (DEBUG) Log.v(TAG, "onMetadataChanged");
28             MediaControllerGlue.this.onMetadataChanged();
29         }
30         @Override
31         public void onPlaybackStateChanged(PlaybackStateCompat state) {
32             if (DEBUG) Log.v(TAG, "onPlaybackStateChanged");
33             onStateChanged();
34         }
35         @Override
36         public void onSessionDestroyed() {
37             if (DEBUG) Log.v(TAG, "onSessionDestroyed");
38             mMediaController = null;
39         }
40         @Override
41         public void onSessionEvent(String event, Bundle extras) {
42             if (DEBUG) Log.v(TAG, "onSessionEvent");
43         }
44     };
45 
46     /**
47      * Constructor for the glue.
48      *
49      * <p>The {@link PlaybackOverlayFragment} must be passed in.
50      * A {@link android.support.v17.leanback.widget.OnItemViewClickedListener} and
51      * {@link PlaybackOverlayFragment.InputEventHandler}
52      * will be set on the fragment.
53      * </p>
54      *
55      * @param context
56      * @param fragment
57      * @param seekSpeeds Array of seek speeds for fast forward and rewind.
58      */
MediaControllerGlue(Context context, PlaybackOverlayFragment fragment, int[] seekSpeeds)59     public MediaControllerGlue(Context context,
60                                PlaybackOverlayFragment fragment,
61                                int[] seekSpeeds) {
62         super(context, fragment, seekSpeeds);
63     }
64 
65     /**
66      * Constructor for the glue.
67      *
68      * <p>The {@link PlaybackOverlayFragment} must be passed in.
69      * A {@link android.support.v17.leanback.widget.OnItemViewClickedListener} and
70      * {@link PlaybackOverlayFragment.InputEventHandler}
71      * will be set on the fragment.
72      * </p>
73      *
74      * @param context
75      * @param fragment
76      * @param fastForwardSpeeds Array of seek speeds for fast forward.
77      * @param rewindSpeeds Array of seek speeds for rewind.
78      */
MediaControllerGlue(Context context, PlaybackOverlayFragment fragment, int[] fastForwardSpeeds, int[] rewindSpeeds)79     public MediaControllerGlue(Context context,
80                                PlaybackOverlayFragment fragment,
81                                int[] fastForwardSpeeds,
82                                int[] rewindSpeeds) {
83         super(context, fragment, fastForwardSpeeds, rewindSpeeds);
84     }
85 
86     /**
87      * Attaches to the given media controller.
88      */
attachToMediaController(MediaControllerCompat mediaController)89     public void attachToMediaController(MediaControllerCompat mediaController) {
90         if (mediaController != mMediaController) {
91             if (DEBUG) Log.v(TAG, "New media controller " + mediaController);
92             detach();
93             mMediaController = mediaController;
94             if (mMediaController != null) {
95                 mMediaController.registerCallback(mCallback);
96             }
97             onMetadataChanged();
98             onStateChanged();
99         }
100     }
101 
102     /**
103      * Detaches from the media controller.  Must be called when the object is no longer
104      * needed.
105      */
detach()106     public void detach() {
107         if (mMediaController != null) {
108             mMediaController.unregisterCallback(mCallback);
109         }
110         mMediaController = null;
111     }
112 
113     /**
114      * Returns the media controller currently attached.
115      */
getMediaController()116     public final MediaControllerCompat getMediaController() {
117         return mMediaController;
118     }
119 
120     @Override
hasValidMedia()121     public boolean hasValidMedia() {
122         return mMediaController != null && mMediaController.getMetadata() != null;
123     }
124 
125     @Override
isMediaPlaying()126     public boolean isMediaPlaying() {
127         return mMediaController.getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING;
128     }
129 
130     @Override
getCurrentSpeedId()131     public int getCurrentSpeedId() {
132         int speed = (int) mMediaController.getPlaybackState().getPlaybackSpeed();
133         if (speed == 0) {
134             return PLAYBACK_SPEED_PAUSED;
135         } else if (speed == 1) {
136             return PLAYBACK_SPEED_NORMAL;
137         } else if (speed > 0) {
138             int[] seekSpeeds = getFastForwardSpeeds();
139             for (int index = 0; index < seekSpeeds.length; index++) {
140                 if (speed == seekSpeeds[index]) {
141                     return PLAYBACK_SPEED_FAST_L0 + index;
142                 }
143             }
144         } else {
145             int[] seekSpeeds = getRewindSpeeds();
146             for (int index = 0; index < seekSpeeds.length; index++) {
147                 if (-speed == seekSpeeds[index]) {
148                     return -PLAYBACK_SPEED_FAST_L0 - index;
149                 }
150             }
151         }
152         Log.w(TAG, "Couldn't find index for speed " + speed);
153         return PLAYBACK_SPEED_INVALID;
154     }
155 
156     @Override
getMediaTitle()157     public CharSequence getMediaTitle() {
158         return mMediaController.getMetadata().getDescription().getTitle();
159     }
160 
161     @Override
getMediaSubtitle()162     public CharSequence getMediaSubtitle() {
163         return mMediaController.getMetadata().getDescription().getSubtitle();
164     }
165 
166     @Override
getMediaDuration()167     public int getMediaDuration() {
168         return (int) mMediaController.getMetadata().getLong(
169                 MediaMetadataCompat.METADATA_KEY_DURATION);
170     }
171 
172     @Override
getCurrentPosition()173     public int getCurrentPosition() {
174         return (int) mMediaController.getPlaybackState().getPosition();
175     }
176 
177     @Override
getMediaArt()178     public Drawable getMediaArt() {
179         Bitmap bitmap = mMediaController.getMetadata().getDescription().getIconBitmap();
180         return bitmap == null ? null : new BitmapDrawable(getContext().getResources(), bitmap);
181     }
182 
183     @Override
getSupportedActions()184     public long getSupportedActions() {
185         long result = 0;
186         long actions = mMediaController.getPlaybackState().getActions();
187         if ((actions & PlaybackStateCompat.ACTION_PLAY_PAUSE) != 0) {
188             result |= ACTION_PLAY_PAUSE;
189         }
190         if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
191             result |= ACTION_SKIP_TO_NEXT;
192         }
193         if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
194             result |= ACTION_SKIP_TO_PREVIOUS;
195         }
196         if ((actions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) {
197             result |= ACTION_FAST_FORWARD;
198         }
199         if ((actions & PlaybackStateCompat.ACTION_REWIND) != 0) {
200             result |= ACTION_REWIND;
201         }
202         return result;
203     }
204 
205     @Override
startPlayback(int speed)206     protected void startPlayback(int speed) {
207         if (DEBUG) Log.v(TAG, "startPlayback speed " + speed);
208         if (speed == PLAYBACK_SPEED_NORMAL) {
209             mMediaController.getTransportControls().play();
210         } else if (speed > 0) {
211             mMediaController.getTransportControls().fastForward();
212         } else {
213             mMediaController.getTransportControls().rewind();
214         }
215     }
216 
217     @Override
pausePlayback()218     protected void pausePlayback() {
219         if (DEBUG) Log.v(TAG, "pausePlayback");
220         mMediaController.getTransportControls().pause();
221     }
222 
223     @Override
skipToNext()224     protected void skipToNext() {
225         if (DEBUG) Log.v(TAG, "skipToNext");
226         mMediaController.getTransportControls().skipToNext();
227     }
228 
229     @Override
skipToPrevious()230     protected void skipToPrevious() {
231         if (DEBUG) Log.v(TAG, "skipToPrevious");
232         mMediaController.getTransportControls().skipToPrevious();
233     }
234 }
235