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