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