• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CallLogListItemViewHolder( Context context, ExtendedBlockingButtonRenderer.Listener eventListener, View.OnClickListener expandCollapseListener, CallLogCache callLogCache, CallLogListItemHelper callLogListItemHelper, VoicemailPlaybackPresenter voicemailPlaybackPresenter, FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler, BlockNumberDialogFragment.Callback filteredNumberDialogCallback, View rootView, QuickContactBadge quickContactView, View primaryActionView, PhoneCallDetailsViews phoneCallDetailsViews, CardView callLogEntryView, TextView dayGroupHeader, ImageView primaryActionButtonView, boolean isArchiveTab)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 
create( View view, Context context, ExtendedBlockingButtonRenderer.Listener eventListener, View.OnClickListener expandCollapseListener, CallLogCache callLogCache, CallLogListItemHelper callLogListItemHelper, VoicemailPlaybackPresenter voicemailPlaybackPresenter, FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler, BlockNumberDialogFragment.Callback filteredNumberDialogCallback, boolean isArchiveTab)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
onCreateContextMenu( final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)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
onMenuItemClick(MenuItem item)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      */
inflateActionViewStub()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 
updatePrimaryActionButton(boolean isExpanded)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      */
bindActionButtons()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      */
showActions(boolean show)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 
showOrHideVoicemailTranscriptionView(boolean isExpanded)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 
updatePhoto()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
onClick(View view)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
createForTest(Context context)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 }