1 /* 2 * Copyright (C) 2013 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; 18 19 import android.content.Context; 20 import android.os.Bundle; 21 import android.os.Trace; 22 import android.support.v4.app.Fragment; 23 import android.support.v4.os.UserManagerCompat; 24 import android.telecom.CallAudioState; 25 import android.telecom.PhoneAccountHandle; 26 import com.android.contacts.common.compat.CallCompat; 27 import com.android.dialer.common.Assert; 28 import com.android.dialer.common.LogUtil; 29 import com.android.dialer.common.concurrent.DialerExecutorComponent; 30 import com.android.dialer.logging.DialerImpression; 31 import com.android.dialer.logging.DialerImpression.Type; 32 import com.android.dialer.logging.Logger; 33 import com.android.dialer.telecom.TelecomUtil; 34 import com.android.incallui.InCallCameraManager.Listener; 35 import com.android.incallui.InCallPresenter.CanAddCallListener; 36 import com.android.incallui.InCallPresenter.InCallDetailsListener; 37 import com.android.incallui.InCallPresenter.InCallState; 38 import com.android.incallui.InCallPresenter.InCallStateListener; 39 import com.android.incallui.InCallPresenter.IncomingCallListener; 40 import com.android.incallui.audiomode.AudioModeProvider; 41 import com.android.incallui.audiomode.AudioModeProvider.AudioModeListener; 42 import com.android.incallui.call.CallList; 43 import com.android.incallui.call.DialerCall; 44 import com.android.incallui.call.DialerCall.CameraDirection; 45 import com.android.incallui.call.TelecomAdapter; 46 import com.android.incallui.call.state.DialerCallState; 47 import com.android.incallui.incall.protocol.InCallButtonIds; 48 import com.android.incallui.incall.protocol.InCallButtonUi; 49 import com.android.incallui.incall.protocol.InCallButtonUiDelegate; 50 import com.android.incallui.multisim.SwapSimWorker; 51 import com.android.incallui.videotech.utils.VideoUtils; 52 53 /** Logic for call buttons. */ 54 public class CallButtonPresenter 55 implements InCallStateListener, 56 AudioModeListener, 57 IncomingCallListener, 58 InCallDetailsListener, 59 CanAddCallListener, 60 Listener, 61 InCallButtonUiDelegate { 62 63 private static final String KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL = 64 "incall_key_automatically_muted_by_add_call"; 65 private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state"; 66 67 private final Context context; 68 private InCallButtonUi inCallButtonUi; 69 private DialerCall call; 70 private boolean automaticallyMutedByAddCall = false; 71 private boolean previousMuteState = false; 72 private boolean isInCallButtonUiReady; 73 private PhoneAccountHandle otherAccount; 74 CallButtonPresenter(Context context)75 public CallButtonPresenter(Context context) { 76 this.context = context.getApplicationContext(); 77 } 78 79 @Override onInCallButtonUiReady(InCallButtonUi ui)80 public void onInCallButtonUiReady(InCallButtonUi ui) { 81 Assert.checkState(!isInCallButtonUiReady); 82 inCallButtonUi = ui; 83 AudioModeProvider.getInstance().addListener(this); 84 85 // register for call state changes last 86 final InCallPresenter inCallPresenter = InCallPresenter.getInstance(); 87 inCallPresenter.addListener(this); 88 inCallPresenter.addIncomingCallListener(this); 89 inCallPresenter.addDetailsListener(this); 90 inCallPresenter.addCanAddCallListener(this); 91 inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this); 92 93 // Update the buttons state immediately for the current call 94 onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(), CallList.getInstance()); 95 isInCallButtonUiReady = true; 96 } 97 98 @Override onInCallButtonUiUnready()99 public void onInCallButtonUiUnready() { 100 Assert.checkState(isInCallButtonUiReady); 101 inCallButtonUi = null; 102 InCallPresenter.getInstance().removeListener(this); 103 AudioModeProvider.getInstance().removeListener(this); 104 InCallPresenter.getInstance().removeIncomingCallListener(this); 105 InCallPresenter.getInstance().removeDetailsListener(this); 106 InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this); 107 InCallPresenter.getInstance().removeCanAddCallListener(this); 108 isInCallButtonUiReady = false; 109 } 110 111 @Override onStateChange(InCallState oldState, InCallState newState, CallList callList)112 public void onStateChange(InCallState oldState, InCallState newState, CallList callList) { 113 Trace.beginSection("CallButtonPresenter.onStateChange"); 114 if (newState == InCallState.OUTGOING) { 115 call = callList.getOutgoingCall(); 116 } else if (newState == InCallState.INCALL) { 117 call = callList.getActiveOrBackgroundCall(); 118 119 // When connected to voice mail, automatically shows the dialpad. 120 // (On previous releases we showed it when in-call shows up, before waiting for 121 // OUTGOING. We may want to do that once we start showing "Voice mail" label on 122 // the dialpad too.) 123 if (oldState == InCallState.OUTGOING && call != null) { 124 if (call.isVoiceMailNumber() && getActivity() != null) { 125 getActivity().showDialpadFragment(true /* show */, true /* animate */); 126 } 127 } 128 } else if (newState == InCallState.INCOMING) { 129 if (getActivity() != null) { 130 getActivity().showDialpadFragment(false /* show */, true /* animate */); 131 } 132 call = callList.getIncomingCall(); 133 } else { 134 call = null; 135 } 136 updateUi(newState, call); 137 Trace.endSection(); 138 } 139 140 /** 141 * Updates the user interface in response to a change in the details of a call. Currently handles 142 * changes to the call buttons in response to a change in the details for a call. This is 143 * important to ensure changes to the active call are reflected in the available buttons. 144 * 145 * @param call The active call. 146 * @param details The call details. 147 */ 148 @Override onDetailsChanged(DialerCall call, android.telecom.Call.Details details)149 public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) { 150 // Only update if the changes are for the currently active call 151 if (inCallButtonUi != null && call != null && call.equals(this.call)) { 152 updateButtonsState(call); 153 } 154 } 155 156 @Override onIncomingCall(InCallState oldState, InCallState newState, DialerCall call)157 public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) { 158 onStateChange(oldState, newState, CallList.getInstance()); 159 } 160 161 @Override onCanAddCallChanged(boolean canAddCall)162 public void onCanAddCallChanged(boolean canAddCall) { 163 if (inCallButtonUi != null && call != null) { 164 updateButtonsState(call); 165 } 166 } 167 168 @Override onAudioStateChanged(CallAudioState audioState)169 public void onAudioStateChanged(CallAudioState audioState) { 170 if (inCallButtonUi != null) { 171 inCallButtonUi.setAudioState(audioState); 172 } 173 } 174 175 @Override getCurrentAudioState()176 public CallAudioState getCurrentAudioState() { 177 return AudioModeProvider.getInstance().getAudioState(); 178 } 179 180 @Override setAudioRoute(int route)181 public void setAudioRoute(int route) { 182 LogUtil.i( 183 "CallButtonPresenter.setAudioRoute", 184 "sending new audio route: " + CallAudioState.audioRouteToString(route)); 185 TelecomAdapter.getInstance().setAudioRoute(route); 186 } 187 188 /** Function assumes that bluetooth is not supported. */ 189 @Override toggleSpeakerphone()190 public void toggleSpeakerphone() { 191 // This function should not be called if bluetooth is available. 192 CallAudioState audioState = getCurrentAudioState(); 193 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) { 194 // It's clear the UI is wrong, so update the supported mode once again. 195 LogUtil.e( 196 "CallButtonPresenter", "toggling speakerphone not allowed when bluetooth supported."); 197 inCallButtonUi.setAudioState(audioState); 198 return; 199 } 200 201 int newRoute; 202 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) { 203 newRoute = CallAudioState.ROUTE_WIRED_OR_EARPIECE; 204 Logger.get(context) 205 .logCallImpression( 206 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_WIRED_OR_EARPIECE, 207 call.getUniqueCallId(), 208 call.getTimeAddedMs()); 209 } else { 210 newRoute = CallAudioState.ROUTE_SPEAKER; 211 Logger.get(context) 212 .logCallImpression( 213 DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_SPEAKERPHONE, 214 call.getUniqueCallId(), 215 call.getTimeAddedMs()); 216 } 217 218 setAudioRoute(newRoute); 219 } 220 221 @Override muteClicked(boolean checked, boolean clickedByUser)222 public void muteClicked(boolean checked, boolean clickedByUser) { 223 LogUtil.i( 224 "CallButtonPresenter", "turning on mute: %s, clicked by user: %s", checked, clickedByUser); 225 if (clickedByUser) { 226 Logger.get(context) 227 .logCallImpression( 228 checked 229 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_MUTE 230 : DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_MUTE, 231 call.getUniqueCallId(), 232 call.getTimeAddedMs()); 233 } 234 TelecomAdapter.getInstance().mute(checked); 235 } 236 237 @Override holdClicked(boolean checked)238 public void holdClicked(boolean checked) { 239 if (call == null) { 240 return; 241 } 242 if (checked) { 243 LogUtil.i("CallButtonPresenter", "putting the call on hold: " + call); 244 call.hold(); 245 } else { 246 LogUtil.i("CallButtonPresenter", "removing the call from hold: " + call); 247 call.unhold(); 248 } 249 } 250 251 @Override swapClicked()252 public void swapClicked() { 253 if (call == null) { 254 return; 255 } 256 257 LogUtil.i("CallButtonPresenter", "swapping the call: " + call); 258 TelecomAdapter.getInstance().swap(call.getId()); 259 } 260 261 @Override mergeClicked()262 public void mergeClicked() { 263 Logger.get(context) 264 .logCallImpression( 265 DialerImpression.Type.IN_CALL_MERGE_BUTTON_PRESSED, 266 call.getUniqueCallId(), 267 call.getTimeAddedMs()); 268 TelecomAdapter.getInstance().merge(call.getId()); 269 } 270 271 @Override addCallClicked()272 public void addCallClicked() { 273 Logger.get(context) 274 .logCallImpression( 275 DialerImpression.Type.IN_CALL_ADD_CALL_BUTTON_PRESSED, 276 call.getUniqueCallId(), 277 call.getTimeAddedMs()); 278 if (automaticallyMutedByAddCall) { 279 // Since clicking add call button brings user to MainActivity and coming back refreshes mute 280 // state, add call button should only be clicked once during InCallActivity shows. Otherwise, 281 // we set previousMuteState wrong. 282 return; 283 } 284 // Automatically mute the current call 285 automaticallyMutedByAddCall = true; 286 previousMuteState = AudioModeProvider.getInstance().getAudioState().isMuted(); 287 // Simulate a click on the mute button 288 muteClicked(true /* checked */, false /* clickedByUser */); 289 TelecomAdapter.getInstance().addCall(); 290 } 291 292 @Override showDialpadClicked(boolean checked)293 public void showDialpadClicked(boolean checked) { 294 Logger.get(context) 295 .logCallImpression( 296 DialerImpression.Type.IN_CALL_SHOW_DIALPAD_BUTTON_PRESSED, 297 call.getUniqueCallId(), 298 call.getTimeAddedMs()); 299 LogUtil.v("CallButtonPresenter", "show dialpad " + String.valueOf(checked)); 300 getActivity().showDialpadFragment(checked /* show */, true /* animate */); 301 } 302 303 @Override changeToVideoClicked()304 public void changeToVideoClicked() { 305 LogUtil.enterBlock("CallButtonPresenter.changeToVideoClicked"); 306 Logger.get(context) 307 .logCallImpression( 308 DialerImpression.Type.VIDEO_CALL_UPGRADE_REQUESTED, 309 call.getUniqueCallId(), 310 call.getTimeAddedMs()); 311 call.getVideoTech().upgradeToVideo(context); 312 } 313 314 @Override changeToRttClicked()315 public void changeToRttClicked() { 316 LogUtil.enterBlock("CallButtonPresenter.changeToRttClicked"); 317 call.sendRttUpgradeRequest(); 318 } 319 320 @Override onEndCallClicked()321 public void onEndCallClicked() { 322 LogUtil.i("CallButtonPresenter.onEndCallClicked", "call: " + call); 323 if (call != null) { 324 call.disconnect(); 325 } 326 } 327 328 @Override showAudioRouteSelector()329 public void showAudioRouteSelector() { 330 inCallButtonUi.showAudioRouteSelector(); 331 } 332 333 @Override swapSimClicked()334 public void swapSimClicked() { 335 LogUtil.enterBlock("CallButtonPresenter.swapSimClicked"); 336 Logger.get(getContext()).logImpression(Type.DUAL_SIM_CHANGE_SIM_PRESSED); 337 SwapSimWorker worker = 338 new SwapSimWorker( 339 getContext(), 340 call, 341 InCallPresenter.getInstance().getCallList(), 342 otherAccount, 343 InCallPresenter.getInstance().acquireInCallUiLock("swapSim")); 344 DialerExecutorComponent.get(getContext()) 345 .dialerExecutorFactory() 346 .createNonUiTaskBuilder(worker) 347 .build() 348 .executeParallel(null); 349 } 350 351 /** 352 * Switches the camera between the front-facing and back-facing camera. 353 * 354 * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or false 355 * if we should switch to using the back-facing camera. 356 */ 357 @Override switchCameraClicked(boolean useFrontFacingCamera)358 public void switchCameraClicked(boolean useFrontFacingCamera) { 359 updateCamera(useFrontFacingCamera); 360 } 361 362 @Override toggleCameraClicked()363 public void toggleCameraClicked() { 364 LogUtil.i("CallButtonPresenter.toggleCameraClicked", ""); 365 if (call == null) { 366 return; 367 } 368 Logger.get(context) 369 .logCallImpression( 370 DialerImpression.Type.IN_CALL_SCREEN_SWAP_CAMERA, 371 call.getUniqueCallId(), 372 call.getTimeAddedMs()); 373 switchCameraClicked( 374 !InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera()); 375 } 376 377 /** 378 * Stop or start client's video transmission. 379 * 380 * @param pause True if pausing the local user's video, or false if starting the local user's 381 * video. 382 */ 383 @Override pauseVideoClicked(boolean pause)384 public void pauseVideoClicked(boolean pause) { 385 LogUtil.i("CallButtonPresenter.pauseVideoClicked", "%s", pause ? "pause" : "unpause"); 386 387 Logger.get(context) 388 .logCallImpression( 389 pause 390 ? DialerImpression.Type.IN_CALL_SCREEN_TURN_OFF_VIDEO 391 : DialerImpression.Type.IN_CALL_SCREEN_TURN_ON_VIDEO, 392 call.getUniqueCallId(), 393 call.getTimeAddedMs()); 394 395 if (pause) { 396 call.getVideoTech().setCamera(null); 397 call.getVideoTech().stopTransmission(); 398 } else { 399 updateCamera( 400 InCallPresenter.getInstance().getInCallCameraManager().isUsingFrontFacingCamera()); 401 call.getVideoTech().resumeTransmission(context); 402 } 403 404 inCallButtonUi.setVideoPaused(pause); 405 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, false); 406 } 407 updateCamera(boolean useFrontFacingCamera)408 private void updateCamera(boolean useFrontFacingCamera) { 409 InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager(); 410 cameraManager.setUseFrontFacingCamera(useFrontFacingCamera); 411 412 String cameraId = cameraManager.getActiveCameraId(); 413 if (cameraId != null) { 414 final int cameraDir = 415 cameraManager.isUsingFrontFacingCamera() 416 ? CameraDirection.CAMERA_DIRECTION_FRONT_FACING 417 : CameraDirection.CAMERA_DIRECTION_BACK_FACING; 418 call.setCameraDir(cameraDir); 419 call.getVideoTech().setCamera(cameraId); 420 } 421 } 422 updateUi(InCallState state, DialerCall call)423 private void updateUi(InCallState state, DialerCall call) { 424 LogUtil.v("CallButtonPresenter", "updating call UI for call: %s", call); 425 426 if (inCallButtonUi == null) { 427 return; 428 } 429 430 if (call != null) { 431 inCallButtonUi.updateInCallButtonUiColors( 432 InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor()); 433 } 434 435 final boolean isEnabled = 436 state.isConnectingOrConnected() && !state.isIncoming() && call != null; 437 inCallButtonUi.setEnabled(isEnabled); 438 439 if (call == null) { 440 return; 441 } 442 443 updateButtonsState(call); 444 } 445 446 /** 447 * Updates the buttons applicable for the UI. 448 * 449 * @param call The active call. 450 */ 451 @SuppressWarnings(value = {"MissingPermission"}) updateButtonsState(DialerCall call)452 private void updateButtonsState(DialerCall call) { 453 LogUtil.v("CallButtonPresenter.updateButtonsState", ""); 454 final boolean isVideo = call.isVideoCall(); 455 456 // Common functionality (audio, hold, etc). 457 // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available: 458 // (1) If the device normally can hold, show HOLD in a disabled state. 459 // (2) If the device doesn't have the concept of hold/swap, remove the button. 460 final boolean showSwap = call.can(android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE); 461 final boolean showHold = 462 !showSwap 463 && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD) 464 && call.can(android.telecom.Call.Details.CAPABILITY_HOLD); 465 final boolean isCallOnHold = call.getState() == DialerCallState.ONHOLD; 466 467 final boolean showAddCall = 468 TelecomAdapter.getInstance().canAddCall() && UserManagerCompat.isUserUnlocked(context); 469 // There can only be two calls so don't show the ability to merge when one of them 470 // is a speak easy call. 471 final boolean showMerge = 472 InCallPresenter.getInstance() 473 .getCallList() 474 .getAllCalls() 475 .stream() 476 .noneMatch(c -> c != null && c.isSpeakEasyCall()) 477 && call.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE); 478 final boolean showUpgradeToVideo = !isVideo && (hasVideoCallCapabilities(call)); 479 final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call); 480 final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE); 481 482 final boolean hasCameraPermission = 483 isVideo && VideoUtils.hasCameraPermissionAndShownPrivacyToast(context); 484 // Disabling local video doesn't seem to work when dialing. See a bug. 485 final boolean showPauseVideo = 486 isVideo 487 && call.getState() != DialerCallState.DIALING 488 && call.getState() != DialerCallState.CONNECTING; 489 490 otherAccount = TelecomUtil.getOtherAccount(getContext(), call.getAccountHandle()); 491 boolean showSwapSim = 492 !call.isEmergencyCall() 493 && otherAccount != null 494 && !call.isVoiceMailNumber() 495 && DialerCallState.isDialing(call.getState()) 496 // Most devices cannot make calls on 2 SIMs at the same time. 497 && InCallPresenter.getInstance().getCallList().getAllCalls().size() == 1; 498 499 boolean showUpgradeToRtt = call.canUpgradeToRttCall(); 500 boolean enableUpgradeToRtt = showUpgradeToRtt && call.getState() == DialerCallState.ACTIVE; 501 502 inCallButtonUi.showButton(InCallButtonIds.BUTTON_AUDIO, true); 503 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP, showSwap); 504 inCallButtonUi.showButton(InCallButtonIds.BUTTON_HOLD, showHold); 505 inCallButtonUi.setHold(isCallOnHold); 506 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MUTE, showMute); 507 inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP_SIM, showSwapSim); 508 inCallButtonUi.showButton(InCallButtonIds.BUTTON_ADD_CALL, true); 509 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_ADD_CALL, showAddCall); 510 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo); 511 inCallButtonUi.showButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, showUpgradeToRtt); 512 inCallButtonUi.enableButton(InCallButtonIds.BUTTON_UPGRADE_TO_RTT, enableUpgradeToRtt); 513 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio); 514 inCallButtonUi.showButton( 515 InCallButtonIds.BUTTON_SWITCH_CAMERA, 516 isVideo && hasCameraPermission && call.getVideoTech().isTransmitting()); 517 inCallButtonUi.showButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, showPauseVideo); 518 if (isVideo) { 519 inCallButtonUi.setVideoPaused(!call.getVideoTech().isTransmitting() || !hasCameraPermission); 520 } 521 inCallButtonUi.showButton(InCallButtonIds.BUTTON_DIALPAD, true); 522 inCallButtonUi.showButton(InCallButtonIds.BUTTON_MERGE, showMerge); 523 524 inCallButtonUi.updateButtonStates(); 525 } 526 hasVideoCallCapabilities(DialerCall call)527 private boolean hasVideoCallCapabilities(DialerCall call) { 528 return call.getVideoTech().isAvailable(context, call.getAccountHandle()); 529 } 530 531 /** 532 * Determines if downgrading from a video call to an audio-only call is supported. In order to 533 * support downgrade to audio, the SDK version must be >= N and the call should NOT have the 534 * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}. 535 * 536 * @param call The call. 537 * @return {@code true} if downgrading to an audio-only call from a video call is supported. 538 */ isDowngradeToAudioSupported(DialerCall call)539 private boolean isDowngradeToAudioSupported(DialerCall call) { 540 // TODO(a bug): If there is an RCS video share session, return true here 541 return !call.can(CallCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO); 542 } 543 544 @Override refreshMuteState()545 public void refreshMuteState() { 546 // Restore the previous mute state 547 if (automaticallyMutedByAddCall 548 && AudioModeProvider.getInstance().getAudioState().isMuted() != previousMuteState) { 549 if (inCallButtonUi == null) { 550 return; 551 } 552 muteClicked(previousMuteState, false /* clickedByUser */); 553 } 554 automaticallyMutedByAddCall = false; 555 } 556 557 @Override onSaveInstanceState(Bundle outState)558 public void onSaveInstanceState(Bundle outState) { 559 outState.putBoolean(KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL, automaticallyMutedByAddCall); 560 outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState); 561 } 562 563 @Override onRestoreInstanceState(Bundle savedInstanceState)564 public void onRestoreInstanceState(Bundle savedInstanceState) { 565 automaticallyMutedByAddCall = 566 savedInstanceState.getBoolean( 567 KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL, automaticallyMutedByAddCall); 568 previousMuteState = savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, previousMuteState); 569 } 570 571 @Override onCameraPermissionGranted()572 public void onCameraPermissionGranted() { 573 if (call != null) { 574 updateButtonsState(call); 575 } 576 } 577 578 @Override onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera)579 public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) { 580 if (inCallButtonUi == null) { 581 return; 582 } 583 inCallButtonUi.setCameraSwitched(!isUsingFrontFacingCamera); 584 } 585 586 @Override getContext()587 public Context getContext() { 588 return context; 589 } 590 getActivity()591 private InCallActivity getActivity() { 592 if (inCallButtonUi != null) { 593 Fragment fragment = inCallButtonUi.getInCallButtonUiFragment(); 594 if (fragment != null) { 595 return (InCallActivity) fragment.getActivity(); 596 } 597 } 598 return null; 599 } 600 } 601