• 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.app.Activity;
17 import android.content.Context;
18 import android.graphics.Bitmap;
19 import android.graphics.Color;
20 import android.graphics.drawable.Drawable;
21 import android.graphics.drawable.BitmapDrawable;
22 import android.graphics.drawable.ColorDrawable;
23 import android.os.Handler;
24 import android.support.annotation.ColorInt;
25 import android.support.v17.leanback.R;
26 import android.support.v7.widget.RecyclerView;
27 import android.util.Log;
28 import android.util.TypedValue;
29 import android.view.KeyEvent;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import android.widget.FrameLayout;
34 import android.widget.ImageView;
35 
36 import java.util.Collection;
37 
38 /**
39  * Renders a {@link DetailsOverviewRow} to display an overview of an item.
40  * Typically this row will be the first row in a fragment
41  * such as the {@link android.support.v17.leanback.app.DetailsFragment
42  * DetailsFragment}.  The View created by the DetailsOverviewRowPresenter is made in three parts:
43  * ImageView on the left, action list view on the bottom and a customizable detailed
44  * description view on the right.
45  *
46  * <p>The detailed description is rendered using a {@link Presenter} passed in
47  * {@link #DetailsOverviewRowPresenter(Presenter)}.  Typically this will be an instance of
48  * {@link AbstractDetailsDescriptionPresenter}.  The application can access the
49  * detailed description ViewHolder from {@link ViewHolder#mDetailsDescriptionViewHolder}.
50  * </p>
51  *
52  * <p>
53  * To participate in activity transition, call {@link #setSharedElementEnterTransition(Activity,
54  * String)} during Activity's onCreate().
55  * </p>
56  *
57  * <p>
58  * Because transition support and layout are fully controlled by DetailsOverviewRowPresenter,
59  * developer can not override DetailsOverviewRowPresenter.ViewHolder for adding/replacing views
60  * of DetailsOverviewRowPresenter.  If further customization is required beyond replacing
61  * the detailed description, the application should create a new row presenter class.
62  * </p>
63  * @deprecated  Use {@link FullWidthDetailsOverviewRowPresenter}
64  */
65 @Deprecated
66 public class DetailsOverviewRowPresenter extends RowPresenter {
67 
68     private static final String TAG = "DetailsOverviewRowPresenter";
69     private static final boolean DEBUG = false;
70 
71     private static final int MORE_ACTIONS_FADE_MS = 100;
72     private static final long DEFAULT_TIMEOUT = 5000;
73 
74     class ActionsItemBridgeAdapter extends ItemBridgeAdapter {
75         DetailsOverviewRowPresenter.ViewHolder mViewHolder;
76 
ActionsItemBridgeAdapter(DetailsOverviewRowPresenter.ViewHolder viewHolder)77         ActionsItemBridgeAdapter(DetailsOverviewRowPresenter.ViewHolder viewHolder) {
78             mViewHolder = viewHolder;
79         }
80 
81         @Override
onBind(final ItemBridgeAdapter.ViewHolder ibvh)82         public void onBind(final ItemBridgeAdapter.ViewHolder ibvh) {
83             if (mViewHolder.getOnItemViewClickedListener() != null ||
84                     mActionClickedListener != null) {
85                 ibvh.getPresenter().setOnClickListener(
86                         ibvh.getViewHolder(), new View.OnClickListener() {
87                             @Override
88                             public void onClick(View v) {
89                                 if (mViewHolder.getOnItemViewClickedListener() != null) {
90                                     mViewHolder.getOnItemViewClickedListener().onItemClicked(
91                                             ibvh.getViewHolder(), ibvh.getItem(),
92                                             mViewHolder, mViewHolder.getRow());
93                                 }
94                                 if (mActionClickedListener != null) {
95                                     mActionClickedListener.onActionClicked((Action) ibvh.getItem());
96                                 }
97                             }
98                         });
99             }
100         }
101         @Override
onUnbind(final ItemBridgeAdapter.ViewHolder ibvh)102         public void onUnbind(final ItemBridgeAdapter.ViewHolder ibvh) {
103             if (mViewHolder.getOnItemViewClickedListener() != null ||
104                     mActionClickedListener != null) {
105                 ibvh.getPresenter().setOnClickListener(ibvh.getViewHolder(), null);
106             }
107         }
108         @Override
onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder)109         public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
110             // Remove first to ensure we don't add ourselves more than once.
111             viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
112             viewHolder.itemView.addOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
113         }
114         @Override
onDetachedFromWindow(ItemBridgeAdapter.ViewHolder viewHolder)115         public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
116             viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
117             mViewHolder.checkFirstAndLastPosition(false);
118         }
119     }
120 
121     /**
122      * A ViewHolder for the DetailsOverviewRow.
123      */
124     public final class ViewHolder extends RowPresenter.ViewHolder {
125         final FrameLayout mOverviewFrame;
126         final ViewGroup mOverviewView;
127         final ImageView mImageView;
128         final ViewGroup mRightPanel;
129         final FrameLayout mDetailsDescriptionFrame;
130         final HorizontalGridView mActionsRow;
131         public final Presenter.ViewHolder mDetailsDescriptionViewHolder;
132         int mNumItems;
133         boolean mShowMoreRight;
134         boolean mShowMoreLeft;
135         ItemBridgeAdapter mActionBridgeAdapter;
136         final Handler mHandler = new Handler();
137 
138         final Runnable mUpdateDrawableCallback = new Runnable() {
139             @Override
140             public void run() {
141                 bindImageDrawable(ViewHolder.this);
142             }
143         };
144 
145         final DetailsOverviewRow.Listener mListener = new DetailsOverviewRow.Listener() {
146             @Override
147             public void onImageDrawableChanged(DetailsOverviewRow row) {
148                 mHandler.removeCallbacks(mUpdateDrawableCallback);
149                 mHandler.post(mUpdateDrawableCallback);
150             }
151 
152             @Override
153             public void onItemChanged(DetailsOverviewRow row) {
154                 if (mDetailsDescriptionViewHolder != null) {
155                     mDetailsPresenter.onUnbindViewHolder(mDetailsDescriptionViewHolder);
156                 }
157                 mDetailsPresenter.onBindViewHolder(mDetailsDescriptionViewHolder, row.getItem());
158             }
159 
160             @Override
161             public void onActionsAdapterChanged(DetailsOverviewRow row) {
162                 bindActions(row.getActionsAdapter());
163             }
164         };
165 
bindActions(ObjectAdapter adapter)166         void bindActions(ObjectAdapter adapter) {
167             mActionBridgeAdapter.setAdapter(adapter);
168             mActionsRow.setAdapter(mActionBridgeAdapter);
169             mNumItems = mActionBridgeAdapter.getItemCount();
170 
171             mShowMoreRight = false;
172             mShowMoreLeft = true;
173             showMoreLeft(false);
174         }
175 
176         final View.OnLayoutChangeListener mLayoutChangeListener =
177                 new View.OnLayoutChangeListener() {
178 
179             @Override
180             public void onLayoutChange(View v, int left, int top, int right,
181                     int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
182                 if (DEBUG) Log.v(TAG, "onLayoutChange " + v);
183                 checkFirstAndLastPosition(false);
184             }
185         };
186 
187         final OnChildSelectedListener mChildSelectedListener = new OnChildSelectedListener() {
188             @Override
189             public void onChildSelected(ViewGroup parent, View view, int position, long id) {
190                 dispatchItemSelection(view);
191             }
192         };
193 
dispatchItemSelection(View view)194         void dispatchItemSelection(View view) {
195             if (!isSelected()) {
196                 return;
197             }
198             ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) (view != null ?
199                     mActionsRow.getChildViewHolder(view) :
200                     mActionsRow.findViewHolderForPosition(mActionsRow.getSelectedPosition()));
201             if (ibvh == null) {
202                 if (getOnItemViewSelectedListener() != null) {
203                     getOnItemViewSelectedListener().onItemSelected(null, null,
204                             ViewHolder.this, getRow());
205                 }
206             } else {
207                 if (getOnItemViewSelectedListener() != null) {
208                     getOnItemViewSelectedListener().onItemSelected(ibvh.getViewHolder(), ibvh.getItem(),
209                             ViewHolder.this, getRow());
210                 }
211             }
212         };
213 
214         final RecyclerView.OnScrollListener mScrollListener =
215                 new RecyclerView.OnScrollListener() {
216 
217             @Override
218             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
219             }
220             @Override
221             public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
222                 checkFirstAndLastPosition(true);
223             }
224         };
225 
getViewCenter(View view)226         private int getViewCenter(View view) {
227             return (view.getRight() - view.getLeft()) / 2;
228         }
229 
checkFirstAndLastPosition(boolean fromScroll)230         private void checkFirstAndLastPosition(boolean fromScroll) {
231             RecyclerView.ViewHolder viewHolder;
232 
233             viewHolder = mActionsRow.findViewHolderForPosition(mNumItems - 1);
234             boolean showRight = (viewHolder == null ||
235                     viewHolder.itemView.getRight() > mActionsRow.getWidth());
236 
237             viewHolder = mActionsRow.findViewHolderForPosition(0);
238             boolean showLeft = (viewHolder == null || viewHolder.itemView.getLeft() < 0);
239 
240             if (DEBUG) Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll +
241                     " showRight " + showRight + " showLeft " + showLeft);
242 
243             showMoreRight(showRight);
244             showMoreLeft(showLeft);
245         }
246 
showMoreLeft(boolean show)247         private void showMoreLeft(boolean show) {
248             if (show != mShowMoreLeft) {
249                 mActionsRow.setFadingLeftEdge(show);
250                 mShowMoreLeft = show;
251             }
252         }
253 
showMoreRight(boolean show)254         private void showMoreRight(boolean show) {
255             if (show != mShowMoreRight) {
256                 mActionsRow.setFadingRightEdge(show);
257                 mShowMoreRight = show;
258             }
259         }
260 
261         /**
262          * Constructor for the ViewHolder.
263          *
264          * @param rootView The root View that this view holder will be attached
265          *        to.
266          */
ViewHolder(View rootView, Presenter detailsPresenter)267         public ViewHolder(View rootView, Presenter detailsPresenter) {
268             super(rootView);
269             mOverviewFrame = (FrameLayout) rootView.findViewById(R.id.details_frame);
270             mOverviewView = (ViewGroup) rootView.findViewById(R.id.details_overview);
271             mImageView = (ImageView) rootView.findViewById(R.id.details_overview_image);
272             mRightPanel = (ViewGroup) rootView.findViewById(R.id.details_overview_right_panel);
273             mDetailsDescriptionFrame =
274                     (FrameLayout) mRightPanel.findViewById(R.id.details_overview_description);
275             mActionsRow =
276                     (HorizontalGridView) mRightPanel.findViewById(R.id.details_overview_actions);
277             mActionsRow.setHasOverlappingRendering(false);
278             mActionsRow.setOnScrollListener(mScrollListener);
279             mActionsRow.setAdapter(mActionBridgeAdapter);
280             mActionsRow.setOnChildSelectedListener(mChildSelectedListener);
281 
282             final int fadeLength = rootView.getResources().getDimensionPixelSize(
283                     R.dimen.lb_details_overview_actions_fade_size);
284             mActionsRow.setFadingRightEdgeLength(fadeLength);
285             mActionsRow.setFadingLeftEdgeLength(fadeLength);
286             mDetailsDescriptionViewHolder =
287                     detailsPresenter.onCreateViewHolder(mDetailsDescriptionFrame);
288             mDetailsDescriptionFrame.addView(mDetailsDescriptionViewHolder.view);
289         }
290     }
291 
292     private final Presenter mDetailsPresenter;
293     private OnActionClickedListener mActionClickedListener;
294 
295     private int mBackgroundColor = Color.TRANSPARENT;
296     private boolean mBackgroundColorSet;
297     private boolean mIsStyleLarge = true;
298 
299     private DetailsOverviewSharedElementHelper mSharedElementHelper;
300 
301     /**
302      * Constructor for a DetailsOverviewRowPresenter.
303      *
304      * @param detailsPresenter The {@link Presenter} used to render the detailed
305      *        description of the row.
306      */
DetailsOverviewRowPresenter(Presenter detailsPresenter)307     public DetailsOverviewRowPresenter(Presenter detailsPresenter) {
308         setHeaderPresenter(null);
309         setSelectEffectEnabled(false);
310         mDetailsPresenter = detailsPresenter;
311     }
312 
313     /**
314      * Sets the listener for Action click events.
315      */
setOnActionClickedListener(OnActionClickedListener listener)316     public void setOnActionClickedListener(OnActionClickedListener listener) {
317         mActionClickedListener = listener;
318     }
319 
320     /**
321      * Returns the listener for Action click events.
322      */
getOnActionClickedListener()323     public OnActionClickedListener getOnActionClickedListener() {
324         return mActionClickedListener;
325     }
326 
327     /**
328      * Sets the background color.  If not set, a default from the theme will be used.
329      */
setBackgroundColor(@olorInt int color)330     public void setBackgroundColor(@ColorInt int color) {
331         mBackgroundColor = color;
332         mBackgroundColorSet = true;
333     }
334 
335     /**
336      * Returns the background color.  If no background color was set, transparent
337      * is returned.
338      */
339     @ColorInt
getBackgroundColor()340     public int getBackgroundColor() {
341         return mBackgroundColor;
342     }
343 
344     /**
345      * Sets the layout style to be large or small. This affects the height of
346      * the overview, including the text description. The default is large.
347      */
setStyleLarge(boolean large)348     public void setStyleLarge(boolean large) {
349         mIsStyleLarge = large;
350     }
351 
352     /**
353      * Returns true if the layout style is large.
354      */
isStyleLarge()355     public boolean isStyleLarge() {
356         return mIsStyleLarge;
357     }
358 
359     /**
360      * Sets the enter transition of target activity to be
361      * transiting into overview row created by this presenter.  The transition will
362      * be cancelled if the overview image is not loaded in the timeout period.
363      * <p>
364      * It assumes shared element passed from calling activity is an ImageView;
365      * the shared element transits to overview image on the starting edge of the detail
366      * overview row, while bounds of overview row grows and reveals text
367      * and action buttons.
368      * <p>
369      * The method must be invoked in target Activity's onCreate().
370      */
setSharedElementEnterTransition(Activity activity, String sharedElementName, long timeoutMs)371     public final void setSharedElementEnterTransition(Activity activity,
372             String sharedElementName, long timeoutMs) {
373         if (mSharedElementHelper == null) {
374             mSharedElementHelper = new DetailsOverviewSharedElementHelper();
375         }
376         mSharedElementHelper.setSharedElementEnterTransition(activity, sharedElementName,
377                 timeoutMs);
378     }
379 
380     /**
381      * Sets the enter transition of target activity to be
382      * transiting into overview row created by this presenter.  The transition will
383      * be cancelled if overview image is not loaded in a default timeout period.
384      * <p>
385      * It assumes shared element passed from calling activity is an ImageView;
386      * the shared element transits to overview image on the starting edge of the detail
387      * overview row, while bounds of overview row grows and reveals text
388      * and action buttons.
389      * <p>
390      * The method must be invoked in target Activity's onCreate().
391      */
setSharedElementEnterTransition(Activity activity, String sharedElementName)392     public final void setSharedElementEnterTransition(Activity activity,
393             String sharedElementName) {
394         setSharedElementEnterTransition(activity, sharedElementName, DEFAULT_TIMEOUT);
395     }
396 
getDefaultBackgroundColor(Context context)397     private int getDefaultBackgroundColor(Context context) {
398         TypedValue outValue = new TypedValue();
399         if (context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) {
400             return context.getResources().getColor(outValue.resourceId);
401         }
402         return context.getResources().getColor(R.color.lb_default_brand_color);
403     }
404 
405     @Override
onRowViewSelected(RowPresenter.ViewHolder vh, boolean selected)406     protected void onRowViewSelected(RowPresenter.ViewHolder vh, boolean selected) {
407         super.onRowViewSelected(vh, selected);
408         if (selected) {
409             ((ViewHolder) vh).dispatchItemSelection(null);
410         }
411     }
412 
413     @Override
createRowViewHolder(ViewGroup parent)414     protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
415         View v = LayoutInflater.from(parent.getContext())
416             .inflate(R.layout.lb_details_overview, parent, false);
417         ViewHolder vh = new ViewHolder(v, mDetailsPresenter);
418 
419         initDetailsOverview(vh);
420 
421         return vh;
422     }
423 
getCardHeight(Context context)424     private int getCardHeight(Context context) {
425         int resId = mIsStyleLarge ? R.dimen.lb_details_overview_height_large :
426             R.dimen.lb_details_overview_height_small;
427         return context.getResources().getDimensionPixelSize(resId);
428     }
429 
initDetailsOverview(final ViewHolder vh)430     private void initDetailsOverview(final ViewHolder vh) {
431         vh.mActionBridgeAdapter = new ActionsItemBridgeAdapter(vh);
432         final View overview = vh.mOverviewFrame;
433         ViewGroup.LayoutParams lp = overview.getLayoutParams();
434         lp.height = getCardHeight(overview.getContext());
435         overview.setLayoutParams(lp);
436 
437         if (!getSelectEffectEnabled()) {
438             vh.mOverviewFrame.setForeground(null);
439         }
440         vh.mActionsRow.setOnUnhandledKeyListener(new BaseGridView.OnUnhandledKeyListener() {
441             @Override
442             public boolean onUnhandledKey(KeyEvent event) {
443                 if (vh.getOnKeyListener() != null) {
444                     if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) {
445                         return true;
446                     }
447                 }
448                 return false;
449             }
450         });
451     }
452 
getNonNegativeWidth(Drawable drawable)453     private static int getNonNegativeWidth(Drawable drawable) {
454         final int width = (drawable == null) ? 0 : drawable.getIntrinsicWidth();
455         return (width > 0 ? width : 0);
456     }
457 
getNonNegativeHeight(Drawable drawable)458     private static int getNonNegativeHeight(Drawable drawable) {
459         final int height = (drawable == null) ? 0 : drawable.getIntrinsicHeight();
460         return (height > 0 ? height : 0);
461     }
462 
bindImageDrawable(ViewHolder vh)463     private void bindImageDrawable(ViewHolder vh) {
464         DetailsOverviewRow row = (DetailsOverviewRow) vh.getRow();
465 
466         ViewGroup.MarginLayoutParams layoutParams =
467                 (ViewGroup.MarginLayoutParams) vh.mImageView.getLayoutParams();
468         final int cardHeight = getCardHeight(vh.mImageView.getContext());
469         final int verticalMargin = vh.mImageView.getResources().getDimensionPixelSize(
470                 R.dimen.lb_details_overview_image_margin_vertical);
471         final int horizontalMargin = vh.mImageView.getResources().getDimensionPixelSize(
472                 R.dimen.lb_details_overview_image_margin_horizontal);
473         final int drawableWidth = getNonNegativeWidth(row.getImageDrawable());
474         final int drawableHeight = getNonNegativeHeight(row.getImageDrawable());
475 
476         boolean scaleImage = row.isImageScaleUpAllowed();
477         boolean useMargin = false;
478 
479         if (row.getImageDrawable() != null) {
480             boolean landscape = false;
481 
482             // If large style and landscape image we always use margin.
483             if (drawableWidth > drawableHeight) {
484                 landscape = true;
485                 if (mIsStyleLarge) {
486                     useMargin = true;
487                 }
488             }
489             // If long dimension bigger than the card height we scale down.
490             if ((landscape && drawableWidth > cardHeight) ||
491                     (!landscape && drawableHeight > cardHeight)) {
492                 scaleImage = true;
493             }
494             // If we're not scaling to fit the card height then we always use margin.
495             if (!scaleImage) {
496                 useMargin = true;
497             }
498             // If using margin than may need to scale down.
499             if (useMargin && !scaleImage) {
500                 if (landscape && drawableWidth > cardHeight - horizontalMargin) {
501                     scaleImage = true;
502                 } else if (!landscape && drawableHeight > cardHeight - 2 * verticalMargin) {
503                     scaleImage = true;
504                 }
505             }
506         }
507 
508         final int bgColor = mBackgroundColorSet ? mBackgroundColor :
509             getDefaultBackgroundColor(vh.mOverviewView.getContext());
510 
511         if (useMargin) {
512             layoutParams.setMarginStart(horizontalMargin);
513             layoutParams.topMargin = layoutParams.bottomMargin = verticalMargin;
514             vh.mOverviewFrame.setBackgroundColor(bgColor);
515             vh.mRightPanel.setBackground(null);
516             vh.mImageView.setBackground(null);
517         } else {
518             layoutParams.leftMargin = layoutParams.topMargin = layoutParams.bottomMargin = 0;
519             vh.mRightPanel.setBackgroundColor(bgColor);
520             vh.mImageView.setBackgroundColor(bgColor);
521             vh.mOverviewFrame.setBackground(null);
522         }
523         RoundedRectHelper.getInstance().setClipToRoundedOutline(vh.mOverviewFrame, true);
524 
525         if (scaleImage) {
526             vh.mImageView.setScaleType(ImageView.ScaleType.FIT_START);
527             vh.mImageView.setAdjustViewBounds(true);
528             vh.mImageView.setMaxWidth(cardHeight);
529             layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
530             layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
531         } else {
532             vh.mImageView.setScaleType(ImageView.ScaleType.CENTER);
533             vh.mImageView.setAdjustViewBounds(false);
534             layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
535             // Limit width to the card height
536             layoutParams.width = Math.min(cardHeight, drawableWidth);
537         }
538         vh.mImageView.setLayoutParams(layoutParams);
539         vh.mImageView.setImageDrawable(row.getImageDrawable());
540         if (row.getImageDrawable() != null && mSharedElementHelper != null) {
541             mSharedElementHelper.onBindToDrawable(vh);
542         }
543     }
544 
545     @Override
onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item)546     protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
547         super.onBindRowViewHolder(holder, item);
548 
549         DetailsOverviewRow row = (DetailsOverviewRow) item;
550         ViewHolder vh = (ViewHolder) holder;
551 
552         bindImageDrawable(vh);
553         mDetailsPresenter.onBindViewHolder(vh.mDetailsDescriptionViewHolder, row.getItem());
554         vh.bindActions(row.getActionsAdapter());
555         row.addListener(vh.mListener);
556     }
557 
558     @Override
onUnbindRowViewHolder(RowPresenter.ViewHolder holder)559     protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
560         ViewHolder vh = (ViewHolder) holder;
561         DetailsOverviewRow dor = (DetailsOverviewRow) vh.getRow();
562         dor.removeListener(vh.mListener);
563         if (vh.mDetailsDescriptionViewHolder != null) {
564             mDetailsPresenter.onUnbindViewHolder(vh.mDetailsDescriptionViewHolder);
565         }
566         super.onUnbindRowViewHolder(holder);
567     }
568 
569     @Override
isUsingDefaultSelectEffect()570     public final boolean isUsingDefaultSelectEffect() {
571         return false;
572     }
573 
574     @Override
onSelectLevelChanged(RowPresenter.ViewHolder holder)575     protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) {
576         super.onSelectLevelChanged(holder);
577         if (getSelectEffectEnabled()) {
578             ViewHolder vh = (ViewHolder) holder;
579             int dimmedColor = vh.mColorDimmer.getPaint().getColor();
580             ((ColorDrawable) vh.mOverviewFrame.getForeground().mutate()).setColor(dimmedColor);
581         }
582     }
583 
584     @Override
onRowViewAttachedToWindow(RowPresenter.ViewHolder vh)585     protected void onRowViewAttachedToWindow(RowPresenter.ViewHolder vh) {
586         super.onRowViewAttachedToWindow(vh);
587         if (mDetailsPresenter != null) {
588             mDetailsPresenter.onViewAttachedToWindow(
589                     ((ViewHolder) vh).mDetailsDescriptionViewHolder);
590         }
591     }
592 
593     @Override
onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh)594     protected void onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh) {
595         super.onRowViewDetachedFromWindow(vh);
596         if (mDetailsPresenter != null) {
597             mDetailsPresenter.onViewDetachedFromWindow(
598                     ((ViewHolder) vh).mDetailsDescriptionViewHolder);
599         }
600     }
601 }
602