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