1 /* 2 * Copyright (C) 2016 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.incallui.answer.impl; 18 19 import android.Manifest.permission; 20 import android.animation.Animator; 21 import android.animation.AnimatorListenerAdapter; 22 import android.animation.AnimatorSet; 23 import android.animation.ObjectAnimator; 24 import android.annotation.SuppressLint; 25 import android.annotation.TargetApi; 26 import android.app.KeyguardManager; 27 import android.app.KeyguardManager.KeyguardDismissCallback; 28 import android.content.Context; 29 import android.content.pm.PackageManager; 30 import android.location.Location; 31 import android.net.Uri; 32 import android.os.Build.VERSION; 33 import android.os.Build.VERSION_CODES; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.Looper; 37 import android.os.Trace; 38 import android.support.annotation.DrawableRes; 39 import android.support.annotation.FloatRange; 40 import android.support.annotation.NonNull; 41 import android.support.annotation.Nullable; 42 import android.support.annotation.StringRes; 43 import android.support.annotation.VisibleForTesting; 44 import android.support.v4.app.Fragment; 45 import android.text.TextUtils; 46 import android.transition.TransitionManager; 47 import android.view.LayoutInflater; 48 import android.view.View; 49 import android.view.View.AccessibilityDelegate; 50 import android.view.View.OnClickListener; 51 import android.view.ViewGroup; 52 import android.view.ViewTreeObserver.OnGlobalLayoutListener; 53 import android.view.accessibility.AccessibilityEvent; 54 import android.view.accessibility.AccessibilityNodeInfo; 55 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 56 import android.widget.ImageView; 57 import android.widget.LinearLayout; 58 import com.android.dialer.common.Assert; 59 import com.android.dialer.common.FragmentUtils; 60 import com.android.dialer.common.LogUtil; 61 import com.android.dialer.common.MathUtil; 62 import com.android.dialer.logging.DialerImpression; 63 import com.android.dialer.logging.Logger; 64 import com.android.dialer.multimedia.MultimediaData; 65 import com.android.dialer.telecom.TelecomUtil; 66 import com.android.dialer.util.ViewUtil; 67 import com.android.incallui.answer.impl.CreateCustomSmsDialogFragment.CreateCustomSmsHolder; 68 import com.android.incallui.answer.impl.SmsBottomSheetFragment.SmsSheetHolder; 69 import com.android.incallui.answer.impl.affordance.SwipeButtonHelper.Callback; 70 import com.android.incallui.answer.impl.affordance.SwipeButtonView; 71 import com.android.incallui.answer.impl.answermethod.AnswerMethod; 72 import com.android.incallui.answer.impl.answermethod.AnswerMethodFactory; 73 import com.android.incallui.answer.impl.answermethod.AnswerMethodHolder; 74 import com.android.incallui.answer.impl.utils.Interpolators; 75 import com.android.incallui.answer.protocol.AnswerScreen; 76 import com.android.incallui.answer.protocol.AnswerScreenDelegate; 77 import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory; 78 import com.android.incallui.call.state.DialerCallState; 79 import com.android.incallui.contactgrid.ContactGridManager; 80 import com.android.incallui.incall.protocol.ContactPhotoType; 81 import com.android.incallui.incall.protocol.InCallScreen; 82 import com.android.incallui.incall.protocol.InCallScreenDelegate; 83 import com.android.incallui.incall.protocol.InCallScreenDelegateFactory; 84 import com.android.incallui.incall.protocol.PrimaryCallState; 85 import com.android.incallui.incall.protocol.PrimaryInfo; 86 import com.android.incallui.incall.protocol.SecondaryInfo; 87 import com.android.incallui.incalluilock.InCallUiLock; 88 import com.android.incallui.maps.MapsComponent; 89 import com.android.incallui.sessiondata.AvatarPresenter; 90 import com.android.incallui.sessiondata.MultimediaFragment; 91 import com.android.incallui.speakeasy.Annotations.SpeakEasyChipResourceId; 92 import com.android.incallui.speakeasy.SpeakEasyComponent; 93 import com.android.incallui.util.AccessibilityUtil; 94 import com.android.incallui.video.protocol.VideoCallScreen; 95 import com.android.incallui.videotech.utils.VideoUtils; 96 import com.google.common.base.Optional; 97 import java.util.ArrayList; 98 import java.util.List; 99 import java.util.Objects; 100 101 /** The new version of the incoming call screen. */ 102 @SuppressLint("ClickableViewAccessibility") 103 public class AnswerFragment extends Fragment 104 implements AnswerScreen, 105 InCallScreen, 106 SmsSheetHolder, 107 CreateCustomSmsHolder, 108 AnswerMethodHolder, 109 MultimediaFragment.Holder { 110 111 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 112 static final String ARG_CALL_ID = "call_id"; 113 114 static final String ARG_IS_RTT_CALL = "is_rtt_call"; 115 116 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 117 static final String ARG_IS_VIDEO_CALL = "is_video_call"; 118 119 static final String ARG_ALLOW_ANSWER_AND_RELEASE = "allow_answer_and_release"; 120 121 static final String ARG_HAS_CALL_ON_HOLD = "has_call_on_hold"; 122 123 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 124 static final String ARG_IS_VIDEO_UPGRADE_REQUEST = "is_video_upgrade_request"; 125 126 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 127 static final String ARG_IS_SELF_MANAGED_CAMERA = "is_self_managed_camera"; 128 129 static final String ARG_ALLOW_SPEAK_EASY = "allow_speak_easy"; 130 131 private static final String STATE_HAS_ANIMATED_ENTRY = "hasAnimated"; 132 133 private static final int HINT_SECONDARY_SHOW_DURATION_MILLIS = 5000; 134 private static final float ANIMATE_LERP_PROGRESS = 0.5f; 135 private static final int STATUS_BAR_DISABLE_RECENT = 0x01000000; 136 private static final int STATUS_BAR_DISABLE_HOME = 0x00200000; 137 private static final int STATUS_BAR_DISABLE_BACK = 0x00400000; 138 fadeToward(View view, float newAlpha)139 private static void fadeToward(View view, float newAlpha) { 140 view.setAlpha(MathUtil.lerp(view.getAlpha(), newAlpha, ANIMATE_LERP_PROGRESS)); 141 } 142 scaleToward(View view, float newScale)143 private static void scaleToward(View view, float newScale) { 144 view.setScaleX(MathUtil.lerp(view.getScaleX(), newScale, ANIMATE_LERP_PROGRESS)); 145 view.setScaleY(MathUtil.lerp(view.getScaleY(), newScale, ANIMATE_LERP_PROGRESS)); 146 } 147 148 private AnswerScreenDelegate answerScreenDelegate; 149 private InCallScreenDelegate inCallScreenDelegate; 150 151 private View importanceBadge; 152 private SwipeButtonView secondaryButton; 153 private SwipeButtonView answerAndReleaseButton; 154 private AffordanceHolderLayout affordanceHolderLayout; 155 private LinearLayout chipContainer; 156 // Use these flags to prevent user from clicking accept/reject buttons multiple times. 157 // We use separate flags because in some rare cases accepting a call may fail to join the room, 158 // and then user is stuck in the incoming call view until it times out. Two flags at least give 159 // the user a chance to get out of the CallActivity. 160 private boolean buttonAcceptClicked; 161 private boolean buttonRejectClicked; 162 private boolean hasAnimatedEntry; 163 private PrimaryInfo primaryInfo = PrimaryInfo.empty(); 164 private PrimaryCallState primaryCallState; 165 private ArrayList<CharSequence> textResponses; 166 private SmsBottomSheetFragment textResponsesFragment; 167 private CreateCustomSmsDialogFragment createCustomSmsDialogFragment; 168 private SecondaryBehavior secondaryBehavior = SecondaryBehavior.REJECT_WITH_SMS; 169 private SecondaryBehavior answerAndReleaseBehavior; 170 private ContactGridManager contactGridManager; 171 private VideoCallScreen answerVideoCallScreen; 172 private Handler handler = new Handler(Looper.getMainLooper()); 173 174 private enum SecondaryBehavior { REJECT_WITH_SMS( R.drawable.quantum_ic_message_white_24, R.string.a11y_description_incoming_call_reject_with_sms, R.string.a11y_incoming_call_reject_with_sms, R.string.call_incoming_swipe_to_decline_with_message)175 REJECT_WITH_SMS( 176 R.drawable.quantum_ic_message_white_24, 177 R.string.a11y_description_incoming_call_reject_with_sms, 178 R.string.a11y_incoming_call_reject_with_sms, 179 R.string.call_incoming_swipe_to_decline_with_message) { 180 @Override 181 public void performAction(AnswerFragment fragment) { 182 fragment.showMessageMenu(); 183 } 184 }, 185 ANSWER_VIDEO_AS_AUDIO( R.drawable.quantum_ic_videocam_off_vd_theme_24, R.string.a11y_description_incoming_call_answer_video_as_audio, R.string.a11y_incoming_call_answer_video_as_audio, R.string.call_incoming_swipe_to_answer_video_as_audio)186 ANSWER_VIDEO_AS_AUDIO( 187 R.drawable.quantum_ic_videocam_off_vd_theme_24, 188 R.string.a11y_description_incoming_call_answer_video_as_audio, 189 R.string.a11y_incoming_call_answer_video_as_audio, 190 R.string.call_incoming_swipe_to_answer_video_as_audio) { 191 @Override 192 public void performAction(AnswerFragment fragment) { 193 fragment.acceptCallByUser(true /* answerVideoAsAudio */); 194 } 195 }, 196 ANSWER_AND_RELEASE( R.drawable.ic_end_answer_32, R.string.a11y_description_incoming_call_answer_and_release, R.string.a11y_incoming_call_answer_and_release, R.string.call_incoming_swipe_to_answer_and_release)197 ANSWER_AND_RELEASE( 198 R.drawable.ic_end_answer_32, 199 R.string.a11y_description_incoming_call_answer_and_release, 200 R.string.a11y_incoming_call_answer_and_release, 201 R.string.call_incoming_swipe_to_answer_and_release) { 202 @Override 203 public void performAction(AnswerFragment fragment) { 204 fragment.performAnswerAndRelease(); 205 } 206 }; 207 208 @DrawableRes public int icon; 209 @StringRes public final int contentDescription; 210 @StringRes public final int accessibilityLabel; 211 @StringRes public final int hintText; 212 SecondaryBehavior( @rawableRes int icon, @StringRes int contentDescription, @StringRes int accessibilityLabel, @StringRes int hintText)213 SecondaryBehavior( 214 @DrawableRes int icon, 215 @StringRes int contentDescription, 216 @StringRes int accessibilityLabel, 217 @StringRes int hintText) { 218 this.icon = icon; 219 this.contentDescription = contentDescription; 220 this.accessibilityLabel = accessibilityLabel; 221 this.hintText = hintText; 222 } 223 performAction(AnswerFragment fragment)224 public abstract void performAction(AnswerFragment fragment); 225 applyToView(ImageView view)226 public void applyToView(ImageView view) { 227 view.setImageResource(icon); 228 view.setContentDescription(view.getContext().getText(contentDescription)); 229 } 230 } 231 performSpeakEasy(View unused)232 private void performSpeakEasy(View unused) { 233 answerScreenDelegate.onSpeakEasyCall(); 234 buttonAcceptClicked = true; 235 } 236 performAnswerAndRelease()237 private void performAnswerAndRelease() { 238 restoreAnswerAndReleaseButtonAnimation(); 239 answerScreenDelegate.onAnswerAndReleaseCall(); 240 buttonAcceptClicked = true; 241 } 242 restoreAnswerAndReleaseButtonAnimation()243 private void restoreAnswerAndReleaseButtonAnimation() { 244 answerAndReleaseButton 245 .animate() 246 .alpha(0) 247 .withEndAction( 248 new Runnable() { 249 @Override 250 public void run() { 251 affordanceHolderLayout.reset(false); 252 secondaryButton.animate().alpha(1); 253 } 254 }); 255 } 256 257 private final AccessibilityDelegate accessibilityDelegate = 258 new AccessibilityDelegate() { 259 @Override 260 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 261 super.onInitializeAccessibilityNodeInfo(host, info); 262 if (host == secondaryButton) { 263 CharSequence label = getText(secondaryBehavior.accessibilityLabel); 264 info.addAction(new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, label)); 265 } else if (host == answerAndReleaseButton) { 266 CharSequence label = getText(answerAndReleaseBehavior.accessibilityLabel); 267 info.addAction(new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, label)); 268 } 269 } 270 271 @Override 272 public boolean performAccessibilityAction(View host, int action, Bundle args) { 273 if (action == AccessibilityNodeInfo.ACTION_CLICK) { 274 if (host == secondaryButton) { 275 performSecondaryButtonAction(); 276 return true; 277 } else if (host == answerAndReleaseButton) { 278 performAnswerAndReleaseButtonAction(); 279 return true; 280 } 281 } 282 return super.performAccessibilityAction(host, action, args); 283 } 284 }; 285 286 private final Callback affordanceCallback = 287 new Callback() { 288 @Override 289 public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) {} 290 291 @Override 292 public void onAnimationToSideEnded(boolean rightPage) { 293 if (rightPage) { 294 performAnswerAndReleaseButtonAction(); 295 } else { 296 performSecondaryButtonAction(); 297 } 298 } 299 300 @Override 301 public float getMaxTranslationDistance() { 302 View view = getView(); 303 if (view == null) { 304 return 0; 305 } 306 return (float) Math.hypot(view.getWidth(), view.getHeight()); 307 } 308 309 @Override 310 public void onSwipingStarted(boolean rightIcon) {} 311 312 @Override 313 public void onSwipingAborted() {} 314 315 @Override 316 public void onIconClicked(boolean rightIcon) { 317 affordanceHolderLayout.startHintAnimation(rightIcon, null); 318 getAnswerMethod() 319 .setHintText( 320 rightIcon 321 ? getText(answerAndReleaseBehavior.hintText) 322 : getText(secondaryBehavior.hintText)); 323 handler.removeCallbacks(swipeHintRestoreTimer); 324 handler.postDelayed(swipeHintRestoreTimer, HINT_SECONDARY_SHOW_DURATION_MILLIS); 325 } 326 327 @Override 328 public SwipeButtonView getLeftIcon() { 329 return secondaryButton; 330 } 331 332 @Override 333 public SwipeButtonView getRightIcon() { 334 return answerAndReleaseButton; 335 } 336 337 @Override 338 public View getLeftPreview() { 339 return null; 340 } 341 342 @Override 343 public View getRightPreview() { 344 return null; 345 } 346 347 @Override 348 public float getAffordanceFalsingFactor() { 349 return 1.0f; 350 } 351 }; 352 353 private Runnable swipeHintRestoreTimer = this::restoreSwipeHintTexts; 354 performSecondaryButtonAction()355 private void performSecondaryButtonAction() { 356 secondaryBehavior.performAction(this); 357 } 358 performAnswerAndReleaseButtonAction()359 private void performAnswerAndReleaseButtonAction() { 360 answerAndReleaseBehavior.performAction(this); 361 } 362 newInstance( String callId, boolean isRttCall, boolean isVideoCall, boolean isVideoUpgradeRequest, boolean isSelfManagedCamera, boolean allowAnswerAndRelease, boolean hasCallOnHold, boolean allowSpeakEasy)363 public static AnswerFragment newInstance( 364 String callId, 365 boolean isRttCall, 366 boolean isVideoCall, 367 boolean isVideoUpgradeRequest, 368 boolean isSelfManagedCamera, 369 boolean allowAnswerAndRelease, 370 boolean hasCallOnHold, 371 boolean allowSpeakEasy) { 372 Bundle bundle = new Bundle(); 373 bundle.putString(ARG_CALL_ID, Assert.isNotNull(callId)); 374 bundle.putBoolean(ARG_IS_RTT_CALL, isRttCall); 375 bundle.putBoolean(ARG_IS_VIDEO_CALL, isVideoCall); 376 bundle.putBoolean(ARG_IS_VIDEO_UPGRADE_REQUEST, isVideoUpgradeRequest); 377 bundle.putBoolean(ARG_IS_SELF_MANAGED_CAMERA, isSelfManagedCamera); 378 bundle.putBoolean(ARG_ALLOW_ANSWER_AND_RELEASE, allowAnswerAndRelease); 379 bundle.putBoolean(ARG_HAS_CALL_ON_HOLD, hasCallOnHold); 380 bundle.putBoolean(ARG_ALLOW_SPEAK_EASY, allowSpeakEasy); 381 382 AnswerFragment instance = new AnswerFragment(); 383 instance.setArguments(bundle); 384 return instance; 385 } 386 387 @Override isActionTimeout()388 public boolean isActionTimeout() { 389 return (buttonAcceptClicked || buttonRejectClicked) && answerScreenDelegate.isActionTimeout(); 390 } 391 392 @Override 393 @NonNull getCallId()394 public String getCallId() { 395 return Assert.isNotNull(getArguments().getString(ARG_CALL_ID)); 396 } 397 398 @Override isVideoUpgradeRequest()399 public boolean isVideoUpgradeRequest() { 400 return getArguments().getBoolean(ARG_IS_VIDEO_UPGRADE_REQUEST); 401 } 402 403 @Override setTextResponses(List<String> textResponses)404 public void setTextResponses(List<String> textResponses) { 405 if (isVideoCall() || isVideoUpgradeRequest()) { 406 LogUtil.i("AnswerFragment.setTextResponses", "no-op for video calls"); 407 } else if (textResponses == null) { 408 LogUtil.i("AnswerFragment.setTextResponses", "no text responses, hiding secondary button"); 409 this.textResponses = null; 410 secondaryButton.setVisibility(View.INVISIBLE); 411 } else if (getActivity().isInMultiWindowMode()) { 412 LogUtil.i("AnswerFragment.setTextResponses", "in multiwindow, hiding secondary button"); 413 this.textResponses = null; 414 secondaryButton.setVisibility(View.INVISIBLE); 415 } else { 416 LogUtil.i("AnswerFragment.setTextResponses", "textResponses.size: " + textResponses.size()); 417 this.textResponses = new ArrayList<>(textResponses); 418 secondaryButton.setVisibility(View.VISIBLE); 419 } 420 } 421 initSecondaryButton()422 private void initSecondaryButton() { 423 secondaryBehavior = 424 isVideoCall() || isVideoUpgradeRequest() 425 ? SecondaryBehavior.ANSWER_VIDEO_AS_AUDIO 426 : SecondaryBehavior.REJECT_WITH_SMS; 427 secondaryBehavior.applyToView(secondaryButton); 428 429 secondaryButton.setOnClickListener( 430 new OnClickListener() { 431 @Override 432 public void onClick(View v) { 433 performSecondaryButtonAction(); 434 } 435 }); 436 secondaryButton.setClickable(AccessibilityUtil.isAccessibilityEnabled(getContext())); 437 secondaryButton.setFocusable(AccessibilityUtil.isAccessibilityEnabled(getContext())); 438 secondaryButton.setAccessibilityDelegate(accessibilityDelegate); 439 440 if (isVideoUpgradeRequest()) { 441 secondaryButton.setVisibility(View.INVISIBLE); 442 } else if (isVideoCall()) { 443 secondaryButton.setVisibility(View.VISIBLE); 444 } 445 446 answerAndReleaseBehavior = SecondaryBehavior.ANSWER_AND_RELEASE; 447 answerAndReleaseBehavior.applyToView(answerAndReleaseButton); 448 449 answerAndReleaseButton.setClickable(AccessibilityUtil.isAccessibilityEnabled(getContext())); 450 answerAndReleaseButton.setFocusable(AccessibilityUtil.isAccessibilityEnabled(getContext())); 451 answerAndReleaseButton.setAccessibilityDelegate(accessibilityDelegate); 452 453 if (allowAnswerAndRelease()) { 454 answerAndReleaseButton.setVisibility(View.VISIBLE); 455 answerScreenDelegate.onAnswerAndReleaseButtonEnabled(); 456 } else { 457 answerAndReleaseButton.setVisibility(View.INVISIBLE); 458 answerScreenDelegate.onAnswerAndReleaseButtonDisabled(); 459 } 460 answerAndReleaseButton.setOnClickListener( 461 new OnClickListener() { 462 @Override 463 public void onClick(View v) { 464 performAnswerAndReleaseButtonAction(); 465 } 466 }); 467 } 468 469 /** Initialize chip buttons */ initChips()470 private void initChips() { 471 472 if (!allowSpeakEasy()) { 473 chipContainer.setVisibility(View.GONE); 474 return; 475 } 476 chipContainer.setVisibility(View.VISIBLE); 477 478 @SpeakEasyChipResourceId 479 Optional<Integer> chipLayoutOptional = SpeakEasyComponent.get(getContext()).speakEasyChip(); 480 if (chipLayoutOptional.isPresent()) { 481 482 LinearLayout chipLayout = 483 (LinearLayout) getLayoutInflater().inflate(chipLayoutOptional.get(), null); 484 485 chipLayout.setOnClickListener(this::performSpeakEasy); 486 487 chipContainer.addView(chipLayout); 488 } 489 } 490 491 @Override allowAnswerAndRelease()492 public boolean allowAnswerAndRelease() { 493 return getArguments().getBoolean(ARG_ALLOW_ANSWER_AND_RELEASE); 494 } 495 496 @Override allowSpeakEasy()497 public boolean allowSpeakEasy() { 498 return getArguments().getBoolean(ARG_ALLOW_SPEAK_EASY); 499 } 500 hasCallOnHold()501 private boolean hasCallOnHold() { 502 return getArguments().getBoolean(ARG_HAS_CALL_ON_HOLD); 503 } 504 505 @Override hasPendingDialogs()506 public boolean hasPendingDialogs() { 507 boolean hasPendingDialogs = 508 textResponsesFragment != null || createCustomSmsDialogFragment != null; 509 LogUtil.i("AnswerFragment.hasPendingDialogs", "" + hasPendingDialogs); 510 return hasPendingDialogs; 511 } 512 513 @Override dismissPendingDialogs()514 public void dismissPendingDialogs() { 515 LogUtil.i("AnswerFragment.dismissPendingDialogs", null); 516 if (textResponsesFragment != null) { 517 textResponsesFragment.dismiss(); 518 textResponsesFragment = null; 519 } 520 521 if (createCustomSmsDialogFragment != null) { 522 createCustomSmsDialogFragment.dismiss(); 523 createCustomSmsDialogFragment = null; 524 } 525 } 526 527 @Override isShowingLocationUi()528 public boolean isShowingLocationUi() { 529 Fragment fragment = getChildFragmentManager().findFragmentById(R.id.incall_location_holder); 530 return fragment != null && fragment.isVisible(); 531 } 532 533 @Override showLocationUi(@ullable Fragment locationUi)534 public void showLocationUi(@Nullable Fragment locationUi) { 535 boolean isShowing = isShowingLocationUi(); 536 if (!isShowing && locationUi != null) { 537 // Show the location fragment. 538 getChildFragmentManager() 539 .beginTransaction() 540 .replace(R.id.incall_location_holder, locationUi) 541 .commitAllowingStateLoss(); 542 } else if (isShowing && locationUi == null) { 543 // Hide the location fragment 544 Fragment fragment = getChildFragmentManager().findFragmentById(R.id.incall_location_holder); 545 getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss(); 546 } 547 } 548 549 @Override getAnswerScreenFragment()550 public Fragment getAnswerScreenFragment() { 551 return this; 552 } 553 getAnswerMethod()554 private AnswerMethod getAnswerMethod() { 555 return ((AnswerMethod) 556 getChildFragmentManager().findFragmentById(R.id.answer_method_container)); 557 } 558 559 @Override setPrimary(PrimaryInfo primaryInfo)560 public void setPrimary(PrimaryInfo primaryInfo) { 561 LogUtil.i("AnswerFragment.setPrimary", primaryInfo.toString()); 562 this.primaryInfo = primaryInfo; 563 updatePrimaryUI(); 564 updateImportanceBadgeVisibility(); 565 } 566 updatePrimaryUI()567 private void updatePrimaryUI() { 568 if (getView() == null) { 569 return; 570 } 571 contactGridManager.setPrimary(primaryInfo); 572 getAnswerMethod().setShowIncomingWillDisconnect(primaryInfo.answeringDisconnectsOngoingCall()); 573 getAnswerMethod() 574 .setContactPhoto( 575 primaryInfo.photoType() == ContactPhotoType.CONTACT ? primaryInfo.photo() : null); 576 updateDataFragment(); 577 578 if (primaryInfo.shouldShowLocation()) { 579 // Hide the avatar to make room for location 580 contactGridManager.setAvatarHidden(true); 581 } 582 } 583 updateDataFragment()584 private void updateDataFragment() { 585 if (!isAdded()) { 586 return; 587 } 588 LogUtil.enterBlock("AnswerFragment.updateDataFragment"); 589 Fragment current = getChildFragmentManager().findFragmentById(R.id.incall_data_container); 590 Fragment newFragment = null; 591 592 MultimediaData multimediaData = getSessionData(); 593 if (multimediaData != null 594 && (!TextUtils.isEmpty(multimediaData.getText()) 595 || (multimediaData.getImageUri() != null) 596 || (multimediaData.getLocation() != null && canShowMap()))) { 597 // Need message fragment 598 String subject = multimediaData.getText(); 599 Uri imageUri = multimediaData.getImageUri(); 600 Location location = multimediaData.getLocation(); 601 if (!(current instanceof MultimediaFragment) 602 || !Objects.equals(((MultimediaFragment) current).getSubject(), subject) 603 || !Objects.equals(((MultimediaFragment) current).getImageUri(), imageUri) 604 || !Objects.equals(((MultimediaFragment) current).getLocation(), location)) { 605 LogUtil.i("AnswerFragment.updateDataFragment", "Replacing multimedia fragment"); 606 // Needs replacement 607 newFragment = 608 MultimediaFragment.newInstance( 609 multimediaData, 610 false /* isInteractive */, 611 !primaryInfo.isSpam() /* showAvatar */, 612 primaryInfo.isSpam()); 613 } 614 } else if (shouldShowAvatar()) { 615 // Needs Avatar 616 if (!(current instanceof AvatarFragment)) { 617 LogUtil.i("AnswerFragment.updateDataFragment", "Replacing avatar fragment"); 618 // Needs replacement 619 newFragment = new AvatarFragment(); 620 } 621 } else { 622 // Needs empty 623 if (current != null) { 624 LogUtil.i("AnswerFragment.updateDataFragment", "Removing current fragment"); 625 getChildFragmentManager().beginTransaction().remove(current).commitNow(); 626 } 627 contactGridManager.setAvatarImageView(null, 0, false); 628 } 629 630 if (newFragment != null) { 631 getChildFragmentManager() 632 .beginTransaction() 633 .replace(R.id.incall_data_container, newFragment) 634 .commitNow(); 635 } 636 } 637 shouldShowAvatar()638 private boolean shouldShowAvatar() { 639 return !isVideoCall() && !isVideoUpgradeRequest(); 640 } 641 canShowMap()642 private boolean canShowMap() { 643 return MapsComponent.get(getContext()).getMaps().isAvailable(); 644 } 645 646 @Override updateAvatar(AvatarPresenter avatarContainer)647 public void updateAvatar(AvatarPresenter avatarContainer) { 648 contactGridManager.setAvatarImageView( 649 avatarContainer.getAvatarImageView(), 650 avatarContainer.getAvatarSize(), 651 avatarContainer.shouldShowAnonymousAvatar()); 652 } 653 654 @Override setSecondary(@onNull SecondaryInfo secondaryInfo)655 public void setSecondary(@NonNull SecondaryInfo secondaryInfo) {} 656 657 @Override setCallState(@onNull PrimaryCallState primaryCallState)658 public void setCallState(@NonNull PrimaryCallState primaryCallState) { 659 LogUtil.i("AnswerFragment.setCallState", primaryCallState.toString()); 660 this.primaryCallState = primaryCallState; 661 contactGridManager.setCallState(primaryCallState); 662 } 663 664 @Override setEndCallButtonEnabled(boolean enabled, boolean animate)665 public void setEndCallButtonEnabled(boolean enabled, boolean animate) {} 666 667 @Override showManageConferenceCallButton(boolean visible)668 public void showManageConferenceCallButton(boolean visible) {} 669 670 @Override isManageConferenceVisible()671 public boolean isManageConferenceVisible() { 672 return false; 673 } 674 675 @Override dispatchPopulateAccessibilityEvent(AccessibilityEvent event)676 public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 677 contactGridManager.dispatchPopulateAccessibilityEvent(event); 678 // Add prompt of how to accept/decline call with swipe gesture. 679 if (AccessibilityUtil.isTouchExplorationEnabled(getContext())) { 680 event 681 .getText() 682 .add(getResources().getString(R.string.a11y_incoming_call_swipe_gesture_prompt)); 683 } 684 } 685 686 @Override showNoteSentToast()687 public void showNoteSentToast() {} 688 689 @Override updateInCallScreenColors()690 public void updateInCallScreenColors() {} 691 692 @Override onInCallScreenDialpadVisibilityChange(boolean isShowing)693 public void onInCallScreenDialpadVisibilityChange(boolean isShowing) {} 694 695 @Override getAnswerAndDialpadContainerResourceId()696 public int getAnswerAndDialpadContainerResourceId() { 697 throw Assert.createUnsupportedOperationFailException(); 698 } 699 700 @Override getInCallScreenFragment()701 public Fragment getInCallScreenFragment() { 702 return this; 703 } 704 705 @Override onDestroy()706 public void onDestroy() { 707 super.onDestroy(); 708 } 709 710 @Override onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)711 public View onCreateView( 712 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 713 Trace.beginSection("AnswerFragment.onCreateView"); 714 Bundle arguments = getArguments(); 715 Assert.checkState(arguments.containsKey(ARG_CALL_ID)); 716 Assert.checkState(arguments.containsKey(ARG_IS_RTT_CALL)); 717 Assert.checkState(arguments.containsKey(ARG_IS_VIDEO_CALL)); 718 Assert.checkState(arguments.containsKey(ARG_IS_VIDEO_UPGRADE_REQUEST)); 719 720 buttonAcceptClicked = false; 721 buttonRejectClicked = false; 722 723 View view = inflater.inflate(R.layout.fragment_incoming_call, container, false); 724 secondaryButton = (SwipeButtonView) view.findViewById(R.id.incoming_secondary_button); 725 answerAndReleaseButton = (SwipeButtonView) view.findViewById(R.id.incoming_secondary_button2); 726 727 affordanceHolderLayout = (AffordanceHolderLayout) view.findViewById(R.id.incoming_container); 728 affordanceHolderLayout.setAffordanceCallback(affordanceCallback); 729 730 chipContainer = view.findViewById(R.id.incall_data_container_chip_container); 731 732 importanceBadge = view.findViewById(R.id.incall_important_call_badge); 733 importanceBadge 734 .getViewTreeObserver() 735 .addOnGlobalLayoutListener( 736 new OnGlobalLayoutListener() { 737 @Override 738 public void onGlobalLayout() { 739 int leftRightPadding = importanceBadge.getHeight() / 2; 740 importanceBadge.setPadding( 741 leftRightPadding, 742 importanceBadge.getPaddingTop(), 743 leftRightPadding, 744 importanceBadge.getPaddingBottom()); 745 } 746 }); 747 updateImportanceBadgeVisibility(); 748 749 contactGridManager = new ContactGridManager(view, null, 0, false /* showAnonymousAvatar */); 750 boolean isInMultiWindowMode = getActivity().isInMultiWindowMode(); 751 contactGridManager.onMultiWindowModeChanged(isInMultiWindowMode); 752 753 Fragment answerMethod = 754 getChildFragmentManager().findFragmentById(R.id.answer_method_container); 755 if (AnswerMethodFactory.needsReplacement(answerMethod)) { 756 getChildFragmentManager() 757 .beginTransaction() 758 .replace( 759 R.id.answer_method_container, AnswerMethodFactory.createAnswerMethod(getActivity())) 760 .commitNow(); 761 } 762 763 answerScreenDelegate = 764 FragmentUtils.getParentUnsafe(this, AnswerScreenDelegateFactory.class) 765 .newAnswerScreenDelegate(this); 766 767 initSecondaryButton(); 768 initChips(); 769 770 int flags = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 771 if (!isInMultiWindowMode 772 && (getActivity().checkSelfPermission(permission.STATUS_BAR) 773 == PackageManager.PERMISSION_GRANTED)) { 774 LogUtil.i("AnswerFragment.onCreateView", "STATUS_BAR permission granted, disabling nav bar"); 775 // These flags will suppress the alert that the activity is in full view mode 776 // during an incoming call on a fresh system/factory reset of the app 777 flags |= STATUS_BAR_DISABLE_BACK | STATUS_BAR_DISABLE_HOME | STATUS_BAR_DISABLE_RECENT; 778 } 779 view.setSystemUiVisibility(flags); 780 if (isVideoCall() || isVideoUpgradeRequest()) { 781 if (VideoUtils.hasCameraPermissionAndShownPrivacyToast(getContext())) { 782 if (isSelfManagedCamera()) { 783 answerVideoCallScreen = new SelfManagedAnswerVideoCallScreen(getCallId(), this, view); 784 } else { 785 answerVideoCallScreen = new AnswerVideoCallScreen(getCallId(), this, view); 786 } 787 } else { 788 view.findViewById(R.id.videocall_video_off).setVisibility(View.VISIBLE); 789 } 790 } 791 792 Trace.endSection(); 793 return view; 794 } 795 796 @Override onAttach(Context context)797 public void onAttach(Context context) { 798 super.onAttach(context); 799 FragmentUtils.checkParent(this, InCallScreenDelegateFactory.class); 800 } 801 802 @Override onViewCreated(final View view, @Nullable Bundle savedInstanceState)803 public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) { 804 Trace.beginSection("AnswerFragment.onViewCreated"); 805 super.onViewCreated(view, savedInstanceState); 806 createInCallScreenDelegate(); 807 updateUI(); 808 809 if (savedInstanceState == null || !savedInstanceState.getBoolean(STATE_HAS_ANIMATED_ENTRY)) { 810 ViewUtil.doOnGlobalLayout(view, this::animateEntry); 811 } 812 Trace.endSection(); 813 } 814 815 @Override onResume()816 public void onResume() { 817 Trace.beginSection("AnswerFragment.onResume"); 818 super.onResume(); 819 LogUtil.i("AnswerFragment.onResume", null); 820 restoreSwipeHintTexts(); 821 inCallScreenDelegate.onInCallScreenResumed(); 822 Trace.endSection(); 823 } 824 825 @Override onStart()826 public void onStart() { 827 Trace.beginSection("AnswerFragment.onStart"); 828 super.onStart(); 829 LogUtil.i("AnswerFragment.onStart", null); 830 831 updateUI(); 832 if (answerVideoCallScreen != null) { 833 answerVideoCallScreen.onVideoScreenStart(); 834 } 835 Trace.endSection(); 836 } 837 838 @Override onStop()839 public void onStop() { 840 Trace.beginSection("AnswerFragment.onStop"); 841 super.onStop(); 842 LogUtil.i("AnswerFragment.onStop", null); 843 844 handler.removeCallbacks(swipeHintRestoreTimer); 845 if (answerVideoCallScreen != null) { 846 answerVideoCallScreen.onVideoScreenStop(); 847 } 848 Trace.endSection(); 849 } 850 851 @Override onPause()852 public void onPause() { 853 Trace.beginSection("AnswerFragment.onPause"); 854 super.onPause(); 855 LogUtil.i("AnswerFragment.onPause", null); 856 inCallScreenDelegate.onInCallScreenPaused(); 857 Trace.endSection(); 858 } 859 860 @Override onDestroyView()861 public void onDestroyView() { 862 LogUtil.i("AnswerFragment.onDestroyView", null); 863 if (answerVideoCallScreen != null) { 864 answerVideoCallScreen = null; 865 } 866 super.onDestroyView(); 867 inCallScreenDelegate.onInCallScreenUnready(); 868 answerScreenDelegate.onAnswerScreenUnready(); 869 } 870 871 @Override onSaveInstanceState(Bundle bundle)872 public void onSaveInstanceState(Bundle bundle) { 873 super.onSaveInstanceState(bundle); 874 bundle.putBoolean(STATE_HAS_ANIMATED_ENTRY, hasAnimatedEntry); 875 } 876 updateUI()877 private void updateUI() { 878 if (getView() == null) { 879 return; 880 } 881 882 if (primaryInfo != null) { 883 updatePrimaryUI(); 884 } 885 if (primaryCallState != null) { 886 contactGridManager.setCallState(primaryCallState); 887 } 888 889 restoreBackgroundMaskColor(); 890 } 891 892 @Override isRttCall()893 public boolean isRttCall() { 894 return getArguments().getBoolean(ARG_IS_RTT_CALL); 895 } 896 897 @Override isVideoCall()898 public boolean isVideoCall() { 899 return getArguments().getBoolean(ARG_IS_VIDEO_CALL); 900 } 901 isSelfManagedCamera()902 public boolean isSelfManagedCamera() { 903 return getArguments().getBoolean(ARG_IS_SELF_MANAGED_CAMERA); 904 } 905 906 @Override onAnswerProgressUpdate(@loatRangefrom = -1f, to = 1f) float answerProgress)907 public void onAnswerProgressUpdate(@FloatRange(from = -1f, to = 1f) float answerProgress) { 908 // Don't fade the window background for call waiting or video upgrades. Fading the background 909 // shows the system wallpaper which looks bad because on reject we switch to another call. 910 if (primaryCallState.state() == DialerCallState.INCOMING && !isVideoCall()) { 911 answerScreenDelegate.updateWindowBackgroundColor(answerProgress); 912 } 913 914 // Fade and scale contact name and video call text 915 float startDelay = .25f; 916 // Header progress is zero over positiveAdjustedProgress = [0, startDelay], 917 // linearly increases over (startDelay, 1] until reaching 1 when positiveAdjustedProgress = 1 918 float headerProgress = Math.max(0, (Math.abs(answerProgress) - 1) / (1 - startDelay) + 1); 919 fadeToward(contactGridManager.getContainerView(), 1 - headerProgress); 920 scaleToward(contactGridManager.getContainerView(), MathUtil.lerp(1f, .75f, headerProgress)); 921 922 if (Math.abs(answerProgress) >= .0001) { 923 affordanceHolderLayout.animateHideLeftRightIcon(); 924 handler.removeCallbacks(swipeHintRestoreTimer); 925 restoreSwipeHintTexts(); 926 } 927 } 928 929 @Override answerFromMethod()930 public void answerFromMethod() { 931 acceptCallByUser(false /* answerVideoAsAudio */); 932 } 933 934 @Override rejectFromMethod()935 public void rejectFromMethod() { 936 rejectCall(); 937 } 938 939 @Override resetAnswerProgress()940 public void resetAnswerProgress() { 941 affordanceHolderLayout.reset(true); 942 restoreBackgroundMaskColor(); 943 } 944 animateEntry(@onNull View rootView)945 private void animateEntry(@NonNull View rootView) { 946 if (!isAdded()) { 947 LogUtil.i( 948 "AnswerFragment.animateEntry", 949 "Not currently added to Activity. Will not start entry animation."); 950 return; 951 } 952 contactGridManager.getContainerView().setAlpha(0f); 953 Animator alpha = 954 ObjectAnimator.ofFloat(contactGridManager.getContainerView(), View.ALPHA, 0, 1); 955 Animator topRow = createTranslation(rootView.findViewById(R.id.contactgrid_top_row)); 956 Animator contactName = createTranslation(rootView.findViewById(R.id.contactgrid_contact_name)); 957 Animator bottomRow = createTranslation(rootView.findViewById(R.id.contactgrid_bottom_row)); 958 Animator important = createTranslation(importanceBadge); 959 Animator dataContainer = createTranslation(rootView.findViewById(R.id.incall_data_container)); 960 961 AnimatorSet animatorSet = new AnimatorSet(); 962 AnimatorSet.Builder builder = animatorSet.play(alpha); 963 builder.with(topRow).with(contactName).with(bottomRow).with(important).with(dataContainer); 964 if (isShowingLocationUi()) { 965 builder.with(createTranslation(rootView.findViewById(R.id.incall_location_holder))); 966 } 967 animatorSet.setDuration( 968 rootView.getResources().getInteger(R.integer.answer_animate_entry_millis)); 969 animatorSet.addListener( 970 new AnimatorListenerAdapter() { 971 @Override 972 public void onAnimationEnd(Animator animation) { 973 hasAnimatedEntry = true; 974 } 975 }); 976 animatorSet.start(); 977 } 978 createTranslation(View view)979 private ObjectAnimator createTranslation(View view) { 980 float translationY = view.getTop() * 0.5f; 981 ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, translationY, 0); 982 animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); 983 return animator; 984 } 985 acceptCallByUser(boolean answerVideoAsAudio)986 private void acceptCallByUser(boolean answerVideoAsAudio) { 987 LogUtil.i("AnswerFragment.acceptCallByUser", answerVideoAsAudio ? " answerVideoAsAudio" : ""); 988 if (!buttonAcceptClicked) { 989 answerScreenDelegate.onAnswer(answerVideoAsAudio); 990 buttonAcceptClicked = true; 991 } 992 } 993 rejectCall()994 private void rejectCall() { 995 LogUtil.i("AnswerFragment.rejectCall", null); 996 if (!buttonRejectClicked) { 997 Context context = getContext(); 998 if (context == null) { 999 LogUtil.w( 1000 "AnswerFragment.rejectCall", 1001 "Null context when rejecting call. Logger call was skipped"); 1002 } else { 1003 Logger.get(context) 1004 .logImpression(DialerImpression.Type.REJECT_INCOMING_CALL_FROM_ANSWER_SCREEN); 1005 } 1006 buttonRejectClicked = true; 1007 answerScreenDelegate.onReject(); 1008 } 1009 } 1010 restoreBackgroundMaskColor()1011 private void restoreBackgroundMaskColor() { 1012 answerScreenDelegate.updateWindowBackgroundColor(0); 1013 } 1014 restoreSwipeHintTexts()1015 private void restoreSwipeHintTexts() { 1016 if (getAnswerMethod() != null) { 1017 if (allowAnswerAndRelease()) { 1018 if (hasCallOnHold()) { 1019 getAnswerMethod() 1020 .setHintText(getText(R.string.call_incoming_default_label_answer_and_release_third)); 1021 } else if (primaryCallState.supportsCallOnHold()) { 1022 getAnswerMethod() 1023 .setHintText(getText(R.string.call_incoming_default_label_answer_and_release_second)); 1024 } 1025 } else { 1026 getAnswerMethod().setHintText(null); 1027 } 1028 } 1029 } 1030 showMessageMenu()1031 private void showMessageMenu() { 1032 LogUtil.i("AnswerFragment.showMessageMenu", "Show sms menu."); 1033 if (getContext() == null || isDetached() || getChildFragmentManager().isDestroyed()) { 1034 return; 1035 } 1036 1037 textResponsesFragment = SmsBottomSheetFragment.newInstance(textResponses); 1038 textResponsesFragment.show(getChildFragmentManager(), null); 1039 secondaryButton 1040 .animate() 1041 .alpha(0) 1042 .withEndAction( 1043 new Runnable() { 1044 @Override 1045 public void run() { 1046 affordanceHolderLayout.reset(false); 1047 secondaryButton.animate().alpha(1); 1048 } 1049 }); 1050 1051 TelecomUtil.silenceRinger(getContext()); 1052 } 1053 1054 @Override acquireInCallUiLock(String tag)1055 public InCallUiLock acquireInCallUiLock(String tag) { 1056 return answerScreenDelegate.acquireInCallUiLock(tag); 1057 } 1058 1059 @Override 1060 @TargetApi(VERSION_CODES.O) smsSelected(@ullable CharSequence text)1061 public void smsSelected(@Nullable CharSequence text) { 1062 LogUtil.i("AnswerFragment.smsSelected", null); 1063 textResponsesFragment = null; 1064 1065 if (text == null) { 1066 if (VERSION.SDK_INT < VERSION_CODES.O) { 1067 LogUtil.i("AnswerFragment.smsSelected", "below O, showing dialog directly"); 1068 showCustomSmsDialog(); 1069 return; 1070 } 1071 if (!getContext().getSystemService(KeyguardManager.class).isKeyguardLocked()) { 1072 LogUtil.i("AnswerFragment.smsSelected", "not locked, showing dialog directly"); 1073 showCustomSmsDialog(); 1074 return; 1075 } 1076 1077 // Show the custom reply dialog only after device is unlocked, as it may cause impersonation 1078 // see b/137134588 1079 LogUtil.i("AnswerFragment.smsSelected", "dismissing keyguard"); 1080 getContext() 1081 .getSystemService(KeyguardManager.class) 1082 .requestDismissKeyguard( 1083 getActivity(), 1084 new KeyguardDismissCallback() { 1085 @Override 1086 public void onDismissCancelled() { 1087 LogUtil.i("AnswerFragment.smsSelected", "onDismissCancelled"); 1088 } 1089 1090 @Override 1091 public void onDismissError() { 1092 LogUtil.i("AnswerFragment.smsSelected", "onDismissError"); 1093 } 1094 1095 @Override 1096 public void onDismissSucceeded() { 1097 LogUtil.i("AnswerFragment.smsSelected", "onDismissSucceeded"); 1098 showCustomSmsDialog(); 1099 } 1100 });return; 1101 } 1102 1103 if (primaryCallState != null && canRejectCallWithSms()) { 1104 rejectCall(); 1105 answerScreenDelegate.onRejectCallWithMessage(text.toString()); 1106 } 1107 } 1108 showCustomSmsDialog()1109 private void showCustomSmsDialog() { 1110 createCustomSmsDialogFragment = CreateCustomSmsDialogFragment.newInstance(); 1111 createCustomSmsDialogFragment.showNow(getChildFragmentManager(), null); 1112 } 1113 1114 @Override smsDismissed()1115 public void smsDismissed() { 1116 LogUtil.i("AnswerFragment.smsDismissed", null); 1117 textResponsesFragment = null; 1118 } 1119 1120 @Override customSmsCreated(@onNull CharSequence text)1121 public void customSmsCreated(@NonNull CharSequence text) { 1122 LogUtil.i("AnswerFragment.customSmsCreated", null); 1123 createCustomSmsDialogFragment = null; 1124 if (primaryCallState != null && canRejectCallWithSms()) { 1125 rejectCall(); 1126 answerScreenDelegate.onRejectCallWithMessage(text.toString()); 1127 } 1128 } 1129 1130 @Override customSmsDismissed()1131 public void customSmsDismissed() { 1132 LogUtil.i("AnswerFragment.customSmsDismissed", null); 1133 createCustomSmsDialogFragment = null; 1134 } 1135 canRejectCallWithSms()1136 private boolean canRejectCallWithSms() { 1137 return primaryCallState != null 1138 && !(primaryCallState.state() == DialerCallState.DISCONNECTED 1139 || primaryCallState.state() == DialerCallState.DISCONNECTING 1140 || primaryCallState.state() == DialerCallState.IDLE); 1141 } 1142 createInCallScreenDelegate()1143 private void createInCallScreenDelegate() { 1144 inCallScreenDelegate = 1145 FragmentUtils.getParentUnsafe(this, InCallScreenDelegateFactory.class) 1146 .newInCallScreenDelegate(); 1147 Assert.isNotNull(inCallScreenDelegate); 1148 inCallScreenDelegate.onInCallScreenDelegateInit(this); 1149 inCallScreenDelegate.onInCallScreenReady(); 1150 } 1151 updateImportanceBadgeVisibility()1152 private void updateImportanceBadgeVisibility() { 1153 if (!isAdded() || getView() == null) { 1154 return; 1155 } 1156 1157 if (!getResources().getBoolean(R.bool.answer_important_call_allowed) || primaryInfo.isSpam()) { 1158 importanceBadge.setVisibility(View.GONE); 1159 return; 1160 } 1161 1162 MultimediaData multimediaData = getSessionData(); 1163 boolean showImportant = multimediaData != null && multimediaData.isImportant(); 1164 TransitionManager.beginDelayedTransition((ViewGroup) importanceBadge.getParent()); 1165 // TODO (keyboardr): Change this back to being View.INVISIBLE once mocks are available to 1166 // properly handle smaller screens 1167 importanceBadge.setVisibility(showImportant ? View.VISIBLE : View.GONE); 1168 } 1169 1170 @Nullable getSessionData()1171 private MultimediaData getSessionData() { 1172 if (primaryInfo == null) { 1173 return null; 1174 } 1175 if (isVideoUpgradeRequest()) { 1176 return null; 1177 } 1178 return primaryInfo.multimediaData(); 1179 } 1180 1181 /** Shows the Avatar image if available. */ 1182 public static class AvatarFragment extends Fragment implements AvatarPresenter { 1183 1184 private ImageView avatarImageView; 1185 1186 @Nullable 1187 @Override onCreateView( LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle)1188 public View onCreateView( 1189 LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) { 1190 return layoutInflater.inflate(R.layout.fragment_avatar, viewGroup, false); 1191 } 1192 1193 @Override onViewCreated(View view, @Nullable Bundle bundle)1194 public void onViewCreated(View view, @Nullable Bundle bundle) { 1195 super.onViewCreated(view, bundle); 1196 avatarImageView = ((ImageView) view.findViewById(R.id.contactgrid_avatar)); 1197 FragmentUtils.getParentUnsafe(this, MultimediaFragment.Holder.class).updateAvatar(this); 1198 } 1199 1200 @NonNull 1201 @Override getAvatarImageView()1202 public ImageView getAvatarImageView() { 1203 return avatarImageView; 1204 } 1205 1206 @Override getAvatarSize()1207 public int getAvatarSize() { 1208 return getResources().getDimensionPixelSize(R.dimen.answer_avatar_size); 1209 } 1210 1211 @Override shouldShowAnonymousAvatar()1212 public boolean shouldShowAnonymousAvatar() { 1213 return false; 1214 } 1215 } 1216 } 1217