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.incall.impl; 18 19 import android.Manifest.permission; 20 import android.annotation.SuppressLint; 21 import android.app.Activity; 22 import android.content.Context; 23 import android.content.pm.PackageManager; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.support.annotation.ColorInt; 27 import android.support.annotation.NonNull; 28 import android.support.annotation.Nullable; 29 import android.support.v4.app.Fragment; 30 import android.support.v4.app.FragmentTransaction; 31 import android.support.v4.content.ContextCompat; 32 import android.telecom.CallAudioState; 33 import android.telephony.TelephonyManager; 34 import android.transition.TransitionManager; 35 import android.view.LayoutInflater; 36 import android.view.View; 37 import android.view.View.OnAttachStateChangeListener; 38 import android.view.View.OnClickListener; 39 import android.view.ViewGroup; 40 import android.view.Window; 41 import android.view.accessibility.AccessibilityEvent; 42 import android.widget.ImageView; 43 import android.widget.RelativeLayout; 44 import android.widget.Toast; 45 import com.android.dialer.common.Assert; 46 import com.android.dialer.common.FragmentUtils; 47 import com.android.dialer.common.LogUtil; 48 import com.android.dialer.logging.DialerImpression; 49 import com.android.dialer.logging.Logger; 50 import com.android.dialer.multimedia.MultimediaData; 51 import com.android.dialer.strictmode.StrictModeUtils; 52 import com.android.dialer.widget.LockableViewPager; 53 import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment; 54 import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment.AudioRouteSelectorPresenter; 55 import com.android.incallui.contactgrid.ContactGridManager; 56 import com.android.incallui.hold.OnHoldFragment; 57 import com.android.incallui.incall.impl.ButtonController.SpeakerButtonController; 58 import com.android.incallui.incall.impl.ButtonController.UpgradeToRttButtonController; 59 import com.android.incallui.incall.impl.InCallButtonGridFragment.OnButtonGridCreatedListener; 60 import com.android.incallui.incall.protocol.InCallButtonIds; 61 import com.android.incallui.incall.protocol.InCallButtonIdsExtension; 62 import com.android.incallui.incall.protocol.InCallButtonUi; 63 import com.android.incallui.incall.protocol.InCallButtonUiDelegate; 64 import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory; 65 import com.android.incallui.incall.protocol.InCallScreen; 66 import com.android.incallui.incall.protocol.InCallScreenDelegate; 67 import com.android.incallui.incall.protocol.InCallScreenDelegateFactory; 68 import com.android.incallui.incall.protocol.PrimaryCallState; 69 import com.android.incallui.incall.protocol.PrimaryCallState.ButtonState; 70 import com.android.incallui.incall.protocol.PrimaryInfo; 71 import com.android.incallui.incall.protocol.SecondaryInfo; 72 import java.util.ArrayList; 73 import java.util.List; 74 75 /** Fragment that shows UI for an ongoing voice call. */ 76 public class InCallFragment extends Fragment 77 implements InCallScreen, 78 InCallButtonUi, 79 OnClickListener, 80 AudioRouteSelectorPresenter, 81 OnButtonGridCreatedListener { 82 83 private List<ButtonController> buttonControllers = new ArrayList<>(); 84 private View endCallButton; 85 private InCallPaginator paginator; 86 private LockableViewPager pager; 87 private InCallPagerAdapter adapter; 88 private ContactGridManager contactGridManager; 89 private InCallScreenDelegate inCallScreenDelegate; 90 private InCallButtonUiDelegate inCallButtonUiDelegate; 91 private InCallButtonGridFragment inCallButtonGridFragment; 92 @Nullable private ButtonChooser buttonChooser; 93 private SecondaryInfo savedSecondaryInfo; 94 private int voiceNetworkType; 95 private int phoneType; 96 private boolean stateRestored; 97 98 // Add animation to educate users. If a call has enriched calling attachments then we'll 99 // initially show the attachment page. After a delay seconds we'll animate to the button grid. 100 private final Handler handler = new Handler(); 101 private final Runnable pagerRunnable = 102 new Runnable() { 103 @Override 104 public void run() { 105 pager.setCurrentItem(adapter.getButtonGridPosition()); 106 } 107 }; 108 isSupportedButton(@nCallButtonIds int id)109 private static boolean isSupportedButton(@InCallButtonIds int id) { 110 return id == InCallButtonIds.BUTTON_AUDIO 111 || id == InCallButtonIds.BUTTON_MUTE 112 || id == InCallButtonIds.BUTTON_DIALPAD 113 || id == InCallButtonIds.BUTTON_HOLD 114 || id == InCallButtonIds.BUTTON_SWAP 115 || id == InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO 116 || id == InCallButtonIds.BUTTON_ADD_CALL 117 || id == InCallButtonIds.BUTTON_MERGE 118 || id == InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE 119 || id == InCallButtonIds.BUTTON_SWAP_SIM 120 || id == InCallButtonIds.BUTTON_UPGRADE_TO_RTT; 121 } 122 123 @Override onAttach(Context context)124 public void onAttach(Context context) { 125 super.onAttach(context); 126 if (savedSecondaryInfo != null) { 127 setSecondary(savedSecondaryInfo); 128 } 129 } 130 131 @Override onCreate(Bundle savedInstanceState)132 public void onCreate(Bundle savedInstanceState) { 133 super.onCreate(savedInstanceState); 134 inCallButtonUiDelegate = 135 FragmentUtils.getParent(this, InCallButtonUiDelegateFactory.class) 136 .newInCallButtonUiDelegate(); 137 if (savedInstanceState != null) { 138 inCallButtonUiDelegate.onRestoreInstanceState(savedInstanceState); 139 stateRestored = true; 140 } 141 } 142 143 @Nullable 144 @Override 145 @SuppressLint("MissingPermission") onCreateView( @onNull LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle)146 public View onCreateView( 147 @NonNull LayoutInflater layoutInflater, 148 @Nullable ViewGroup viewGroup, 149 @Nullable Bundle bundle) { 150 LogUtil.i("InCallFragment.onCreateView", null); 151 getActivity().setTheme(R.style.Theme_InCallScreen); 152 // Bypass to avoid StrictModeResourceMismatchViolation 153 final View view = 154 StrictModeUtils.bypass( 155 () -> layoutInflater.inflate(R.layout.frag_incall_voice, viewGroup, false)); 156 contactGridManager = 157 new ContactGridManager( 158 view, 159 (ImageView) view.findViewById(R.id.contactgrid_avatar), 160 getResources().getDimensionPixelSize(R.dimen.incall_avatar_size), 161 true /* showAnonymousAvatar */); 162 contactGridManager.onMultiWindowModeChanged(getActivity().isInMultiWindowMode()); 163 164 paginator = (InCallPaginator) view.findViewById(R.id.incall_paginator); 165 pager = (LockableViewPager) view.findViewById(R.id.incall_pager); 166 pager.setOnTouchListener( 167 (v, event) -> { 168 handler.removeCallbacks(pagerRunnable); 169 return false; 170 }); 171 172 endCallButton = view.findViewById(R.id.incall_end_call); 173 endCallButton.setOnClickListener(this); 174 175 if (ContextCompat.checkSelfPermission(getContext(), permission.READ_PHONE_STATE) 176 != PackageManager.PERMISSION_GRANTED) { 177 voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; 178 } else { 179 voiceNetworkType = 180 getContext().getSystemService(TelephonyManager.class).getVoiceNetworkType(); 181 } 182 // TODO(a bug): Change to use corresponding phone type used for current call. 183 phoneType = getContext().getSystemService(TelephonyManager.class).getPhoneType(); 184 185 // Workaround to adjust padding for status bar and navigation bar since fitsSystemWindows 186 // doesn't work well when switching with other fragments. 187 view.addOnAttachStateChangeListener( 188 new OnAttachStateChangeListener() { 189 @Override 190 public void onViewAttachedToWindow(View v) { 191 View container = v.findViewById(R.id.incall_ui_container); 192 int topInset = v.getRootWindowInsets().getSystemWindowInsetTop(); 193 int bottomInset = v.getRootWindowInsets().getSystemWindowInsetBottom(); 194 if (topInset != container.getPaddingTop()) { 195 TransitionManager.beginDelayedTransition(((ViewGroup) container.getParent())); 196 container.setPadding(0, topInset, 0, bottomInset); 197 } 198 } 199 200 @Override 201 public void onViewDetachedFromWindow(View v) {} 202 }); 203 return view; 204 } 205 206 @Override onResume()207 public void onResume() { 208 super.onResume(); 209 inCallScreenDelegate.onInCallScreenResumed(); 210 } 211 212 @Override onViewCreated(@onNull View view, @Nullable Bundle bundle)213 public void onViewCreated(@NonNull View view, @Nullable Bundle bundle) { 214 LogUtil.i("InCallFragment.onViewCreated", null); 215 super.onViewCreated(view, bundle); 216 inCallScreenDelegate = 217 FragmentUtils.getParent(this, InCallScreenDelegateFactory.class).newInCallScreenDelegate(); 218 Assert.isNotNull(inCallScreenDelegate); 219 220 buttonControllers.add(new ButtonController.MuteButtonController(inCallButtonUiDelegate)); 221 buttonControllers.add(new ButtonController.SpeakerButtonController(inCallButtonUiDelegate)); 222 buttonControllers.add(new ButtonController.DialpadButtonController(inCallButtonUiDelegate)); 223 buttonControllers.add(new ButtonController.HoldButtonController(inCallButtonUiDelegate)); 224 buttonControllers.add(new ButtonController.AddCallButtonController(inCallButtonUiDelegate)); 225 buttonControllers.add(new ButtonController.SwapButtonController(inCallButtonUiDelegate)); 226 buttonControllers.add(new ButtonController.MergeButtonController(inCallButtonUiDelegate)); 227 buttonControllers.add(new ButtonController.SwapSimButtonController(inCallButtonUiDelegate)); 228 buttonControllers.add( 229 new ButtonController.UpgradeToVideoButtonController(inCallButtonUiDelegate)); 230 buttonControllers.add(new UpgradeToRttButtonController(inCallButtonUiDelegate)); 231 buttonControllers.add( 232 new ButtonController.ManageConferenceButtonController(inCallScreenDelegate)); 233 buttonControllers.add( 234 new ButtonController.SwitchToSecondaryButtonController(inCallScreenDelegate)); 235 236 inCallScreenDelegate.onInCallScreenDelegateInit(this); 237 inCallScreenDelegate.onInCallScreenReady(); 238 } 239 240 @Override onPause()241 public void onPause() { 242 super.onPause(); 243 inCallScreenDelegate.onInCallScreenPaused(); 244 } 245 246 @Override onDestroyView()247 public void onDestroyView() { 248 super.onDestroyView(); 249 inCallScreenDelegate.onInCallScreenUnready(); 250 } 251 252 @Override onSaveInstanceState(Bundle outState)253 public void onSaveInstanceState(Bundle outState) { 254 super.onSaveInstanceState(outState); 255 inCallButtonUiDelegate.onSaveInstanceState(outState); 256 } 257 258 @Override onClick(View view)259 public void onClick(View view) { 260 if (view == endCallButton) { 261 LogUtil.i("InCallFragment.onClick", "end call button clicked"); 262 Logger.get(getContext()) 263 .logImpression(DialerImpression.Type.IN_CALL_DIALPAD_HANG_UP_BUTTON_PRESSED); 264 inCallScreenDelegate.onEndCallClicked(); 265 } else { 266 LogUtil.e("InCallFragment.onClick", "unknown view: " + view); 267 Assert.fail(); 268 } 269 } 270 271 @Override setPrimary(@onNull PrimaryInfo primaryInfo)272 public void setPrimary(@NonNull PrimaryInfo primaryInfo) { 273 LogUtil.i("InCallFragment.setPrimary", primaryInfo.toString()); 274 setAdapterMedia(primaryInfo.multimediaData(), primaryInfo.showInCallButtonGrid()); 275 contactGridManager.setPrimary(primaryInfo); 276 277 if (primaryInfo.shouldShowLocation()) { 278 // Hide the avatar to make room for location 279 contactGridManager.setAvatarHidden(true); 280 281 // Need to let the dialpad move up a little further when location info is being shown 282 View dialpadView = getView().findViewById(R.id.incall_dialpad_container); 283 ViewGroup.LayoutParams params = dialpadView.getLayoutParams(); 284 if (params instanceof RelativeLayout.LayoutParams) { 285 ((RelativeLayout.LayoutParams) params).removeRule(RelativeLayout.BELOW); 286 } 287 dialpadView.setLayoutParams(params); 288 } 289 } 290 setAdapterMedia(MultimediaData multimediaData, boolean showInCallButtonGrid)291 private void setAdapterMedia(MultimediaData multimediaData, boolean showInCallButtonGrid) { 292 if (adapter == null) { 293 adapter = 294 new InCallPagerAdapter(getChildFragmentManager(), multimediaData, showInCallButtonGrid); 295 pager.setAdapter(adapter); 296 } else { 297 adapter.setAttachments(multimediaData); 298 } 299 300 if (adapter.getCount() > 1 && getResources().getInteger(R.integer.incall_num_rows) > 1) { 301 paginator.setVisibility(View.VISIBLE); 302 paginator.setupWithViewPager(pager); 303 pager.setSwipingLocked(false); 304 if (!stateRestored) { 305 handler.postDelayed(pagerRunnable, 4_000); 306 } else { 307 pager.setCurrentItem(adapter.getButtonGridPosition(), false /* animateScroll */); 308 } 309 } else { 310 paginator.setVisibility(View.GONE); 311 } 312 } 313 314 @Override setSecondary(@onNull SecondaryInfo secondaryInfo)315 public void setSecondary(@NonNull SecondaryInfo secondaryInfo) { 316 LogUtil.i("InCallFragment.setSecondary", secondaryInfo.toString()); 317 updateButtonStates(); 318 319 if (!isAdded()) { 320 savedSecondaryInfo = secondaryInfo; 321 return; 322 } 323 savedSecondaryInfo = null; 324 FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); 325 Fragment oldBanner = getChildFragmentManager().findFragmentById(R.id.incall_on_hold_banner); 326 if (secondaryInfo.shouldShow()) { 327 transaction.replace(R.id.incall_on_hold_banner, OnHoldFragment.newInstance(secondaryInfo)); 328 } else { 329 if (oldBanner != null) { 330 transaction.remove(oldBanner); 331 } 332 } 333 transaction.setCustomAnimations(R.anim.abc_slide_in_top, R.anim.abc_slide_out_top); 334 transaction.commitNowAllowingStateLoss(); 335 } 336 337 @Override setCallState(@onNull PrimaryCallState primaryCallState)338 public void setCallState(@NonNull PrimaryCallState primaryCallState) { 339 LogUtil.i("InCallFragment.setCallState", primaryCallState.toString()); 340 contactGridManager.setCallState(primaryCallState); 341 getButtonController(InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY) 342 .setAllowed(primaryCallState.swapToSecondaryButtonState() != ButtonState.NOT_SUPPORT); 343 getButtonController(InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY) 344 .setEnabled(primaryCallState.swapToSecondaryButtonState() == ButtonState.ENABLED); 345 buttonChooser = 346 ButtonChooserFactory.newButtonChooser( 347 voiceNetworkType, primaryCallState.isWifi(), phoneType); 348 updateButtonStates(); 349 } 350 351 @Override setEndCallButtonEnabled(boolean enabled, boolean animate)352 public void setEndCallButtonEnabled(boolean enabled, boolean animate) { 353 if (endCallButton != null) { 354 endCallButton.setEnabled(enabled); 355 } 356 } 357 358 @Override showManageConferenceCallButton(boolean visible)359 public void showManageConferenceCallButton(boolean visible) { 360 getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).setAllowed(visible); 361 getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).setEnabled(visible); 362 updateButtonStates(); 363 } 364 365 @Override isManageConferenceVisible()366 public boolean isManageConferenceVisible() { 367 return getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).isAllowed(); 368 } 369 370 @Override dispatchPopulateAccessibilityEvent(AccessibilityEvent event)371 public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 372 contactGridManager.dispatchPopulateAccessibilityEvent(event); 373 } 374 375 @Override showNoteSentToast()376 public void showNoteSentToast() { 377 LogUtil.i("InCallFragment.showNoteSentToast", null); 378 Toast.makeText(getContext(), R.string.incall_note_sent, Toast.LENGTH_LONG).show(); 379 } 380 381 @Override updateInCallScreenColors()382 public void updateInCallScreenColors() {} 383 384 @Override onInCallScreenDialpadVisibilityChange(boolean isShowing)385 public void onInCallScreenDialpadVisibilityChange(boolean isShowing) { 386 LogUtil.i("InCallFragment.onInCallScreenDialpadVisibilityChange", "isShowing: " + isShowing); 387 // Take note that the dialpad button isShowing 388 getButtonController(InCallButtonIds.BUTTON_DIALPAD).setChecked(isShowing); 389 390 // This check is needed because there is a race condition where we attempt to update 391 // ButtonGridFragment before it is ready, so we check whether it is ready first and once it is 392 // ready, #onButtonGridCreated will mark the dialpad button as isShowing. 393 if (inCallButtonGridFragment != null) { 394 // Update the Android Button's state to isShowing. 395 inCallButtonGridFragment.onInCallScreenDialpadVisibilityChange(isShowing); 396 } 397 Activity activity = getActivity(); 398 Window window = activity.getWindow(); 399 window.setNavigationBarColor( 400 activity.getColor( 401 isShowing ? android.R.color.background_dark : android.R.color.transparent)); 402 } 403 404 @Override getAnswerAndDialpadContainerResourceId()405 public int getAnswerAndDialpadContainerResourceId() { 406 return R.id.incall_dialpad_container; 407 } 408 409 @Override getInCallScreenFragment()410 public Fragment getInCallScreenFragment() { 411 return this; 412 } 413 414 @Override showButton(@nCallButtonIds int buttonId, boolean show)415 public void showButton(@InCallButtonIds int buttonId, boolean show) { 416 LogUtil.v( 417 "InCallFragment.showButton", 418 "buttionId: %s, show: %b", 419 InCallButtonIdsExtension.toString(buttonId), 420 show); 421 if (isSupportedButton(buttonId)) { 422 getButtonController(buttonId).setAllowed(show); 423 if (buttonId == InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO && show) { 424 Logger.get(getContext()) 425 .logImpression(DialerImpression.Type.UPGRADE_TO_VIDEO_CALL_BUTTON_SHOWN); 426 } 427 } 428 } 429 430 @Override enableButton(@nCallButtonIds int buttonId, boolean enable)431 public void enableButton(@InCallButtonIds int buttonId, boolean enable) { 432 LogUtil.v( 433 "InCallFragment.enableButton", 434 "buttonId: %s, enable: %b", 435 InCallButtonIdsExtension.toString(buttonId), 436 enable); 437 if (isSupportedButton(buttonId)) { 438 getButtonController(buttonId).setEnabled(enable); 439 } 440 } 441 442 @Override setEnabled(boolean enabled)443 public void setEnabled(boolean enabled) { 444 LogUtil.v("InCallFragment.setEnabled", "enabled: " + enabled); 445 for (ButtonController buttonController : buttonControllers) { 446 buttonController.setEnabled(enabled); 447 } 448 } 449 450 @Override setHold(boolean value)451 public void setHold(boolean value) { 452 getButtonController(InCallButtonIds.BUTTON_HOLD).setChecked(value); 453 } 454 455 @Override setCameraSwitched(boolean isBackFacingCamera)456 public void setCameraSwitched(boolean isBackFacingCamera) {} 457 458 @Override setVideoPaused(boolean isPaused)459 public void setVideoPaused(boolean isPaused) {} 460 461 @Override setAudioState(CallAudioState audioState)462 public void setAudioState(CallAudioState audioState) { 463 LogUtil.i("InCallFragment.setAudioState", "audioState: " + audioState); 464 ((SpeakerButtonController) getButtonController(InCallButtonIds.BUTTON_AUDIO)) 465 .setAudioState(audioState); 466 getButtonController(InCallButtonIds.BUTTON_MUTE).setChecked(audioState.isMuted()); 467 } 468 469 @Override updateButtonStates()470 public void updateButtonStates() { 471 // When the incall screen is ready, this method is called from #setSecondary, even though the 472 // incall button ui is not ready yet. This method is called again once the incall button ui is 473 // ready though, so this operation is safe and will be executed asap. 474 if (inCallButtonGridFragment == null) { 475 return; 476 } 477 int numVisibleButtons = 478 inCallButtonGridFragment.updateButtonStates( 479 buttonControllers, buttonChooser, voiceNetworkType, phoneType); 480 481 int visibility = numVisibleButtons == 0 ? View.GONE : View.VISIBLE; 482 pager.setVisibility(visibility); 483 if (adapter != null 484 && adapter.getCount() > 1 485 && getResources().getInteger(R.integer.incall_num_rows) > 1) { 486 paginator.setVisibility(View.VISIBLE); 487 pager.setSwipingLocked(false); 488 } else { 489 paginator.setVisibility(View.GONE); 490 if (adapter != null) { 491 pager.setSwipingLocked(true); 492 pager.setCurrentItem(adapter.getButtonGridPosition()); 493 } 494 } 495 } 496 497 @Override updateInCallButtonUiColors(@olorInt int color)498 public void updateInCallButtonUiColors(@ColorInt int color) { 499 inCallButtonGridFragment.updateButtonColor(color); 500 } 501 502 @Override getInCallButtonUiFragment()503 public Fragment getInCallButtonUiFragment() { 504 return this; 505 } 506 507 @Override showAudioRouteSelector()508 public void showAudioRouteSelector() { 509 AudioRouteSelectorDialogFragment.newInstance(inCallButtonUiDelegate.getCurrentAudioState()) 510 .show(getChildFragmentManager(), null); 511 } 512 513 @Override onAudioRouteSelected(int audioRoute)514 public void onAudioRouteSelected(int audioRoute) { 515 inCallButtonUiDelegate.setAudioRoute(audioRoute); 516 } 517 518 @Override onAudioRouteSelectorDismiss()519 public void onAudioRouteSelectorDismiss() {} 520 521 @NonNull 522 @Override getButtonController(@nCallButtonIds int id)523 public ButtonController getButtonController(@InCallButtonIds int id) { 524 for (ButtonController buttonController : buttonControllers) { 525 if (buttonController.getInCallButtonId() == id) { 526 return buttonController; 527 } 528 } 529 Assert.fail(); 530 return null; 531 } 532 533 @Override onButtonGridCreated(InCallButtonGridFragment inCallButtonGridFragment)534 public void onButtonGridCreated(InCallButtonGridFragment inCallButtonGridFragment) { 535 LogUtil.i("InCallFragment.onButtonGridCreated", "InCallUiReady"); 536 this.inCallButtonGridFragment = inCallButtonGridFragment; 537 inCallButtonUiDelegate.onInCallButtonUiReady(this); 538 updateButtonStates(); 539 } 540 541 @Override onButtonGridDestroyed()542 public void onButtonGridDestroyed() { 543 LogUtil.i("InCallFragment.onButtonGridCreated", "InCallUiUnready"); 544 inCallButtonUiDelegate.onInCallButtonUiUnready(); 545 this.inCallButtonGridFragment = null; 546 } 547 548 @Override isShowingLocationUi()549 public boolean isShowingLocationUi() { 550 Fragment fragment = getLocationFragment(); 551 return fragment != null && fragment.isVisible(); 552 } 553 554 @Override showLocationUi(@ullable Fragment locationUi)555 public void showLocationUi(@Nullable Fragment locationUi) { 556 boolean isVisible = isShowingLocationUi(); 557 if (locationUi != null && !isVisible) { 558 // Show the location fragment. 559 getChildFragmentManager() 560 .beginTransaction() 561 .replace(R.id.incall_location_holder, locationUi) 562 .commitAllowingStateLoss(); 563 } else if (locationUi == null && isVisible) { 564 // Hide the location fragment 565 getChildFragmentManager() 566 .beginTransaction() 567 .remove(getLocationFragment()) 568 .commitAllowingStateLoss(); 569 } 570 } 571 572 @Override onMultiWindowModeChanged(boolean isInMultiWindowMode)573 public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { 574 super.onMultiWindowModeChanged(isInMultiWindowMode); 575 if (isInMultiWindowMode == isShowingLocationUi()) { 576 LogUtil.i("InCallFragment.onMultiWindowModeChanged", "hide = " + isInMultiWindowMode); 577 // Need to show or hide location 578 showLocationUi(isInMultiWindowMode ? null : getLocationFragment()); 579 } 580 contactGridManager.onMultiWindowModeChanged(isInMultiWindowMode); 581 } 582 getLocationFragment()583 private Fragment getLocationFragment() { 584 return getChildFragmentManager().findFragmentById(R.id.incall_location_holder); 585 } 586 } 587