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 }