PlaybackControlsRowPresenter.java revision 46443cb5b092f1d9156342645088eead9da026f6
1/*
2 * Copyright (C) 2014 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.widget;
15
16import android.support.v17.leanback.R;
17import android.support.v17.leanback.widget.ControlBarPresenter.OnControlClickedListener;
18import android.support.v17.leanback.widget.ControlBarPresenter.OnControlSelectedListener;
19import android.content.Context;
20import android.graphics.Color;
21import android.util.TypedValue;
22import android.view.KeyEvent;
23import android.view.LayoutInflater;
24import android.view.View;
25import android.view.ViewGroup;
26import android.view.ViewGroup.LayoutParams;
27import android.view.ViewGroup.MarginLayoutParams;
28import android.widget.ImageView;
29
30/**
31 * A PlaybackControlsRowPresenter renders a {@link PlaybackControlsRow} to display a
32 * series of playback control buttons. Typically this row will be the first row in a fragment
33 * such as the {@link android.support.v17.leanback.app.PlaybackOverlayFragment
34 * PlaybackControlsFragment}.
35 */
36public class PlaybackControlsRowPresenter extends RowPresenter {
37
38    static class BoundData extends PlaybackControlsPresenter.BoundData {
39        ViewHolder mRowViewHolder;
40    }
41
42    /**
43     * A ViewHolder for the PlaybackControlsRow.
44     */
45    public class ViewHolder extends RowPresenter.ViewHolder {
46        public final Presenter.ViewHolder mDescriptionViewHolder;
47        final ViewGroup mCard;
48        final ImageView mImageView;
49        final ViewGroup mDescriptionDock;
50        final ViewGroup mControlsDock;
51        final ViewGroup mSecondaryControlsDock;
52        final View mSpacer;
53        final View mBottomSpacer;
54        View mBgView;
55        int mCardHeight;
56        int mControlsDockMarginStart;
57        int mControlsDockMarginEnd;
58        PlaybackControlsPresenter.ViewHolder mControlsVh;
59        Presenter.ViewHolder mSecondaryControlsVh;
60        BoundData mControlsBoundData = new BoundData();
61        BoundData mSecondaryBoundData = new BoundData();
62        Presenter.ViewHolder mSelectedViewHolder;
63        Object mSelectedItem;
64        final PlaybackControlsRow.OnPlaybackStateChangedListener mListener =
65                new PlaybackControlsRow.OnPlaybackStateChangedListener() {
66            @Override
67            public void onCurrentTimeChanged(int ms) {
68                mPlaybackControlsPresenter.setCurrentTime(mControlsVh, ms);
69            }
70            @Override
71            public void onBufferedProgressChanged(int ms) {
72                mPlaybackControlsPresenter.setSecondaryProgress(mControlsVh, ms);
73            }
74        };
75
76        ViewHolder(View rootView, Presenter descriptionPresenter) {
77            super(rootView);
78            mCard = (ViewGroup) rootView.findViewById(R.id.controls_card);
79            mImageView = (ImageView) rootView.findViewById(R.id.image);
80            mDescriptionDock = (ViewGroup) rootView.findViewById(R.id.description_dock);
81            mControlsDock = (ViewGroup) rootView.findViewById(R.id.controls_dock);
82            mSecondaryControlsDock =
83                    (ViewGroup) rootView.findViewById(R.id.secondary_controls_dock);
84            mSpacer = rootView.findViewById(R.id.spacer);
85            mBottomSpacer = rootView.findViewById(R.id.bottom_spacer);
86            mDescriptionViewHolder = descriptionPresenter == null ? null :
87                    descriptionPresenter.onCreateViewHolder(mDescriptionDock);
88            if (mDescriptionViewHolder != null) {
89                mDescriptionDock.addView(mDescriptionViewHolder.view);
90            }
91        }
92
93        void dispatchItemSelection() {
94            if (!isSelected()) {
95                return;
96            }
97            if (mSelectedViewHolder == null) {
98                if (getOnItemSelectedListener() != null) {
99                    getOnItemSelectedListener().onItemSelected(null, getRow());
100                }
101                if (getOnItemViewSelectedListener() != null) {
102                    getOnItemViewSelectedListener().onItemSelected(null, null,
103                            ViewHolder.this, getRow());
104                }
105            } else {
106                if (getOnItemSelectedListener() != null) {
107                    getOnItemSelectedListener().onItemSelected(mSelectedItem, getRow());
108                }
109                if (getOnItemViewSelectedListener() != null) {
110                    getOnItemViewSelectedListener().onItemSelected(mSelectedViewHolder, mSelectedItem,
111                            ViewHolder.this, getRow());
112                }
113            }
114        };
115
116        Presenter getPresenter(boolean primary) {
117            ObjectAdapter adapter = primary ?
118                    ((PlaybackControlsRow) getRow()).getPrimaryActionsAdapter() :
119                            ((PlaybackControlsRow) getRow()).getSecondaryActionsAdapter();
120            if (adapter == null) {
121                return null;
122            }
123            if (adapter.getPresenterSelector() instanceof ControlButtonPresenterSelector) {
124                ControlButtonPresenterSelector selector =
125                        (ControlButtonPresenterSelector) adapter.getPresenterSelector();
126                return primary ? selector.getPrimaryPresenter() :
127                    selector.getSecondaryPresenter();
128            }
129            return adapter.getPresenter(adapter.size() > 0 ? adapter.get(0) : null);
130        }
131
132        void setBackground(View view) {
133            if (mBgView != null) {
134                RoundedRectHelper.getInstance().clearBackground(mBgView);
135                ShadowHelper.getInstance().setZ(mBgView, 0);
136            }
137            mBgView = view;
138            RoundedRectHelper.getInstance().setRoundedRectBackground(view, mBackgroundColorSet ?
139                    mBackgroundColor : getDefaultBackgroundColor(view.getContext()));
140
141            if (sShadowZ == 0) {
142                sShadowZ = view.getResources().getDimensionPixelSize(
143                        R.dimen.lb_playback_controls_z);
144            }
145            ShadowHelper.getInstance().setZ(view, sShadowZ);
146        }
147    }
148
149    private int mBackgroundColor = Color.TRANSPARENT;
150    private boolean mBackgroundColorSet;
151    private int mProgressColor = Color.TRANSPARENT;
152    private boolean mProgressColorSet;
153    private boolean mSecondaryActionsHidden;
154    private Presenter mDescriptionPresenter;
155    private PlaybackControlsPresenter mPlaybackControlsPresenter;
156    private ControlBarPresenter mSecondaryControlsPresenter;
157    private OnActionClickedListener mOnActionClickedListener;
158    private static float sShadowZ;
159
160    private final OnControlSelectedListener mOnControlSelectedListener =
161            new OnControlSelectedListener() {
162        @Override
163        public void onControlSelected(Presenter.ViewHolder itemViewHolder, Object item,
164                ControlBarPresenter.BoundData data) {
165            ViewHolder vh = ((BoundData) data).mRowViewHolder;
166            if (vh.mSelectedViewHolder != itemViewHolder || vh.mSelectedItem != item) {
167                vh.mSelectedViewHolder = itemViewHolder;
168                vh.mSelectedItem = item;
169                vh.dispatchItemSelection();
170            }
171        }
172    };
173
174    private final OnControlClickedListener mOnControlClickedListener =
175            new OnControlClickedListener() {
176        @Override
177        public void onControlClicked(Presenter.ViewHolder itemViewHolder, Object item,
178                ControlBarPresenter.BoundData data) {
179            ViewHolder vh = ((BoundData) data).mRowViewHolder;
180            if (getOnItemClickedListener() != null) {
181                getOnItemClickedListener().onItemClicked(item, vh.getRow());
182            }
183            if (getOnItemViewClickedListener() != null) {
184                getOnItemViewClickedListener().onItemClicked(itemViewHolder, item,
185                        vh, vh.getRow());
186            }
187            if (mOnActionClickedListener != null && item instanceof Action) {
188                mOnActionClickedListener.onActionClicked((Action) item);
189            }
190        }
191    };
192
193    /**
194     * Constructor for a PlaybackControlsRowPresenter.
195     *
196     * @param descriptionPresenter Presenter for displaying item details.
197     */
198    public PlaybackControlsRowPresenter(Presenter descriptionPresenter) {
199        setHeaderPresenter(null);
200        setSelectEffectEnabled(false);
201
202        mDescriptionPresenter = descriptionPresenter;
203        mPlaybackControlsPresenter = new PlaybackControlsPresenter(R.layout.lb_playback_controls);
204        mSecondaryControlsPresenter = new ControlBarPresenter(R.layout.lb_control_bar);
205
206        mPlaybackControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener);
207        mSecondaryControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener);
208        mPlaybackControlsPresenter.setOnControlClickedListener(mOnControlClickedListener);
209        mSecondaryControlsPresenter.setOnControlClickedListener(mOnControlClickedListener);
210    }
211
212    /**
213     * Constructor for a PlaybackControlsRowPresenter.
214     */
215    public PlaybackControlsRowPresenter() {
216        this(null);
217    }
218
219    /**
220     * Sets the listener for {@link Action} click events.
221     */
222    public void setOnActionClickedListener(OnActionClickedListener listener) {
223        mOnActionClickedListener = listener;
224    }
225
226    /**
227     * Gets the listener for {@link Action} click events.
228     */
229    public OnActionClickedListener getOnActionClickedListener() {
230        return mOnActionClickedListener;
231    }
232
233    /**
234     * Sets the background color.  If not set, a default from the theme will be used.
235     */
236    public void setBackgroundColor(int color) {
237        mBackgroundColor = color;
238        mBackgroundColorSet = true;
239    }
240
241    /**
242     * Returns the background color.  If no background color was set, transparent
243     * is returned.
244     */
245    public int getBackgroundColor() {
246        return mBackgroundColor;
247    }
248
249    /**
250     * Sets the primary color for the progress bar.  If not set, a default from
251     * the theme will be used.
252     */
253    public void setProgressColor(int color) {
254        mProgressColor = color;
255        mProgressColorSet = true;
256    }
257
258    /**
259     * Returns the primary color for the progress bar.  If no color was set, transparent
260     * is returned.
261     */
262    public int getProgressColor() {
263        return mProgressColor;
264    }
265
266    /**
267     * Sets the secondary actions to be hidden behind a "more actions" button.
268     * When "more actions" is selected, the primary actions are replaced with
269     * the secondary actions.
270     */
271    public void setSecondaryActionsHidden(boolean hidden) {
272        mSecondaryActionsHidden = hidden;
273    }
274
275    /**
276     * Returns true if secondary actions are hidden.
277     */
278    public boolean areSecondaryActionsHidden() {
279        return mSecondaryActionsHidden;
280    }
281
282    /**
283     * Shows or hides space at the bottom of the playback controls row.
284     * This allows the row to hug the bottom of the display when no
285     * other rows are present.
286     */
287    public void showBottomSpace(ViewHolder vh, boolean show) {
288        vh.mBottomSpacer.setVisibility(show ? View.VISIBLE : View.GONE);
289    }
290
291    /**
292     * Display the primary actions.
293     */
294    public void showPrimaryActions(ViewHolder vh) {
295        mPlaybackControlsPresenter.showPrimaryActions(vh.mControlsVh);
296    }
297
298    private int getDefaultBackgroundColor(Context context) {
299        TypedValue outValue = new TypedValue();
300        if (context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) {
301            return context.getResources().getColor(outValue.resourceId);
302        }
303        return context.getResources().getColor(R.color.lb_default_brand_color);
304    }
305
306    private int getDefaultProgressColor(Context context) {
307        TypedValue outValue = new TypedValue();
308        if (context.getTheme()
309                .resolveAttribute(R.attr.playbackProgressPrimaryColor, outValue, true)) {
310            return context.getResources().getColor(outValue.resourceId);
311        }
312        return context.getResources().getColor(R.color.lb_playback_progress_color_no_theme);
313    }
314
315    @Override
316    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
317        View v = LayoutInflater.from(parent.getContext())
318            .inflate(R.layout.lb_playback_controls_row, parent, false);
319        ViewHolder vh = new ViewHolder(v, mDescriptionPresenter);
320        initRow(vh);
321        return vh;
322    }
323
324    private void initRow(final ViewHolder vh) {
325        vh.mCardHeight = vh.mCard.getLayoutParams().height;
326
327        MarginLayoutParams lp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams();
328        vh.mControlsDockMarginStart = lp.getMarginStart();
329        vh.mControlsDockMarginEnd = lp.getMarginEnd();
330
331        vh.mControlsVh = (PlaybackControlsPresenter.ViewHolder)
332                mPlaybackControlsPresenter.onCreateViewHolder(vh.mControlsDock);
333        mPlaybackControlsPresenter.setProgressColor(vh.mControlsVh,
334                mProgressColorSet ? mProgressColor :
335                        getDefaultProgressColor(vh.mControlsDock.getContext()));
336        vh.mControlsDock.addView(vh.mControlsVh.view);
337
338        vh.mSecondaryControlsVh =
339                mSecondaryControlsPresenter.onCreateViewHolder(vh.mSecondaryControlsDock);
340        if (!mSecondaryActionsHidden) {
341            vh.mSecondaryControlsDock.addView(vh.mSecondaryControlsVh.view);
342        }
343        ((PlaybackControlsRowView) vh.view).setOnUnhandledKeyListener(
344                new PlaybackControlsRowView.OnUnhandledKeyListener() {
345            @Override
346            public boolean onUnhandledKey(KeyEvent event) {
347                if (vh.getOnKeyListener() != null) {
348                    if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) {
349                        return true;
350                    }
351                }
352                return false;
353            }
354        });
355    }
356
357    @Override
358    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
359        super.onBindRowViewHolder(holder, item);
360
361        ViewHolder vh = (ViewHolder) holder;
362        PlaybackControlsRow row = (PlaybackControlsRow) vh.getRow();
363
364        mPlaybackControlsPresenter.enableSecondaryActions(mSecondaryActionsHidden);
365
366        if (row.getItem() == null) {
367            LayoutParams lp = vh.mCard.getLayoutParams();
368            lp.height = LayoutParams.WRAP_CONTENT;
369            vh.mCard.setLayoutParams(lp);
370            vh.mDescriptionDock.setVisibility(View.GONE);
371            vh.mSpacer.setVisibility(View.GONE);
372        } else {
373            LayoutParams lp = vh.mCard.getLayoutParams();
374            lp.height = vh.mCardHeight;
375            vh.mCard.setLayoutParams(lp);
376            vh.mDescriptionDock.setVisibility(View.VISIBLE);
377            if (vh.mDescriptionViewHolder != null) {
378                mDescriptionPresenter.onBindViewHolder(vh.mDescriptionViewHolder, row.getItem());
379            }
380            vh.mSpacer.setVisibility(View.VISIBLE);
381        }
382
383        MarginLayoutParams lp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams();
384        if (row.getImageDrawable() == null || row.getItem() == null) {
385            vh.mImageView.setImageDrawable(null);
386            vh.setBackground(vh.mControlsDock);
387            lp.setMarginStart(0);
388            lp.setMarginEnd(0);
389            mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, true);
390        } else {
391            vh.mImageView.setImageDrawable(row.getImageDrawable());
392            vh.setBackground(vh.mCard);
393            lp.setMarginStart(vh.mControlsDockMarginStart);
394            lp.setMarginEnd(vh.mControlsDockMarginEnd);
395            mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, false);
396        }
397        vh.mControlsDock.setLayoutParams(lp);
398
399        vh.mControlsBoundData.adapter = row.getPrimaryActionsAdapter();
400        vh.mControlsBoundData.secondaryActionsAdapter = row.getSecondaryActionsAdapter();
401        vh.mControlsBoundData.presenter = vh.getPresenter(true);
402        vh.mControlsBoundData.mRowViewHolder = vh;
403        mPlaybackControlsPresenter.onBindViewHolder(vh.mControlsVh, vh.mControlsBoundData);
404
405        vh.mSecondaryBoundData.adapter = row.getSecondaryActionsAdapter();
406        vh.mSecondaryBoundData.presenter = vh.getPresenter(false);
407        vh.mSecondaryBoundData.mRowViewHolder = vh;
408        mSecondaryControlsPresenter.onBindViewHolder(vh.mSecondaryControlsVh,
409                vh.mSecondaryBoundData);
410
411        mPlaybackControlsPresenter.setTotalTime(vh.mControlsVh, row.getTotalTime());
412        mPlaybackControlsPresenter.setCurrentTime(vh.mControlsVh, row.getCurrentTime());
413        mPlaybackControlsPresenter.setSecondaryProgress(vh.mControlsVh, row.getBufferedProgress());
414        row.setOnPlaybackStateChangedListener(vh.mListener);
415    }
416
417    @Override
418    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
419        ViewHolder vh = (ViewHolder) holder;
420        PlaybackControlsRow row = (PlaybackControlsRow) vh.getRow();
421
422        if (vh.mDescriptionViewHolder != null) {
423            mDescriptionPresenter.onUnbindViewHolder(vh.mDescriptionViewHolder);
424        }
425        mPlaybackControlsPresenter.onUnbindViewHolder(vh.mControlsVh);
426        mSecondaryControlsPresenter.onUnbindViewHolder(vh.mSecondaryControlsVh);
427        row.setOnPlaybackStateChangedListener(null);
428
429        super.onUnbindRowViewHolder(holder);
430    }
431
432    protected void onRowViewSelected(RowPresenter.ViewHolder vh, boolean selected) {
433        super.onRowViewSelected(vh, selected);
434        if (selected) {
435            ((ViewHolder) vh).dispatchItemSelection();
436        }
437    }
438
439    @Override
440    protected void onRowViewAttachedToWindow(RowPresenter.ViewHolder vh) {
441        super.onRowViewAttachedToWindow(vh);
442        if (mDescriptionPresenter != null) {
443            mDescriptionPresenter.onViewAttachedToWindow(
444                    ((ViewHolder) vh).mDescriptionViewHolder);
445        }
446    }
447
448    @Override
449    protected void onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh) {
450        super.onRowViewDetachedFromWindow(vh);
451        if (mDescriptionPresenter != null) {
452            mDescriptionPresenter.onViewDetachedFromWindow(
453                    ((ViewHolder) vh).mDescriptionViewHolder);
454        }
455    }
456}
457