FullWidthDetailsOverviewRowPresenter.java revision 0246318f27a905a31df5a8af445cfe67d31dfb68
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.app.Activity;
17import android.content.Context;
18import android.content.res.Resources;
19import android.graphics.Bitmap;
20import android.graphics.Color;
21import android.graphics.Rect;
22import android.graphics.drawable.Drawable;
23import android.graphics.drawable.BitmapDrawable;
24import android.graphics.drawable.ColorDrawable;
25import android.os.Handler;
26import android.support.v17.leanback.R;
27import android.support.v17.leanback.widget.ListRowPresenter.ViewHolder;
28import android.support.v7.widget.RecyclerView;
29import android.util.Log;
30import android.util.TypedValue;
31import android.view.KeyEvent;
32import android.view.LayoutInflater;
33import android.view.View;
34import android.view.ViewGroup;
35import android.view.ViewGroup.MarginLayoutParams;
36import android.widget.FrameLayout;
37import android.widget.ImageView;
38
39import java.util.Collection;
40
41/**
42 * Renders a {@link DetailsOverviewRow} to display an overview of an item. Typically this row will
43 * be the first row in a fragment such as the
44 * {@link android.support.v17.leanback.app.DetailsFragment}. The View created by the
45 * FullWidthDetailsOverviewRowPresenter is made in three parts: logo view on the left, action list view on
46 * the top and a customizable detailed description view on the right.
47 *
48 * <p>The detailed description is rendered using a {@link Presenter} passed in
49 * {@link #FullWidthDetailsOverviewRowPresenter(Presenter)}. Typically this will be an instance of
50 * {@link AbstractDetailsDescriptionPresenter}. The application can access the detailed description
51 * ViewHolder from {@link ViewHolder#getDetailsDescriptionViewHolder()}.
52 * </p>
53 *
54 * <p>The logo view is rendered using a customizable {@link DetailsOverviewLogoPresenter} passed in
55 * {@link #FullWidthDetailsOverviewRowPresenter(Presenter, DetailsOverviewLogoPresenter)}. The application
56 * can access the logo ViewHolder from {@link ViewHolder#getLogoViewHolder()}.
57 * </p>
58 *
59 * <p>
60 * To support activity shared element transition, call {@link #setListener(Listener)} with
61 * {@link FullWidthDetailsOverviewSharedElementHelper} during Activity's onCreate(). Application is free to
62 * create its own "shared element helper" class using the Listener for image binding.
63 * Call {@link #setParticipatingEntranceTransition(boolean)} with false
64 * </p>
65 *
66 * <p>
67 * The view has three states: {@link #STATE_HALF} {@link #STATE_FULL} and {@link #STATE_SMALL}. See
68 * {@link android.support.v17.leanback.app.DetailsFragment} where it switches states based on
69 * selected row position.
70 * </p>
71 */
72public class FullWidthDetailsOverviewRowPresenter extends RowPresenter {
73
74    private static final String TAG = "FullWidthDetailsOverviewRowPresenter";
75    private static final boolean DEBUG = false;
76
77    private static Rect sTmpRect = new Rect();
78
79    /**
80     * This is the default state corresponding to layout file.  The view takes full width
81     * of screen and covers bottom half of the screen.
82     */
83    public static final int STATE_HALF = 0;
84    /**
85     * This is the state when the view covers full width and height of screen.
86     */
87    public static final int STATE_FULL = 1;
88    /**
89     * This is the state where the view shrinks to a small banner.
90     */
91    public static final int STATE_SMALL = 2;
92
93    /**
94     * Listeners for events on ViewHolder.
95     */
96    public static abstract class Listener {
97
98        /**
99         * {@link FullWidthDetailsOverviewRowPresenter#notifyOnBindLogo(ViewHolder)} is called.
100         * @param vh  The ViewHolder that has bound logo view.
101         */
102        public void onBindLogo(ViewHolder vh) {
103        }
104
105    }
106
107    class ActionsItemBridgeAdapter extends ItemBridgeAdapter {
108        FullWidthDetailsOverviewRowPresenter.ViewHolder mViewHolder;
109
110        ActionsItemBridgeAdapter(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder) {
111            mViewHolder = viewHolder;
112        }
113
114        @Override
115        public void onBind(final ItemBridgeAdapter.ViewHolder ibvh) {
116            if (mViewHolder.getOnItemViewClickedListener() != null ||
117                    mActionClickedListener != null) {
118                ibvh.getPresenter().setOnClickListener(
119                        ibvh.getViewHolder(), new View.OnClickListener() {
120                            @Override
121                            public void onClick(View v) {
122                                if (mViewHolder.getOnItemViewClickedListener() != null) {
123                                    mViewHolder.getOnItemViewClickedListener().onItemClicked(
124                                            ibvh.getViewHolder(), ibvh.getItem(),
125                                            mViewHolder, mViewHolder.getRow());
126                                }
127                                if (mActionClickedListener != null) {
128                                    mActionClickedListener.onActionClicked((Action) ibvh.getItem());
129                                }
130                            }
131                        });
132            }
133        }
134        @Override
135        public void onUnbind(final ItemBridgeAdapter.ViewHolder ibvh) {
136            if (mViewHolder.getOnItemViewClickedListener() != null ||
137                    mActionClickedListener != null) {
138                ibvh.getPresenter().setOnClickListener(ibvh.getViewHolder(), null);
139            }
140        }
141        @Override
142        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
143            // Remove first to ensure we don't add ourselves more than once.
144            viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
145            viewHolder.itemView.addOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
146        }
147        @Override
148        public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
149            viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
150            mViewHolder.checkFirstAndLastPosition(false);
151        }
152    }
153
154    /**
155     * A ViewHolder for the DetailsOverviewRow.
156     */
157    public class ViewHolder extends RowPresenter.ViewHolder {
158
159        protected final DetailsOverviewRow.Listener mRowListener = createRowListener();
160
161        protected DetailsOverviewRow.Listener createRowListener() {
162            return new DetailsOverviewRowListener();
163        }
164
165        public class DetailsOverviewRowListener extends DetailsOverviewRow.Listener {
166            @Override
167            public void onImageDrawableChanged(DetailsOverviewRow row) {
168                mHandler.removeCallbacks(mUpdateDrawableCallback);
169                mHandler.post(mUpdateDrawableCallback);
170            }
171
172            @Override
173            public void onItemChanged(DetailsOverviewRow row) {
174                if (mDetailsDescriptionViewHolder != null) {
175                    mDetailsPresenter.onUnbindViewHolder(mDetailsDescriptionViewHolder);
176                }
177                mDetailsPresenter.onBindViewHolder(mDetailsDescriptionViewHolder, row.getItem());
178            }
179
180            @Override
181            public void onActionsAdapterChanged(DetailsOverviewRow row) {
182                bindActions(row.getActionsAdapter());
183            }
184        };
185
186        final ViewGroup mOverviewRoot;
187        final ViewGroup mOverviewFrame;
188        final FrameLayout mDetailsDescriptionFrame;
189        final HorizontalGridView mActionsRow;
190        final Presenter.ViewHolder mDetailsDescriptionViewHolder;
191        final DetailsOverviewLogoPresenter.ViewHolder mDetailsLogoViewHolder;
192        int mNumItems;
193        boolean mShowMoreRight;
194        boolean mShowMoreLeft;
195        ItemBridgeAdapter mActionBridgeAdapter;
196        protected final Handler mHandler = new Handler();
197        int mState = STATE_HALF;
198
199        final Runnable mUpdateDrawableCallback = new Runnable() {
200            @Override
201            public void run() {
202                mDetailsOverviewLogoPresenter.onBindViewHolder(mDetailsLogoViewHolder, getRow());
203            }
204        };
205
206        void bindActions(ObjectAdapter adapter) {
207            mActionBridgeAdapter.setAdapter(adapter);
208            mActionsRow.setAdapter(mActionBridgeAdapter);
209            mNumItems = mActionBridgeAdapter.getItemCount();
210
211            mShowMoreRight = false;
212            mShowMoreLeft = true;
213            showMoreLeft(false);
214        }
215
216        final View.OnLayoutChangeListener mLayoutChangeListener =
217                new View.OnLayoutChangeListener() {
218
219            @Override
220            public void onLayoutChange(View v, int left, int top, int right,
221                    int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
222                if (DEBUG) Log.v(TAG, "onLayoutChange " + v);
223                checkFirstAndLastPosition(false);
224            }
225        };
226
227        final OnChildSelectedListener mChildSelectedListener = new OnChildSelectedListener() {
228            @Override
229            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
230                dispatchItemSelection(view);
231            }
232        };
233
234        void dispatchItemSelection(View view) {
235            if (!isSelected()) {
236                return;
237            }
238            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) (view != null ?
239                    mActionsRow.getChildViewHolder(view) :
240                    mActionsRow.findViewHolderForPosition(mActionsRow.getSelectedPosition()));
241            if (ibvh == null) {
242                if (getOnItemViewSelectedListener() != null) {
243                    getOnItemViewSelectedListener().onItemSelected(null, null,
244                            ViewHolder.this, getRow());
245                }
246            } else {
247                if (getOnItemViewSelectedListener() != null) {
248                    getOnItemViewSelectedListener().onItemSelected(ibvh.getViewHolder(), ibvh.getItem(),
249                            ViewHolder.this, getRow());
250                }
251            }
252        };
253
254        final RecyclerView.OnScrollListener mScrollListener =
255                new RecyclerView.OnScrollListener() {
256
257            @Override
258            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
259            }
260            @Override
261            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
262                checkFirstAndLastPosition(true);
263            }
264        };
265
266        private int getViewCenter(View view) {
267            return (view.getRight() - view.getLeft()) / 2;
268        }
269
270        private void checkFirstAndLastPosition(boolean fromScroll) {
271            RecyclerView.ViewHolder viewHolder;
272
273            viewHolder = mActionsRow.findViewHolderForPosition(mNumItems - 1);
274            boolean showRight = (viewHolder == null ||
275                    viewHolder.itemView.getRight() > mActionsRow.getWidth());
276
277            viewHolder = mActionsRow.findViewHolderForPosition(0);
278            boolean showLeft = (viewHolder == null || viewHolder.itemView.getLeft() < 0);
279
280            if (DEBUG) Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll +
281                    " showRight " + showRight + " showLeft " + showLeft);
282
283            showMoreRight(showRight);
284            showMoreLeft(showLeft);
285        }
286
287        private void showMoreLeft(boolean show) {
288            if (show != mShowMoreLeft) {
289                mActionsRow.setFadingLeftEdge(show);
290                mShowMoreLeft = show;
291            }
292        }
293
294        private void showMoreRight(boolean show) {
295            if (show != mShowMoreRight) {
296                mActionsRow.setFadingRightEdge(show);
297                mShowMoreRight = show;
298            }
299        }
300
301        /**
302         * Constructor for the ViewHolder.
303         *
304         * @param rootView The root View that this view holder will be attached
305         *        to.
306         */
307        public ViewHolder(View rootView, Presenter detailsPresenter,
308                DetailsOverviewLogoPresenter logoPresenter) {
309            super(rootView);
310            mOverviewRoot = (ViewGroup) rootView.findViewById(R.id.details_root);
311            mOverviewFrame = (ViewGroup) rootView.findViewById(R.id.details_frame);
312            mDetailsDescriptionFrame =
313                    (FrameLayout) rootView.findViewById(R.id.details_overview_description);
314            mActionsRow =
315                    (HorizontalGridView) mOverviewFrame.findViewById(R.id.details_overview_actions);
316            mActionsRow.setHasOverlappingRendering(false);
317            mActionsRow.setOnScrollListener(mScrollListener);
318            mActionsRow.setAdapter(mActionBridgeAdapter);
319            mActionsRow.setOnChildSelectedListener(mChildSelectedListener);
320
321            final int fadeLength = rootView.getResources().getDimensionPixelSize(
322                    R.dimen.lb_details_overview_actions_fade_size);
323            mActionsRow.setFadingRightEdgeLength(fadeLength);
324            mActionsRow.setFadingLeftEdgeLength(fadeLength);
325            mDetailsDescriptionViewHolder =
326                    detailsPresenter.onCreateViewHolder(mDetailsDescriptionFrame);
327            mDetailsDescriptionFrame.addView(mDetailsDescriptionViewHolder.view);
328            mDetailsLogoViewHolder = (DetailsOverviewLogoPresenter.ViewHolder)
329                    logoPresenter.onCreateViewHolder(mOverviewRoot);
330            mOverviewRoot.addView(mDetailsLogoViewHolder.view);
331        }
332
333        /**
334         * Returns the rectangle area with a color background.
335         */
336        public final ViewGroup getOverviewView() {
337            return mOverviewFrame;
338        }
339
340        /**
341         * Returns the ViewHolder for logo.
342         */
343        public final DetailsOverviewLogoPresenter.ViewHolder getLogoViewHolder() {
344            return mDetailsLogoViewHolder;
345        }
346
347        /**
348         * Returns the ViewHolder for DetailsDescription.
349         */
350        public final Presenter.ViewHolder getDetailsDescriptionViewHolder() {
351            return mDetailsDescriptionViewHolder;
352        }
353
354        /**
355         * Returns the root view for inserting details description.
356         */
357        public final ViewGroup getDetailsDescriptionFrame() {
358            return mDetailsDescriptionFrame;
359        }
360
361        /**
362         * Returns the view of actions row.
363         */
364        public final ViewGroup getActionsRow() {
365            return mActionsRow;
366        }
367
368        /**
369         * Returns current state of the ViewHolder set by
370         * {@link FullWidthDetailsOverviewRowPresenter#setState(ViewHolder, int)}.
371         */
372        public final int getState() {
373            return mState;
374        }
375    }
376
377    protected int mInitialState = STATE_HALF;
378
379    private final Presenter mDetailsPresenter;
380    private final DetailsOverviewLogoPresenter mDetailsOverviewLogoPresenter;
381    private OnActionClickedListener mActionClickedListener;
382
383    private int mBackgroundColor = Color.TRANSPARENT;
384    private boolean mBackgroundColorSet;
385
386    private Listener mListener;
387    private boolean mParticipatingEntranceTransition;
388
389    /**
390     * Constructor for a FullWidthDetailsOverviewRowPresenter.
391     *
392     * @param detailsPresenter The {@link Presenter} used to render the detailed
393     *        description of the row.
394     */
395    public FullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter) {
396        this(detailsPresenter, new DetailsOverviewLogoPresenter());
397    }
398
399    /**
400     * Constructor for a FullWidthDetailsOverviewRowPresenter.
401     *
402     * @param detailsPresenter The {@link Presenter} used to render the detailed
403     *        description of the row.
404     * @param logoPresenter  The {@link Presenter} used to render the logo view.
405     */
406    public FullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter,
407            DetailsOverviewLogoPresenter logoPresenter) {
408        setHeaderPresenter(null);
409        setSelectEffectEnabled(false);
410        mDetailsPresenter = detailsPresenter;
411        mDetailsOverviewLogoPresenter = logoPresenter;
412    }
413
414    /**
415     * Sets the listener for Action click events.
416     */
417    public void setOnActionClickedListener(OnActionClickedListener listener) {
418        mActionClickedListener = listener;
419    }
420
421    /**
422     * Returns the listener for Action click events.
423     */
424    public OnActionClickedListener getOnActionClickedListener() {
425        return mActionClickedListener;
426    }
427
428    /**
429     * Sets the background color.  If not set, a default from the theme will be used.
430     */
431    public void setBackgroundColor(int color) {
432        mBackgroundColor = color;
433        mBackgroundColorSet = true;
434    }
435
436    /**
437     * Returns the background color.  If no background color was set, transparent
438     * is returned.
439     */
440    public int getBackgroundColor() {
441        return mBackgroundColor;
442    }
443
444    /**
445     * Returns true if the overview should be part of shared element transition.
446     */
447    public final boolean isParticipatingEntranceTransition() {
448        return mParticipatingEntranceTransition;
449    }
450
451    /**
452     * Sets if the overview should be part of shared element transition.
453     */
454    public final void setParticipatingEntranceTransition(boolean participating) {
455        mParticipatingEntranceTransition = participating;
456    }
457
458    /**
459     * Change the initial state used to create ViewHolder.
460     */
461    public final void setInitialState(int state) {
462        mInitialState = state;
463    }
464
465    /**
466     * Returns the initial state used to create ViewHolder.
467     */
468    public final int getInitialState() {
469        return mInitialState;
470    }
471
472    @Override
473    protected boolean isClippingChildren() {
474        return true;
475    }
476
477    /**
478     * Set listener for details overview presenter. Must be called before creating
479     * ViewHolder.
480     */
481    public final void setListener(Listener listener) {
482        mListener = listener;
483    }
484
485    private int getDefaultBackgroundColor(Context context) {
486        TypedValue outValue = new TypedValue();
487        if (context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) {
488            return context.getResources().getColor(outValue.resourceId);
489        }
490        return context.getResources().getColor(R.color.lb_default_brand_color);
491    }
492
493    /**
494     * Get resource id to inflate the layout.  The layout must match {@link #STATE_HALF}
495     */
496    protected int getLayoutResourceId() {
497        return R.layout.lb_fullwidth_details_overview;
498    }
499
500    @Override
501    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
502        View v = LayoutInflater.from(parent.getContext())
503            .inflate(getLayoutResourceId(), parent, false);
504        final ViewHolder vh = new ViewHolder(v, mDetailsPresenter, mDetailsOverviewLogoPresenter);
505        mDetailsOverviewLogoPresenter.setContext(vh.mDetailsLogoViewHolder, vh, this);
506        setState(vh, mInitialState);
507
508        vh.mActionBridgeAdapter = new ActionsItemBridgeAdapter(vh);
509        final View overview = vh.mOverviewFrame;
510        final int bgColor = mBackgroundColorSet ? mBackgroundColor :
511                getDefaultBackgroundColor(overview.getContext());
512        overview.setBackgroundColor(bgColor);
513        RoundedRectHelper.getInstance().setClipToRoundedOutline(overview, true);
514
515        vh.mActionsRow.setOnUnhandledKeyListener(new BaseGridView.OnUnhandledKeyListener() {
516            @Override
517            public boolean onUnhandledKey(KeyEvent event) {
518                if (vh.getOnKeyListener() != null) {
519                    if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) {
520                        return true;
521                    }
522                }
523                return false;
524            }
525        });
526        return vh;
527    }
528
529    private static int getNonNegativeWidth(Drawable drawable) {
530        final int width = (drawable == null) ? 0 : drawable.getIntrinsicWidth();
531        return (width > 0 ? width : 0);
532    }
533
534    private static int getNonNegativeHeight(Drawable drawable) {
535        final int height = (drawable == null) ? 0 : drawable.getIntrinsicHeight();
536        return (height > 0 ? height : 0);
537    }
538
539    @Override
540    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
541        super.onBindRowViewHolder(holder, item);
542
543        DetailsOverviewRow row = (DetailsOverviewRow) item;
544        ViewHolder vh = (ViewHolder) holder;
545
546        mDetailsOverviewLogoPresenter.onBindViewHolder(vh.mDetailsLogoViewHolder, row);
547        mDetailsPresenter.onBindViewHolder(vh.mDetailsDescriptionViewHolder, row.getItem());
548        vh.bindActions(row.getActionsAdapter());
549        row.addListener(vh.mRowListener);
550    }
551
552    @Override
553    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
554        ViewHolder vh = (ViewHolder) holder;
555        DetailsOverviewRow dor = (DetailsOverviewRow) vh.getRow();
556        dor.removeListener(vh.mRowListener);
557        mDetailsPresenter.onUnbindViewHolder(vh.mDetailsDescriptionViewHolder);
558        mDetailsOverviewLogoPresenter.onUnbindViewHolder(vh.mDetailsLogoViewHolder);
559        super.onUnbindRowViewHolder(holder);
560    }
561
562    @Override
563    public final boolean isUsingDefaultSelectEffect() {
564        return false;
565    }
566
567    @Override
568    protected void onRowViewAttachedToWindow(RowPresenter.ViewHolder vh) {
569        super.onRowViewAttachedToWindow(vh);
570        ViewHolder viewHolder = (ViewHolder) vh;
571        mDetailsPresenter.onViewAttachedToWindow(viewHolder.mDetailsDescriptionViewHolder);
572        mDetailsOverviewLogoPresenter.onViewAttachedToWindow(viewHolder.mDetailsLogoViewHolder);
573    }
574
575    @Override
576    protected void onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh) {
577        super.onRowViewDetachedFromWindow(vh);
578        ViewHolder viewHolder = (ViewHolder) vh;
579        mDetailsPresenter.onViewDetachedFromWindow(viewHolder.mDetailsDescriptionViewHolder);
580        mDetailsOverviewLogoPresenter.onViewDetachedFromWindow(viewHolder.mDetailsLogoViewHolder);
581    }
582
583    /**
584     * Called by {@link DetailsOverviewLogoPresenter} to notify logo was bound to view.
585     * Application should not directly call this method.
586     * @param viewHolder  The row ViewHolder that has logo bound to view.
587     */
588    public final void notifyOnBindLogo(ViewHolder viewHolder) {
589        onLayoutOverviewFrame(viewHolder, viewHolder.getState(), true);
590        onLayoutLogo(viewHolder, viewHolder.getState(), true);
591        if (mListener != null) {
592            mListener.onBindLogo(viewHolder);
593        }
594    }
595
596    /**
597     * Layout logo position based on current state.  Subclass may override.
598     * The method is called when a logo is bound to view or state changes.
599     * @param viewHolder  The row ViewHolder that contains the logo.
600     * @param oldState    The old state,  can be same as current viewHolder.getState()
601     * @param logoChanged Whether logo was changed.
602     */
603    protected void onLayoutLogo(ViewHolder viewHolder, int oldState, boolean logoChanged) {
604        View v = viewHolder.getLogoViewHolder().view;
605        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
606                v.getLayoutParams();
607        lp.setMarginStart(v.getResources().getDimensionPixelSize(R.dimen.lb_details_v2_left)
608                - lp.width);
609        switch (viewHolder.getState()) {
610        case STATE_FULL:
611        default:
612            lp.topMargin =
613                    v.getResources().getDimensionPixelSize(R.dimen.lb_details_v2_blank_height)
614                    - lp.height / 2;
615            break;
616        case STATE_HALF:
617            lp.topMargin = v.getResources().getDimensionPixelSize(
618                    R.dimen.lb_details_v2_blank_height) + v.getResources()
619                    .getDimensionPixelSize(R.dimen.lb_details_v2_actions_height) + v
620                    .getResources().getDimensionPixelSize(
621                    R.dimen.lb_details_v2_description_margin_top);
622            break;
623        case STATE_SMALL:
624            lp.topMargin = 0;
625            break;
626        }
627        v.setLayoutParams(lp);
628    }
629
630    /**
631     * Layout overview frame based on current state.  Subclass may override.
632     * The method is called when a logo is bound to view or state changes.
633     * @param viewHolder  The row ViewHolder that contains the logo.
634     * @param oldState    The old state,  can be same as current viewHolder.getState()
635     * @param logoChanged Whether logo was changed.
636     */
637    protected void onLayoutOverviewFrame(ViewHolder viewHolder, int oldState, boolean logoChanged) {
638        boolean wasBanner = oldState == STATE_SMALL;
639        boolean isBanner = viewHolder.getState() == STATE_SMALL;
640        if (wasBanner != isBanner || logoChanged) {
641            Resources res = viewHolder.view.getResources();
642            MarginLayoutParams lpFrame =
643                    (MarginLayoutParams) viewHolder.getOverviewView().getLayoutParams();
644            int framePaddingStart;
645            if (isBanner) {
646                lpFrame.topMargin = 0;
647                if (mDetailsOverviewLogoPresenter.isBoundToImage(viewHolder.getLogoViewHolder(),
648                        (DetailsOverviewRow) viewHolder.getRow())) {
649                    View logoView = viewHolder.getLogoViewHolder().view;
650                    ViewGroup.MarginLayoutParams lpLogo =
651                            (ViewGroup.MarginLayoutParams) logoView.getLayoutParams();
652                    framePaddingStart = lpLogo.width;
653                } else {
654                    framePaddingStart = 0;
655                }
656                lpFrame.leftMargin = lpFrame.rightMargin =
657                        res.getDimensionPixelSize(R.dimen.lb_details_v2_left) - framePaddingStart;
658            } else {
659                lpFrame.topMargin = res.getDimensionPixelSize(R.dimen.lb_details_v2_blank_height);
660                framePaddingStart = res.getDimensionPixelSize(R.dimen.lb_details_v2_left);
661                lpFrame.leftMargin = lpFrame.rightMargin = 0;
662            }
663            viewHolder.getOverviewView().setLayoutParams(lpFrame);
664            viewHolder.getOverviewView().setPaddingRelative(framePaddingStart,
665                    viewHolder.getOverviewView().getPaddingTop(),
666                    viewHolder.getOverviewView().getPaddingEnd(),
667                    viewHolder.getOverviewView().getPaddingBottom());
668            ViewGroup.LayoutParams lpActions = viewHolder.getActionsRow().getLayoutParams();
669            lpActions.height =
670                    isBanner ? 0 : res.getDimensionPixelSize(R.dimen.lb_details_v2_actions_height);
671            viewHolder.getActionsRow().setLayoutParams(lpActions);
672        }
673    }
674
675    /**
676     * Switch state of a ViewHolder.
677     * @param viewHolder   The ViewHolder to change state.
678     * @param state        New state, can be {@link #STATE_FULL}, {@link #STATE_HALF}
679     *                     or {@link #STATE_SMALL}.
680     */
681    public final void setState(ViewHolder viewHolder, int state) {
682        if (viewHolder.getState() != state) {
683            int oldState = viewHolder.getState();
684            viewHolder.mState = state;
685            onStateChanged(viewHolder, oldState);
686        }
687    }
688
689    /**
690     * Called when {@link ViewHolder#getState()} changes.  Subclass may override.
691     * The default implementation calls {@link #onLayoutLogo(ViewHolder, int, boolean)} and
692     * {@link #onLayoutOverviewFrame(ViewHolder, int, boolean)}.
693     * @param viewHolder   The ViewHolder which state changed.
694     * @param oldState     The old state.
695     */
696    protected void onStateChanged(ViewHolder viewHolder, int oldState) {
697        onLayoutOverviewFrame(viewHolder, oldState, false);
698        onLayoutLogo(viewHolder, oldState, false);
699    }
700
701    @Override
702    public void setEntranceTransitionState(RowPresenter.ViewHolder holder,
703            boolean afterEntrance) {
704        super.setEntranceTransitionState(holder, afterEntrance);
705        if (mParticipatingEntranceTransition) {
706            holder.view.setVisibility(afterEntrance? View.VISIBLE : View.INVISIBLE);
707        }
708    }
709}
710