1package android.support.v17.leanback.app;
2
3import android.content.Context;
4import android.graphics.Bitmap;
5import android.graphics.drawable.BitmapDrawable;
6import android.graphics.drawable.Drawable;
7import android.os.Bundle;
8import android.support.v4.media.MediaMetadataCompat;
9import android.support.v4.media.session.MediaControllerCompat;
10import android.support.v4.media.session.PlaybackStateCompat;
11import 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 */
18public 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     */
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     */
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     */
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     */
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     */
116    public final MediaControllerCompat getMediaController() {
117        return mMediaController;
118    }
119
120    @Override
121    public boolean hasValidMedia() {
122        return mMediaController != null && mMediaController.getMetadata() != null;
123    }
124
125    @Override
126    public boolean isMediaPlaying() {
127        return mMediaController.getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING;
128    }
129
130    @Override
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
157    public CharSequence getMediaTitle() {
158        return mMediaController.getMetadata().getDescription().getTitle();
159    }
160
161    @Override
162    public CharSequence getMediaSubtitle() {
163        return mMediaController.getMetadata().getDescription().getSubtitle();
164    }
165
166    @Override
167    public int getMediaDuration() {
168        return (int) mMediaController.getMetadata().getLong(
169                MediaMetadataCompat.METADATA_KEY_DURATION);
170    }
171
172    @Override
173    public int getCurrentPosition() {
174        return (int) mMediaController.getPlaybackState().getPosition();
175    }
176
177    @Override
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
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
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
218    protected void pausePlayback() {
219        if (DEBUG) Log.v(TAG, "pausePlayback");
220        mMediaController.getTransportControls().pause();
221    }
222
223    @Override
224    protected void skipToNext() {
225        if (DEBUG) Log.v(TAG, "skipToNext");
226        mMediaController.getTransportControls().skipToNext();
227    }
228
229    @Override
230    protected void skipToPrevious() {
231        if (DEBUG) Log.v(TAG, "skipToPrevious");
232        mMediaController.getTransportControls().skipToPrevious();
233    }
234}
235