1 /* 2 * Copyright (C) 2014 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.app.Activity; 20 import android.content.Context; 21 import android.graphics.Point; 22 import android.os.Handler; 23 import android.support.annotation.Nullable; 24 import android.telecom.InCallService.VideoCall; 25 import android.telecom.VideoProfile; 26 import android.telecom.VideoProfile.CameraCapabilities; 27 import android.view.Surface; 28 import android.view.SurfaceView; 29 import com.android.dialer.common.Assert; 30 import com.android.dialer.common.LogUtil; 31 import com.android.dialer.compat.CompatUtils; 32 import com.android.dialer.configprovider.ConfigProviderBindings; 33 import com.android.dialer.util.PermissionsUtil; 34 import com.android.incallui.InCallPresenter.InCallDetailsListener; 35 import com.android.incallui.InCallPresenter.InCallOrientationListener; 36 import com.android.incallui.InCallPresenter.InCallStateListener; 37 import com.android.incallui.InCallPresenter.IncomingCallListener; 38 import com.android.incallui.call.CallList; 39 import com.android.incallui.call.DialerCall; 40 import com.android.incallui.call.DialerCall.CameraDirection; 41 import com.android.incallui.call.DialerCall.State; 42 import com.android.incallui.call.InCallVideoCallCallbackNotifier; 43 import com.android.incallui.call.InCallVideoCallCallbackNotifier.SurfaceChangeListener; 44 import com.android.incallui.util.AccessibilityUtil; 45 import com.android.incallui.video.protocol.VideoCallScreen; 46 import com.android.incallui.video.protocol.VideoCallScreenDelegate; 47 import com.android.incallui.videosurface.protocol.VideoSurfaceDelegate; 48 import com.android.incallui.videosurface.protocol.VideoSurfaceTexture; 49 import com.android.incallui.videotech.utils.SessionModificationState; 50 import com.android.incallui.videotech.utils.VideoUtils; 51 import java.util.Objects; 52 53 /** 54 * Logic related to the {@link VideoCallScreen} and for managing changes to the video calling 55 * surfaces based on other user interface events and incoming events from the {@class 56 * VideoCallListener}. 57 * 58 * <p>When a call's video state changes to bi-directional video, the {@link 59 * com.android.incallui.VideoCallPresenter} performs the following negotiation with the telephony 60 * layer: 61 * 62 * <ul> 63 * <li>{@code VideoCallPresenter} creates and informs telephony of the display surface. 64 * <li>{@code VideoCallPresenter} creates the preview surface. 65 * <li>{@code VideoCallPresenter} informs telephony of the currently selected camera. 66 * <li>Telephony layer sends {@link CameraCapabilities}, including the dimensions of the video for 67 * the current camera. 68 * <li>{@code VideoCallPresenter} adjusts size of the preview surface to match the aspect ratio of 69 * the camera. 70 * <li>{@code VideoCallPresenter} informs telephony of the new preview surface. 71 * </ul> 72 * 73 * <p>When downgrading to an audio-only video state, the {@code VideoCallPresenter} nulls both 74 * surfaces. 75 */ 76 public class VideoCallPresenter 77 implements IncomingCallListener, 78 InCallOrientationListener, 79 InCallStateListener, 80 InCallDetailsListener, 81 SurfaceChangeListener, 82 InCallPresenter.InCallEventListener, 83 VideoCallScreenDelegate { 84 85 private static boolean isVideoMode = false; 86 87 private final Handler handler = new Handler(); 88 private VideoCallScreen videoCallScreen; 89 90 /** The current context. */ 91 private Context context; 92 93 /** The call the video surfaces are currently related to */ 94 private DialerCall primaryCall; 95 /** 96 * The {@link VideoCall} used to inform the video telephony layer of changes to the video 97 * surfaces. 98 */ 99 private VideoCall videoCall; 100 /** Determines if the current UI state represents a video call. */ 101 private int currentVideoState; 102 /** DialerCall's current state */ 103 private int currentCallState = DialerCall.State.INVALID; 104 /** Determines the device orientation (portrait/lanscape). */ 105 private int deviceOrientation = InCallOrientationEventListener.SCREEN_ORIENTATION_UNKNOWN; 106 /** Tracks the state of the preview surface negotiation with the telephony layer. */ 107 private int previewSurfaceState = PreviewSurfaceState.NONE; 108 /** 109 * Determines whether video calls should automatically enter full screen mode after {@link 110 * #autoFullscreenTimeoutMillis} milliseconds. 111 */ 112 private boolean isAutoFullscreenEnabled = false; 113 /** 114 * Determines the number of milliseconds after which a video call will automatically enter 115 * fullscreen mode. Requires {@link #isAutoFullscreenEnabled} to be {@code true}. 116 */ 117 private int autoFullscreenTimeoutMillis = 0; 118 /** 119 * Determines if the countdown is currently running to automatically enter full screen video mode. 120 */ 121 private boolean autoFullScreenPending = false; 122 /** Whether if the call is remotely held. */ 123 private boolean isRemotelyHeld = false; 124 /** 125 * Runnable which is posted to schedule automatically entering fullscreen mode. Will not auto 126 * enter fullscreen mode if the dialpad is visible (doing so would make it impossible to exit the 127 * dialpad). 128 */ 129 private Runnable autoFullscreenRunnable = 130 new Runnable() { 131 @Override 132 public void run() { 133 if (autoFullScreenPending 134 && !InCallPresenter.getInstance().isDialpadVisible() 135 && isVideoMode) { 136 137 LogUtil.v("VideoCallPresenter.mAutoFullScreenRunnable", "entering fullscreen mode"); 138 InCallPresenter.getInstance().setFullScreen(true); 139 autoFullScreenPending = false; 140 } else { 141 LogUtil.v( 142 "VideoCallPresenter.mAutoFullScreenRunnable", 143 "skipping scheduled fullscreen mode."); 144 } 145 } 146 }; 147 148 private boolean isVideoCallScreenUiReady; 149 isCameraRequired(int videoState, int sessionModificationState)150 private static boolean isCameraRequired(int videoState, int sessionModificationState) { 151 return VideoProfile.isBidirectional(videoState) 152 || VideoProfile.isTransmissionEnabled(videoState) 153 || isVideoUpgrade(sessionModificationState); 154 } 155 156 /** 157 * Determines if the incoming video surface should be shown based on the current videoState and 158 * callState. The video surface is shown when incoming video is not paused, the call is active or 159 * dialing and video reception is enabled. 160 * 161 * @param videoState The current video state. 162 * @param callState The current call state. 163 * @return {@code true} if the incoming video surface should be shown, {@code false} otherwise. 164 */ showIncomingVideo(int videoState, int callState)165 public static boolean showIncomingVideo(int videoState, int callState) { 166 if (!CompatUtils.isVideoCompatible()) { 167 return false; 168 } 169 170 boolean isPaused = VideoProfile.isPaused(videoState); 171 boolean isCallActive = callState == DialerCall.State.ACTIVE; 172 // Show incoming Video for dialing calls to support early media 173 boolean isCallOutgoingPending = 174 DialerCall.State.isDialing(callState) || callState == DialerCall.State.CONNECTING; 175 176 return !isPaused 177 && (isCallActive || isCallOutgoingPending) 178 && VideoProfile.isReceptionEnabled(videoState); 179 } 180 181 /** 182 * Determines if the outgoing video surface should be shown based on the current videoState. The 183 * video surface is shown if video transmission is enabled. 184 * 185 * @return {@code true} if the the outgoing video surface should be shown, {@code false} 186 * otherwise. 187 */ showOutgoingVideo( Context context, int videoState, int sessionModificationState)188 public static boolean showOutgoingVideo( 189 Context context, int videoState, int sessionModificationState) { 190 if (!VideoUtils.hasCameraPermissionAndShownPrivacyToast(context)) { 191 LogUtil.i("VideoCallPresenter.showOutgoingVideo", "Camera permission is disabled by user."); 192 return false; 193 } 194 195 if (!CompatUtils.isVideoCompatible()) { 196 return false; 197 } 198 199 return VideoProfile.isTransmissionEnabled(videoState) 200 || isVideoUpgrade(sessionModificationState); 201 } 202 updateCameraSelection(DialerCall call)203 private static void updateCameraSelection(DialerCall call) { 204 LogUtil.v("VideoCallPresenter.updateCameraSelection", "call=" + call); 205 LogUtil.v("VideoCallPresenter.updateCameraSelection", "call=" + toSimpleString(call)); 206 207 final DialerCall activeCall = CallList.getInstance().getActiveCall(); 208 int cameraDir; 209 210 // this function should never be called with null call object, however if it happens we 211 // should handle it gracefully. 212 if (call == null) { 213 cameraDir = CameraDirection.CAMERA_DIRECTION_UNKNOWN; 214 LogUtil.e( 215 "VideoCallPresenter.updateCameraSelection", 216 "call is null. Setting camera direction to default value (CAMERA_DIRECTION_UNKNOWN)"); 217 } 218 219 // Clear camera direction if this is not a video call. 220 else if (isAudioCall(call) && !isVideoUpgrade(call)) { 221 cameraDir = CameraDirection.CAMERA_DIRECTION_UNKNOWN; 222 call.setCameraDir(cameraDir); 223 } 224 225 // If this is a waiting video call, default to active call's camera, 226 // since we don't want to change the current camera for waiting call 227 // without user's permission. 228 else if (isVideoCall(activeCall) && isIncomingVideoCall(call)) { 229 cameraDir = activeCall.getCameraDir(); 230 } 231 232 // Infer the camera direction from the video state and store it, 233 // if this is an outgoing video call. 234 else if (isOutgoingVideoCall(call) && !isCameraDirectionSet(call)) { 235 cameraDir = toCameraDirection(call.getVideoState()); 236 call.setCameraDir(cameraDir); 237 } 238 239 // Use the stored camera dir if this is an outgoing video call for which camera direction 240 // is set. 241 else if (isOutgoingVideoCall(call)) { 242 cameraDir = call.getCameraDir(); 243 } 244 245 // Infer the camera direction from the video state and store it, 246 // if this is an active video call and camera direction is not set. 247 else if (isActiveVideoCall(call) && !isCameraDirectionSet(call)) { 248 cameraDir = toCameraDirection(call.getVideoState()); 249 call.setCameraDir(cameraDir); 250 } 251 252 // Use the stored camera dir if this is an active video call for which camera direction 253 // is set. 254 else if (isActiveVideoCall(call)) { 255 cameraDir = call.getCameraDir(); 256 } 257 258 // For all other cases infer the camera direction but don't store it in the call object. 259 else { 260 cameraDir = toCameraDirection(call.getVideoState()); 261 } 262 263 LogUtil.i( 264 "VideoCallPresenter.updateCameraSelection", 265 "setting camera direction to %d, call: %s", 266 cameraDir, 267 call); 268 final InCallCameraManager cameraManager = 269 InCallPresenter.getInstance().getInCallCameraManager(); 270 cameraManager.setUseFrontFacingCamera( 271 cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING); 272 } 273 toCameraDirection(int videoState)274 private static int toCameraDirection(int videoState) { 275 return VideoProfile.isTransmissionEnabled(videoState) 276 && !VideoProfile.isBidirectional(videoState) 277 ? CameraDirection.CAMERA_DIRECTION_BACK_FACING 278 : CameraDirection.CAMERA_DIRECTION_FRONT_FACING; 279 } 280 isCameraDirectionSet(DialerCall call)281 private static boolean isCameraDirectionSet(DialerCall call) { 282 return isVideoCall(call) && call.getCameraDir() != CameraDirection.CAMERA_DIRECTION_UNKNOWN; 283 } 284 toSimpleString(DialerCall call)285 private static String toSimpleString(DialerCall call) { 286 return call == null ? null : call.toSimpleString(); 287 } 288 289 /** 290 * Initializes the presenter. 291 * 292 * @param context The current context. 293 */ 294 @Override initVideoCallScreenDelegate(Context context, VideoCallScreen videoCallScreen)295 public void initVideoCallScreenDelegate(Context context, VideoCallScreen videoCallScreen) { 296 this.context = context; 297 this.videoCallScreen = videoCallScreen; 298 isAutoFullscreenEnabled = 299 this.context.getResources().getBoolean(R.bool.video_call_auto_fullscreen); 300 autoFullscreenTimeoutMillis = 301 this.context.getResources().getInteger(R.integer.video_call_auto_fullscreen_timeout); 302 } 303 304 /** Called when the user interface is ready to be used. */ 305 @Override onVideoCallScreenUiReady()306 public void onVideoCallScreenUiReady() { 307 LogUtil.v("VideoCallPresenter.onVideoCallScreenUiReady", ""); 308 Assert.checkState(!isVideoCallScreenUiReady); 309 310 // Do not register any listeners if video calling is not compatible to safeguard against 311 // any accidental calls of video calling code. 312 if (!CompatUtils.isVideoCompatible()) { 313 return; 314 } 315 316 deviceOrientation = InCallOrientationEventListener.getCurrentOrientation(); 317 318 // Register for call state changes last 319 InCallPresenter.getInstance().addListener(this); 320 InCallPresenter.getInstance().addDetailsListener(this); 321 InCallPresenter.getInstance().addIncomingCallListener(this); 322 InCallPresenter.getInstance().addOrientationListener(this); 323 // To get updates of video call details changes 324 InCallPresenter.getInstance().addInCallEventListener(this); 325 InCallPresenter.getInstance().getLocalVideoSurfaceTexture().setDelegate(new LocalDelegate()); 326 InCallPresenter.getInstance().getRemoteVideoSurfaceTexture().setDelegate(new RemoteDelegate()); 327 328 // Register for surface and video events from {@link InCallVideoCallListener}s. 329 InCallVideoCallCallbackNotifier.getInstance().addSurfaceChangeListener(this); 330 currentVideoState = VideoProfile.STATE_AUDIO_ONLY; 331 currentCallState = DialerCall.State.INVALID; 332 333 InCallPresenter.InCallState inCallState = InCallPresenter.getInstance().getInCallState(); 334 onStateChange(inCallState, inCallState, CallList.getInstance()); 335 isVideoCallScreenUiReady = true; 336 } 337 338 /** Called when the user interface is no longer ready to be used. */ 339 @Override onVideoCallScreenUiUnready()340 public void onVideoCallScreenUiUnready() { 341 LogUtil.v("VideoCallPresenter.onVideoCallScreenUiUnready", ""); 342 Assert.checkState(isVideoCallScreenUiReady); 343 344 if (!CompatUtils.isVideoCompatible()) { 345 return; 346 } 347 348 cancelAutoFullScreen(); 349 350 InCallPresenter.getInstance().removeListener(this); 351 InCallPresenter.getInstance().removeDetailsListener(this); 352 InCallPresenter.getInstance().removeIncomingCallListener(this); 353 InCallPresenter.getInstance().removeOrientationListener(this); 354 InCallPresenter.getInstance().removeInCallEventListener(this); 355 InCallPresenter.getInstance().getLocalVideoSurfaceTexture().setDelegate(null); 356 357 InCallVideoCallCallbackNotifier.getInstance().removeSurfaceChangeListener(this); 358 359 // Ensure that the call's camera direction is updated (most likely to UNKNOWN). Normally this 360 // happens after any call state changes but we're unregistering from InCallPresenter above so 361 // we won't get any more call state changes. See a bug. 362 if (primaryCall != null) { 363 updateCameraSelection(primaryCall); 364 } 365 366 isVideoCallScreenUiReady = false; 367 } 368 369 /** 370 * Handles clicks on the video surfaces. If not currently in fullscreen mode, will set fullscreen. 371 */ onSurfaceClick()372 private void onSurfaceClick() { 373 LogUtil.i("VideoCallPresenter.onSurfaceClick", ""); 374 cancelAutoFullScreen(); 375 if (!InCallPresenter.getInstance().isFullscreen()) { 376 InCallPresenter.getInstance().setFullScreen(true); 377 } else { 378 InCallPresenter.getInstance().setFullScreen(false); 379 maybeAutoEnterFullscreen(primaryCall); 380 // If Activity is not multiwindow, fullscreen will be driven by SystemUI visibility changes 381 // instead. See #onSystemUiVisibilityChange(boolean) 382 383 // TODO (keyboardr): onSystemUiVisibilityChange isn't being called the first time 384 // visibility changes after orientation change, so this is currently always done as a backup. 385 } 386 } 387 388 @Override onSystemUiVisibilityChange(boolean visible)389 public void onSystemUiVisibilityChange(boolean visible) { 390 // If the SystemUI has changed to be visible, take us out of fullscreen mode 391 LogUtil.i("VideoCallPresenter.onSystemUiVisibilityChange", "visible: " + visible); 392 if (visible) { 393 InCallPresenter.getInstance().setFullScreen(false); 394 maybeAutoEnterFullscreen(primaryCall); 395 } 396 } 397 398 @Override getLocalVideoSurfaceTexture()399 public VideoSurfaceTexture getLocalVideoSurfaceTexture() { 400 return InCallPresenter.getInstance().getLocalVideoSurfaceTexture(); 401 } 402 403 @Override getRemoteVideoSurfaceTexture()404 public VideoSurfaceTexture getRemoteVideoSurfaceTexture() { 405 return InCallPresenter.getInstance().getRemoteVideoSurfaceTexture(); 406 } 407 408 @Override setSurfaceViews(SurfaceView preview, SurfaceView remote)409 public void setSurfaceViews(SurfaceView preview, SurfaceView remote) { 410 throw Assert.createUnsupportedOperationFailException(); 411 } 412 413 @Override getDeviceOrientation()414 public int getDeviceOrientation() { 415 return deviceOrientation; 416 } 417 418 /** 419 * This should only be called when user approved the camera permission, which is local action and 420 * does NOT change any call states. 421 */ 422 @Override onCameraPermissionGranted()423 public void onCameraPermissionGranted() { 424 LogUtil.i("VideoCallPresenter.onCameraPermissionGranted", ""); 425 PermissionsUtil.setCameraPrivacyToastShown(context); 426 enableCamera(primaryCall, isCameraRequired()); 427 showVideoUi( 428 primaryCall.getVideoState(), 429 primaryCall.getState(), 430 primaryCall.getVideoTech().getSessionModificationState(), 431 primaryCall.isRemotelyHeld()); 432 InCallPresenter.getInstance().getInCallCameraManager().onCameraPermissionGranted(); 433 } 434 435 /** 436 * Called when the user interacts with the UI. If a fullscreen timer is pending then we start the 437 * timer from scratch to avoid having the UI disappear while the user is interacting with it. 438 */ 439 @Override resetAutoFullscreenTimer()440 public void resetAutoFullscreenTimer() { 441 if (autoFullScreenPending) { 442 LogUtil.i("VideoCallPresenter.resetAutoFullscreenTimer", "resetting"); 443 handler.removeCallbacks(autoFullscreenRunnable); 444 handler.postDelayed(autoFullscreenRunnable, autoFullscreenTimeoutMillis); 445 } 446 } 447 448 /** 449 * Handles incoming calls. 450 * 451 * @param oldState The old in call state. 452 * @param newState The new in call state. 453 * @param call The call. 454 */ 455 @Override onIncomingCall( InCallPresenter.InCallState oldState, InCallPresenter.InCallState newState, DialerCall call)456 public void onIncomingCall( 457 InCallPresenter.InCallState oldState, InCallPresenter.InCallState newState, DialerCall call) { 458 // If video call screen ui is already destroyed, this shouldn't be called. But the UI may be 459 // updated synchronized by {@link CallCardPresenter#onIncomingCall} before this is called, this 460 // could still be called. Thus just do nothing in this case. 461 if (!isVideoCallScreenUiReady) { 462 LogUtil.i("VideoCallPresenter.onIncomingCall", "UI is not ready"); 463 return; 464 } 465 // same logic should happen as with onStateChange() 466 onStateChange(oldState, newState, CallList.getInstance()); 467 } 468 469 /** 470 * Handles state changes (including incoming calls) 471 * 472 * @param newState The in call state. 473 * @param callList The call list. 474 */ 475 @Override onStateChange( InCallPresenter.InCallState oldState, InCallPresenter.InCallState newState, CallList callList)476 public void onStateChange( 477 InCallPresenter.InCallState oldState, 478 InCallPresenter.InCallState newState, 479 CallList callList) { 480 LogUtil.v( 481 "VideoCallPresenter.onStateChange", 482 "oldState: %s, newState: %s, isVideoMode: %b", 483 oldState, 484 newState, 485 isVideoMode()); 486 487 if (newState == InCallPresenter.InCallState.NO_CALLS) { 488 if (isVideoMode()) { 489 exitVideoMode(); 490 } 491 492 InCallPresenter.getInstance().cleanupSurfaces(); 493 } 494 495 // Determine the primary active call). 496 DialerCall primary = null; 497 498 // Determine the call which is the focus of the user's attention. In the case of an 499 // incoming call waiting call, the primary call is still the active video call, however 500 // the determination of whether we should be in fullscreen mode is based on the type of the 501 // incoming call, not the active video call. 502 DialerCall currentCall = null; 503 504 if (newState == InCallPresenter.InCallState.INCOMING) { 505 // We don't want to replace active video call (primary call) 506 // with a waiting call, since user may choose to ignore/decline the waiting call and 507 // this should have no impact on current active video call, that is, we should not 508 // change the camera or UI unless the waiting VT call becomes active. 509 primary = callList.getActiveCall(); 510 currentCall = callList.getIncomingCall(); 511 if (!isActiveVideoCall(primary)) { 512 primary = callList.getIncomingCall(); 513 } 514 } else if (newState == InCallPresenter.InCallState.OUTGOING) { 515 currentCall = primary = callList.getOutgoingCall(); 516 } else if (newState == InCallPresenter.InCallState.PENDING_OUTGOING) { 517 currentCall = primary = callList.getPendingOutgoingCall(); 518 } else if (newState == InCallPresenter.InCallState.INCALL) { 519 currentCall = primary = callList.getActiveCall(); 520 } 521 522 final boolean primaryChanged = !Objects.equals(primaryCall, primary); 523 LogUtil.i( 524 "VideoCallPresenter.onStateChange", 525 "primaryChanged: %b, primary: %s, mPrimaryCall: %s", 526 primaryChanged, 527 primary, 528 primaryCall); 529 if (primaryChanged) { 530 onPrimaryCallChanged(primary); 531 } else if (primaryCall != null) { 532 updateVideoCall(primary); 533 } 534 updateCallCache(primary); 535 536 // If the call context changed, potentially exit fullscreen or schedule auto enter of 537 // fullscreen mode. 538 // If the current call context is no longer a video call, exit fullscreen mode. 539 maybeExitFullscreen(currentCall); 540 // Schedule auto-enter of fullscreen mode if the current call context is a video call 541 maybeAutoEnterFullscreen(currentCall); 542 } 543 544 /** 545 * Handles a change to the fullscreen mode of the app. 546 * 547 * @param isFullscreenMode {@code true} if the app is now fullscreen, {@code false} otherwise. 548 */ 549 @Override onFullscreenModeChanged(boolean isFullscreenMode)550 public void onFullscreenModeChanged(boolean isFullscreenMode) { 551 cancelAutoFullScreen(); 552 if (primaryCall != null) { 553 updateFullscreenAndGreenScreenMode( 554 primaryCall.getState(), primaryCall.getVideoTech().getSessionModificationState()); 555 } else { 556 updateFullscreenAndGreenScreenMode(State.INVALID, SessionModificationState.NO_REQUEST); 557 } 558 } 559 checkForVideoStateChange(DialerCall call)560 private void checkForVideoStateChange(DialerCall call) { 561 final boolean shouldShowVideoUi = shouldShowVideoUiForCall(call); 562 final boolean hasVideoStateChanged = currentVideoState != call.getVideoState(); 563 564 LogUtil.v( 565 "VideoCallPresenter.checkForVideoStateChange", 566 "shouldShowVideoUi: %b, hasVideoStateChanged: %b, isVideoMode: %b, previousVideoState: %s," 567 + " newVideoState: %s", 568 shouldShowVideoUi, 569 hasVideoStateChanged, 570 isVideoMode(), 571 VideoProfile.videoStateToString(currentVideoState), 572 VideoProfile.videoStateToString(call.getVideoState())); 573 if (!hasVideoStateChanged) { 574 return; 575 } 576 577 updateCameraSelection(call); 578 579 if (shouldShowVideoUi) { 580 adjustVideoMode(call); 581 } else if (isVideoMode()) { 582 exitVideoMode(); 583 } 584 } 585 checkForCallStateChange(DialerCall call)586 private void checkForCallStateChange(DialerCall call) { 587 final boolean shouldShowVideoUi = shouldShowVideoUiForCall(call); 588 final boolean hasCallStateChanged = 589 currentCallState != call.getState() || isRemotelyHeld != call.isRemotelyHeld(); 590 isRemotelyHeld = call.isRemotelyHeld(); 591 592 LogUtil.v( 593 "VideoCallPresenter.checkForCallStateChange", 594 "shouldShowVideoUi: %b, hasCallStateChanged: %b, isVideoMode: %b", 595 shouldShowVideoUi, 596 hasCallStateChanged, 597 isVideoMode()); 598 599 if (!hasCallStateChanged) { 600 return; 601 } 602 603 if (shouldShowVideoUi) { 604 final InCallCameraManager cameraManager = 605 InCallPresenter.getInstance().getInCallCameraManager(); 606 607 String prevCameraId = cameraManager.getActiveCameraId(); 608 updateCameraSelection(call); 609 String newCameraId = cameraManager.getActiveCameraId(); 610 611 if (!Objects.equals(prevCameraId, newCameraId) && isActiveVideoCall(call)) { 612 enableCamera(call, true); 613 } 614 } 615 616 // Make sure we hide or show the video UI if needed. 617 showVideoUi( 618 call.getVideoState(), 619 call.getState(), 620 call.getVideoTech().getSessionModificationState(), 621 call.isRemotelyHeld()); 622 } 623 onPrimaryCallChanged(DialerCall newPrimaryCall)624 private void onPrimaryCallChanged(DialerCall newPrimaryCall) { 625 final boolean shouldShowVideoUi = shouldShowVideoUiForCall(newPrimaryCall); 626 final boolean isVideoMode = isVideoMode(); 627 628 LogUtil.v( 629 "VideoCallPresenter.onPrimaryCallChanged", 630 "shouldShowVideoUi: %b, isVideoMode: %b", 631 shouldShowVideoUi, 632 isVideoMode); 633 634 if (!shouldShowVideoUi && isVideoMode) { 635 // Terminate video mode if new primary call is not a video call 636 // and we are currently in video mode. 637 LogUtil.i("VideoCallPresenter.onPrimaryCallChanged", "exiting video mode..."); 638 exitVideoMode(); 639 } else if (shouldShowVideoUi) { 640 LogUtil.i("VideoCallPresenter.onPrimaryCallChanged", "entering video mode..."); 641 642 updateCameraSelection(newPrimaryCall); 643 adjustVideoMode(newPrimaryCall); 644 } 645 checkForOrientationAllowedChange(newPrimaryCall); 646 } 647 isVideoMode()648 private boolean isVideoMode() { 649 return isVideoMode; 650 } 651 updateCallCache(DialerCall call)652 private void updateCallCache(DialerCall call) { 653 if (call == null) { 654 currentVideoState = VideoProfile.STATE_AUDIO_ONLY; 655 currentCallState = DialerCall.State.INVALID; 656 videoCall = null; 657 primaryCall = null; 658 } else { 659 currentVideoState = call.getVideoState(); 660 videoCall = call.getVideoCall(); 661 currentCallState = call.getState(); 662 primaryCall = call; 663 } 664 } 665 666 /** 667 * Handles changes to the details of the call. The {@link VideoCallPresenter} is interested in 668 * changes to the video state. 669 * 670 * @param call The call for which the details changed. 671 * @param details The new call details. 672 */ 673 @Override onDetailsChanged(DialerCall call, android.telecom.Call.Details details)674 public void onDetailsChanged(DialerCall call, android.telecom.Call.Details details) { 675 LogUtil.v( 676 "VideoCallPresenter.onDetailsChanged", 677 "call: %s, details: %s, mPrimaryCall: %s", 678 call, 679 details, 680 primaryCall); 681 if (call == null) { 682 return; 683 } 684 // If the details change is not for the currently active call no update is required. 685 if (!call.equals(primaryCall)) { 686 LogUtil.v("VideoCallPresenter.onDetailsChanged", "details not for current active call"); 687 return; 688 } 689 690 updateVideoCall(call); 691 692 updateCallCache(call); 693 } 694 updateVideoCall(DialerCall call)695 private void updateVideoCall(DialerCall call) { 696 checkForVideoCallChange(call); 697 checkForVideoStateChange(call); 698 checkForCallStateChange(call); 699 checkForOrientationAllowedChange(call); 700 updateFullscreenAndGreenScreenMode( 701 call.getState(), call.getVideoTech().getSessionModificationState()); 702 } 703 checkForOrientationAllowedChange(@ullable DialerCall call)704 private void checkForOrientationAllowedChange(@Nullable DialerCall call) { 705 InCallPresenter.getInstance() 706 .setInCallAllowsOrientationChange(isVideoCall(call) || isVideoUpgrade(call)); 707 } 708 updateFullscreenAndGreenScreenMode( int callState, @SessionModificationState int sessionModificationState)709 private void updateFullscreenAndGreenScreenMode( 710 int callState, @SessionModificationState int sessionModificationState) { 711 if (videoCallScreen != null) { 712 boolean shouldShowFullscreen = InCallPresenter.getInstance().isFullscreen(); 713 boolean shouldShowGreenScreen = 714 callState == State.DIALING 715 || callState == State.CONNECTING 716 || callState == State.INCOMING 717 || isVideoUpgrade(sessionModificationState); 718 videoCallScreen.updateFullscreenAndGreenScreenMode( 719 shouldShowFullscreen, shouldShowGreenScreen); 720 } 721 } 722 723 /** Checks for a change to the video call and changes it if required. */ checkForVideoCallChange(DialerCall call)724 private void checkForVideoCallChange(DialerCall call) { 725 final VideoCall videoCall = call.getVideoCall(); 726 LogUtil.v( 727 "VideoCallPresenter.checkForVideoCallChange", 728 "videoCall: %s, mVideoCall: %s", 729 videoCall, 730 this.videoCall); 731 if (!Objects.equals(videoCall, this.videoCall)) { 732 changeVideoCall(call); 733 } 734 } 735 736 /** 737 * Handles a change to the video call. Sets the surfaces on the previous call to null and sets the 738 * surfaces on the new video call accordingly. 739 * 740 * @param call The new video call. 741 */ changeVideoCall(DialerCall call)742 private void changeVideoCall(DialerCall call) { 743 final VideoCall videoCall = call == null ? null : call.getVideoCall(); 744 LogUtil.i( 745 "VideoCallPresenter.changeVideoCall", 746 "videoCall: %s, mVideoCall: %s", 747 videoCall, 748 this.videoCall); 749 final boolean hasChanged = this.videoCall == null && videoCall != null; 750 751 this.videoCall = videoCall; 752 if (this.videoCall == null) { 753 LogUtil.v("VideoCallPresenter.changeVideoCall", "video call or primary call is null. Return"); 754 return; 755 } 756 757 if (shouldShowVideoUiForCall(call) && hasChanged) { 758 adjustVideoMode(call); 759 } 760 } 761 isCameraRequired()762 private boolean isCameraRequired() { 763 return primaryCall != null 764 && isCameraRequired( 765 primaryCall.getVideoState(), primaryCall.getVideoTech().getSessionModificationState()); 766 } 767 768 /** 769 * Adjusts the current video mode by setting up the preview and display surfaces as necessary. 770 * Expected to be called whenever the video state associated with a call changes (e.g. a user 771 * turns their camera on or off) to ensure the correct surfaces are shown/hidden. TODO(vt): Need 772 * to adjust size and orientation of preview surface here. 773 */ adjustVideoMode(DialerCall call)774 private void adjustVideoMode(DialerCall call) { 775 VideoCall videoCall = call.getVideoCall(); 776 int newVideoState = call.getVideoState(); 777 778 LogUtil.i( 779 "VideoCallPresenter.adjustVideoMode", 780 "videoCall: %s, videoState: %d", 781 videoCall, 782 newVideoState); 783 if (videoCallScreen == null) { 784 LogUtil.e("VideoCallPresenter.adjustVideoMode", "error VideoCallScreen is null so returning"); 785 return; 786 } 787 788 showVideoUi( 789 newVideoState, 790 call.getState(), 791 call.getVideoTech().getSessionModificationState(), 792 call.isRemotelyHeld()); 793 794 // Communicate the current camera to telephony and make a request for the camera 795 // capabilities. 796 if (videoCall != null) { 797 Surface surface = getRemoteVideoSurfaceTexture().getSavedSurface(); 798 if (surface != null) { 799 LogUtil.v( 800 "VideoCallPresenter.adjustVideoMode", "calling setDisplaySurface with: " + surface); 801 videoCall.setDisplaySurface(surface); 802 } 803 804 Assert.checkState( 805 deviceOrientation != InCallOrientationEventListener.SCREEN_ORIENTATION_UNKNOWN); 806 videoCall.setDeviceOrientation(deviceOrientation); 807 enableCamera( 808 call, isCameraRequired(newVideoState, call.getVideoTech().getSessionModificationState())); 809 } 810 int previousVideoState = currentVideoState; 811 currentVideoState = newVideoState; 812 isVideoMode = true; 813 814 // adjustVideoMode may be called if we are already in a 1-way video state. In this case 815 // we do not want to trigger auto-fullscreen mode. 816 if (!isVideoCall(previousVideoState) && isVideoCall(newVideoState)) { 817 maybeAutoEnterFullscreen(call); 818 } 819 } 820 shouldShowVideoUiForCall(@ullable DialerCall call)821 private static boolean shouldShowVideoUiForCall(@Nullable DialerCall call) { 822 if (call == null) { 823 return false; 824 } 825 826 if (isVideoCall(call)) { 827 return true; 828 } 829 830 if (isVideoUpgrade(call)) { 831 return true; 832 } 833 834 return false; 835 } 836 enableCamera(DialerCall call, boolean isCameraRequired)837 private void enableCamera(DialerCall call, boolean isCameraRequired) { 838 LogUtil.v("VideoCallPresenter.enableCamera", "call: %s, enabling: %b", call, isCameraRequired); 839 if (call == null) { 840 LogUtil.i("VideoCallPresenter.enableCamera", "call is null"); 841 return; 842 } 843 844 boolean hasCameraPermission = VideoUtils.hasCameraPermissionAndShownPrivacyToast(context); 845 if (!hasCameraPermission) { 846 call.getVideoTech().setCamera(null); 847 previewSurfaceState = PreviewSurfaceState.NONE; 848 // TODO(wangqi): Inform remote party that the video is off. This is similar to a bug. 849 } else if (isCameraRequired) { 850 InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager(); 851 call.getVideoTech().setCamera(cameraManager.getActiveCameraId()); 852 previewSurfaceState = PreviewSurfaceState.CAMERA_SET; 853 } else { 854 previewSurfaceState = PreviewSurfaceState.NONE; 855 call.getVideoTech().setCamera(null); 856 } 857 } 858 859 /** Exits video mode by hiding the video surfaces and making other adjustments (eg. audio). */ exitVideoMode()860 private void exitVideoMode() { 861 LogUtil.i("VideoCallPresenter.exitVideoMode", ""); 862 863 showVideoUi( 864 VideoProfile.STATE_AUDIO_ONLY, 865 DialerCall.State.ACTIVE, 866 SessionModificationState.NO_REQUEST, 867 false /* isRemotelyHeld */); 868 enableCamera(primaryCall, false); 869 InCallPresenter.getInstance().setFullScreen(false); 870 InCallPresenter.getInstance().enableScreenTimeout(false); 871 isVideoMode = false; 872 } 873 874 /** 875 * Based on the current video state and call state, show or hide the incoming and outgoing video 876 * surfaces. The outgoing video surface is shown any time video is transmitting. The incoming 877 * video surface is shown whenever the video is un-paused and active. 878 * 879 * @param videoState The video state. 880 * @param callState The call state. 881 */ showVideoUi( int videoState, int callState, @SessionModificationState int sessionModificationState, boolean isRemotelyHeld)882 private void showVideoUi( 883 int videoState, 884 int callState, 885 @SessionModificationState int sessionModificationState, 886 boolean isRemotelyHeld) { 887 if (videoCallScreen == null) { 888 LogUtil.e("VideoCallPresenter.showVideoUi", "videoCallScreen is null returning"); 889 return; 890 } 891 boolean showIncomingVideo = showIncomingVideo(videoState, callState); 892 boolean showOutgoingVideo = showOutgoingVideo(context, videoState, sessionModificationState); 893 LogUtil.i( 894 "VideoCallPresenter.showVideoUi", 895 "showIncoming: %b, showOutgoing: %b, isRemotelyHeld: %b", 896 showIncomingVideo, 897 showOutgoingVideo, 898 isRemotelyHeld); 899 updateRemoteVideoSurfaceDimensions(); 900 videoCallScreen.showVideoViews(showOutgoingVideo, showIncomingVideo, isRemotelyHeld); 901 902 InCallPresenter.getInstance().enableScreenTimeout(VideoProfile.isAudioOnly(videoState)); 903 updateFullscreenAndGreenScreenMode(callState, sessionModificationState); 904 } 905 906 /** 907 * Handles peer video dimension changes. 908 * 909 * @param call The call which experienced a peer video dimension change. 910 * @param width The new peer video width . 911 * @param height The new peer video height. 912 */ 913 @Override onUpdatePeerDimensions(DialerCall call, int width, int height)914 public void onUpdatePeerDimensions(DialerCall call, int width, int height) { 915 LogUtil.i("VideoCallPresenter.onUpdatePeerDimensions", "width: %d, height: %d", width, height); 916 if (videoCallScreen == null) { 917 LogUtil.e("VideoCallPresenter.onUpdatePeerDimensions", "videoCallScreen is null"); 918 return; 919 } 920 if (!call.equals(primaryCall)) { 921 LogUtil.e( 922 "VideoCallPresenter.onUpdatePeerDimensions", "current call is not equal to primary"); 923 return; 924 } 925 926 // Change size of display surface to match the peer aspect ratio 927 if (width > 0 && height > 0 && videoCallScreen != null) { 928 getRemoteVideoSurfaceTexture().setSourceVideoDimensions(new Point(width, height)); 929 videoCallScreen.onRemoteVideoDimensionsChanged(); 930 } 931 } 932 933 /** 934 * Handles a change to the dimensions of the local camera. Receiving the camera capabilities 935 * triggers the creation of the video 936 * 937 * @param call The call which experienced the camera dimension change. 938 * @param width The new camera video width. 939 * @param height The new camera video height. 940 */ 941 @Override onCameraDimensionsChange(DialerCall call, int width, int height)942 public void onCameraDimensionsChange(DialerCall call, int width, int height) { 943 LogUtil.i( 944 "VideoCallPresenter.onCameraDimensionsChange", 945 "call: %s, width: %d, height: %d", 946 call, 947 width, 948 height); 949 if (videoCallScreen == null) { 950 LogUtil.e("VideoCallPresenter.onCameraDimensionsChange", "ui is null"); 951 return; 952 } 953 954 if (!call.equals(primaryCall)) { 955 LogUtil.e("VideoCallPresenter.onCameraDimensionsChange", "not the primary call"); 956 return; 957 } 958 959 previewSurfaceState = PreviewSurfaceState.CAPABILITIES_RECEIVED; 960 changePreviewDimensions(width, height); 961 962 // Check if the preview surface is ready yet; if it is, set it on the {@code VideoCall}. 963 // If it not yet ready, it will be set when when creation completes. 964 Surface surface = getLocalVideoSurfaceTexture().getSavedSurface(); 965 if (surface != null) { 966 previewSurfaceState = PreviewSurfaceState.SURFACE_SET; 967 videoCall.setPreviewSurface(surface); 968 } 969 } 970 971 /** 972 * Changes the dimensions of the preview surface. 973 * 974 * @param width The new width. 975 * @param height The new height. 976 */ changePreviewDimensions(int width, int height)977 private void changePreviewDimensions(int width, int height) { 978 if (videoCallScreen == null) { 979 return; 980 } 981 982 // Resize the surface used to display the preview video 983 getLocalVideoSurfaceTexture().setSurfaceDimensions(new Point(width, height)); 984 videoCallScreen.onLocalVideoDimensionsChanged(); 985 } 986 987 /** 988 * Handles changes to the device orientation. 989 * 990 * @param orientation The screen orientation of the device (one of: {@link 991 * InCallOrientationEventListener#SCREEN_ORIENTATION_0}, {@link 992 * InCallOrientationEventListener#SCREEN_ORIENTATION_90}, {@link 993 * InCallOrientationEventListener#SCREEN_ORIENTATION_180}, {@link 994 * InCallOrientationEventListener#SCREEN_ORIENTATION_270}). 995 */ 996 @Override onDeviceOrientationChanged(int orientation)997 public void onDeviceOrientationChanged(int orientation) { 998 LogUtil.i( 999 "VideoCallPresenter.onDeviceOrientationChanged", 1000 "orientation: %d -> %d", 1001 deviceOrientation, 1002 orientation); 1003 deviceOrientation = orientation; 1004 1005 if (videoCallScreen == null) { 1006 LogUtil.e("VideoCallPresenter.onDeviceOrientationChanged", "videoCallScreen is null"); 1007 return; 1008 } 1009 1010 Point previewDimensions = getLocalVideoSurfaceTexture().getSurfaceDimensions(); 1011 if (previewDimensions == null) { 1012 return; 1013 } 1014 LogUtil.v( 1015 "VideoCallPresenter.onDeviceOrientationChanged", 1016 "orientation: %d, size: %s", 1017 orientation, 1018 previewDimensions); 1019 changePreviewDimensions(previewDimensions.x, previewDimensions.y); 1020 1021 videoCallScreen.onLocalVideoOrientationChanged(); 1022 } 1023 1024 /** 1025 * Exits fullscreen mode if the current call context has changed to a non-video call. 1026 * 1027 * @param call The call. 1028 */ maybeExitFullscreen(DialerCall call)1029 protected void maybeExitFullscreen(DialerCall call) { 1030 if (call == null) { 1031 return; 1032 } 1033 1034 if (!isVideoCall(call) || call.getState() == DialerCall.State.INCOMING) { 1035 LogUtil.i("VideoCallPresenter.maybeExitFullscreen", "exiting fullscreen"); 1036 InCallPresenter.getInstance().setFullScreen(false); 1037 } 1038 } 1039 1040 /** 1041 * Schedules auto-entering of fullscreen mode. Will not enter full screen mode if any of the 1042 * following conditions are met: 1. No call 2. DialerCall is not active 3. The current video state 1043 * is not bi-directional. 4. Already in fullscreen mode 5. In accessibility mode 1044 * 1045 * @param call The current call. 1046 */ maybeAutoEnterFullscreen(DialerCall call)1047 protected void maybeAutoEnterFullscreen(DialerCall call) { 1048 if (!isAutoFullscreenEnabled) { 1049 return; 1050 } 1051 1052 if (call == null 1053 || call.getState() != DialerCall.State.ACTIVE 1054 || !isBidirectionalVideoCall(call) 1055 || InCallPresenter.getInstance().isFullscreen() 1056 || (context != null && AccessibilityUtil.isTouchExplorationEnabled(context))) { 1057 // Ensure any previously scheduled attempt to enter fullscreen is cancelled. 1058 cancelAutoFullScreen(); 1059 return; 1060 } 1061 1062 if (autoFullScreenPending) { 1063 LogUtil.v("VideoCallPresenter.maybeAutoEnterFullscreen", "already pending."); 1064 return; 1065 } 1066 LogUtil.v("VideoCallPresenter.maybeAutoEnterFullscreen", "scheduled"); 1067 autoFullScreenPending = true; 1068 handler.removeCallbacks(autoFullscreenRunnable); 1069 handler.postDelayed(autoFullscreenRunnable, autoFullscreenTimeoutMillis); 1070 } 1071 1072 /** Cancels pending auto fullscreen mode. */ 1073 @Override cancelAutoFullScreen()1074 public void cancelAutoFullScreen() { 1075 if (!autoFullScreenPending) { 1076 LogUtil.v("VideoCallPresenter.cancelAutoFullScreen", "none pending."); 1077 return; 1078 } 1079 LogUtil.v("VideoCallPresenter.cancelAutoFullScreen", "cancelling pending"); 1080 autoFullScreenPending = false; 1081 handler.removeCallbacks(autoFullscreenRunnable); 1082 } 1083 1084 @Override shouldShowCameraPermissionToast()1085 public boolean shouldShowCameraPermissionToast() { 1086 if (primaryCall == null) { 1087 LogUtil.i("VideoCallPresenter.shouldShowCameraPermissionToast", "null call"); 1088 return false; 1089 } 1090 if (primaryCall.didShowCameraPermission()) { 1091 LogUtil.i( 1092 "VideoCallPresenter.shouldShowCameraPermissionToast", "already shown for this call"); 1093 return false; 1094 } 1095 if (!ConfigProviderBindings.get(context).getBoolean("camera_permission_dialog_allowed", true)) { 1096 LogUtil.i("VideoCallPresenter.shouldShowCameraPermissionToast", "disabled by config"); 1097 return false; 1098 } 1099 return !VideoUtils.hasCameraPermission(context) 1100 || !PermissionsUtil.hasCameraPrivacyToastShown(context); 1101 } 1102 1103 @Override onCameraPermissionDialogShown()1104 public void onCameraPermissionDialogShown() { 1105 if (primaryCall != null) { 1106 primaryCall.setDidShowCameraPermission(true); 1107 } 1108 } 1109 updateRemoteVideoSurfaceDimensions()1110 private void updateRemoteVideoSurfaceDimensions() { 1111 Activity activity = videoCallScreen.getVideoCallScreenFragment().getActivity(); 1112 if (activity != null) { 1113 Point screenSize = new Point(); 1114 activity.getWindowManager().getDefaultDisplay().getSize(screenSize); 1115 getRemoteVideoSurfaceTexture().setSurfaceDimensions(screenSize); 1116 } 1117 } 1118 isVideoUpgrade(DialerCall call)1119 private static boolean isVideoUpgrade(DialerCall call) { 1120 return call != null 1121 && (call.hasSentVideoUpgradeRequest() || call.hasReceivedVideoUpgradeRequest()); 1122 } 1123 isVideoUpgrade(@essionModificationState int state)1124 private static boolean isVideoUpgrade(@SessionModificationState int state) { 1125 return VideoUtils.hasSentVideoUpgradeRequest(state) 1126 || VideoUtils.hasReceivedVideoUpgradeRequest(state); 1127 } 1128 1129 private class LocalDelegate implements VideoSurfaceDelegate { 1130 @Override onSurfaceCreated(VideoSurfaceTexture videoCallSurface)1131 public void onSurfaceCreated(VideoSurfaceTexture videoCallSurface) { 1132 if (videoCallScreen == null) { 1133 LogUtil.e("VideoCallPresenter.LocalDelegate.onSurfaceCreated", "no UI"); 1134 return; 1135 } 1136 if (videoCall == null) { 1137 LogUtil.e("VideoCallPresenter.LocalDelegate.onSurfaceCreated", "no video call"); 1138 return; 1139 } 1140 1141 // If the preview surface has just been created and we have already received camera 1142 // capabilities, but not yet set the surface, we will set the surface now. 1143 if (previewSurfaceState == PreviewSurfaceState.CAPABILITIES_RECEIVED) { 1144 previewSurfaceState = PreviewSurfaceState.SURFACE_SET; 1145 videoCall.setPreviewSurface(videoCallSurface.getSavedSurface()); 1146 } else if (previewSurfaceState == PreviewSurfaceState.NONE && isCameraRequired()) { 1147 enableCamera(primaryCall, true); 1148 } 1149 } 1150 1151 @Override onSurfaceReleased(VideoSurfaceTexture videoCallSurface)1152 public void onSurfaceReleased(VideoSurfaceTexture videoCallSurface) { 1153 if (videoCall == null) { 1154 LogUtil.e("VideoCallPresenter.LocalDelegate.onSurfaceReleased", "no video call"); 1155 return; 1156 } 1157 1158 videoCall.setPreviewSurface(null); 1159 enableCamera(primaryCall, false); 1160 } 1161 1162 @Override onSurfaceDestroyed(VideoSurfaceTexture videoCallSurface)1163 public void onSurfaceDestroyed(VideoSurfaceTexture videoCallSurface) { 1164 if (videoCall == null) { 1165 LogUtil.e("VideoCallPresenter.LocalDelegate.onSurfaceDestroyed", "no video call"); 1166 return; 1167 } 1168 1169 boolean isChangingConfigurations = InCallPresenter.getInstance().isChangingConfigurations(); 1170 if (!isChangingConfigurations) { 1171 enableCamera(primaryCall, false); 1172 } else { 1173 LogUtil.i( 1174 "VideoCallPresenter.LocalDelegate.onSurfaceDestroyed", 1175 "activity is being destroyed due to configuration changes. Not closing the camera."); 1176 } 1177 } 1178 1179 @Override onSurfaceClick(VideoSurfaceTexture videoCallSurface)1180 public void onSurfaceClick(VideoSurfaceTexture videoCallSurface) { 1181 VideoCallPresenter.this.onSurfaceClick(); 1182 } 1183 } 1184 1185 private class RemoteDelegate implements VideoSurfaceDelegate { 1186 @Override onSurfaceCreated(VideoSurfaceTexture videoCallSurface)1187 public void onSurfaceCreated(VideoSurfaceTexture videoCallSurface) { 1188 if (videoCallScreen == null) { 1189 LogUtil.e("VideoCallPresenter.RemoteDelegate.onSurfaceCreated", "no UI"); 1190 return; 1191 } 1192 if (videoCall == null) { 1193 LogUtil.e("VideoCallPresenter.RemoteDelegate.onSurfaceCreated", "no video call"); 1194 return; 1195 } 1196 videoCall.setDisplaySurface(videoCallSurface.getSavedSurface()); 1197 } 1198 1199 @Override onSurfaceReleased(VideoSurfaceTexture videoCallSurface)1200 public void onSurfaceReleased(VideoSurfaceTexture videoCallSurface) { 1201 if (videoCall == null) { 1202 LogUtil.e("VideoCallPresenter.RemoteDelegate.onSurfaceReleased", "no video call"); 1203 return; 1204 } 1205 videoCall.setDisplaySurface(null); 1206 } 1207 1208 @Override onSurfaceDestroyed(VideoSurfaceTexture videoCallSurface)1209 public void onSurfaceDestroyed(VideoSurfaceTexture videoCallSurface) {} 1210 1211 @Override onSurfaceClick(VideoSurfaceTexture videoCallSurface)1212 public void onSurfaceClick(VideoSurfaceTexture videoCallSurface) { 1213 VideoCallPresenter.this.onSurfaceClick(); 1214 } 1215 } 1216 1217 /** Defines the state of the preview surface negotiation with the telephony layer. */ 1218 private static class PreviewSurfaceState { 1219 1220 /** 1221 * The camera has not yet been set on the {@link VideoCall}; negotiation has not yet started. 1222 */ 1223 private static final int NONE = 0; 1224 1225 /** 1226 * The camera has been set on the {@link VideoCall}, but camera capabilities have not yet been 1227 * received. 1228 */ 1229 private static final int CAMERA_SET = 1; 1230 1231 /** 1232 * The camera capabilties have been received from telephony, but the surface has not yet been 1233 * set on the {@link VideoCall}. 1234 */ 1235 private static final int CAPABILITIES_RECEIVED = 2; 1236 1237 /** The surface has been set on the {@link VideoCall}. */ 1238 private static final int SURFACE_SET = 3; 1239 } 1240 isBidirectionalVideoCall(DialerCall call)1241 private static boolean isBidirectionalVideoCall(DialerCall call) { 1242 return CompatUtils.isVideoCompatible() && VideoProfile.isBidirectional(call.getVideoState()); 1243 } 1244 isIncomingVideoCall(DialerCall call)1245 private static boolean isIncomingVideoCall(DialerCall call) { 1246 if (!isVideoCall(call)) { 1247 return false; 1248 } 1249 final int state = call.getState(); 1250 return (state == DialerCall.State.INCOMING) || (state == DialerCall.State.CALL_WAITING); 1251 } 1252 isActiveVideoCall(DialerCall call)1253 private static boolean isActiveVideoCall(DialerCall call) { 1254 return isVideoCall(call) && call.getState() == DialerCall.State.ACTIVE; 1255 } 1256 isOutgoingVideoCall(DialerCall call)1257 private static boolean isOutgoingVideoCall(DialerCall call) { 1258 if (!isVideoCall(call)) { 1259 return false; 1260 } 1261 final int state = call.getState(); 1262 return DialerCall.State.isDialing(state) 1263 || state == DialerCall.State.CONNECTING 1264 || state == DialerCall.State.SELECT_PHONE_ACCOUNT; 1265 } 1266 isAudioCall(DialerCall call)1267 private static boolean isAudioCall(DialerCall call) { 1268 if (!CompatUtils.isVideoCompatible()) { 1269 return true; 1270 } 1271 1272 return call != null && VideoProfile.isAudioOnly(call.getVideoState()); 1273 } 1274 isVideoCall(@ullable DialerCall call)1275 private static boolean isVideoCall(@Nullable DialerCall call) { 1276 return call != null && call.isVideoCall(); 1277 } 1278 isVideoCall(int videoState)1279 private static boolean isVideoCall(int videoState) { 1280 return CompatUtils.isVideoCompatible() 1281 && (VideoProfile.isTransmissionEnabled(videoState) 1282 || VideoProfile.isReceptionEnabled(videoState)); 1283 } 1284 } 1285