Home | History | Annotate | Download | only in contact
      1 /*
      2  * Copyright (C) 2015 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 
     17 package com.android.messaging.ui.contact;
     18 
     19 import android.app.Activity;
     20 import android.app.Fragment;
     21 import android.database.Cursor;
     22 import android.graphics.Rect;
     23 import android.os.Bundle;
     24 import android.support.v7.app.ActionBar;
     25 import android.support.v7.widget.Toolbar;
     26 import android.support.v7.widget.Toolbar.OnMenuItemClickListener;
     27 import android.text.Editable;
     28 import android.text.InputType;
     29 import android.text.TextUtils;
     30 import android.text.TextWatcher;
     31 import android.transition.Explode;
     32 import android.transition.Transition;
     33 import android.transition.Transition.EpicenterCallback;
     34 import android.transition.TransitionManager;
     35 import android.view.LayoutInflater;
     36 import android.view.Menu;
     37 import android.view.MenuItem;
     38 import android.view.View;
     39 import android.view.View.OnClickListener;
     40 import android.view.ViewGroup;
     41 
     42 import com.android.messaging.R;
     43 import com.android.messaging.datamodel.DataModel;
     44 import com.android.messaging.datamodel.action.ActionMonitor;
     45 import com.android.messaging.datamodel.action.GetOrCreateConversationAction;
     46 import com.android.messaging.datamodel.action.GetOrCreateConversationAction.GetOrCreateConversationActionListener;
     47 import com.android.messaging.datamodel.action.GetOrCreateConversationAction.GetOrCreateConversationActionMonitor;
     48 import com.android.messaging.datamodel.binding.Binding;
     49 import com.android.messaging.datamodel.binding.BindingBase;
     50 import com.android.messaging.datamodel.data.ContactListItemData;
     51 import com.android.messaging.datamodel.data.ContactPickerData;
     52 import com.android.messaging.datamodel.data.ContactPickerData.ContactPickerDataListener;
     53 import com.android.messaging.datamodel.data.ParticipantData;
     54 import com.android.messaging.ui.CustomHeaderPagerViewHolder;
     55 import com.android.messaging.ui.CustomHeaderViewPager;
     56 import com.android.messaging.ui.animation.ViewGroupItemVerticalExplodeAnimation;
     57 import com.android.messaging.ui.contact.ContactRecipientAutoCompleteView.ContactChipsChangeListener;
     58 import com.android.messaging.util.Assert;
     59 import com.android.messaging.util.Assert.RunsOnMainThread;
     60 import com.android.messaging.util.ContactUtil;
     61 import com.android.messaging.util.ImeUtil;
     62 import com.android.messaging.util.LogUtil;
     63 import com.android.messaging.util.OsUtil;
     64 import com.android.messaging.util.PhoneUtils;
     65 import com.android.messaging.util.UiUtils;
     66 import com.google.common.annotations.VisibleForTesting;
     67 
     68 import java.util.ArrayList;
     69 import java.util.Set;
     70 
     71 
     72 /**
     73  * Shows lists of contacts to start conversations with.
     74  */
     75 public class ContactPickerFragment extends Fragment implements ContactPickerDataListener,
     76         ContactListItemView.HostInterface, ContactChipsChangeListener, OnMenuItemClickListener,
     77         GetOrCreateConversationActionListener {
     78     public static final String FRAGMENT_TAG = "contactpicker";
     79 
     80     // Undefined contact picker mode. We should never be in this state after the host activity has
     81     // been created.
     82     public static final int MODE_UNDEFINED = 0;
     83 
     84     // The initial contact picker mode for starting a new conversation with one contact.
     85     public static final int MODE_PICK_INITIAL_CONTACT = 1;
     86 
     87     // The contact picker mode where one initial contact has been picked and we are showing
     88     // only the chips edit box.
     89     public static final int MODE_CHIPS_ONLY = 2;
     90 
     91     // The contact picker mode for picking more contacts after starting the initial 1-1.
     92     public static final int MODE_PICK_MORE_CONTACTS = 3;
     93 
     94     // The contact picker mode when max number of participants is reached.
     95     public static final int MODE_PICK_MAX_PARTICIPANTS = 4;
     96 
     97     public interface ContactPickerFragmentHost {
     98         void onGetOrCreateNewConversation(String conversationId);
     99         void onBackButtonPressed();
    100         void onInitiateAddMoreParticipants();
    101         void onParticipantCountChanged(boolean canAddMoreParticipants);
    102         void invalidateActionBar();
    103     }
    104 
    105     @VisibleForTesting
    106     final Binding<ContactPickerData> mBinding = BindingBase.createBinding(this);
    107 
    108     private ContactPickerFragmentHost mHost;
    109     private ContactRecipientAutoCompleteView mRecipientTextView;
    110     private CustomHeaderViewPager mCustomHeaderViewPager;
    111     private AllContactsListViewHolder mAllContactsListViewHolder;
    112     private FrequentContactsListViewHolder mFrequentContactsListViewHolder;
    113     private View mRootView;
    114     private View mPendingExplodeView;
    115     private View mComposeDivider;
    116     private Toolbar mToolbar;
    117     private int mContactPickingMode = MODE_UNDEFINED;
    118 
    119     // Keeps track of the currently selected phone numbers in the chips view to enable fast lookup.
    120     private Set<String> mSelectedPhoneNumbers = null;
    121 
    122     /**
    123      * {@inheritDoc} from Fragment
    124      */
    125     @Override
    126     public void onCreate(final Bundle savedInstanceState) {
    127         super.onCreate(savedInstanceState);
    128         mAllContactsListViewHolder = new AllContactsListViewHolder(getActivity(), this);
    129         mFrequentContactsListViewHolder = new FrequentContactsListViewHolder(getActivity(), this);
    130 
    131         if (ContactUtil.hasReadContactsPermission()) {
    132             mBinding.bind(DataModel.get().createContactPickerData(getActivity(), this));
    133             mBinding.getData().init(getLoaderManager(), mBinding);
    134         }
    135     }
    136 
    137     /**
    138      * {@inheritDoc} from Fragment
    139      */
    140     @Override
    141     public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
    142             final Bundle savedInstanceState) {
    143         final View view = inflater.inflate(R.layout.contact_picker_fragment, container, false);
    144         mRecipientTextView = (ContactRecipientAutoCompleteView)
    145                 view.findViewById(R.id.recipient_text_view);
    146         mRecipientTextView.setThreshold(0);
    147         mRecipientTextView.setDropDownAnchor(R.id.compose_contact_divider);
    148 
    149         mRecipientTextView.setContactChipsListener(this);
    150         mRecipientTextView.setDropdownChipLayouter(new ContactDropdownLayouter(inflater,
    151                 getActivity(), this));
    152         mRecipientTextView.setAdapter(new ContactRecipientAdapter(getActivity(), this));
    153         mRecipientTextView.addTextChangedListener(new TextWatcher() {
    154             @Override
    155             public void onTextChanged(final CharSequence s, final int start, final int before,
    156                     final int count) {
    157             }
    158 
    159             @Override
    160             public void beforeTextChanged(final CharSequence s, final int start, final int count,
    161                     final int after) {
    162             }
    163 
    164             @Override
    165             public void afterTextChanged(final Editable s) {
    166                 updateTextInputButtonsVisibility();
    167             }
    168         });
    169 
    170         final CustomHeaderPagerViewHolder[] viewHolders = {
    171                 mFrequentContactsListViewHolder,
    172                 mAllContactsListViewHolder };
    173 
    174         mCustomHeaderViewPager = (CustomHeaderViewPager) view.findViewById(R.id.contact_pager);
    175         mCustomHeaderViewPager.setViewHolders(viewHolders);
    176         mCustomHeaderViewPager.setViewPagerTabHeight(CustomHeaderViewPager.DEFAULT_TAB_STRIP_SIZE);
    177         mCustomHeaderViewPager.setBackgroundColor(getResources()
    178                 .getColor(R.color.contact_picker_background));
    179 
    180         // The view pager defaults to the frequent contacts page.
    181         mCustomHeaderViewPager.setCurrentItem(0);
    182 
    183         mToolbar = (Toolbar) view.findViewById(R.id.toolbar);
    184         mToolbar.setNavigationIcon(R.drawable.ic_arrow_back_light);
    185         mToolbar.setNavigationContentDescription(R.string.back);
    186         mToolbar.setNavigationOnClickListener(new OnClickListener() {
    187             @Override
    188             public void onClick(final View v) {
    189                 mHost.onBackButtonPressed();
    190             }
    191         });
    192 
    193         mToolbar.inflateMenu(R.menu.compose_menu);
    194         mToolbar.setOnMenuItemClickListener(this);
    195 
    196         mComposeDivider = view.findViewById(R.id.compose_contact_divider);
    197         mRootView = view;
    198         return view;
    199     }
    200 
    201     /**
    202      * {@inheritDoc}
    203      *
    204      * Called when the host activity has been created. At this point, the host activity should
    205      * have set the contact picking mode for us so that we may update our visuals.
    206      */
    207     @Override
    208     public void onActivityCreated(final Bundle savedInstanceState) {
    209         super.onActivityCreated(savedInstanceState);
    210         Assert.isTrue(mContactPickingMode != MODE_UNDEFINED);
    211         updateVisualsForContactPickingMode(false /* animate */);
    212         mHost.invalidateActionBar();
    213     }
    214 
    215     @Override
    216     public void onDestroy() {
    217         super.onDestroy();
    218         // We could not have bound to the data if the permission was denied.
    219         if (mBinding.isBound()) {
    220             mBinding.unbind();
    221         }
    222 
    223         if (mMonitor != null) {
    224             mMonitor.unregister();
    225         }
    226         mMonitor = null;
    227     }
    228 
    229     @Override
    230     public boolean onMenuItemClick(final MenuItem menuItem) {
    231         switch (menuItem.getItemId()) {
    232             case R.id.action_ime_dialpad_toggle:
    233                 final int baseInputType = InputType.TYPE_TEXT_FLAG_MULTI_LINE;
    234                 if ((mRecipientTextView.getInputType() & InputType.TYPE_CLASS_PHONE) !=
    235                         InputType.TYPE_CLASS_PHONE) {
    236                     mRecipientTextView.setInputType(baseInputType | InputType.TYPE_CLASS_PHONE);
    237                     menuItem.setIcon(R.drawable.ic_ime_light);
    238                 } else {
    239                     mRecipientTextView.setInputType(baseInputType | InputType.TYPE_CLASS_TEXT);
    240                     menuItem.setIcon(R.drawable.ic_numeric_dialpad);
    241                 }
    242                 ImeUtil.get().showImeKeyboard(getActivity(), mRecipientTextView);
    243                 return true;
    244 
    245             case R.id.action_add_more_participants:
    246                 mHost.onInitiateAddMoreParticipants();
    247                 return true;
    248 
    249             case R.id.action_confirm_participants:
    250                 maybeGetOrCreateConversation();
    251                 return true;
    252 
    253             case R.id.action_delete_text:
    254                 Assert.equals(MODE_PICK_INITIAL_CONTACT, mContactPickingMode);
    255                 mRecipientTextView.setText("");
    256                 return true;
    257         }
    258         return false;
    259     }
    260 
    261     @Override // From ContactPickerDataListener
    262     public void onAllContactsCursorUpdated(final Cursor data) {
    263         mBinding.ensureBound();
    264         mAllContactsListViewHolder.onContactsCursorUpdated(data);
    265     }
    266 
    267     @Override // From ContactPickerDataListener
    268     public void onFrequentContactsCursorUpdated(final Cursor data) {
    269         mBinding.ensureBound();
    270         mFrequentContactsListViewHolder.onContactsCursorUpdated(data);
    271         if (data != null && data.getCount() == 0) {
    272             // Show the all contacts list when there's no frequents.
    273             mCustomHeaderViewPager.setCurrentItem(1);
    274         }
    275     }
    276 
    277     @Override // From ContactListItemView.HostInterface
    278     public void onContactListItemClicked(final ContactListItemData item,
    279             final ContactListItemView view) {
    280         if (!isContactSelected(item)) {
    281             if (mContactPickingMode == MODE_PICK_INITIAL_CONTACT) {
    282                 mPendingExplodeView = view;
    283             }
    284             mRecipientTextView.appendRecipientEntry(item.getRecipientEntry());
    285         } else if (mContactPickingMode != MODE_PICK_INITIAL_CONTACT) {
    286             mRecipientTextView.removeRecipientEntry(item.getRecipientEntry());
    287         }
    288     }
    289 
    290     @Override // From ContactListItemView.HostInterface
    291     public boolean isContactSelected(final ContactListItemData item) {
    292         return mSelectedPhoneNumbers != null &&
    293                 mSelectedPhoneNumbers.contains(PhoneUtils.getDefault().getCanonicalBySystemLocale(
    294                         item.getRecipientEntry().getDestination()));
    295     }
    296 
    297     /**
    298      * Call this immediately after attaching the fragment, or when there's a ui state change that
    299      * changes our host (i.e. restore from saved instance state).
    300      */
    301     public void setHost(final ContactPickerFragmentHost host) {
    302         mHost = host;
    303     }
    304 
    305     public void setContactPickingMode(final int mode, final boolean animate) {
    306         if (mContactPickingMode != mode) {
    307             // Guard against impossible transitions.
    308             Assert.isTrue(
    309                     // We may start from undefined mode to any mode when we are restoring state.
    310                     (mContactPickingMode == MODE_UNDEFINED) ||
    311                     (mContactPickingMode == MODE_PICK_INITIAL_CONTACT && mode == MODE_CHIPS_ONLY) ||
    312                     (mContactPickingMode == MODE_CHIPS_ONLY && mode == MODE_PICK_MORE_CONTACTS) ||
    313                     (mContactPickingMode == MODE_PICK_MORE_CONTACTS
    314                             && mode == MODE_PICK_MAX_PARTICIPANTS) ||
    315                     (mContactPickingMode == MODE_PICK_MAX_PARTICIPANTS
    316                             && mode == MODE_PICK_MORE_CONTACTS));
    317 
    318             mContactPickingMode = mode;
    319             updateVisualsForContactPickingMode(animate);
    320         }
    321     }
    322 
    323     private void showImeKeyboard() {
    324         Assert.notNull(mRecipientTextView);
    325         mRecipientTextView.requestFocus();
    326 
    327         // showImeKeyboard() won't work until the layout is ready, so wait until layout is complete
    328         // before showing the soft keyboard.
    329         UiUtils.doOnceAfterLayoutChange(mRootView, new Runnable() {
    330             @Override
    331             public void run() {
    332                 final Activity activity = getActivity();
    333                 if (activity != null) {
    334                     ImeUtil.get().showImeKeyboard(activity, mRecipientTextView);
    335                 }
    336             }
    337         });
    338         mRecipientTextView.invalidate();
    339     }
    340 
    341     private void updateVisualsForContactPickingMode(final boolean animate) {
    342         // Don't update visuals if the visuals haven't been inflated yet.
    343         if (mRootView != null) {
    344             final Menu menu = mToolbar.getMenu();
    345             final MenuItem addMoreParticipantsItem = menu.findItem(
    346                     R.id.action_add_more_participants);
    347             final MenuItem confirmParticipantsItem = menu.findItem(
    348                     R.id.action_confirm_participants);
    349             switch (mContactPickingMode) {
    350                 case MODE_PICK_INITIAL_CONTACT:
    351                     addMoreParticipantsItem.setVisible(false);
    352                     confirmParticipantsItem.setVisible(false);
    353                     mCustomHeaderViewPager.setVisibility(View.VISIBLE);
    354                     mComposeDivider.setVisibility(View.INVISIBLE);
    355                     mRecipientTextView.setEnabled(true);
    356                     showImeKeyboard();
    357                     break;
    358 
    359                 case MODE_CHIPS_ONLY:
    360                     if (animate) {
    361                         if (mPendingExplodeView == null) {
    362                             // The user didn't click on any contact item, so use the toolbar as
    363                             // the view to "explode."
    364                             mPendingExplodeView = mToolbar;
    365                         }
    366                         startExplodeTransitionForContactLists(false /* show */);
    367 
    368                         ViewGroupItemVerticalExplodeAnimation.startAnimationForView(
    369                                 mCustomHeaderViewPager, mPendingExplodeView, mRootView,
    370                                 true /* snapshotView */, UiUtils.COMPOSE_TRANSITION_DURATION);
    371                         showHideContactPagerWithAnimation(false /* show */);
    372                     } else {
    373                         mCustomHeaderViewPager.setVisibility(View.GONE);
    374                     }
    375 
    376                     addMoreParticipantsItem.setVisible(true);
    377                     confirmParticipantsItem.setVisible(false);
    378                     mComposeDivider.setVisibility(View.VISIBLE);
    379                     mRecipientTextView.setEnabled(true);
    380                     break;
    381 
    382                 case MODE_PICK_MORE_CONTACTS:
    383                     if (animate) {
    384                         // Correctly set the start visibility state for the view pager and
    385                         // individual list items (hidden initially), so that the transition
    386                         // manager can properly track the visibility change for the explode.
    387                         mCustomHeaderViewPager.setVisibility(View.VISIBLE);
    388                         toggleContactListItemsVisibilityForPendingTransition(false /* show */);
    389                         startExplodeTransitionForContactLists(true /* show */);
    390                     }
    391                     addMoreParticipantsItem.setVisible(false);
    392                     confirmParticipantsItem.setVisible(true);
    393                     mCustomHeaderViewPager.setVisibility(View.VISIBLE);
    394                     mComposeDivider.setVisibility(View.INVISIBLE);
    395                     mRecipientTextView.setEnabled(true);
    396                     showImeKeyboard();
    397                     break;
    398 
    399                 case MODE_PICK_MAX_PARTICIPANTS:
    400                     addMoreParticipantsItem.setVisible(false);
    401                     confirmParticipantsItem.setVisible(true);
    402                     mCustomHeaderViewPager.setVisibility(View.VISIBLE);
    403                     mComposeDivider.setVisibility(View.INVISIBLE);
    404                     // TODO: Verify that this is okay for accessibility
    405                     mRecipientTextView.setEnabled(false);
    406                     break;
    407 
    408                 default:
    409                     Assert.fail("Unsupported contact picker mode!");
    410                     break;
    411             }
    412             updateTextInputButtonsVisibility();
    413         }
    414     }
    415 
    416     private void updateTextInputButtonsVisibility() {
    417         final Menu menu = mToolbar.getMenu();
    418         final MenuItem keypadToggleItem = menu.findItem(R.id.action_ime_dialpad_toggle);
    419         final MenuItem deleteTextItem = menu.findItem(R.id.action_delete_text);
    420         if (mContactPickingMode == MODE_PICK_INITIAL_CONTACT) {
    421             if (TextUtils.isEmpty(mRecipientTextView.getText())) {
    422                 deleteTextItem.setVisible(false);
    423                 keypadToggleItem.setVisible(true);
    424             } else {
    425                 deleteTextItem.setVisible(true);
    426                 keypadToggleItem.setVisible(false);
    427             }
    428         } else {
    429             deleteTextItem.setVisible(false);
    430             keypadToggleItem.setVisible(false);
    431         }
    432     }
    433 
    434     private void maybeGetOrCreateConversation() {
    435         final ArrayList<ParticipantData> participants =
    436                 mRecipientTextView.getRecipientParticipantDataForConversationCreation();
    437         if (ContactPickerData.isTooManyParticipants(participants.size())) {
    438             UiUtils.showToast(R.string.too_many_participants);
    439         } else if (participants.size() > 0 && mMonitor == null) {
    440             mMonitor = GetOrCreateConversationAction.getOrCreateConversation(participants,
    441                     null, this);
    442         }
    443     }
    444 
    445     /**
    446      * Watches changes in contact chips to determine possible state transitions (e.g. creating
    447      * the initial conversation, adding more participants or finish the current conversation)
    448      */
    449     @Override
    450     public void onContactChipsChanged(final int oldCount, final int newCount) {
    451         Assert.isTrue(oldCount != newCount);
    452         if (mContactPickingMode == MODE_PICK_INITIAL_CONTACT) {
    453             // Initial picking mode. Start a conversation once a recipient has been picked.
    454             maybeGetOrCreateConversation();
    455         } else if (mContactPickingMode == MODE_CHIPS_ONLY) {
    456             // oldCount == 0 means we are restoring from savedInstanceState to add the existing
    457             // chips, don't switch to "add more participants" mode in this case.
    458             if (oldCount > 0 && mRecipientTextView.isFocused()) {
    459                 // Chips only mode. The user may have picked an additional contact or deleted the
    460                 // only existing contact. Either way, switch to picking more participants mode.
    461                 mHost.onInitiateAddMoreParticipants();
    462             }
    463         }
    464         mHost.onParticipantCountChanged(ContactPickerData.getCanAddMoreParticipants(newCount));
    465 
    466         // Refresh our local copy of the selected chips set to keep it up-to-date.
    467         mSelectedPhoneNumbers =  mRecipientTextView.getSelectedDestinations();
    468         invalidateContactLists();
    469     }
    470 
    471     /**
    472      * Listens for notification that invalid contacts have been removed during resolving them.
    473      * These contacts were not local contacts, valid email, or valid phone numbers
    474      */
    475     @Override
    476     public void onInvalidContactChipsPruned(final int prunedCount) {
    477         Assert.isTrue(prunedCount > 0);
    478         UiUtils.showToast(R.plurals.add_invalid_contact_error, prunedCount);
    479     }
    480 
    481     /**
    482      * Listens for notification that the user has pressed enter/done on the keyboard with all
    483      * contacts in place and we should create or go to the existing conversation now
    484      */
    485     @Override
    486     public void onEntryComplete() {
    487         if (mContactPickingMode == MODE_PICK_INITIAL_CONTACT ||
    488                 mContactPickingMode == MODE_PICK_MORE_CONTACTS ||
    489                 mContactPickingMode == MODE_PICK_MAX_PARTICIPANTS) {
    490             // Avoid multiple calls to create in race cases (hit done right after selecting contact)
    491             maybeGetOrCreateConversation();
    492         }
    493     }
    494 
    495     private void invalidateContactLists() {
    496         mAllContactsListViewHolder.invalidateList();
    497         mFrequentContactsListViewHolder.invalidateList();
    498     }
    499 
    500     /**
    501      * Kicks off a scene transition that animates visibility changes of individual contact list
    502      * items via explode animation.
    503      * @param show whether the contact lists are to be shown or hidden.
    504      */
    505     private void startExplodeTransitionForContactLists(final boolean show) {
    506         if (!OsUtil.isAtLeastL()) {
    507             // Explode animation is not supported pre-L.
    508             return;
    509         }
    510         final Explode transition = new Explode();
    511         final Rect epicenter = mPendingExplodeView == null ? null :
    512             UiUtils.getMeasuredBoundsOnScreen(mPendingExplodeView);
    513         transition.setDuration(UiUtils.COMPOSE_TRANSITION_DURATION);
    514         transition.setInterpolator(UiUtils.EASE_IN_INTERPOLATOR);
    515         transition.setEpicenterCallback(new EpicenterCallback() {
    516             @Override
    517             public Rect onGetEpicenter(final Transition transition) {
    518                 return epicenter;
    519             }
    520         });
    521 
    522         // Kick off the delayed scene explode transition. Anything happens after this line in this
    523         // method before the next frame will be tracked by the transition manager for visibility
    524         // changes and animated accordingly.
    525         TransitionManager.beginDelayedTransition(mCustomHeaderViewPager,
    526                 transition);
    527 
    528         toggleContactListItemsVisibilityForPendingTransition(show);
    529     }
    530 
    531     /**
    532      * Toggle the visibility of contact list items in the contact lists for them to be tracked by
    533      * the transition manager for pending explode transition.
    534      */
    535     private void toggleContactListItemsVisibilityForPendingTransition(final boolean show) {
    536         if (!OsUtil.isAtLeastL()) {
    537             // Explode animation is not supported pre-L.
    538             return;
    539         }
    540         mAllContactsListViewHolder.toggleVisibilityForPendingTransition(show, mPendingExplodeView);
    541         mFrequentContactsListViewHolder.toggleVisibilityForPendingTransition(show,
    542                 mPendingExplodeView);
    543     }
    544 
    545     private void showHideContactPagerWithAnimation(final boolean show) {
    546         final boolean isPagerVisible = (mCustomHeaderViewPager.getVisibility() == View.VISIBLE);
    547         if (show == isPagerVisible) {
    548             return;
    549         }
    550 
    551         mCustomHeaderViewPager.animate().alpha(show ? 1F : 0F)
    552             .setStartDelay(!show ? UiUtils.COMPOSE_TRANSITION_DURATION : 0)
    553             .withStartAction(new Runnable() {
    554                 @Override
    555                 public void run() {
    556                     mCustomHeaderViewPager.setVisibility(View.VISIBLE);
    557                     mCustomHeaderViewPager.setAlpha(show ? 0F : 1F);
    558                 }
    559             })
    560             .withEndAction(new Runnable() {
    561                 @Override
    562                 public void run() {
    563                     mCustomHeaderViewPager.setVisibility(show ? View.VISIBLE : View.GONE);
    564                     mCustomHeaderViewPager.setAlpha(1F);
    565                 }
    566             });
    567     }
    568 
    569     @Override
    570     public void onContactCustomColorLoaded(final ContactPickerData data) {
    571         mBinding.ensureBound(data);
    572         invalidateContactLists();
    573     }
    574 
    575     public void updateActionBar(final ActionBar actionBar) {
    576         // Hide the action bar for contact picker mode. The custom ToolBar containing chips UI
    577         // etc. will take the spot of the action bar.
    578         actionBar.hide();
    579         UiUtils.setStatusBarColor(getActivity(),
    580                 getResources().getColor(R.color.compose_notification_bar_background));
    581     }
    582 
    583     private GetOrCreateConversationActionMonitor mMonitor;
    584 
    585     @Override
    586     @RunsOnMainThread
    587     public void onGetOrCreateConversationSucceeded(final ActionMonitor monitor,
    588             final Object data, final String conversationId) {
    589         Assert.isTrue(monitor == mMonitor);
    590         Assert.isTrue(conversationId != null);
    591 
    592         mRecipientTextView.setInputType(InputType.TYPE_TEXT_FLAG_MULTI_LINE |
    593                 InputType.TYPE_CLASS_TEXT);
    594         mHost.onGetOrCreateNewConversation(conversationId);
    595 
    596         mMonitor = null;
    597     }
    598 
    599     @Override
    600     @RunsOnMainThread
    601     public void onGetOrCreateConversationFailed(final ActionMonitor monitor,
    602             final Object data) {
    603         Assert.isTrue(monitor == mMonitor);
    604         LogUtil.e(LogUtil.BUGLE_TAG, "onGetOrCreateConversationFailed");
    605         mMonitor = null;
    606     }
    607 }
    608