Home | History | Annotate | Download | only in quickcontact
      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  */
     16 package com.android.contacts.quickcontact;
     17 
     18 import android.animation.Animator;
     19 import android.animation.Animator.AnimatorListener;
     20 import android.animation.AnimatorSet;
     21 import android.animation.ObjectAnimator;
     22 import android.app.Activity;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.res.Resources;
     26 import android.graphics.ColorFilter;
     27 import android.graphics.Rect;
     28 import android.graphics.drawable.Drawable;
     29 import android.os.Bundle;
     30 import android.support.v7.widget.CardView;
     31 import android.text.Spannable;
     32 import android.text.TextUtils;
     33 import android.transition.ChangeBounds;
     34 import android.transition.Fade;
     35 import android.transition.Transition;
     36 import android.transition.Transition.TransitionListener;
     37 import android.transition.TransitionManager;
     38 import android.transition.TransitionSet;
     39 import android.util.AttributeSet;
     40 import android.util.Log;
     41 import android.util.Property;
     42 import android.view.ContextMenu.ContextMenuInfo;
     43 import android.view.LayoutInflater;
     44 import android.view.MotionEvent;
     45 import android.view.View;
     46 import android.view.ViewConfiguration;
     47 import android.view.ViewGroup;
     48 import android.widget.ImageView;
     49 import android.widget.LinearLayout;
     50 import android.widget.LinearLayout.LayoutParams;
     51 import android.widget.RelativeLayout;
     52 import android.widget.TextView;
     53 
     54 import com.android.contacts.R;
     55 import com.android.contacts.common.dialog.CallSubjectDialog;
     56 
     57 import java.util.ArrayList;
     58 import java.util.List;
     59 
     60 /**
     61  * Display entries in a LinearLayout that can be expanded to show all entries.
     62  */
     63 public class ExpandingEntryCardView extends CardView {
     64 
     65     private static final String TAG = "ExpandingEntryCardView";
     66     private static final int DURATION_EXPAND_ANIMATION_FADE_IN = 200;
     67     private static final int DURATION_COLLAPSE_ANIMATION_FADE_OUT = 75;
     68     private static final int DELAY_EXPAND_ANIMATION_FADE_IN = 100;
     69 
     70     public static final int DURATION_EXPAND_ANIMATION_CHANGE_BOUNDS = 300;
     71     public static final int DURATION_COLLAPSE_ANIMATION_CHANGE_BOUNDS = 300;
     72 
     73     private static final Property<View, Integer> VIEW_LAYOUT_HEIGHT_PROPERTY =
     74             new Property<View, Integer>(Integer.class, "height") {
     75                 @Override
     76                 public void set(View view, Integer height) {
     77                     LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)
     78                             view.getLayoutParams();
     79                     params.height = height;
     80                     view.setLayoutParams(params);
     81                 }
     82 
     83                 @Override
     84                 public Integer get(View view) {
     85                     return view.getLayoutParams().height;
     86                 }
     87             };
     88 
     89     /**
     90      * Entry data.
     91      */
     92     public static final class Entry {
     93         // No action when clicking a button is specified.
     94         public static final int ACTION_NONE = 1;
     95         // Button action is an intent.
     96         public static final int ACTION_INTENT = 2;
     97         // Button action will open the call with subject dialog.
     98         public static final int ACTION_CALL_WITH_SUBJECT = 3;
     99 
    100         private final int mId;
    101         private final Drawable mIcon;
    102         private final String mHeader;
    103         private final String mSubHeader;
    104         private final Drawable mSubHeaderIcon;
    105         private final String mText;
    106         private final Drawable mTextIcon;
    107         private Spannable mPrimaryContentDescription;
    108         private final Intent mIntent;
    109         private final Drawable mAlternateIcon;
    110         private final Intent mAlternateIntent;
    111         private Spannable mAlternateContentDescription;
    112         private final boolean mShouldApplyColor;
    113         private final boolean mIsEditable;
    114         private final EntryContextMenuInfo mEntryContextMenuInfo;
    115         private final Drawable mThirdIcon;
    116         private final Intent mThirdIntent;
    117         private final String mThirdContentDescription;
    118         private final int mIconResourceId;
    119         private final int mThirdAction;
    120         private final Bundle mThirdExtras;
    121 
    122         public Entry(int id, Drawable mainIcon, String header, String subHeader,
    123                 Drawable subHeaderIcon, String text, Drawable textIcon,
    124                 Spannable primaryContentDescription, Intent intent,
    125                 Drawable alternateIcon, Intent alternateIntent,
    126                 Spannable alternateContentDescription, boolean shouldApplyColor, boolean isEditable,
    127                 EntryContextMenuInfo entryContextMenuInfo, Drawable thirdIcon, Intent thirdIntent,
    128                 String thirdContentDescription, int thirdAction, Bundle thirdExtras,
    129                 int iconResourceId) {
    130             mId = id;
    131             mIcon = mainIcon;
    132             mHeader = header;
    133             mSubHeader = subHeader;
    134             mSubHeaderIcon = subHeaderIcon;
    135             mText = text;
    136             mTextIcon = textIcon;
    137             mPrimaryContentDescription = primaryContentDescription;
    138             mIntent = intent;
    139             mAlternateIcon = alternateIcon;
    140             mAlternateIntent = alternateIntent;
    141             mAlternateContentDescription = alternateContentDescription;
    142             mShouldApplyColor = shouldApplyColor;
    143             mIsEditable = isEditable;
    144             mEntryContextMenuInfo = entryContextMenuInfo;
    145             mThirdIcon = thirdIcon;
    146             mThirdIntent = thirdIntent;
    147             mThirdContentDescription = thirdContentDescription;
    148             mThirdAction = thirdAction;
    149             mThirdExtras = thirdExtras;
    150             mIconResourceId = iconResourceId;
    151         }
    152 
    153         Drawable getIcon() {
    154             return mIcon;
    155         }
    156 
    157         String getHeader() {
    158             return mHeader;
    159         }
    160 
    161         String getSubHeader() {
    162             return mSubHeader;
    163         }
    164 
    165         Drawable getSubHeaderIcon() {
    166             return mSubHeaderIcon;
    167         }
    168 
    169         public String getText() {
    170             return mText;
    171         }
    172 
    173         Drawable getTextIcon() {
    174             return mTextIcon;
    175         }
    176 
    177         Spannable getPrimaryContentDescription() {
    178             return mPrimaryContentDescription;
    179         }
    180 
    181         Intent getIntent() {
    182             return mIntent;
    183         }
    184 
    185         Drawable getAlternateIcon() {
    186             return mAlternateIcon;
    187         }
    188 
    189         Intent getAlternateIntent() {
    190             return mAlternateIntent;
    191         }
    192 
    193         Spannable getAlternateContentDescription() {
    194             return mAlternateContentDescription;
    195         }
    196 
    197         boolean shouldApplyColor() {
    198             return mShouldApplyColor;
    199         }
    200 
    201         boolean isEditable() {
    202             return mIsEditable;
    203         }
    204 
    205         int getId() {
    206             return mId;
    207         }
    208 
    209         EntryContextMenuInfo getEntryContextMenuInfo() {
    210             return mEntryContextMenuInfo;
    211         }
    212 
    213         Drawable getThirdIcon() {
    214             return mThirdIcon;
    215         }
    216 
    217         Intent getThirdIntent() {
    218             return mThirdIntent;
    219         }
    220 
    221         String getThirdContentDescription() {
    222             return mThirdContentDescription;
    223         }
    224 
    225         int getIconResourceId() {
    226             return mIconResourceId;
    227         }
    228 
    229         public int getThirdAction() {
    230             return mThirdAction;
    231         }
    232 
    233         public Bundle getThirdExtras() {
    234             return mThirdExtras;
    235         }
    236     }
    237 
    238     public interface ExpandingEntryCardViewListener {
    239         void onCollapse(int heightDelta);
    240         void onExpand();
    241         void onExpandDone();
    242     }
    243 
    244     private View mExpandCollapseButton;
    245     private TextView mExpandCollapseTextView;
    246     private TextView mTitleTextView;
    247     private CharSequence mExpandButtonText;
    248     private CharSequence mCollapseButtonText;
    249     private OnClickListener mOnClickListener;
    250     private OnCreateContextMenuListener mOnCreateContextMenuListener;
    251     private boolean mIsExpanded = false;
    252     /**
    253      * The max number of entries to show in a collapsed card. If there are less entries passed in,
    254      * then they are all shown.
    255      */
    256     private int mCollapsedEntriesCount;
    257     private ExpandingEntryCardViewListener mListener;
    258     private List<List<Entry>> mEntries;
    259     private int mNumEntries = 0;
    260     private boolean mAllEntriesInflated = false;
    261     private List<List<View>> mEntryViews;
    262     private LinearLayout mEntriesViewGroup;
    263     private final ImageView mExpandCollapseArrow;
    264     private int mThemeColor;
    265     private ColorFilter mThemeColorFilter;
    266     /**
    267      * Whether to prioritize the first entry type. If prioritized, we should show at least two
    268      * of this entry type.
    269      */
    270     private boolean mShowFirstEntryTypeTwice;
    271     private boolean mIsAlwaysExpanded;
    272     /** The ViewGroup to run the expand/collapse animation on */
    273     private ViewGroup mAnimationViewGroup;
    274     private LinearLayout mBadgeContainer;
    275     private final List<ImageView> mBadges;
    276     private final List<Integer> mBadgeIds;
    277     private final int mDividerLineHeightPixels;
    278     /**
    279      * List to hold the separators. This saves us from reconstructing every expand/collapse and
    280      * provides a smoother animation.
    281      */
    282     private List<View> mSeparators;
    283     private LinearLayout mContainer;
    284 
    285     private final OnClickListener mExpandCollapseButtonListener = new OnClickListener() {
    286         @Override
    287         public void onClick(View v) {
    288             if (mIsExpanded) {
    289                 collapse();
    290             } else {
    291                 expand();
    292             }
    293         }
    294     };
    295 
    296     public ExpandingEntryCardView(Context context) {
    297         this(context, null);
    298     }
    299 
    300     public ExpandingEntryCardView(Context context, AttributeSet attrs) {
    301         super(context, attrs);
    302         LayoutInflater inflater = LayoutInflater.from(context);
    303         View expandingEntryCardView = inflater.inflate(R.layout.expanding_entry_card_view, this);
    304         mEntriesViewGroup = (LinearLayout)
    305                 expandingEntryCardView.findViewById(R.id.content_area_linear_layout);
    306         mTitleTextView = (TextView) expandingEntryCardView.findViewById(R.id.title);
    307         mContainer = (LinearLayout) expandingEntryCardView.findViewById(R.id.container);
    308 
    309         mExpandCollapseButton = inflater.inflate(
    310                 R.layout.quickcontact_expanding_entry_card_button, this, false);
    311         mExpandCollapseTextView = (TextView) mExpandCollapseButton.findViewById(R.id.text);
    312         mExpandCollapseArrow = (ImageView) mExpandCollapseButton.findViewById(R.id.arrow);
    313         mExpandCollapseButton.setOnClickListener(mExpandCollapseButtonListener);
    314         mBadgeContainer = (LinearLayout) mExpandCollapseButton.findViewById(R.id.badge_container);
    315         mDividerLineHeightPixels = getResources()
    316                 .getDimensionPixelSize(R.dimen.divider_line_height);
    317 
    318         mBadges = new ArrayList<ImageView>();
    319         mBadgeIds = new ArrayList<Integer>();
    320     }
    321 
    322     public void initialize(List<List<Entry>> entries, int numInitialVisibleEntries,
    323             boolean isExpanded, boolean isAlwaysExpanded, ExpandingEntryCardViewListener listener,
    324             ViewGroup animationViewGroup) {
    325         initialize(entries, numInitialVisibleEntries, isExpanded, isAlwaysExpanded,
    326                 listener, animationViewGroup, /* showFirstEntryTypeTwice = */ false);
    327     }
    328 
    329     /**
    330      * Sets the Entry list to display.
    331      *
    332      * @param entries The Entry list to display.
    333      */
    334     public void initialize(List<List<Entry>> entries, int numInitialVisibleEntries,
    335             boolean isExpanded, boolean isAlwaysExpanded,
    336             ExpandingEntryCardViewListener listener, ViewGroup animationViewGroup,
    337             boolean showFirstEntryTypeTwice) {
    338         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
    339         mIsExpanded = isExpanded;
    340         mIsAlwaysExpanded = isAlwaysExpanded;
    341         // If isAlwaysExpanded is true, mIsExpanded should be true
    342         mIsExpanded |= mIsAlwaysExpanded;
    343         mEntryViews = new ArrayList<List<View>>(entries.size());
    344         mEntries = entries;
    345         mNumEntries = 0;
    346         mAllEntriesInflated = false;
    347         mShowFirstEntryTypeTwice = showFirstEntryTypeTwice;
    348         for (List<Entry> entryList : mEntries) {
    349             mNumEntries += entryList.size();
    350             mEntryViews.add(new ArrayList<View>());
    351         }
    352         mCollapsedEntriesCount = Math.min(numInitialVisibleEntries, mNumEntries);
    353         // We need a separator between each list, but not after the last one
    354         if (entries.size() > 1) {
    355             mSeparators = new ArrayList<>(entries.size() - 1);
    356         }
    357         mListener = listener;
    358         mAnimationViewGroup = animationViewGroup;
    359 
    360         if (mIsExpanded) {
    361             updateExpandCollapseButton(getCollapseButtonText(), /* duration = */ 0);
    362             inflateAllEntries(layoutInflater);
    363         } else {
    364             updateExpandCollapseButton(getExpandButtonText(), /* duration = */ 0);
    365             inflateInitialEntries(layoutInflater);
    366         }
    367         insertEntriesIntoViewGroup();
    368         applyColor();
    369     }
    370 
    371     /**
    372      * Sets the text for the expand button.
    373      *
    374      * @param expandButtonText The expand button text.
    375      */
    376     public void setExpandButtonText(CharSequence expandButtonText) {
    377         mExpandButtonText = expandButtonText;
    378         if (mExpandCollapseTextView != null && !mIsExpanded) {
    379             mExpandCollapseTextView.setText(expandButtonText);
    380         }
    381     }
    382 
    383     /**
    384      * Sets the text for the expand button.
    385      *
    386      * @param expandButtonText The expand button text.
    387      */
    388     public void setCollapseButtonText(CharSequence expandButtonText) {
    389         mCollapseButtonText = expandButtonText;
    390         if (mExpandCollapseTextView != null && mIsExpanded) {
    391             mExpandCollapseTextView.setText(mCollapseButtonText);
    392         }
    393     }
    394 
    395     @Override
    396     public void setOnClickListener(OnClickListener listener) {
    397         mOnClickListener = listener;
    398     }
    399 
    400     @Override
    401     public void setOnCreateContextMenuListener (OnCreateContextMenuListener listener) {
    402         mOnCreateContextMenuListener = listener;
    403     }
    404 
    405     private List<View> calculateEntriesToRemoveDuringCollapse() {
    406         final List<View> viewsToRemove = getViewsToDisplay(true);
    407         final List<View> viewsCollapsed = getViewsToDisplay(false);
    408         viewsToRemove.removeAll(viewsCollapsed);
    409         return viewsToRemove;
    410     }
    411 
    412     private void insertEntriesIntoViewGroup() {
    413         mEntriesViewGroup.removeAllViews();
    414 
    415         for (View view : getViewsToDisplay(mIsExpanded)) {
    416             mEntriesViewGroup.addView(view);
    417         }
    418 
    419         removeView(mExpandCollapseButton);
    420         if (mCollapsedEntriesCount < mNumEntries
    421                 && mExpandCollapseButton.getParent() == null && !mIsAlwaysExpanded) {
    422             mContainer.addView(mExpandCollapseButton, -1);
    423         }
    424     }
    425 
    426     /**
    427      * Returns the list of views that should be displayed. This changes depending on whether
    428      * the card is expanded or collapsed.
    429      */
    430     private List<View> getViewsToDisplay(boolean isExpanded) {
    431         final List<View> viewsToDisplay = new ArrayList<View>();
    432         if (isExpanded) {
    433             for (int i = 0; i < mEntryViews.size(); i++) {
    434                 List<View> viewList = mEntryViews.get(i);
    435                 if (i > 0) {
    436                     View separator;
    437                     if (mSeparators.size() <= i - 1) {
    438                         separator = generateSeparator(viewList.get(0));
    439                         mSeparators.add(separator);
    440                     } else {
    441                         separator = mSeparators.get(i - 1);
    442                     }
    443                     viewsToDisplay.add(separator);
    444                 }
    445                 for (View view : viewList) {
    446                     viewsToDisplay.add(view);
    447                 }
    448             }
    449         } else {
    450             // We want to insert mCollapsedEntriesCount entries into the group. extraEntries is the
    451             // number of entries that need to be added that are not the head element of a list
    452             // to reach mCollapsedEntriesCount.
    453             int numInViewGroup = 0;
    454             int extraEntries = mCollapsedEntriesCount - mEntryViews.size();
    455             for (int i = 0; i < mEntryViews.size() && numInViewGroup < mCollapsedEntriesCount;
    456                     i++) {
    457                 List<View> entryViewList = mEntryViews.get(i);
    458                 if (i > 0) {
    459                     View separator;
    460                     if (mSeparators.size() <= i - 1) {
    461                         separator = generateSeparator(entryViewList.get(0));
    462                         mSeparators.add(separator);
    463                     } else {
    464                         separator = mSeparators.get(i - 1);
    465                     }
    466                     viewsToDisplay.add(separator);
    467                 }
    468                 viewsToDisplay.add(entryViewList.get(0));
    469                 numInViewGroup++;
    470 
    471                 int indexInEntryViewList = 1;
    472                 if (mShowFirstEntryTypeTwice && i == 0 && entryViewList.size() > 1) {
    473                     viewsToDisplay.add(entryViewList.get(1));
    474                     numInViewGroup++;
    475                     extraEntries--;
    476                     indexInEntryViewList++;
    477                 }
    478 
    479                 // Insert entries in this list to hit mCollapsedEntriesCount.
    480                 for (int j = indexInEntryViewList;
    481                         j < entryViewList.size() && numInViewGroup < mCollapsedEntriesCount &&
    482                         extraEntries > 0;
    483                         j++) {
    484                     viewsToDisplay.add(entryViewList.get(j));
    485                     numInViewGroup++;
    486                     extraEntries--;
    487                 }
    488             }
    489         }
    490 
    491         formatEntryIfFirst(viewsToDisplay);
    492         return viewsToDisplay;
    493     }
    494 
    495     private void formatEntryIfFirst(List<View> entriesViewGroup) {
    496         // If no title and the first entry in the group, add extra padding
    497         if (TextUtils.isEmpty(mTitleTextView.getText()) &&
    498                 entriesViewGroup.size() > 0) {
    499             final View entry = entriesViewGroup.get(0);
    500             entry.setPadding(entry.getPaddingLeft(),
    501                     getResources().getDimensionPixelSize(
    502                             R.dimen.expanding_entry_card_item_padding_top) +
    503                     getResources().getDimensionPixelSize(
    504                             R.dimen.expanding_entry_card_null_title_top_extra_padding),
    505                     entry.getPaddingRight(),
    506                     entry.getPaddingBottom());
    507         }
    508     }
    509 
    510     private View generateSeparator(View entry) {
    511         View separator = new View(getContext());
    512         Resources res = getResources();
    513 
    514         separator.setBackgroundColor(res.getColor(
    515                 R.color.divider_line_color_light));
    516         LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
    517                 ViewGroup.LayoutParams.MATCH_PARENT, mDividerLineHeightPixels);
    518         // The separator is aligned with the text in the entry. This is offset by a default
    519         // margin. If there is an icon present, the icon's width and margin are added
    520         int marginStart = res.getDimensionPixelSize(
    521                 R.dimen.expanding_entry_card_item_padding_start);
    522         ImageView entryIcon = (ImageView) entry.findViewById(R.id.icon);
    523         if (entryIcon.getVisibility() == View.VISIBLE) {
    524             int imageWidthAndMargin =
    525                     res.getDimensionPixelSize(R.dimen.expanding_entry_card_item_icon_width) +
    526                     res.getDimensionPixelSize(R.dimen.expanding_entry_card_item_image_spacing);
    527             marginStart += imageWidthAndMargin;
    528         }
    529         layoutParams.setMarginStart(marginStart);
    530         separator.setLayoutParams(layoutParams);
    531         return separator;
    532     }
    533 
    534     private CharSequence getExpandButtonText() {
    535         if (!TextUtils.isEmpty(mExpandButtonText)) {
    536             return mExpandButtonText;
    537         } else {
    538             // Default to "See more".
    539             return getResources().getText(R.string.expanding_entry_card_view_see_more);
    540         }
    541     }
    542 
    543     private CharSequence getCollapseButtonText() {
    544         if (!TextUtils.isEmpty(mCollapseButtonText)) {
    545             return mCollapseButtonText;
    546         } else {
    547             // Default to "See less".
    548             return getResources().getText(R.string.expanding_entry_card_view_see_less);
    549         }
    550     }
    551 
    552     /**
    553      * Inflates the initial entries to be shown.
    554      */
    555     private void inflateInitialEntries(LayoutInflater layoutInflater) {
    556         // If the number of collapsed entries equals total entries, inflate all
    557         if (mCollapsedEntriesCount == mNumEntries) {
    558             inflateAllEntries(layoutInflater);
    559         } else {
    560             // Otherwise inflate the top entry from each list
    561             // extraEntries is used to add extra entries until mCollapsedEntriesCount is reached.
    562             int numInflated = 0;
    563             int extraEntries = mCollapsedEntriesCount - mEntries.size();
    564             for (int i = 0; i < mEntries.size() && numInflated < mCollapsedEntriesCount; i++) {
    565                 List<Entry> entryList = mEntries.get(i);
    566                 List<View> entryViewList = mEntryViews.get(i);
    567 
    568                 entryViewList.add(createEntryView(layoutInflater, entryList.get(0),
    569                         /* showIcon = */ View.VISIBLE));
    570                 numInflated++;
    571 
    572                 int indexInEntryViewList = 1;
    573                 if (mShowFirstEntryTypeTwice && i == 0 && entryList.size() > 1) {
    574                     entryViewList.add(createEntryView(layoutInflater, entryList.get(1),
    575                         /* showIcon = */ View.INVISIBLE));
    576                     numInflated++;
    577                     extraEntries--;
    578                     indexInEntryViewList++;
    579                 }
    580 
    581                 // Inflate entries in this list to hit mCollapsedEntriesCount.
    582                 for (int j = indexInEntryViewList; j < entryList.size()
    583                         && numInflated < mCollapsedEntriesCount
    584                         && extraEntries > 0; j++) {
    585                     entryViewList.add(createEntryView(layoutInflater, entryList.get(j),
    586                             /* showIcon = */ View.INVISIBLE));
    587                     numInflated++;
    588                     extraEntries--;
    589                 }
    590             }
    591         }
    592     }
    593 
    594     /**
    595      * Inflates all entries.
    596      */
    597     private void inflateAllEntries(LayoutInflater layoutInflater) {
    598         if (mAllEntriesInflated) {
    599             return;
    600         }
    601         for (int i = 0; i < mEntries.size(); i++) {
    602             List<Entry> entryList = mEntries.get(i);
    603             List<View> viewList = mEntryViews.get(i);
    604             for (int j = viewList.size(); j < entryList.size(); j++) {
    605                 final int iconVisibility;
    606                 final Entry entry = entryList.get(j);
    607                 // If the entry does not have an icon, mark gone. Else if it has an icon, show
    608                 // for the first Entry in the list only
    609                 if (entry.getIcon() == null) {
    610                     iconVisibility = View.GONE;
    611                 } else if (j == 0) {
    612                     iconVisibility = View.VISIBLE;
    613                 } else {
    614                     iconVisibility = View.INVISIBLE;
    615                 }
    616                 viewList.add(createEntryView(layoutInflater, entry, iconVisibility));
    617             }
    618         }
    619         mAllEntriesInflated = true;
    620     }
    621 
    622     public void setColorAndFilter(int color, ColorFilter colorFilter) {
    623         mThemeColor = color;
    624         mThemeColorFilter = colorFilter;
    625         applyColor();
    626     }
    627 
    628     public void setEntryHeaderColor(int color) {
    629         if (mEntries != null) {
    630             for (List<View> entryList : mEntryViews) {
    631                 for (View entryView : entryList) {
    632                     TextView header = (TextView) entryView.findViewById(R.id.header);
    633                     if (header != null) {
    634                         header.setTextColor(color);
    635                     }
    636                 }
    637             }
    638         }
    639     }
    640 
    641     /**
    642      * The ColorFilter is passed in along with the color so that a new one only needs to be created
    643      * once for the entire activity.
    644      * 1. Title
    645      * 2. Entry icons
    646      * 3. Expand/Collapse Text
    647      * 4. Expand/Collapse Button
    648      */
    649     public void applyColor() {
    650         if (mThemeColor != 0 && mThemeColorFilter != null) {
    651             // Title
    652             if (mTitleTextView != null) {
    653                 mTitleTextView.setTextColor(mThemeColor);
    654             }
    655 
    656             // Entry icons
    657             if (mEntries != null) {
    658                 for (List<Entry> entryList : mEntries) {
    659                     for (Entry entry : entryList) {
    660                         if (entry.shouldApplyColor()) {
    661                             Drawable icon = entry.getIcon();
    662                             if (icon != null) {
    663                                 icon.mutate();
    664                                 icon.setColorFilter(mThemeColorFilter);
    665                             }
    666                         }
    667                         Drawable alternateIcon = entry.getAlternateIcon();
    668                         if (alternateIcon != null) {
    669                             alternateIcon.mutate();
    670                             alternateIcon.setColorFilter(mThemeColorFilter);
    671                         }
    672                         Drawable thirdIcon = entry.getThirdIcon();
    673                         if (thirdIcon != null) {
    674                             thirdIcon.mutate();
    675                             thirdIcon.setColorFilter(mThemeColorFilter);
    676                         }
    677                     }
    678                 }
    679             }
    680 
    681             // Expand/Collapse
    682             mExpandCollapseTextView.setTextColor(mThemeColor);
    683             mExpandCollapseArrow.setColorFilter(mThemeColorFilter);
    684         }
    685     }
    686 
    687     private View createEntryView(LayoutInflater layoutInflater, final Entry entry,
    688             int iconVisibility) {
    689         final EntryView view = (EntryView) layoutInflater.inflate(
    690                 R.layout.expanding_entry_card_item, this, false);
    691 
    692         view.setContextMenuInfo(entry.getEntryContextMenuInfo());
    693         if (!TextUtils.isEmpty(entry.getPrimaryContentDescription())) {
    694             view.setContentDescription(entry.getPrimaryContentDescription());
    695         }
    696 
    697         final ImageView icon = (ImageView) view.findViewById(R.id.icon);
    698         icon.setVisibility(iconVisibility);
    699         if (entry.getIcon() != null) {
    700             icon.setImageDrawable(entry.getIcon());
    701         }
    702         final TextView header = (TextView) view.findViewById(R.id.header);
    703         if (!TextUtils.isEmpty(entry.getHeader())) {
    704             header.setText(entry.getHeader());
    705         } else {
    706             header.setVisibility(View.GONE);
    707         }
    708 
    709         final TextView subHeader = (TextView) view.findViewById(R.id.sub_header);
    710         if (!TextUtils.isEmpty(entry.getSubHeader())) {
    711             subHeader.setText(entry.getSubHeader());
    712         } else {
    713             subHeader.setVisibility(View.GONE);
    714         }
    715 
    716         final ImageView subHeaderIcon = (ImageView) view.findViewById(R.id.icon_sub_header);
    717         if (entry.getSubHeaderIcon() != null) {
    718             subHeaderIcon.setImageDrawable(entry.getSubHeaderIcon());
    719         } else {
    720             subHeaderIcon.setVisibility(View.GONE);
    721         }
    722 
    723         final TextView text = (TextView) view.findViewById(R.id.text);
    724         if (!TextUtils.isEmpty(entry.getText())) {
    725             text.setText(entry.getText());
    726         } else {
    727             text.setVisibility(View.GONE);
    728         }
    729 
    730         final ImageView textIcon = (ImageView) view.findViewById(R.id.icon_text);
    731         if (entry.getTextIcon() != null) {
    732             textIcon.setImageDrawable(entry.getTextIcon());
    733         } else {
    734             textIcon.setVisibility(View.GONE);
    735         }
    736 
    737         if (entry.getIntent() != null) {
    738             view.setOnClickListener(mOnClickListener);
    739             view.setTag(new EntryTag(entry.getId(), entry.getIntent()));
    740         }
    741 
    742         if (entry.getIntent() == null && entry.getEntryContextMenuInfo() == null) {
    743             // Remove the click effect
    744             view.setBackground(null);
    745         }
    746 
    747         // If only the header is visible, add a top margin to match icon's top margin.
    748         // Also increase the space below the header for visual comfort.
    749         if (header.getVisibility() == View.VISIBLE && subHeader.getVisibility() == View.GONE &&
    750                 text.getVisibility() == View.GONE) {
    751             RelativeLayout.LayoutParams headerLayoutParams =
    752                     (RelativeLayout.LayoutParams) header.getLayoutParams();
    753             headerLayoutParams.topMargin = (int) (getResources().getDimension(
    754                     R.dimen.expanding_entry_card_item_header_only_margin_top));
    755             headerLayoutParams.bottomMargin += (int) (getResources().getDimension(
    756                     R.dimen.expanding_entry_card_item_header_only_margin_bottom));
    757             header.setLayoutParams(headerLayoutParams);
    758         }
    759 
    760         // Adjust the top padding size for entries with an invisible icon. The padding depends on
    761         // if there is a sub header or text section
    762         if (iconVisibility == View.INVISIBLE &&
    763                 (!TextUtils.isEmpty(entry.getSubHeader()) || !TextUtils.isEmpty(entry.getText()))) {
    764             view.setPaddingRelative(view.getPaddingStart(),
    765                     getResources().getDimensionPixelSize(
    766                             R.dimen.expanding_entry_card_item_no_icon_margin_top),
    767                     view.getPaddingEnd(),
    768                     view.getPaddingBottom());
    769         } else if (iconVisibility == View.INVISIBLE &&  TextUtils.isEmpty(entry.getSubHeader())
    770                 && TextUtils.isEmpty(entry.getText())) {
    771             view.setPaddingRelative(view.getPaddingStart(), 0, view.getPaddingEnd(),
    772                     view.getPaddingBottom());
    773         }
    774 
    775         final ImageView alternateIcon = (ImageView) view.findViewById(R.id.icon_alternate);
    776         final ImageView thirdIcon = (ImageView) view.findViewById(R.id.third_icon);
    777 
    778         if (entry.getAlternateIcon() != null && entry.getAlternateIntent() != null) {
    779             alternateIcon.setImageDrawable(entry.getAlternateIcon());
    780             alternateIcon.setOnClickListener(mOnClickListener);
    781             alternateIcon.setTag(new EntryTag(entry.getId(), entry.getAlternateIntent()));
    782             alternateIcon.setVisibility(View.VISIBLE);
    783             alternateIcon.setContentDescription(entry.getAlternateContentDescription());
    784         }
    785 
    786         if (entry.getThirdIcon() != null && entry.getThirdAction() != Entry.ACTION_NONE) {
    787             thirdIcon.setImageDrawable(entry.getThirdIcon());
    788             if (entry.getThirdAction() == Entry.ACTION_INTENT) {
    789                 thirdIcon.setOnClickListener(mOnClickListener);
    790                 thirdIcon.setTag(new EntryTag(entry.getId(), entry.getThirdIntent()));
    791             } else if (entry.getThirdAction() == Entry.ACTION_CALL_WITH_SUBJECT) {
    792                 thirdIcon.setOnClickListener(new View.OnClickListener() {
    793                     @Override
    794                     public void onClick(View v) {
    795                         Object tag = v.getTag();
    796                         if (!(tag instanceof Bundle)) {
    797                             return;
    798                         }
    799 
    800                         Context context = getContext();
    801                         if (context instanceof Activity) {
    802                             CallSubjectDialog.start((Activity) context, entry.getThirdExtras());
    803                         }
    804                     }
    805                 });
    806                 thirdIcon.setTag(entry.getThirdExtras());
    807             }
    808             thirdIcon.setVisibility(View.VISIBLE);
    809             thirdIcon.setContentDescription(entry.getThirdContentDescription());
    810         }
    811 
    812         // Set a custom touch listener for expanding the extra icon touch areas
    813         view.setOnTouchListener(new EntryTouchListener(view, alternateIcon, thirdIcon));
    814         view.setOnCreateContextMenuListener(mOnCreateContextMenuListener);
    815 
    816         return view;
    817     }
    818 
    819     private void updateExpandCollapseButton(CharSequence buttonText, long duration) {
    820         if (mIsExpanded) {
    821             final ObjectAnimator animator = ObjectAnimator.ofFloat(mExpandCollapseArrow,
    822                     "rotation", 180);
    823             animator.setDuration(duration);
    824             animator.start();
    825         } else {
    826             final ObjectAnimator animator = ObjectAnimator.ofFloat(mExpandCollapseArrow,
    827                     "rotation", 0);
    828             animator.setDuration(duration);
    829             animator.start();
    830         }
    831         updateBadges();
    832 
    833         mExpandCollapseTextView.setText(buttonText);
    834     }
    835 
    836     private void updateBadges() {
    837         if (mIsExpanded) {
    838             mBadgeContainer.removeAllViews();
    839         } else {
    840             int numberOfMimeTypesShown = mCollapsedEntriesCount;
    841             if (mShowFirstEntryTypeTwice && mEntries.size() > 0
    842                     && mEntries.get(0).size() > 1) {
    843                 numberOfMimeTypesShown--;
    844             }
    845             // Inflate badges if not yet created
    846             if (mBadges.size() < mEntries.size() - numberOfMimeTypesShown) {
    847                 for (int i = numberOfMimeTypesShown; i < mEntries.size(); i++) {
    848                     Drawable badgeDrawable = mEntries.get(i).get(0).getIcon();
    849                     int badgeResourceId = mEntries.get(i).get(0).getIconResourceId();
    850                     // Do not add the same badge twice
    851                     if (badgeResourceId != 0 && mBadgeIds.contains(badgeResourceId)) {
    852                         continue;
    853                     }
    854                     if (badgeDrawable != null) {
    855                         ImageView badgeView = new ImageView(getContext());
    856                         LinearLayout.LayoutParams badgeViewParams = new LinearLayout.LayoutParams(
    857                                 (int) getResources().getDimension(
    858                                         R.dimen.expanding_entry_card_item_icon_width),
    859                                 (int) getResources().getDimension(
    860                                         R.dimen.expanding_entry_card_item_icon_height));
    861                         badgeViewParams.setMarginEnd((int) getResources().getDimension(
    862                                 R.dimen.expanding_entry_card_badge_separator_margin));
    863                         badgeView.setLayoutParams(badgeViewParams);
    864                         badgeView.setImageDrawable(badgeDrawable);
    865                         mBadges.add(badgeView);
    866                         mBadgeIds.add(badgeResourceId);
    867                     }
    868                 }
    869             }
    870             mBadgeContainer.removeAllViews();
    871             for (ImageView badge : mBadges) {
    872                 mBadgeContainer.addView(badge);
    873             }
    874         }
    875     }
    876 
    877     private void expand() {
    878         ChangeBounds boundsTransition = new ChangeBounds();
    879         boundsTransition.setDuration(DURATION_EXPAND_ANIMATION_CHANGE_BOUNDS);
    880 
    881         Fade fadeIn = new Fade(Fade.IN);
    882         fadeIn.setDuration(DURATION_EXPAND_ANIMATION_FADE_IN);
    883         fadeIn.setStartDelay(DELAY_EXPAND_ANIMATION_FADE_IN);
    884 
    885         TransitionSet transitionSet = new TransitionSet();
    886         transitionSet.addTransition(boundsTransition);
    887         transitionSet.addTransition(fadeIn);
    888 
    889         transitionSet.excludeTarget(R.id.text, /* exclude = */ true);
    890 
    891         final ViewGroup transitionViewContainer = mAnimationViewGroup == null ?
    892                 this : mAnimationViewGroup;
    893 
    894         transitionSet.addListener(new TransitionListener() {
    895             @Override
    896             public void onTransitionStart(Transition transition) {
    897                 mListener.onExpand();
    898             }
    899 
    900             @Override
    901             public void onTransitionEnd(Transition transition) {
    902                 mListener.onExpandDone();
    903             }
    904 
    905             @Override
    906             public void onTransitionCancel(Transition transition) {
    907             }
    908 
    909             @Override
    910             public void onTransitionPause(Transition transition) {
    911             }
    912 
    913             @Override
    914             public void onTransitionResume(Transition transition) {
    915             }
    916         });
    917 
    918         TransitionManager.beginDelayedTransition(transitionViewContainer, transitionSet);
    919 
    920         mIsExpanded = true;
    921         // In order to insert new entries, we may need to inflate them for the first time
    922         inflateAllEntries(LayoutInflater.from(getContext()));
    923         insertEntriesIntoViewGroup();
    924         updateExpandCollapseButton(getCollapseButtonText(),
    925                 DURATION_EXPAND_ANIMATION_CHANGE_BOUNDS);
    926     }
    927 
    928     private void collapse() {
    929         final List<View> views = calculateEntriesToRemoveDuringCollapse();
    930 
    931         // This animation requires layout changes, unlike the expand() animation: the action bar
    932         // might get scrolled open in order to fill empty space. As a result, we can't use
    933         // ChangeBounds here. Instead manually animate view height and alpha. This isn't as
    934         // efficient as the bounds and translation changes performed by ChangeBounds. Nonetheless, a
    935         // reasonable frame-rate is achieved collapsing a dozen elements on a user Svelte N4. So the
    936         // performance hit doesn't justify writing a less maintainable animation.
    937         final AnimatorSet set = new AnimatorSet();
    938         final List<Animator> animators = new ArrayList<Animator>(views.size());
    939         int totalSizeChange = 0;
    940         for (View viewToRemove : views) {
    941             final ObjectAnimator animator = ObjectAnimator.ofObject(viewToRemove,
    942                     VIEW_LAYOUT_HEIGHT_PROPERTY, null, viewToRemove.getHeight(), 0);
    943             totalSizeChange += viewToRemove.getHeight();
    944             animator.setDuration(DURATION_COLLAPSE_ANIMATION_CHANGE_BOUNDS);
    945             animators.add(animator);
    946             viewToRemove.animate().alpha(0).setDuration(DURATION_COLLAPSE_ANIMATION_FADE_OUT);
    947         }
    948         set.playTogether(animators);
    949         set.start();
    950         set.addListener(new AnimatorListener() {
    951             @Override
    952             public void onAnimationStart(Animator animation) {
    953             }
    954 
    955             @Override
    956             public void onAnimationEnd(Animator animation) {
    957                 // Now that the views have been animated away, actually remove them from the view
    958                 // hierarchy. Reset their appearance so that they look appropriate when they
    959                 // get added back later.
    960                 insertEntriesIntoViewGroup();
    961                 for (View view : views) {
    962                     if (view instanceof EntryView) {
    963                         VIEW_LAYOUT_HEIGHT_PROPERTY.set(view, LayoutParams.WRAP_CONTENT);
    964                     } else {
    965                         VIEW_LAYOUT_HEIGHT_PROPERTY.set(view, mDividerLineHeightPixels);
    966                     }
    967                     view.animate().cancel();
    968                     view.setAlpha(1);
    969                 }
    970             }
    971 
    972             @Override
    973             public void onAnimationCancel(Animator animation) {
    974             }
    975 
    976             @Override
    977             public void onAnimationRepeat(Animator animation) {
    978             }
    979         });
    980 
    981         mListener.onCollapse(totalSizeChange);
    982         mIsExpanded = false;
    983         updateExpandCollapseButton(getExpandButtonText(),
    984                 DURATION_COLLAPSE_ANIMATION_CHANGE_BOUNDS);
    985     }
    986 
    987     /**
    988      * Returns whether the view is currently in its expanded state.
    989      */
    990     public boolean isExpanded() {
    991         return mIsExpanded;
    992     }
    993 
    994     /**
    995      * Sets the title text of this ExpandingEntryCardView.
    996      * @param title The title to set. A null title will result in the title being removed.
    997      */
    998     public void setTitle(String title) {
    999         if (mTitleTextView == null) {
   1000             Log.e(TAG, "mTitleTextView is null");
   1001         }
   1002         mTitleTextView.setText(title);
   1003         mTitleTextView.setVisibility(TextUtils.isEmpty(title) ? View.GONE : View.VISIBLE);
   1004         findViewById(R.id.title_separator).setVisibility(TextUtils.isEmpty(title) ?
   1005                 View.GONE : View.VISIBLE);
   1006         // If the title is set after children have been added, reset the top entry's padding to
   1007         // the default. Else if the title is cleared after children have been added, set
   1008         // the extra top padding
   1009         if (!TextUtils.isEmpty(title) && mEntriesViewGroup.getChildCount() > 0) {
   1010             View firstEntry = mEntriesViewGroup.getChildAt(0);
   1011             firstEntry.setPadding(firstEntry.getPaddingLeft(),
   1012                     getResources().getDimensionPixelSize(
   1013                             R.dimen.expanding_entry_card_item_padding_top),
   1014                     firstEntry.getPaddingRight(),
   1015                     firstEntry.getPaddingBottom());
   1016         } else if (!TextUtils.isEmpty(title) && mEntriesViewGroup.getChildCount() > 0) {
   1017             View firstEntry = mEntriesViewGroup.getChildAt(0);
   1018             firstEntry.setPadding(firstEntry.getPaddingLeft(),
   1019                     getResources().getDimensionPixelSize(
   1020                             R.dimen.expanding_entry_card_item_padding_top) +
   1021                             getResources().getDimensionPixelSize(
   1022                                     R.dimen.expanding_entry_card_null_title_top_extra_padding),
   1023                     firstEntry.getPaddingRight(),
   1024                     firstEntry.getPaddingBottom());
   1025         }
   1026     }
   1027 
   1028     public boolean shouldShow() {
   1029         return mEntries != null && mEntries.size() > 0;
   1030     }
   1031 
   1032     public static final class EntryView extends RelativeLayout {
   1033         private EntryContextMenuInfo mEntryContextMenuInfo;
   1034 
   1035         public EntryView(Context context) {
   1036             super(context);
   1037         }
   1038 
   1039         public EntryView(Context context, AttributeSet attrs) {
   1040             super(context, attrs);
   1041         }
   1042 
   1043         public void setContextMenuInfo(EntryContextMenuInfo info) {
   1044             mEntryContextMenuInfo = info;
   1045         }
   1046 
   1047         @Override
   1048         protected ContextMenuInfo getContextMenuInfo() {
   1049             return mEntryContextMenuInfo;
   1050         }
   1051     }
   1052 
   1053     public static final class EntryContextMenuInfo implements ContextMenuInfo {
   1054         private final String mCopyText;
   1055         private final String mCopyLabel;
   1056         private final String mMimeType;
   1057         private final long mId;
   1058         private final boolean mIsSuperPrimary;
   1059 
   1060         public EntryContextMenuInfo(String copyText, String copyLabel, String mimeType, long id,
   1061                 boolean isSuperPrimary) {
   1062             mCopyText = copyText;
   1063             mCopyLabel = copyLabel;
   1064             mMimeType = mimeType;
   1065             mId = id;
   1066             mIsSuperPrimary = isSuperPrimary;
   1067         }
   1068 
   1069         public String getCopyText() {
   1070             return mCopyText;
   1071         }
   1072 
   1073         public String getCopyLabel() {
   1074             return mCopyLabel;
   1075         }
   1076 
   1077         public String getMimeType() {
   1078             return mMimeType;
   1079         }
   1080 
   1081         public long getId() {
   1082             return mId;
   1083         }
   1084 
   1085         public boolean isSuperPrimary() {
   1086             return mIsSuperPrimary;
   1087         }
   1088     }
   1089 
   1090     static final class EntryTag {
   1091         private final int mId;
   1092         private final Intent mIntent;
   1093 
   1094         public EntryTag(int id, Intent intent) {
   1095             mId = id;
   1096             mIntent = intent;
   1097         }
   1098 
   1099         public int getId() {
   1100             return mId;
   1101         }
   1102 
   1103         public Intent getIntent() {
   1104             return mIntent;
   1105         }
   1106     }
   1107 
   1108     /**
   1109      * This custom touch listener increases the touch area for the second and third icons, if
   1110      * they are present. This is necessary to maintain other properties on an entry view, like
   1111      * using a top padding on entry. Based off of {@link android.view.TouchDelegate}
   1112      */
   1113     private static final class EntryTouchListener implements View.OnTouchListener {
   1114         private final View mEntry;
   1115         private final ImageView mAlternateIcon;
   1116         private final ImageView mThirdIcon;
   1117         /** mTouchedView locks in a view on touch down */
   1118         private View mTouchedView;
   1119         /** mSlop adds some space to account for touches that are just outside the hit area */
   1120         private int mSlop;
   1121 
   1122         public EntryTouchListener(View entry, ImageView alternateIcon, ImageView thirdIcon) {
   1123             mEntry = entry;
   1124             mAlternateIcon = alternateIcon;
   1125             mThirdIcon = thirdIcon;
   1126             mSlop = ViewConfiguration.get(entry.getContext()).getScaledTouchSlop();
   1127         }
   1128 
   1129         @Override
   1130         public boolean onTouch(View v, MotionEvent event) {
   1131             View touchedView = mTouchedView;
   1132             boolean sendToTouched = false;
   1133             boolean hit = true;
   1134             boolean handled = false;
   1135 
   1136             switch (event.getAction()) {
   1137                 case MotionEvent.ACTION_DOWN:
   1138                     if (hitThirdIcon(event)) {
   1139                         mTouchedView = mThirdIcon;
   1140                         sendToTouched = true;
   1141                     } else if (hitAlternateIcon(event)) {
   1142                         mTouchedView = mAlternateIcon;
   1143                         sendToTouched = true;
   1144                     } else {
   1145                         mTouchedView = mEntry;
   1146                         sendToTouched = false;
   1147                     }
   1148                     touchedView = mTouchedView;
   1149                     break;
   1150                 case MotionEvent.ACTION_UP:
   1151                 case MotionEvent.ACTION_MOVE:
   1152                     sendToTouched = mTouchedView != null && mTouchedView != mEntry;
   1153                     if (sendToTouched) {
   1154                         final Rect slopBounds = new Rect();
   1155                         touchedView.getHitRect(slopBounds);
   1156                         slopBounds.inset(-mSlop, -mSlop);
   1157                         if (!slopBounds.contains((int) event.getX(), (int) event.getY())) {
   1158                             hit = false;
   1159                         }
   1160                     }
   1161                     break;
   1162                 case MotionEvent.ACTION_CANCEL:
   1163                     sendToTouched = mTouchedView != null && mTouchedView != mEntry;
   1164                     mTouchedView = null;
   1165                     break;
   1166             }
   1167             if (sendToTouched) {
   1168                 if (hit) {
   1169                     event.setLocation(touchedView.getWidth() / 2, touchedView.getHeight() / 2);
   1170                 } else {
   1171                     // Offset event coordinates to be outside the target view (in case it does
   1172                     // something like tracking pressed state)
   1173                     event.setLocation(-(mSlop * 2), -(mSlop * 2));
   1174                 }
   1175                 handled = touchedView.dispatchTouchEvent(event);
   1176             }
   1177             return handled;
   1178         }
   1179 
   1180         private boolean hitThirdIcon(MotionEvent event) {
   1181             if (mEntry.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
   1182                 return mThirdIcon.getVisibility() == View.VISIBLE &&
   1183                         event.getX() < mThirdIcon.getRight();
   1184             } else {
   1185                 return mThirdIcon.getVisibility() == View.VISIBLE &&
   1186                         event.getX() > mThirdIcon.getLeft();
   1187             }
   1188         }
   1189 
   1190         /**
   1191          * Should be used after checking if third icon was hit
   1192          */
   1193         private boolean hitAlternateIcon(MotionEvent event) {
   1194             // LayoutParams used to add the start margin to the touch area
   1195             final RelativeLayout.LayoutParams alternateIconParams =
   1196                     (RelativeLayout.LayoutParams) mAlternateIcon.getLayoutParams();
   1197             if (mEntry.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
   1198                 return mAlternateIcon.getVisibility() == View.VISIBLE &&
   1199                         event.getX() < mAlternateIcon.getRight() + alternateIconParams.rightMargin;
   1200             } else {
   1201                 return mAlternateIcon.getVisibility() == View.VISIBLE &&
   1202                         event.getX() > mAlternateIcon.getLeft() - alternateIconParams.leftMargin;
   1203             }
   1204         }
   1205     }
   1206 }
   1207