Home | History | Annotate | Download | only in calllog
      1 /*
      2  * Copyright (C) 2011 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.dialer.calllog;
     18 
     19 import android.app.Activity;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.res.Resources;
     23 import android.net.Uri;
     24 import android.provider.CallLog;
     25 import android.provider.CallLog.Calls;
     26 import android.provider.ContactsContract.CommonDataKinds.Phone;
     27 import android.support.v7.widget.CardView;
     28 import android.support.v7.widget.RecyclerView;
     29 import android.telecom.PhoneAccountHandle;
     30 import android.text.BidiFormatter;
     31 import android.text.TextDirectionHeuristics;
     32 import android.text.TextUtils;
     33 import android.view.ContextMenu;
     34 import android.view.MenuItem;
     35 import android.view.View;
     36 import android.view.ViewStub;
     37 import android.widget.ImageButton;
     38 import android.widget.ImageView;
     39 import android.widget.QuickContactBadge;
     40 import android.widget.TextView;
     41 
     42 import com.android.contacts.common.CallUtil;
     43 import com.android.contacts.common.ClipboardUtils;
     44 import com.android.contacts.common.ContactPhotoManager;
     45 import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
     46 import com.android.contacts.common.compat.CompatUtils;
     47 import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
     48 import com.android.contacts.common.dialog.CallSubjectDialog;
     49 import com.android.contacts.common.testing.NeededForTesting;
     50 import com.android.contacts.common.util.UriUtils;
     51 import com.android.dialer.DialtactsActivity;
     52 import com.android.dialer.R;
     53 import com.android.dialer.calllog.calllogcache.CallLogCache;
     54 import com.android.dialer.compat.FilteredNumberCompat;
     55 import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
     56 import com.android.dialer.filterednumber.BlockNumberDialogFragment;
     57 import com.android.dialer.filterednumber.FilteredNumbersUtil;
     58 import com.android.dialer.logging.Logger;
     59 import com.android.dialer.logging.ScreenEvent;
     60 import com.android.dialer.service.ExtendedBlockingButtonRenderer;
     61 import com.android.dialer.util.DialerUtils;
     62 import com.android.dialer.util.PhoneNumberUtil;
     63 import com.android.dialer.voicemail.VoicemailPlaybackLayout;
     64 import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
     65 import com.android.dialerbind.ObjectFactory;
     66 import com.google.common.collect.Lists;
     67 
     68 import java.util.List;
     69 
     70 /**
     71  * This is an object containing references to views contained by the call log list item. This
     72  * improves performance by reducing the frequency with which we need to find views by IDs.
     73  *
     74  * This object also contains UI logic pertaining to the view, to isolate it from the CallLogAdapter.
     75  */
     76 public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
     77         implements View.OnClickListener, MenuItem.OnMenuItemClickListener,
     78         View.OnCreateContextMenuListener {
     79 
     80     /** The root view of the call log list item */
     81     public final View rootView;
     82     /** The quick contact badge for the contact. */
     83     public final QuickContactBadge quickContactView;
     84     /** The primary action view of the entry. */
     85     public final View primaryActionView;
     86     /** The details of the phone call. */
     87     public final PhoneCallDetailsViews phoneCallDetailsViews;
     88     /** The text of the header for a day grouping. */
     89     public final TextView dayGroupHeader;
     90     /** The view containing the details for the call log row, including the action buttons. */
     91     public final CardView callLogEntryView;
     92     /** The actionable view which places a call to the number corresponding to the call log row. */
     93     public final ImageView primaryActionButtonView;
     94 
     95     /** The view containing call log item actions.  Null until the ViewStub is inflated. */
     96     public View actionsView;
     97     /** The button views below are assigned only when the action section is expanded. */
     98     public VoicemailPlaybackLayout voicemailPlaybackView;
     99     public View callButtonView;
    100     public View videoCallButtonView;
    101     public View createNewContactButtonView;
    102     public View addToExistingContactButtonView;
    103     public View sendMessageView;
    104     public View detailsButtonView;
    105     public View callWithNoteButtonView;
    106     public ImageView workIconView;
    107 
    108     /**
    109      * The row Id for the first call associated with the call log entry.  Used as a key for the
    110      * map used to track which call log entries have the action button section expanded.
    111      */
    112     public long rowId;
    113 
    114     /**
    115      * The call Ids for the calls represented by the current call log entry.  Used when the user
    116      * deletes a call log entry.
    117      */
    118     public long[] callIds;
    119 
    120     /**
    121      * The callable phone number for the current call log entry.  Cached here as the call back
    122      * intent is set only when the actions ViewStub is inflated.
    123      */
    124     public String number;
    125 
    126     /**
    127      * The post-dial numbers that are dialed following the phone number.
    128      */
    129     public String postDialDigits;
    130 
    131     /**
    132      * The formatted phone number to display.
    133      */
    134     public String displayNumber;
    135 
    136     /**
    137      * The phone number presentation for the current call log entry.  Cached here as the call back
    138      * intent is set only when the actions ViewStub is inflated.
    139      */
    140     public int numberPresentation;
    141 
    142     /**
    143      * The type of the phone number (e.g. main, work, etc).
    144      */
    145     public String numberType;
    146 
    147     /**
    148      * The country iso for the call. Cached here as the call back
    149      * intent is set only when the actions ViewStub is inflated.
    150      */
    151     public String countryIso;
    152 
    153     /**
    154      * The type of call for the current call log entry.  Cached here as the call back
    155      * intent is set only when the actions ViewStub is inflated.
    156      */
    157     public int callType;
    158 
    159     /**
    160      * ID for blocked numbers database.
    161      * Set when context menu is created, if the number is blocked.
    162      */
    163     public Integer blockId;
    164 
    165     /**
    166      * The account for the current call log entry.  Cached here as the call back
    167      * intent is set only when the actions ViewStub is inflated.
    168      */
    169     public PhoneAccountHandle accountHandle;
    170 
    171     /**
    172      * If the call has an associated voicemail message, the URI of the voicemail message for
    173      * playback.  Cached here as the voicemail intent is only set when the actions ViewStub is
    174      * inflated.
    175      */
    176     public String voicemailUri;
    177 
    178     /**
    179      * The name or number associated with the call.  Cached here for use when setting content
    180      * descriptions on buttons in the actions ViewStub when it is inflated.
    181      */
    182     public CharSequence nameOrNumber;
    183 
    184     /**
    185      * The call type or Location associated with the call. Cached here for use when setting text
    186      * for a voicemail log's call button
    187      */
    188     public CharSequence callTypeOrLocation;
    189 
    190     /**
    191      * Whether this row is for a business or not.
    192      */
    193     public boolean isBusiness;
    194 
    195     /**
    196      * The contact info for the contact displayed in this list item.
    197      */
    198     public ContactInfo info;
    199 
    200     /**
    201      * Whether the current log entry is a blocked number or not. Used in updatePhoto()
    202      */
    203     public boolean isBlocked;
    204 
    205     /**
    206      * Whether this is the archive tab or not.
    207      */
    208     public final boolean isArchiveTab;
    209 
    210     private final Context mContext;
    211     private final CallLogCache mCallLogCache;
    212     private final CallLogListItemHelper mCallLogListItemHelper;
    213     private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
    214     private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
    215 
    216     private final BlockNumberDialogFragment.Callback mFilteredNumberDialogCallback;
    217 
    218     private final int mPhotoSize;
    219     private ViewStub mExtendedBlockingViewStub;
    220     private final ExtendedBlockingButtonRenderer mExtendedBlockingButtonRenderer;
    221 
    222     private View.OnClickListener mExpandCollapseListener;
    223     private boolean mVoicemailPrimaryActionButtonClicked;
    224 
    225     private CallLogListItemViewHolder(
    226             Context context,
    227             ExtendedBlockingButtonRenderer.Listener eventListener,
    228             View.OnClickListener expandCollapseListener,
    229             CallLogCache callLogCache,
    230             CallLogListItemHelper callLogListItemHelper,
    231             VoicemailPlaybackPresenter voicemailPlaybackPresenter,
    232             FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
    233             BlockNumberDialogFragment.Callback filteredNumberDialogCallback,
    234             View rootView,
    235             QuickContactBadge quickContactView,
    236             View primaryActionView,
    237             PhoneCallDetailsViews phoneCallDetailsViews,
    238             CardView callLogEntryView,
    239             TextView dayGroupHeader,
    240             ImageView primaryActionButtonView,
    241             boolean isArchiveTab) {
    242         super(rootView);
    243 
    244         mContext = context;
    245         mExpandCollapseListener = expandCollapseListener;
    246         mCallLogCache = callLogCache;
    247         mCallLogListItemHelper = callLogListItemHelper;
    248         mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
    249         mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
    250         mFilteredNumberDialogCallback = filteredNumberDialogCallback;
    251 
    252         this.rootView = rootView;
    253         this.quickContactView = quickContactView;
    254         this.primaryActionView = primaryActionView;
    255         this.phoneCallDetailsViews = phoneCallDetailsViews;
    256         this.callLogEntryView = callLogEntryView;
    257         this.dayGroupHeader = dayGroupHeader;
    258         this.primaryActionButtonView = primaryActionButtonView;
    259         this.workIconView = (ImageView) rootView.findViewById(R.id.work_profile_icon);
    260         this.isArchiveTab = isArchiveTab;
    261         Resources resources = mContext.getResources();
    262         mPhotoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size);
    263 
    264         // Set text height to false on the TextViews so they don't have extra padding.
    265         phoneCallDetailsViews.nameView.setElegantTextHeight(false);
    266         phoneCallDetailsViews.callLocationAndDate.setElegantTextHeight(false);
    267 
    268         quickContactView.setOverlay(null);
    269         if (CompatUtils.hasPrioritizedMimeType()) {
    270             quickContactView.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE);
    271         }
    272         primaryActionButtonView.setOnClickListener(this);
    273         primaryActionView.setOnClickListener(mExpandCollapseListener);
    274         primaryActionView.setOnCreateContextMenuListener(this);
    275         mExtendedBlockingButtonRenderer =
    276                 ObjectFactory.newExtendedBlockingButtonRenderer(mContext, eventListener);
    277     }
    278 
    279     public static CallLogListItemViewHolder create(
    280             View view,
    281             Context context,
    282             ExtendedBlockingButtonRenderer.Listener eventListener,
    283             View.OnClickListener expandCollapseListener,
    284             CallLogCache callLogCache,
    285             CallLogListItemHelper callLogListItemHelper,
    286             VoicemailPlaybackPresenter voicemailPlaybackPresenter,
    287             FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
    288             BlockNumberDialogFragment.Callback filteredNumberDialogCallback,
    289             boolean isArchiveTab) {
    290 
    291         return new CallLogListItemViewHolder(
    292                 context,
    293                 eventListener,
    294                 expandCollapseListener,
    295                 callLogCache,
    296                 callLogListItemHelper,
    297                 voicemailPlaybackPresenter,
    298                 filteredNumberAsyncQueryHandler,
    299                 filteredNumberDialogCallback,
    300                 view,
    301                 (QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
    302                 view.findViewById(R.id.primary_action_view),
    303                 PhoneCallDetailsViews.fromView(view),
    304                 (CardView) view.findViewById(R.id.call_log_row),
    305                 (TextView) view.findViewById(R.id.call_log_day_group_label),
    306                 (ImageView) view.findViewById(R.id.primary_action_button),
    307                 isArchiveTab);
    308     }
    309 
    310     @Override
    311     public void onCreateContextMenu(
    312             final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    313         if (TextUtils.isEmpty(number)) {
    314             return;
    315         }
    316 
    317         if (callType == CallLog.Calls.VOICEMAIL_TYPE) {
    318             menu.setHeaderTitle(mContext.getResources().getText(R.string.voicemail));
    319         } else {
    320             menu.setHeaderTitle(PhoneNumberUtilsCompat.createTtsSpannable(
    321                     BidiFormatter.getInstance().unicodeWrap(number, TextDirectionHeuristics.LTR)));
    322         }
    323 
    324         menu.add(ContextMenu.NONE, R.id.context_menu_copy_to_clipboard, ContextMenu.NONE,
    325                 R.string.action_copy_number_text)
    326                 .setOnMenuItemClickListener(this);
    327 
    328         // The edit number before call does not show up if any of the conditions apply:
    329         // 1) Number cannot be called
    330         // 2) Number is the voicemail number
    331         // 3) Number is a SIP address
    332 
    333         if (PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation)
    334                 && !mCallLogCache.isVoicemailNumber(accountHandle, number)
    335                 && !PhoneNumberUtil.isSipNumber(number)) {
    336             menu.add(ContextMenu.NONE, R.id.context_menu_edit_before_call, ContextMenu.NONE,
    337                     R.string.action_edit_number_before_call)
    338                     .setOnMenuItemClickListener(this);
    339         }
    340 
    341         if (callType == CallLog.Calls.VOICEMAIL_TYPE
    342                 && phoneCallDetailsViews.voicemailTranscriptionView.length() > 0) {
    343             menu.add(ContextMenu.NONE, R.id.context_menu_copy_transcript_to_clipboard,
    344                     ContextMenu.NONE, R.string.copy_transcript_text)
    345                     .setOnMenuItemClickListener(this);
    346         }
    347 
    348         if (FilteredNumberCompat.canAttemptBlockOperations(mContext)
    349                 && FilteredNumbersUtil.canBlockNumber(mContext, number, countryIso)) {
    350             mFilteredNumberAsyncQueryHandler.isBlockedNumber(
    351                     new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
    352                         @Override
    353                         public void onCheckComplete(Integer id) {
    354                             blockId = id;
    355                             int blockTitleId = blockId == null ? R.string.action_block_number
    356                                     : R.string.action_unblock_number;
    357                             final MenuItem blockItem = menu.add(
    358                                     ContextMenu.NONE,
    359                                     R.id.context_menu_block_number,
    360                                     ContextMenu.NONE,
    361                                     blockTitleId);
    362                             blockItem.setOnMenuItemClickListener(
    363                                     CallLogListItemViewHolder.this);
    364                         }
    365                     }, number, countryIso);
    366         }
    367 
    368         Logger.logScreenView(ScreenEvent.CALL_LOG_CONTEXT_MENU, (Activity) mContext);
    369     }
    370 
    371     @Override
    372     public boolean onMenuItemClick(MenuItem item) {
    373         int resId = item.getItemId();
    374         if (resId == R.id.context_menu_block_number) {
    375             FilteredNumberCompat
    376                     .showBlockNumberDialogFlow(mContext.getContentResolver(), blockId, number,
    377                             countryIso, displayNumber, R.id.floating_action_button_container,
    378                             ((Activity) mContext).getFragmentManager(),
    379                             mFilteredNumberDialogCallback);
    380             return true;
    381         } else if (resId == R.id.context_menu_copy_to_clipboard) {
    382             ClipboardUtils.copyText(mContext, null, number, true);
    383             return true;
    384         } else if (resId == R.id.context_menu_copy_transcript_to_clipboard) {
    385             ClipboardUtils.copyText(mContext, null,
    386                     phoneCallDetailsViews.voicemailTranscriptionView.getText(), true);
    387             return true;
    388         } else if (resId == R.id.context_menu_edit_before_call) {
    389             final Intent intent = new Intent(
    390                     Intent.ACTION_DIAL, CallUtil.getCallUri(number));
    391             intent.setClass(mContext, DialtactsActivity.class);
    392             DialerUtils.startActivityWithErrorToast(mContext, intent);
    393             return true;
    394         }
    395         return false;
    396     }
    397 
    398     /**
    399      * Configures the action buttons in the expandable actions ViewStub. The ViewStub is not
    400      * inflated during initial binding, so click handlers, tags and accessibility text must be set
    401      * here, if necessary.
    402      */
    403     public void inflateActionViewStub() {
    404         ViewStub stub = (ViewStub) rootView.findViewById(R.id.call_log_entry_actions_stub);
    405         if (stub != null) {
    406             actionsView = stub.inflate();
    407 
    408             voicemailPlaybackView = (VoicemailPlaybackLayout) actionsView
    409                     .findViewById(R.id.voicemail_playback_layout);
    410             if (isArchiveTab) {
    411                 voicemailPlaybackView.hideArchiveButton();
    412             }
    413 
    414 
    415             callButtonView = actionsView.findViewById(R.id.call_action);
    416             callButtonView.setOnClickListener(this);
    417 
    418             videoCallButtonView = actionsView.findViewById(R.id.video_call_action);
    419             videoCallButtonView.setOnClickListener(this);
    420 
    421             createNewContactButtonView = actionsView.findViewById(R.id.create_new_contact_action);
    422             createNewContactButtonView.setOnClickListener(this);
    423 
    424             addToExistingContactButtonView =
    425                     actionsView.findViewById(R.id.add_to_existing_contact_action);
    426             addToExistingContactButtonView.setOnClickListener(this);
    427 
    428             sendMessageView = actionsView.findViewById(R.id.send_message_action);
    429             sendMessageView.setOnClickListener(this);
    430 
    431             detailsButtonView = actionsView.findViewById(R.id.details_action);
    432             detailsButtonView.setOnClickListener(this);
    433 
    434             callWithNoteButtonView = actionsView.findViewById(R.id.call_with_note_action);
    435             callWithNoteButtonView.setOnClickListener(this);
    436 
    437             mExtendedBlockingViewStub =
    438                     (ViewStub) actionsView.findViewById(R.id.extended_blocking_actions_container);
    439         }
    440 
    441         bindActionButtons();
    442     }
    443 
    444     private void updatePrimaryActionButton(boolean isExpanded) {
    445         if (!TextUtils.isEmpty(voicemailUri)) {
    446             // Treat as voicemail list item; show play button if not expanded.
    447             if (!isExpanded) {
    448                 primaryActionButtonView.setImageResource(R.drawable.ic_play_arrow_24dp);
    449                 primaryActionButtonView.setContentDescription(TextUtils.expandTemplate(
    450                         mContext.getString(R.string.description_voicemail_action),
    451                         nameOrNumber));
    452                 primaryActionButtonView.setVisibility(View.VISIBLE);
    453             } else {
    454                 primaryActionButtonView.setVisibility(View.GONE);
    455             }
    456         } else {
    457             // Treat as normal list item; show call button, if possible.
    458             if (PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation)) {
    459                 boolean isVoicemailNumber =
    460                         mCallLogCache.isVoicemailNumber(accountHandle, number);
    461                 if (isVoicemailNumber) {
    462                     // Call to generic voicemail number, in case there are multiple accounts.
    463                     primaryActionButtonView.setTag(
    464                             IntentProvider.getReturnVoicemailCallIntentProvider());
    465                 } else {
    466                     primaryActionButtonView.setTag(
    467                             IntentProvider.getReturnCallIntentProvider(number + postDialDigits));
    468                 }
    469 
    470                 primaryActionButtonView.setContentDescription(TextUtils.expandTemplate(
    471                         mContext.getString(R.string.description_call_action),
    472                         nameOrNumber));
    473                 primaryActionButtonView.setImageResource(R.drawable.ic_call_24dp);
    474                 primaryActionButtonView.setVisibility(View.VISIBLE);
    475             } else {
    476                 primaryActionButtonView.setTag(null);
    477                 primaryActionButtonView.setVisibility(View.GONE);
    478             }
    479         }
    480     }
    481 
    482     /**
    483      * Binds text titles, click handlers and intents to the voicemail, details and callback action
    484      * buttons.
    485      */
    486     private void bindActionButtons() {
    487         boolean canPlaceCallToNumber = PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation);
    488 
    489         if (!TextUtils.isEmpty(voicemailUri) && canPlaceCallToNumber) {
    490             callButtonView.setTag(IntentProvider.getReturnCallIntentProvider(number));
    491             ((TextView) callButtonView.findViewById(R.id.call_action_text))
    492                     .setText(TextUtils.expandTemplate(
    493                             mContext.getString(R.string.call_log_action_call),
    494                             nameOrNumber));
    495             TextView callTypeOrLocationView = ((TextView) callButtonView.findViewById(
    496                     R.id.call_type_or_location_text));
    497             if (callType == Calls.VOICEMAIL_TYPE && !TextUtils.isEmpty(callTypeOrLocation)) {
    498                 callTypeOrLocationView.setText(callTypeOrLocation);
    499                 callTypeOrLocationView.setVisibility(View.VISIBLE);
    500             } else {
    501                 callTypeOrLocationView.setVisibility(View.GONE);
    502             }
    503             callButtonView.setVisibility(View.VISIBLE);
    504         } else {
    505             callButtonView.setVisibility(View.GONE);
    506         }
    507 
    508         // If one of the calls had video capabilities, show the video call button.
    509         if (mCallLogCache.isVideoEnabled() && canPlaceCallToNumber &&
    510                 phoneCallDetailsViews.callTypeIcons.isVideoShown()) {
    511             videoCallButtonView.setTag(IntentProvider.getReturnVideoCallIntentProvider(number));
    512             videoCallButtonView.setVisibility(View.VISIBLE);
    513         } else {
    514             videoCallButtonView.setVisibility(View.GONE);
    515         }
    516 
    517         // For voicemail calls, show the voicemail playback layout; hide otherwise.
    518         if (callType == Calls.VOICEMAIL_TYPE && mVoicemailPlaybackPresenter != null
    519                 && !TextUtils.isEmpty(voicemailUri)) {
    520             voicemailPlaybackView.setVisibility(View.VISIBLE);
    521 
    522             Uri uri = Uri.parse(voicemailUri);
    523             mVoicemailPlaybackPresenter.setPlaybackView(
    524                     voicemailPlaybackView, uri, mVoicemailPrimaryActionButtonClicked);
    525             mVoicemailPrimaryActionButtonClicked = false;
    526             // Only mark voicemail as read when not in archive tab
    527             if (!isArchiveTab) {
    528                 CallLogAsyncTaskUtil.markVoicemailAsRead(mContext, uri);
    529             }
    530         } else {
    531             voicemailPlaybackView.setVisibility(View.GONE);
    532         }
    533 
    534         if (callType == Calls.VOICEMAIL_TYPE) {
    535             detailsButtonView.setVisibility(View.GONE);
    536         } else {
    537             detailsButtonView.setVisibility(View.VISIBLE);
    538             detailsButtonView.setTag(
    539                     IntentProvider.getCallDetailIntentProvider(rowId, callIds, null));
    540         }
    541 
    542         if (info != null && UriUtils.isEncodedContactUri(info.lookupUri)) {
    543             createNewContactButtonView.setTag(IntentProvider.getAddContactIntentProvider(
    544                     info.lookupUri, info.name, info.number, info.type, true /* isNewContact */));
    545             createNewContactButtonView.setVisibility(View.VISIBLE);
    546 
    547             addToExistingContactButtonView.setTag(IntentProvider.getAddContactIntentProvider(
    548                     info.lookupUri, info.name, info.number, info.type, false /* isNewContact */));
    549             addToExistingContactButtonView.setVisibility(View.VISIBLE);
    550         } else {
    551             createNewContactButtonView.setVisibility(View.GONE);
    552             addToExistingContactButtonView.setVisibility(View.GONE);
    553         }
    554 
    555         if (canPlaceCallToNumber) {
    556             sendMessageView.setTag(IntentProvider.getSendSmsIntentProvider(number));
    557             sendMessageView.setVisibility(View.VISIBLE);
    558         } else {
    559             sendMessageView.setVisibility(View.GONE);
    560         }
    561 
    562         mCallLogListItemHelper.setActionContentDescriptions(this);
    563 
    564         boolean supportsCallSubject =
    565                 mCallLogCache.doesAccountSupportCallSubject(accountHandle);
    566         boolean isVoicemailNumber =
    567                 mCallLogCache.isVoicemailNumber(accountHandle, number);
    568         callWithNoteButtonView.setVisibility(
    569                 supportsCallSubject && !isVoicemailNumber ? View.VISIBLE : View.GONE);
    570 
    571         if(mExtendedBlockingButtonRenderer != null){
    572             List<View> completeLogListItems = Lists.newArrayList(
    573                     createNewContactButtonView,
    574                     addToExistingContactButtonView,
    575                     sendMessageView,
    576                     callButtonView,
    577                     callWithNoteButtonView,
    578                     detailsButtonView,
    579                     voicemailPlaybackView);
    580 
    581             List<View> blockedNumberVisibleViews = Lists.newArrayList(detailsButtonView);
    582             List<View> extendedBlockingVisibleViews = Lists.newArrayList(detailsButtonView);
    583 
    584             ExtendedBlockingButtonRenderer.ViewHolderInfo viewHolderInfo =
    585                     new ExtendedBlockingButtonRenderer.ViewHolderInfo(
    586                             completeLogListItems,
    587                             extendedBlockingVisibleViews,
    588                             blockedNumberVisibleViews,
    589                             number,
    590                             countryIso,
    591                             nameOrNumber.toString(),
    592                             displayNumber);
    593             mExtendedBlockingButtonRenderer.setViewHolderInfo(viewHolderInfo);
    594 
    595             mExtendedBlockingButtonRenderer.render(mExtendedBlockingViewStub);
    596         }
    597     }
    598 
    599     /**
    600      * Show or hide the action views, such as voicemail, details, and add contact.
    601      *
    602      * If the action views have never been shown yet for this view, inflate the view stub.
    603      */
    604     public void showActions(boolean show) {
    605         showOrHideVoicemailTranscriptionView(show);
    606 
    607         if (show) {
    608             // Inflate the view stub if necessary, and wire up the event handlers.
    609             inflateActionViewStub();
    610 
    611             actionsView.setVisibility(View.VISIBLE);
    612             actionsView.setAlpha(1.0f);
    613         } else {
    614             // When recycling a view, it is possible the actionsView ViewStub was previously
    615             // inflated so we should hide it in this case.
    616             if (actionsView != null) {
    617                 actionsView.setVisibility(View.GONE);
    618             }
    619         }
    620 
    621         updatePrimaryActionButton(show);
    622     }
    623 
    624     public void showOrHideVoicemailTranscriptionView(boolean isExpanded) {
    625         if (callType != Calls.VOICEMAIL_TYPE) {
    626             return;
    627         }
    628 
    629         final TextView view = phoneCallDetailsViews.voicemailTranscriptionView;
    630         if (!isExpanded || TextUtils.isEmpty(view.getText())) {
    631             view.setVisibility(View.GONE);
    632             return;
    633         }
    634         view.setVisibility(View.VISIBLE);
    635     }
    636 
    637     public void updatePhoto() {
    638         quickContactView.assignContactUri(info.lookupUri);
    639 
    640         final boolean isVoicemail = mCallLogCache.isVoicemailNumber(accountHandle, number);
    641         int contactType = ContactPhotoManager.TYPE_DEFAULT;
    642         if (isVoicemail) {
    643             contactType = ContactPhotoManager.TYPE_VOICEMAIL;
    644         } else if (isBusiness) {
    645             contactType = ContactPhotoManager.TYPE_BUSINESS;
    646         }
    647 
    648         final String lookupKey = info.lookupUri != null
    649                 ? UriUtils.getLookupKeyFromUri(info.lookupUri) : null;
    650         final String displayName = TextUtils.isEmpty(info.name) ? displayNumber : info.name;
    651         final DefaultImageRequest request = new DefaultImageRequest(
    652                 displayName, lookupKey, contactType, true /* isCircular */);
    653 
    654         if (info.photoId == 0 && info.photoUri != null) {
    655             ContactPhotoManager.getInstance(mContext).loadPhoto(quickContactView, info.photoUri,
    656                     mPhotoSize, false /* darkTheme */, true /* isCircular */, request);
    657         } else {
    658             ContactPhotoManager.getInstance(mContext).loadThumbnail(quickContactView, info.photoId,
    659                     false /* darkTheme */, true /* isCircular */, request);
    660         }
    661 
    662         if (mExtendedBlockingButtonRenderer != null) {
    663             mExtendedBlockingButtonRenderer.updatePhotoAndLabelIfNecessary(
    664                     number,
    665                     countryIso,
    666                     quickContactView,
    667                     phoneCallDetailsViews.callLocationAndDate);
    668         }
    669     }
    670 
    671     @Override
    672     public void onClick(View view) {
    673         if (view.getId() == R.id.primary_action_button && !TextUtils.isEmpty(voicemailUri)) {
    674             mVoicemailPrimaryActionButtonClicked = true;
    675             mExpandCollapseListener.onClick(primaryActionView);
    676         } else if (view.getId() == R.id.call_with_note_action) {
    677             CallSubjectDialog.start(
    678                     (Activity) mContext,
    679                     info.photoId,
    680                     info.photoUri,
    681                     info.lookupUri,
    682                     (String) nameOrNumber /* top line of contact view in call subject dialog */,
    683                     isBusiness,
    684                     number,
    685                     TextUtils.isEmpty(info.name) ? null : displayNumber, /* second line of contact
    686                                                                            view in dialog. */
    687                     numberType, /* phone number type (e.g. mobile) in second line of contact view */
    688                     accountHandle);
    689         } else {
    690             final IntentProvider intentProvider = (IntentProvider) view.getTag();
    691             if (intentProvider != null) {
    692                 final Intent intent = intentProvider.getIntent(mContext);
    693                 // See IntentProvider.getCallDetailIntentProvider() for why this may be null.
    694                 if (intent != null) {
    695                     DialerUtils.startActivityWithErrorToast(mContext, intent);
    696                 }
    697             }
    698         }
    699     }
    700 
    701     @NeededForTesting
    702     public static CallLogListItemViewHolder createForTest(Context context) {
    703         Resources resources = context.getResources();
    704         CallLogCache callLogCache =
    705                 CallLogCache.getCallLogCache(context);
    706         PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
    707                 context, resources, callLogCache);
    708 
    709         CallLogListItemViewHolder viewHolder = new CallLogListItemViewHolder(
    710                 context,
    711                 null,
    712                 null /* expandCollapseListener */,
    713                 callLogCache,
    714                 new CallLogListItemHelper(phoneCallDetailsHelper, resources, callLogCache),
    715                 null /* voicemailPlaybackPresenter */,
    716                 null /* filteredNumberAsyncQueryHandler */,
    717                 null /* filteredNumberDialogCallback */,
    718                 new View(context),
    719                 new QuickContactBadge(context),
    720                 new View(context),
    721                 PhoneCallDetailsViews.createForTest(context),
    722                 new CardView(context),
    723                 new TextView(context),
    724                 new ImageView(context),
    725                 false);
    726         viewHolder.detailsButtonView = new TextView(context);
    727         viewHolder.actionsView = new View(context);
    728         viewHolder.voicemailPlaybackView = new VoicemailPlaybackLayout(context);
    729         viewHolder.workIconView = new ImageButton(context);
    730         return viewHolder;
    731     }
    732 }