ExpandingEntryCardView.java revision 6b6328915d66f0c5947badc3d1973c31f29eef62
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.contacts.quickcontact;
17
18import android.content.Context;
19import android.content.Intent;
20import android.content.res.Resources;
21import android.graphics.ColorFilter;
22import android.graphics.Rect;
23import android.graphics.drawable.Drawable;
24import android.text.TextUtils;
25import android.transition.ChangeBounds;
26import android.transition.ChangeScroll;
27import android.transition.Fade;
28import android.transition.Transition;
29import android.transition.Transition.TransitionListener;
30import android.transition.TransitionManager;
31import android.transition.TransitionSet;
32import android.util.AttributeSet;
33import android.util.Log;
34import android.view.LayoutInflater;
35import android.view.TouchDelegate;
36import android.view.View;
37import android.view.ViewGroup;
38import android.widget.ImageView;
39import android.widget.LinearLayout;
40import android.widget.RelativeLayout;
41import android.widget.TextView;
42
43import com.android.contacts.R;
44
45import java.util.ArrayList;
46import java.util.List;
47
48/**
49 * Display entries in a LinearLayout that can be expanded to show all entries.
50 */
51public class ExpandingEntryCardView extends LinearLayout {
52
53    private static final String TAG = "ExpandingEntryCardView";
54    private static final int DURATION_EXPAND_ANIMATION_FADE_IN = 200;
55    private static final int DELAY_EXPAND_ANIMATION_FADE_IN = 100;
56
57    public static final int DURATION_EXPAND_ANIMATION_CHANGE_BOUNDS = 300;
58    public static final int DURATION_COLLAPSE_ANIMATION_CHANGE_BOUNDS = 300;
59
60    /**
61     * Entry data.
62     */
63    public static final class Entry {
64
65        private final int mViewId;
66        private final Drawable mIcon;
67        private final String mHeader;
68        private final String mSubHeader;
69        private final Drawable mSubHeaderIcon;
70        private final String mText;
71        private final Drawable mTextIcon;
72        private final Intent mIntent;
73        private final Drawable mAlternateIcon;
74        private final Intent mAlternateIntent;
75        private final String mAlternateContentDescription;
76        private final boolean mShouldApplyColor;
77        private final boolean mIsEditable;
78
79        public Entry(int viewId, Drawable icon, String header, String subHeader, String text,
80                Intent intent, Drawable alternateIcon, Intent alternateIntent,
81                String alternateContentDescription, boolean shouldApplyColor,
82                boolean isEditable) {
83            this(viewId, icon, header, subHeader, null, text, null, intent, alternateIcon,
84                    alternateIntent, alternateContentDescription, shouldApplyColor, isEditable);
85        }
86
87        public Entry(int viewId, Drawable mainIcon, String header, String subHeader,
88                Drawable subHeaderIcon, String text, Drawable textIcon, Intent intent,
89                Drawable alternateIcon, Intent alternateIntent, String alternateContentDescription,
90                boolean shouldApplyColor, boolean isEditable) {
91            mViewId = viewId;
92            mIcon = mainIcon;
93            mHeader = header;
94            mSubHeader = subHeader;
95            mSubHeaderIcon = subHeaderIcon;
96            mText = text;
97            mTextIcon = textIcon;
98            mIntent = intent;
99            mAlternateIcon = alternateIcon;
100            mAlternateIntent = alternateIntent;
101            mAlternateContentDescription = alternateContentDescription;
102            mShouldApplyColor = shouldApplyColor;
103            mIsEditable = isEditable;
104        }
105
106        Drawable getIcon() {
107            return mIcon;
108        }
109
110        String getHeader() {
111            return mHeader;
112        }
113
114        String getSubHeader() {
115            return mSubHeader;
116        }
117
118        Drawable getSubHeaderIcon() {
119            return mSubHeaderIcon;
120        }
121
122        public String getText() {
123            return mText;
124        }
125
126        Drawable getTextIcon() {
127            return mTextIcon;
128        }
129
130        Intent getIntent() {
131            return mIntent;
132        }
133
134        Drawable getAlternateIcon() {
135            return mAlternateIcon;
136        }
137
138        Intent getAlternateIntent() {
139            return mAlternateIntent;
140        }
141
142        String getAlternateContentDescription() {
143            return mAlternateContentDescription;
144        }
145
146        boolean shouldApplyColor() {
147            return mShouldApplyColor;
148        }
149
150        boolean isEditable() {
151            return mIsEditable;
152        }
153
154        int getViewId() {
155            return mViewId;
156        }
157    }
158
159    public interface ExpandingEntryCardViewListener {
160        void onCollapse(int heightDelta);
161        void onExpand(int heightDelta);
162    }
163
164    private View mExpandCollapseButton;
165    private TextView mExpandCollapseTextView;
166    private TextView mTitleTextView;
167    private CharSequence mExpandButtonText;
168    private CharSequence mCollapseButtonText;
169    private OnClickListener mOnClickListener;
170    private boolean mIsExpanded = false;
171    private int mCollapsedEntriesCount;
172    private ExpandingEntryCardViewListener mListener;
173    private List<List<Entry>> mEntries;
174    private int mNumEntries = 0;
175    private boolean mAllEntriesInflated = false;
176    private List<List<View>> mEntryViews;
177    private LinearLayout mEntriesViewGroup;
178    private final Drawable mCollapseArrowDrawable;
179    private final Drawable mExpandArrowDrawable;
180    private int mThemeColor;
181    private ColorFilter mThemeColorFilter;
182    private boolean mIsAlwaysExpanded;
183    /** The ViewGroup to run the expand/collapse animation on */
184    private ViewGroup mAnimationViewGroup;
185    private LinearLayout mBadgeContainer;
186    private final List<ImageView> mBadges;
187
188    private final OnClickListener mExpandCollapseButtonListener = new OnClickListener() {
189        @Override
190        public void onClick(View v) {
191            if (mIsExpanded) {
192                collapse();
193            } else {
194                expand();
195            }
196        }
197    };
198
199    public ExpandingEntryCardView(Context context) {
200        this(context, null);
201    }
202
203    public ExpandingEntryCardView(Context context, AttributeSet attrs) {
204        super(context, attrs);
205        LayoutInflater inflater = LayoutInflater.from(context);
206        View expandingEntryCardView = inflater.inflate(R.layout.expanding_entry_card_view, this);
207        mEntriesViewGroup = (LinearLayout)
208                expandingEntryCardView.findViewById(R.id.content_area_linear_layout);
209        mTitleTextView = (TextView) expandingEntryCardView.findViewById(R.id.title);
210        mCollapseArrowDrawable =
211                getResources().getDrawable(R.drawable.expanding_entry_card_collapse_white_24);
212        mExpandArrowDrawable =
213                getResources().getDrawable(R.drawable.expanding_entry_card_expand_white_24);
214
215        mExpandCollapseButton = inflater.inflate(
216                R.layout.quickcontact_expanding_entry_card_button, this, false);
217        mExpandCollapseTextView = (TextView) mExpandCollapseButton.findViewById(R.id.text);
218        mExpandCollapseButton.setOnClickListener(mExpandCollapseButtonListener);
219        mBadgeContainer = (LinearLayout) mExpandCollapseButton.findViewById(R.id.badge_container);
220
221        mBadges = new ArrayList<ImageView>();
222    }
223
224    /**
225     * Sets the Entry list to display.
226     *
227     * @param entries The Entry list to display.
228     */
229    public void initialize(List<List<Entry>> entries, int numInitialVisibleEntries,
230            boolean isExpanded, boolean isAlwaysExpanded,
231            ExpandingEntryCardViewListener listener, ViewGroup animationViewGroup) {
232        LayoutInflater layoutInflater = LayoutInflater.from(getContext());
233        mIsExpanded = isExpanded;
234        mIsAlwaysExpanded = isAlwaysExpanded;
235        // If isAlwaysExpanded is true, mIsExpanded should be true
236        mIsExpanded |= mIsAlwaysExpanded;
237        mEntryViews = new ArrayList<List<View>>(entries.size());
238        mEntries = entries;
239        mNumEntries = 0;
240        mAllEntriesInflated = false;
241        for (List<Entry> entryList : mEntries) {
242            mNumEntries += entryList.size();
243            mEntryViews.add(new ArrayList<View>());
244        }
245        mCollapsedEntriesCount = Math.min(numInitialVisibleEntries, mNumEntries);
246        // Only show the head of each entry list if the initial visible number falls between the
247        // number of lists and the total number of entries
248        if (mCollapsedEntriesCount > mEntries.size()) {
249            mCollapsedEntriesCount = mEntries.size();
250        }
251        mListener = listener;
252        mAnimationViewGroup = animationViewGroup;
253
254        if (mIsExpanded) {
255            updateExpandCollapseButton(getCollapseButtonText());
256            inflateAllEntries(layoutInflater);
257        } else {
258            updateExpandCollapseButton(getExpandButtonText());
259            inflateInitialEntries(layoutInflater);
260        }
261        insertEntriesIntoViewGroup();
262        applyColor();
263    }
264
265    /**
266     * Sets the text for the expand button.
267     *
268     * @param expandButtonText The expand button text.
269     */
270    public void setExpandButtonText(CharSequence expandButtonText) {
271        mExpandButtonText = expandButtonText;
272        if (mExpandCollapseTextView != null && !mIsExpanded) {
273            mExpandCollapseTextView.setText(expandButtonText);
274        }
275    }
276
277    /**
278     * Sets the text for the expand button.
279     *
280     * @param expandButtonText The expand button text.
281     */
282    public void setCollapseButtonText(CharSequence expandButtonText) {
283        mCollapseButtonText = expandButtonText;
284        if (mExpandCollapseTextView != null && mIsExpanded) {
285            mExpandCollapseTextView.setText(mCollapseButtonText);
286        }
287    }
288
289    @Override
290    public void setOnClickListener(OnClickListener listener) {
291        mOnClickListener = listener;
292    }
293
294    private void insertEntriesIntoViewGroup() {
295        mEntriesViewGroup.removeAllViews();
296
297        if (mIsExpanded) {
298            for (List<View> viewList : mEntryViews) {
299                for (View view : viewList) {
300                    addEntry(view);
301                }
302            }
303        } else {
304            for (int i = 0; i < mCollapsedEntriesCount; i++) {
305                addEntry(mEntryViews.get(i).get(0));
306            }
307        }
308
309        removeView(mExpandCollapseButton);
310        if (mCollapsedEntriesCount < mNumEntries
311                && mExpandCollapseButton.getParent() == null && !mIsAlwaysExpanded) {
312            addView(mExpandCollapseButton, -1);
313        }
314    }
315
316    private void addEntry(View entry) {
317        if (mEntriesViewGroup.getChildCount() > 0) {
318            mEntriesViewGroup.addView(createSeparator(entry));
319        }
320        mEntriesViewGroup.addView(entry);
321    }
322
323    private View createSeparator(View entry) {
324        View separator = new View(getContext());
325        separator.setBackgroundColor(getResources().getColor(
326                R.color.expanding_entry_card_item_separator_color));
327        LayoutParams layoutParams = generateDefaultLayoutParams();
328        Resources resources = getResources();
329        layoutParams.height = resources.getDimensionPixelSize(
330                R.dimen.expanding_entry_card_item_separator_height);
331        // The separator is aligned with the text in the entry. This is offset by a default
332        // margin. If there is an icon present, the icon's width and margin are added
333        int marginStart = resources.getDimensionPixelSize(
334                R.dimen.expanding_entry_card_item_padding_start);
335        ImageView entryIcon = (ImageView) entry.findViewById(R.id.icon);
336        if (entryIcon.getDrawable() != null) {
337            int imageWidthAndMargin =
338                    resources.getDimensionPixelSize(
339                            R.dimen.expanding_entry_card_item_icon_width) +
340                    resources.getDimensionPixelSize(
341                            R.dimen.expanding_entry_card_item_image_spacing);
342            marginStart += imageWidthAndMargin;
343        }
344        if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
345            layoutParams.rightMargin = marginStart;
346        } else {
347            layoutParams.leftMargin = marginStart;
348        }
349        separator.setLayoutParams(layoutParams);
350        return separator;
351    }
352
353    private CharSequence getExpandButtonText() {
354        if (!TextUtils.isEmpty(mExpandButtonText)) {
355            return mExpandButtonText;
356        } else {
357            // Default to "See more".
358            return getResources().getText(R.string.expanding_entry_card_view_see_more);
359        }
360    }
361
362    private CharSequence getCollapseButtonText() {
363        if (!TextUtils.isEmpty(mCollapseButtonText)) {
364            return mCollapseButtonText;
365        } else {
366            // Default to "See less".
367            return getResources().getText(R.string.expanding_entry_card_view_see_less);
368        }
369    }
370
371    /**
372     * Inflates the initial entries to be shown.
373     */
374    private void inflateInitialEntries(LayoutInflater layoutInflater) {
375        // If the number of collapsed entries equals total entries, inflate all
376        if (mCollapsedEntriesCount == mNumEntries) {
377            inflateAllEntries(layoutInflater);
378        } else {
379            // Otherwise inflate the top entry from each list
380            for (int i = 0; i < mCollapsedEntriesCount; i++) {
381                mEntryViews.get(i).add(createEntryView(layoutInflater, mEntries.get(i).get(0)));
382            }
383        }
384    }
385
386    /**
387     * Inflates all entries.
388     */
389    private void inflateAllEntries(LayoutInflater layoutInflater) {
390        if (mAllEntriesInflated) {
391            return;
392        }
393        for (int i = 0; i < mEntries.size(); i++) {
394            List<Entry> entryList = mEntries.get(i);
395            List<View> viewList = mEntryViews.get(i);
396            for (int j = viewList.size(); j < entryList.size(); j++) {
397                viewList.add(createEntryView(layoutInflater, entryList.get(j)));
398            }
399        }
400        mAllEntriesInflated = true;
401    }
402
403    public void setColorAndFilter(int color, ColorFilter colorFilter) {
404        mThemeColor = color;
405        mThemeColorFilter = colorFilter;
406        applyColor();
407    }
408
409    public void setEntryHeaderColor(int color) {
410        if (mEntries != null) {
411            for (List<View> entryList : mEntryViews) {
412                for (View entryView : entryList) {
413                    TextView header = (TextView) entryView.findViewById(R.id.header);
414                    if (header != null) {
415                        header.setTextColor(color);
416                    }
417                }
418            }
419        }
420    }
421
422    /**
423     * The ColorFilter is passed in along with the color so that a new one only needs to be created
424     * once for the entire activity.
425     * 1. Title
426     * 2. Entry icons
427     * 3. Expand/Collapse Text
428     * 4. Expand/Collapse Button
429     */
430    public void applyColor() {
431        if (mThemeColor != 0 && mThemeColorFilter != null) {
432            // Title
433            if (mTitleTextView != null) {
434                mTitleTextView.setTextColor(mThemeColor);
435            }
436
437            // Entry icons
438            if (mEntries != null) {
439                for (List<Entry> entryList : mEntries) {
440                    for (Entry entry : entryList) {
441                        if (entry.shouldApplyColor()) {
442                            Drawable icon = entry.getIcon();
443                            if (icon != null) {
444                                icon.setColorFilter(mThemeColorFilter);
445                            }
446                        }
447                        Drawable alternateIcon = entry.getAlternateIcon();
448                        if (alternateIcon != null) {
449                            alternateIcon.setColorFilter(mThemeColorFilter);
450                        }
451                    }
452                }
453            }
454
455            // Expand/Collapse
456            mExpandCollapseTextView.setTextColor(mThemeColor);
457            mCollapseArrowDrawable.setColorFilter(mThemeColorFilter);
458            mExpandArrowDrawable.setColorFilter(mThemeColorFilter);
459        }
460    }
461
462    private View createEntryView(LayoutInflater layoutInflater, Entry entry) {
463        final View view = layoutInflater.inflate(
464                R.layout.expanding_entry_card_item, this, false);
465
466        view.setId(entry.getViewId());
467
468        final ImageView icon = (ImageView) view.findViewById(R.id.icon);
469        if (entry.getIcon() != null) {
470            icon.setImageDrawable(entry.getIcon());
471        } else {
472            icon.setVisibility(View.GONE);
473        }
474
475        final TextView header = (TextView) view.findViewById(R.id.header);
476        if (!TextUtils.isEmpty(entry.getHeader())) {
477            header.setText(entry.getHeader());
478        } else {
479            header.setVisibility(View.GONE);
480        }
481
482        final TextView subHeader = (TextView) view.findViewById(R.id.sub_header);
483        if (!TextUtils.isEmpty(entry.getSubHeader())) {
484            subHeader.setText(entry.getSubHeader());
485        } else {
486            subHeader.setVisibility(View.GONE);
487        }
488
489        final ImageView subHeaderIcon = (ImageView) view.findViewById(R.id.icon_sub_header);
490        if (entry.getSubHeaderIcon() != null) {
491            subHeaderIcon.setImageDrawable(entry.getSubHeaderIcon());
492        } else {
493            subHeaderIcon.setVisibility(View.GONE);
494        }
495
496        final TextView text = (TextView) view.findViewById(R.id.text);
497        if (!TextUtils.isEmpty(entry.getText())) {
498            text.setText(entry.getText());
499        } else {
500            text.setVisibility(View.GONE);
501        }
502
503        final ImageView textIcon = (ImageView) view.findViewById(R.id.icon_text);
504        if (entry.getTextIcon() != null) {
505            textIcon.setImageDrawable(entry.getTextIcon());
506        } else {
507            textIcon.setVisibility(View.GONE);
508        }
509
510        if (entry.getIntent() != null) {
511            view.setOnClickListener(mOnClickListener);
512            view.setTag(entry.getIntent());
513        }
514
515        // If only the header is visible, add a top margin to match icon's top margin
516        if (header.getVisibility() == View.VISIBLE && subHeader.getVisibility() == View.GONE &&
517                text.getVisibility() == View.GONE) {
518            RelativeLayout.LayoutParams headerLayoutParams =
519                    (RelativeLayout.LayoutParams) header.getLayoutParams();
520            headerLayoutParams.topMargin = (int) (getResources().getDimension(
521                    R.dimen.expanding_entry_card_item_icon_margin_top));
522            header.setLayoutParams(headerLayoutParams);
523        }
524
525        final ImageView alternateIcon = (ImageView) view.findViewById(R.id.icon_alternate);
526        if (entry.getAlternateIcon() != null && entry.getAlternateIntent() != null) {
527            alternateIcon.setImageDrawable(entry.getAlternateIcon());
528            alternateIcon.setOnClickListener(mOnClickListener);
529            alternateIcon.setTag(entry.getAlternateIntent());
530            alternateIcon.setId(entry.getViewId());
531            alternateIcon.setVisibility(View.VISIBLE);
532            alternateIcon.setContentDescription(entry.getAlternateContentDescription());
533
534            // Expand the clickable area for alternate icon to be top to bottom and to end edge
535            // of the entry view
536            view.post(new Runnable() {
537                @Override
538                public void run() {
539                    final Rect alternateIconRect = new Rect();
540                    alternateIcon.getHitRect(alternateIconRect);
541
542                    alternateIconRect.bottom = view.getHeight();
543                    alternateIconRect.top = 0;
544                    if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
545                        alternateIconRect.left = 0;
546                    } else {
547                        alternateIconRect.right = view.getWidth();
548                    }
549                    final TouchDelegate touchDelegate =
550                            new TouchDelegate(alternateIconRect, alternateIcon);
551                    view.setTouchDelegate(touchDelegate);
552                }
553            });
554        }
555
556        return view;
557    }
558
559    private void updateExpandCollapseButton(CharSequence buttonText) {
560        final Drawable arrow = mIsExpanded ? mCollapseArrowDrawable : mExpandArrowDrawable;
561        updateBadges();
562        if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
563            mExpandCollapseTextView.setCompoundDrawablesWithIntrinsicBounds(null, null, arrow,
564                    null);
565        } else {
566            mExpandCollapseTextView.setCompoundDrawablesWithIntrinsicBounds(arrow, null, null,
567                    null);
568        }
569        mExpandCollapseTextView.setText(buttonText);
570    }
571
572    private void updateBadges() {
573        if (mIsExpanded) {
574            mBadgeContainer.removeAllViews();
575        } else {
576            // Inflate badges if not yet created
577            if (mBadges.size() < mEntries.size() - mCollapsedEntriesCount) {
578                for (int i = mCollapsedEntriesCount; i < mEntries.size(); i++) {
579                    Drawable badgeDrawable = mEntries.get(i).get(0).getIcon();
580                    if (badgeDrawable != null) {
581                        ImageView badgeView = new ImageView(getContext());
582                        LinearLayout.LayoutParams badgeViewParams = new LinearLayout.LayoutParams(
583                                (int) getResources().getDimension(
584                                        R.dimen.expanding_entry_card_item_icon_width),
585                                (int) getResources().getDimension(
586                                        R.dimen.expanding_entry_card_item_icon_height));
587                        badgeViewParams.setMarginEnd((int) getResources().getDimension(
588                                R.dimen.expanding_entry_card_badge_separator_margin));
589                        badgeView.setLayoutParams(badgeViewParams);
590                        badgeView.setImageDrawable(badgeDrawable);
591                        mBadges.add(badgeView);
592                    }
593                }
594            }
595            mBadgeContainer.removeAllViews();
596            for (ImageView badge : mBadges) {
597                mBadgeContainer.addView(badge);
598            }
599        }
600    }
601
602    private void expand() {
603        ChangeBounds boundsTransition = new ChangeBounds();
604        boundsTransition.setDuration(DURATION_EXPAND_ANIMATION_CHANGE_BOUNDS);
605
606        Fade fadeIn = new Fade(Fade.IN);
607        fadeIn.setDuration(DURATION_EXPAND_ANIMATION_FADE_IN);
608        fadeIn.setStartDelay(DELAY_EXPAND_ANIMATION_FADE_IN);
609
610        TransitionSet transitionSet = new TransitionSet();
611        transitionSet.addTransition(boundsTransition);
612        transitionSet.addTransition(fadeIn);
613
614        final ViewGroup transitionViewContainer = mAnimationViewGroup == null ?
615                this : mAnimationViewGroup;
616
617        transitionSet.addListener(new TransitionListener() {
618            @Override
619            public void onTransitionStart(Transition transition) {
620                // The listener is used to turn off suppressing, the proper delta is not necessary
621                mListener.onExpand(0);
622            }
623
624            @Override
625            public void onTransitionEnd(Transition transition) {
626            }
627
628            @Override
629            public void onTransitionCancel(Transition transition) {
630            }
631
632            @Override
633            public void onTransitionPause(Transition transition) {
634            }
635
636            @Override
637            public void onTransitionResume(Transition transition) {
638            }
639        });
640
641        TransitionManager.beginDelayedTransition(transitionViewContainer, transitionSet);
642
643        mIsExpanded = true;
644        // In order to insert new entries, we may need to inflate them for the first time
645        inflateAllEntries(LayoutInflater.from(getContext()));
646        insertEntriesIntoViewGroup();
647        updateExpandCollapseButton(getCollapseButtonText());
648    }
649
650    private void collapse() {
651        final int startingHeight = mEntriesViewGroup.getMeasuredHeight();
652        mIsExpanded = false;
653        updateExpandCollapseButton(getExpandButtonText());
654
655        final ChangeBounds boundsTransition = new ChangeBounds();
656        boundsTransition.setDuration(DURATION_COLLAPSE_ANIMATION_CHANGE_BOUNDS);
657
658        final ChangeScroll scrollTransition = new ChangeScroll();
659        scrollTransition.setDuration(DURATION_COLLAPSE_ANIMATION_CHANGE_BOUNDS);
660
661        TransitionSet transitionSet = new TransitionSet();
662        transitionSet.addTransition(boundsTransition);
663        transitionSet.addTransition(scrollTransition);
664
665        final ViewGroup transitionViewContainer = mAnimationViewGroup == null ?
666                this : mAnimationViewGroup;
667
668        boundsTransition.addListener(new TransitionListener() {
669            @Override
670            public void onTransitionStart(Transition transition) {
671                /*
672                 * onTransitionStart is called after the view hierarchy has been changed but before
673                 * the animation begins.
674                 */
675                int finishingHeight = mEntriesViewGroup.getMeasuredHeight();
676                mListener.onCollapse(startingHeight - finishingHeight);
677            }
678
679            @Override
680            public void onTransitionEnd(Transition transition) {
681            }
682
683            @Override
684            public void onTransitionCancel(Transition transition) {
685            }
686
687            @Override
688            public void onTransitionPause(Transition transition) {
689            }
690
691            @Override
692            public void onTransitionResume(Transition transition) {
693            }
694        });
695
696        TransitionManager.beginDelayedTransition(transitionViewContainer, transitionSet);
697
698        insertEntriesIntoViewGroup();
699    }
700
701    /**
702     * Returns whether the view is currently in its expanded state.
703     */
704    public boolean isExpanded() {
705        return mIsExpanded;
706    }
707
708    /**
709     * Sets the title text of this ExpandingEntryCardView.
710     * @param title The title to set. A null title will result in the title being removed.
711     */
712    public void setTitle(String title) {
713        if (mTitleTextView == null) {
714            Log.e(TAG, "mTitleTextView is null");
715        }
716        if (title == null) {
717            mTitleTextView.setVisibility(View.GONE);
718            findViewById(R.id.title_separator).setVisibility(View.GONE);
719        }
720        mTitleTextView.setText(title);
721        mTitleTextView.setVisibility(View.VISIBLE);
722        findViewById(R.id.title_separator).setVisibility(View.VISIBLE);
723    }
724
725    public boolean shouldShow() {
726        return mEntries != null && mEntries.size() > 0;
727    }
728}
729