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