1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14package android.support.v17.leanback.app;
15
16import android.content.Context;
17import android.support.v17.leanback.media.PlaybackGlueHost;
18import android.support.v17.leanback.widget.Action;
19import android.support.v17.leanback.widget.OnActionClickedListener;
20import android.support.v17.leanback.widget.OnItemViewClickedListener;
21import android.support.v17.leanback.widget.PlaybackControlsRow;
22import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
23import android.support.v17.leanback.widget.PlaybackRowPresenter;
24import android.support.v17.leanback.widget.Presenter;
25import android.support.v17.leanback.widget.PresenterSelector;
26import android.support.v17.leanback.widget.Row;
27import android.support.v17.leanback.widget.RowPresenter;
28import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
29import android.view.InputEvent;
30import android.view.KeyEvent;
31import android.view.View;
32
33/**
34 * A helper class for managing a {@link android.support.v17.leanback.widget.PlaybackControlsRow}
35 * and {@link PlaybackGlueHost} that implements a
36 * recommended approach to handling standard playback control actions such as play/pause,
37 * fast forward/rewind at progressive speed levels, and skip to next/previous. This helper class
38 * is a glue layer in that manages the configuration of and interaction between the
39 * leanback UI components by defining a functional interface to the media player.
40 *
41 * <p>You can instantiate a concrete subclass such as MediaPlayerGlue or you must
42 * subclass this abstract helper.  To create a subclass you must implement all of the
43 * abstract methods and the subclass must invoke {@link #onMetadataChanged()} and
44 * {@link #onStateChanged()} appropriately.
45 * </p>
46 *
47 * <p>To use an instance of the glue layer, first construct an instance.  Constructor parameters
48 * inform the glue what speed levels are supported for fast forward/rewind.
49 * </p>
50 *
51 * <p>If you have your own controls row you must pass it to {@link #setControlsRow}.
52 * The row will be updated by the glue layer based on the media metadata and playback state.
53 * Alternatively, you may call {@link #createControlsRowAndPresenter()} which will set a controls
54 * row and return a row presenter you can use to present the row.
55 * </p>
56 *
57 * <p>The helper sets a {@link android.support.v17.leanback.widget.SparseArrayObjectAdapter}
58 * on the controls row as the primary actions adapter, and adds actions to it. You can provide
59 * additional actions by overriding {@link #createPrimaryActionsAdapter}. This helper does not
60 * deal in secondary actions so those you may add separately.
61 * </p>
62 *
63 * <p>Provide a click listener on your fragment and if an action is clicked, call
64 * {@link #onActionClicked}. If you set a listener by calling {@link #setOnItemViewClickedListener},
65 * your listener will be called for all unhandled actions.
66 * </p>
67 *
68 * <p>This helper implements a key event handler. If you pass a
69 * {@link PlaybackOverlayFragment}, it will configure its
70 * fragment to intercept all key events.  Otherwise, you should set the glue object as key event
71 * handler to the ViewHolder when bound by your row presenter; see
72 * {@link RowPresenter.ViewHolder#setOnKeyListener(android.view.View.OnKeyListener)}.
73 * </p>
74 *
75 * <p>To update the controls row progress during playback, override {@link #enableProgressUpdating}
76 * to manage the lifecycle of a periodic callback to {@link #updateProgress()}.
77 * {@link #getUpdatePeriod()} provides a recommended update period.
78 * </p>
79 * @deprecated Use {@link android.support.v17.leanback.media.PlaybackControlGlue}
80 */
81@Deprecated
82public abstract class PlaybackControlGlue extends
83        android.support.v17.leanback.media.PlaybackControlGlue {
84
85    OnItemViewClickedListener mExternalOnItemViewClickedListener;
86
87    /**
88     * Constructor for the glue.
89     *
90     * @param context
91     * @param seekSpeeds Array of seek speeds for fast forward and rewind.
92     */
93    public PlaybackControlGlue(Context context, int[] seekSpeeds) {
94        super(context, seekSpeeds, seekSpeeds);
95    }
96
97    /**
98     * Constructor for the glue.
99     *
100     * @param context
101     * @param fastForwardSpeeds Array of seek speeds for fast forward.
102     * @param rewindSpeeds Array of seek speeds for rewind.
103     */
104    public PlaybackControlGlue(Context context,
105                               int[] fastForwardSpeeds,
106                               int[] rewindSpeeds) {
107        super(context, fastForwardSpeeds, rewindSpeeds);
108    }
109
110    /**
111     * Constructor for the glue.
112     *
113     * @param context
114     * @param fragment Optional; if using a {@link PlaybackOverlayFragment}, pass it in.
115     * @param seekSpeeds Array of seek speeds for fast forward and rewind.
116     */
117    public PlaybackControlGlue(Context context,
118                               PlaybackOverlayFragment fragment,
119                               int[] seekSpeeds) {
120        this(context, fragment, seekSpeeds, seekSpeeds);
121    }
122
123    /**
124     * Constructor for the glue.
125     *
126     * @param context
127     * @param fragment Optional; if using a {@link PlaybackOverlayFragment}, pass it in.
128     * @param fastForwardSpeeds Array of seek speeds for fast forward.
129     * @param rewindSpeeds Array of seek speeds for rewind.
130     */
131    public PlaybackControlGlue(Context context,
132                               PlaybackOverlayFragment fragment,
133                               int[] fastForwardSpeeds,
134                               int[] rewindSpeeds) {
135        super(context, fastForwardSpeeds, rewindSpeeds);
136        setHost(fragment == null ? (PlaybackGlueHost) null : new PlaybackGlueHostOld(fragment));
137    }
138
139    @Override
140    protected void onAttachedToHost(PlaybackGlueHost host) {
141        super.onAttachedToHost(host);
142        if (host instanceof PlaybackGlueHostOld) {
143            ((PlaybackGlueHostOld) host).mGlue = this;
144        }
145    }
146
147    /**
148     * Returns the fragment.
149     */
150    public PlaybackOverlayFragment getFragment() {
151        if (getHost() instanceof PlaybackGlueHostOld) {
152            return ((PlaybackGlueHostOld)getHost()).mFragment;
153        }
154        return null;
155    }
156
157    /**
158     * Start playback at the given speed.
159     * @deprecated use {@link #play()} instead.
160     *
161     * @param speed The desired playback speed.  For normal playback this will be
162     *              {@link #PLAYBACK_SPEED_NORMAL}; higher positive values for fast forward,
163     *              and negative values for rewind.
164     */
165    @Deprecated
166    protected void startPlayback(int speed) {}
167
168    /**
169     * Pause playback.
170     * @deprecated use {@link #pause()} instead.
171     */
172    @Deprecated
173    protected void pausePlayback() {}
174
175    /**
176     * Skip to the next track.
177     * @deprecated use {@link #next()} instead.
178     */
179    @Deprecated
180    protected void skipToNext() {}
181
182    /**
183     * Skip to the previous track.
184     * @deprecated use {@link #previous()} instead.
185     */
186    @Deprecated
187    protected void skipToPrevious() {}
188
189    @Override
190    public final void next() {
191        skipToNext();
192    }
193
194    @Override
195    public final void previous() {
196        skipToPrevious();
197    }
198
199    @Override
200    public final void play(int speed) {
201        startPlayback(speed);
202    }
203
204    @Override
205    public final void pause() {
206        pausePlayback();
207    }
208
209    /**
210     * This method invoked when the playback controls row has changed. The adapter
211     * containing this row should be notified.
212     */
213    protected void onRowChanged(PlaybackControlsRow row) {
214    }
215
216    /**
217     * Set the {@link OnItemViewClickedListener} to be called if the click event
218     * is not handled internally.
219     * @param listener
220     * @deprecated Don't call this. Instead use the listener on the fragment yourself.
221     */
222    @Deprecated
223    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
224        mExternalOnItemViewClickedListener = listener;
225    }
226
227    /**
228     * Returns the {@link OnItemViewClickedListener}.
229     * @deprecated Don't call this. Instead use the listener on the fragment yourself.
230     */
231    @Deprecated
232    public OnItemViewClickedListener getOnItemViewClickedListener() {
233        return mExternalOnItemViewClickedListener;
234    }
235
236    @Override
237    protected void onCreateControlsRowAndPresenter() {
238        // backward compatible, we dont create row / presenter by default.
239        // User is expected to call createControlsRowAndPresenter() or setControlsRow()
240        // explicitly.
241    }
242
243    /**
244     * Helper method for instantiating a
245     * {@link android.support.v17.leanback.widget.PlaybackControlsRow} and corresponding
246     * {@link android.support.v17.leanback.widget.PlaybackControlsRowPresenter}.
247     */
248    public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
249        super.onCreateControlsRowAndPresenter();
250        return getControlsRowPresenter();
251    }
252
253    @Override
254    protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
255            PresenterSelector presenterSelector) {
256        return super.createPrimaryActionsAdapter(presenterSelector);
257    }
258
259    /**
260     * Interface allowing the application to handle input events.
261     * @deprecated Use
262     * {@link PlaybackGlueHost#setOnKeyInterceptListener(View.OnKeyListener)}.
263     */
264    @Deprecated
265    public interface InputEventHandler {
266        /**
267         * Called when an {@link InputEvent} is received.
268         *
269         * @return If the event should be consumed, return true. To allow the event to
270         * continue on to the next handler, return false.
271         */
272        boolean handleInputEvent(InputEvent event);
273    }
274
275    static final class PlaybackGlueHostOld extends PlaybackGlueHost {
276        final PlaybackOverlayFragment mFragment;
277        PlaybackControlGlue mGlue;
278        OnActionClickedListener mActionClickedListener;
279
280        public PlaybackGlueHostOld(PlaybackOverlayFragment fragment) {
281            mFragment = fragment;
282            mFragment.setOnItemViewClickedListener(new OnItemViewClickedListener() {
283                @Override
284                public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
285                                          RowPresenter.ViewHolder rowViewHolder, Row row) {
286                    if (item instanceof Action
287                            && rowViewHolder instanceof PlaybackRowPresenter.ViewHolder
288                            && mActionClickedListener != null) {
289                        mActionClickedListener.onActionClicked((Action) item);
290                    } else if (mGlue != null && mGlue.getOnItemViewClickedListener() != null) {
291                        mGlue.getOnItemViewClickedListener().onItemClicked(itemViewHolder,
292                                item, rowViewHolder, row);
293                    }
294                }
295            });
296        }
297
298        @Override
299        public void setFadingEnabled(boolean enable) {
300            mFragment.setFadingEnabled(enable);
301        }
302
303        @Override
304        public void setOnKeyInterceptListener(final View.OnKeyListener onKeyListener) {
305            mFragment.setEventHandler( new InputEventHandler() {
306                @Override
307                public boolean handleInputEvent(InputEvent event) {
308                    if (event instanceof KeyEvent) {
309                        KeyEvent keyEvent = (KeyEvent) event;
310                        return onKeyListener.onKey(null, keyEvent.getKeyCode(), keyEvent);
311                    }
312                    return false;
313                }
314            });
315        }
316
317        @Override
318        public void setOnActionClickedListener(final OnActionClickedListener listener) {
319            mActionClickedListener = listener;
320        }
321
322        @Override
323        public void setHostCallback(HostCallback callback) {
324            mFragment.setHostCallback(callback);
325        }
326
327        @Override
328        public void fadeOut() {
329            mFragment.fadeOut();
330        }
331
332        @Override
333        public void notifyPlaybackRowChanged() {
334            mGlue.onRowChanged(mGlue.getControlsRow());
335        }
336    }
337}
338