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