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