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