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