diff options
Diffstat (limited to 'InCallUI/src/com/android/incallui/VideoCallPresenter.java')
-rw-r--r-- | InCallUI/src/com/android/incallui/VideoCallPresenter.java | 725 |
1 files changed, 605 insertions, 120 deletions
diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java index d63c6e8b1..e4a5db97a 100644 --- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java +++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java @@ -18,10 +18,14 @@ package com.android.incallui; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Point; import android.os.Handler; import android.telecom.AudioState; import android.telecom.CameraCapabilities; +import android.telecom.Connection; +import android.telecom.Connection.VideoProvider; import android.telecom.InCallService.VideoCall; +import android.telecom.VideoProfile; import android.view.Surface; import com.android.contacts.common.CallUtil; @@ -31,10 +35,14 @@ import com.android.incallui.InCallPresenter.InCallStateListener; import com.android.incallui.InCallPresenter.IncomingCallListener; import com.android.incallui.InCallVideoCallListenerNotifier.SurfaceChangeListener; import com.android.incallui.InCallVideoCallListenerNotifier.VideoEventListener; +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.TelephonyProperties; import com.google.common.base.Preconditions; import java.util.Objects; +import android.os.SystemProperties; + /** * Logic related to the {@link VideoCallFragment} and for managing changes to the video calling * surfaces based on other user interface events and incoming events from the @@ -61,6 +69,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi IncomingCallListener, InCallOrientationListener, InCallStateListener, InCallDetailsListener, SurfaceChangeListener, VideoEventListener, InCallVideoCallListenerNotifier.SessionModificationListener { + public static final String TAG = "VideoCallPresenter"; private static final String TAG = VideoCallPresenter.class.getSimpleName(); @@ -126,7 +135,12 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi /** * Determines if the current UI state represents a video call. */ - private boolean mIsVideoCall; + private int mCurrentVideoState; + + /** + * Call's current state + */ + private int mCurrentCallState = Call.State.INVALID; /** * Determines the device orientation (portrait/lanscape). @@ -146,7 +160,15 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi /** * Saves the audio mode which was selected prior to going into a video call. */ - private int mPreVideoAudioMode = AudioModeProvider.AUDIO_MODE_INVALID; + private static int sPrevVideoAudioMode = AudioModeProvider.AUDIO_MODE_INVALID; + + private static boolean mIsVideoMode = false; + + /** + * Stores the current call substate. + */ + private int mCurrentCallSubstate; + /** Handler which resets request state to NO_REQUEST after an interval. */ private Handler mSessionModificationResetHandler; @@ -172,9 +194,11 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi @Override public void onUiReady(VideoCallUi ui) { super.onUiReady(ui); + Log.d(this, "onUiReady:"); // Register for call state changes last InCallPresenter.getInstance().addListener(this); + InCallPresenter.getInstance().addDetailsListener(this); InCallPresenter.getInstance().addIncomingCallListener(this); InCallPresenter.getInstance().addOrientationListener(this); // To get updates of video call details changes @@ -184,7 +208,8 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi InCallVideoCallListenerNotifier.getInstance().addSurfaceChangeListener(this); InCallVideoCallListenerNotifier.getInstance().addVideoEventListener(this); InCallVideoCallListenerNotifier.getInstance().addSessionModificationListener(this); - mIsVideoCall = false; + mCurrentVideoState = VideoProfile.VideoState.AUDIO_ONLY; + mCurrentCallState = Call.State.INVALID; } /** @@ -195,44 +220,44 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi @Override public void onUiUnready(VideoCallUi ui) { super.onUiUnready(ui); + Log.d(this, "onUiUnready:"); InCallPresenter.getInstance().removeListener(this); + InCallPresenter.getInstance().removeDetailsListener(this); InCallPresenter.getInstance().removeIncomingCallListener(this); InCallPresenter.getInstance().removeOrientationListener(this); + InCallVideoCallListenerNotifier.getInstance().removeSurfaceChangeListener(this); InCallVideoCallListenerNotifier.getInstance().removeVideoEventListener(this); InCallVideoCallListenerNotifier.getInstance().removeSessionModificationListener(this); } /** - * @return The {@link VideoCall}. - */ - private VideoCall getVideoCall() { - return mVideoCall; - } - - /** * Handles the creation of a surface in the {@link VideoCallFragment}. * * @param surface The surface which was created. */ public void onSurfaceCreated(int surface) { - if (DEBUG) { - Log.i(TAG, "onSurfaceCreated: " + surface); - } - final VideoCallUi ui = getUi(); + Log.d(this, "onSurfaceCreated surface=" + surface + " mVideoCall=" + mVideoCall); + Log.d(this, "onSurfaceCreated PreviewSurfaceState=" + mPreviewSurfaceState); + Log.d(this, "onSurfaceCreated presenter=" + this); + final VideoCallUi ui = getUi(); if (ui == null || mVideoCall == null) { + Log.w(this, "onSurfaceCreated: Error bad state VideoCallUi=" + ui + " mVideoCall=" + + mVideoCall); return; } // If the preview surface has just been created and we have already received camera // capabilities, but not yet set the surface, we will set the surface now. - if (surface == VideoCallFragment.SURFACE_PREVIEW && - mPreviewSurfaceState == PreviewSurfaceState.CAPABILITIES_RECEIVED) { - - mPreviewSurfaceState = PreviewSurfaceState.SURFACE_SET; - mVideoCall.setPreviewSurface(ui.getPreviewVideoSurface()); + if (surface == VideoCallFragment.SURFACE_PREVIEW ) { + if (mPreviewSurfaceState == PreviewSurfaceState.CAPABILITIES_RECEIVED) { + mPreviewSurfaceState = PreviewSurfaceState.SURFACE_SET; + mVideoCall.setPreviewSurface(ui.getPreviewVideoSurface()); + } else if (mPreviewSurfaceState == PreviewSurfaceState.NONE && isCameraRequired()){ + enableCamera(mVideoCall, true); + } } else if (surface == VideoCallFragment.SURFACE_DISPLAY) { mVideoCall.setDisplaySurface(ui.getDisplayVideoSurface()); } @@ -252,12 +277,15 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi /** * Handles the destruction of a surface in the {@link VideoCallFragment}. + * Note: The surface is being released, that is, it is no longer valid. * * @param surface The surface which was destroyed. */ - public void onSurfaceDestroyed(int surface) { - final VideoCallUi ui = getUi(); - if (ui == null || mVideoCall == null) { + public void onSurfaceReleased(int surface) { + Log.d(this, "onSurfaceReleased: mSurfaceId=" + surface); + if ( mVideoCall == null) { + Log.w(this, "onSurfaceReleased: VideoCall is null. mSurfaceId=" + + surface); return; } @@ -265,12 +293,44 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi mVideoCall.setDisplaySurface(null); } else if (surface == VideoCallFragment.SURFACE_PREVIEW) { mVideoCall.setPreviewSurface(null); - // Also disable camera as preview is closed - mVideoCall.setCamera(null); + enableCamera(mVideoCall, false); } } /** + * Called by {@link VideoCallFragment} when the surface is detached from UI (TextureView). + * Note: The surface will be cached by {@link VideoCallFragment}, so we don't immediately + * null out incoming video surface. + * @see VideoCallPresenter#onSurfaceReleased(int) + * + * @param surface The surface which was detached. + */ + public void onSurfaceDestroyed(int surface) { + Log.d(this, "onSurfaceDestroyed: mSurfaceId=" + surface); + if (mVideoCall == null) { + return; + } + + final boolean isChangingConfigurations = + InCallPresenter.getInstance().isChangingConfigurations(); + Log.d(this, "onSurfaceDestroyed: isChangingConfigurations=" + isChangingConfigurations); + + if (surface == VideoCallFragment.SURFACE_PREVIEW) { + if (!isChangingConfigurations) { + enableCamera(mVideoCall, false); + } else { + Log.w(this, "onSurfaceDestroyed: Activity is being destroyed due " + + "to configuration changes. Not closing the camera."); + } + } + } + + private void toggleFullScreen() { + mIsFullScreen = !mIsFullScreen; + InCallPresenter.getInstance().setFullScreenVideoState(mIsFullScreen); + } + + /** * Handles clicks on the video surfaces by toggling full screen state. * Informs the {@link InCallPresenter} of the change so that it can inform the * {@link CallCardPresenter} of the change. @@ -278,8 +338,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi * @param surfaceId The video surface receiving the click. */ public void onSurfaceClick(int surfaceId) { - mIsFullScreen = !mIsFullScreen; - InCallPresenter.getInstance().setFullScreenVideoState(mIsFullScreen); + toggleFullScreen(); } @@ -305,41 +364,152 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi @Override public void onStateChange(InCallPresenter.InCallState oldState, InCallPresenter.InCallState newState, CallList callList) { - // Bail if video calling is disabled for the device. - if (!CallUtil.isVideoEnabled(mContext)) { - return; - } + Log.d(this, "onStateChange oldState" + oldState + " newState=" + newState + + " isVideoMode=" + isVideoMode()); if (newState == InCallPresenter.InCallState.NO_CALLS) { - exitVideoMode(); + updateAudioMode(false); + + if (isVideoMode()) { + exitVideoMode(); + } + + cleanupSurfaces(); } // Determine the primary active call). Call primary = null; if (newState == InCallPresenter.InCallState.INCOMING) { - primary = callList.getIncomingCall(); + // We don't want to replace active video call (primary call) + // with a waiting call, since user may choose to ignore/decline the waiting call and + // this should have no impact on current active video call, that is, we should not + // change the camera or UI unless the waiting VT call becomes active. + primary = callList.getActiveCall(); + if (!CallUtils.isActiveVideoCall(primary)) { + primary = callList.getIncomingCall(); + } } else if (newState == InCallPresenter.InCallState.OUTGOING) { primary = callList.getOutgoingCall(); + } else if (newState == InCallPresenter.InCallState.PENDING_OUTGOING) { + primary = callList.getPendingOutgoingCall(); } else if (newState == InCallPresenter.InCallState.INCALL) { primary = callList.getActiveCall(); } final boolean primaryChanged = !Objects.equals(mPrimaryCall, primary); + Log.d(this, "onStateChange primaryChanged=" + primaryChanged); + Log.d(this, "onStateChange primary= " + primary); + Log.d(this, "onStateChange mPrimaryCall = " + mPrimaryCall); if (primaryChanged) { - mPrimaryCall = primary; - - if (primary != null) { - checkForVideoCallChange(); - mIsVideoCall = mPrimaryCall.isVideoCall(mContext); - if (mIsVideoCall) { - enterVideoMode(); - } else { - exitVideoMode(); - } - } else if (primary == null) { - // If no primary call, ensure we exit video state and clean up the video surfaces. - exitVideoMode(); + onPrimaryCallChanged(primary); + } else if(mPrimaryCall!=null) { + updateVideoCall(primary); + } + updateCallCache(primary); + } + + private void checkForVideoStateChange(Call call) { + final boolean isVideoCall = CallUtils.isVideoCall(call); + final boolean hasVideoStateChanged = mCurrentVideoState != call.getVideoState(); + + Log.d(this, "checkForVideoStateChange: isVideoCall= " + isVideoCall + + " hasVideoStateChanged=" + + hasVideoStateChanged + " isVideoMode=" + isVideoMode()); + + if (!hasVideoStateChanged) { return;} + + updateCameraSelection(call); + + if (isVideoCall) { + enterVideoMode(call.getVideoCall(), call.getVideoState()); + } else if (isVideoMode()) { + exitVideoMode(); + } + } + + private void checkForCallStateChange(Call call) { + final boolean isVideoCall = CallUtils.isVideoCall(call); + final boolean hasCallStateChanged = mCurrentCallState != call.getState(); + + Log.d(this, "checkForCallStateChange: isVideoCall= " + isVideoCall + + " hasCallStateChanged=" + + hasCallStateChanged + " isVideoMode=" + isVideoMode()); + + if (!hasCallStateChanged) { return; } + + final InCallCameraManager cameraManager = InCallPresenter.getInstance(). + getInCallCameraManager(); + + String prevCameraId = cameraManager.getActiveCameraId(); + + updateCameraSelection(call); + + String newCameraId = cameraManager.getActiveCameraId(); + + if (!Objects.equals(prevCameraId, newCameraId) && CallUtils.isActiveVideoCall(call)) { + enableCamera(call.getVideoCall(), true); + } + } + + private void checkForCallSubstateChange(Call call) { + if (mCurrentCallSubstate != call.getCallSubstate()) { + VideoCallUi ui = getUi(); + if (ui == null) { + Log.e(this, "Error VideoCallUi is null. Return."); + return; } + mCurrentCallSubstate = call.getCallSubstate(); + // Display a call substate changed message on UI. + ui.showCallSubstateChanged(mCurrentCallSubstate); + } + } + + private void cleanupSurfaces() { + final VideoCallUi ui = getUi(); + if (ui == null) { + Log.w(this, "cleanupSurfaces"); + return; + } + ui.cleanupSurfaces(); + } + + private void onPrimaryCallChanged(Call newPrimaryCall) { + final boolean isVideoCall = CallUtils.isVideoCall(newPrimaryCall); + final boolean isVideoMode = isVideoMode(); + + Log.d(this, "onPrimaryCallChanged: isVideoCall=" + isVideoCall + " isVideoMode=" + + isVideoMode); + + if (!isVideoCall && isVideoMode) { + // Terminate video mode if new primary call is not a video call + // and we are currently in video mode. + Log.d(this, "onPrimaryCallChanged: Exiting video mode..."); + exitVideoMode(); + } else if (isVideoCall) { + Log.d(this, "onPrimaryCallChanged: Entering video mode..."); + + updateCameraSelection(newPrimaryCall); + enterVideoMode(newPrimaryCall.getVideoCall(), newPrimaryCall.getVideoState()); + } + } + + private boolean isVideoMode() { + return mIsVideoMode; + } + + private void updateCallCache(Call call) { + if (call == null) { + mCurrentVideoState = VideoProfile.VideoState.AUDIO_ONLY; + mCurrentCallSubstate = Connection.SUBSTATE_NONE; + mCurrentCallState = Call.State.INVALID; + mVideoCall = null; + mPrimaryCall = null; + } else { + mCurrentVideoState = call.getVideoState(); + mCurrentCallSubstate = call.getCallSubstate(); + mVideoCall = call.getVideoCall(); + mCurrentCallState = call.getState(); + mPrimaryCall = call; } } @@ -352,120 +522,214 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi */ @Override public void onDetailsChanged(Call call, android.telecom.Call.Details details) { + Log.d(this, " onDetailsChanged call=" + call + " details=" + details + " mPrimaryCall=" + + mPrimaryCall); // If the details change is not for the currently active call no update is required. if (!call.equals(mPrimaryCall)) { + Log.d(this," onDetailsChanged: Details not for current active call so returning. "); return; } - checkForVideoStateChange(); + updateVideoCall(call); + checkForCallSubstateChange(call); + + updateCallCache(call); } - /** - * Checks for a change to the video call and changes it if required. - */ - private void checkForVideoCallChange() { - VideoCall videoCall = mPrimaryCall.getTelecommCall().getVideoCall(); - if (!Objects.equals(videoCall, mVideoCall)) { - changeVideoCall(videoCall); - } + private void updateVideoCall(Call call) { + checkForVideoCallChange(call); + checkForVideoStateChange(call); + checkForCallStateChange(call); } /** - * Checks to see if the current video state has changed and updates the UI if required. + * Checks for a change to the video call and changes it if required. */ - private void checkForVideoStateChange() { - boolean newVideoState = mPrimaryCall.isVideoCall(mContext); - - // Check if video state changed - if (mIsVideoCall != newVideoState) { - mIsVideoCall = newVideoState; - - if (mIsVideoCall) { - enterVideoMode(); - } else { - exitVideoMode(); - } + private void checkForVideoCallChange(Call call) { + final VideoCall videoCall = call.getTelecommCall().getVideoCall(); + Log.d(this, "checkForVideoCallChange: videoCall=" + videoCall + " mVideoCall=" + + mVideoCall); + if (!Objects.equals(videoCall, mVideoCall)) { + changeVideoCall(call); } } /** - * Handles a change to the video call. Sets the surfaces on the previous call to null and sets + * Handles a change to the video call. Sets the surfaces on the previous call to null and sets * the surfaces on the new video call accordingly. * * @param videoCall The new video call. */ - private void changeVideoCall(VideoCall videoCall) { + private void changeVideoCall(Call call) { + final VideoCall videoCall = call.getTelecommCall().getVideoCall(); + Log.d(this, "changeVideoCall to videoCall=" + videoCall + " mVideoCall=" + mVideoCall); // Null out the surfaces on the previous video call. if (mVideoCall != null) { - mVideoCall.setDisplaySurface(null); - mVideoCall.setPreviewSurface(null); + // Log.d(this, "Null out the surfaces on the previous video call."); + // mVideoCall.setDisplaySurface(null); + // mVideoCall.setPreviewSurface(null); } + final boolean hasChanged = mVideoCall == null && videoCall != null; + mVideoCall = videoCall; + if (mVideoCall == null || call == null) { + Log.d(this, "Video call or primary call is null. Return"); + return; + } + + if (CallUtils.isVideoCall(call) && hasChanged) { + enterVideoMode(call.getVideoCall(), call.getVideoState()); + } + } + + private static boolean isCameraRequired(int videoState) { + return VideoProfile.VideoState.isBidirectional(videoState) || + VideoProfile.VideoState.isTransmissionEnabled(videoState); + } + + private boolean isCameraRequired() { + return mPrimaryCall != null ? isCameraRequired(mPrimaryCall.getVideoState()) : false; } /** * Enters video mode by showing the video surfaces and making other adjustments (eg. audio). * TODO(vt): Need to adjust size and orientation of preview surface here. */ - private void enterVideoMode() { - if (DEBUG) { - Log.i(TAG, "enterVideoMode"); - } + private void enterVideoMode(VideoCall videoCall, int newVideoState) { + Log.d(this, "enterVideoMode videoCall= " + videoCall + " videoState: " + newVideoState); VideoCallUi ui = getUi(); if (ui == null) { + Log.e(this, "Error VideoCallUi is null so returning"); return; } - ui.showVideoUi(true); + showVideoUi(newVideoState); InCallPresenter.getInstance().setInCallAllowsOrientationChange(true); // Communicate the current camera to telephony and make a request for the camera // capabilities. - if (mVideoCall != null) { - // Do not reset the surfaces if we just restarted the activity due to an orientation - // change. - if (ui.isActivityRestart()) { - return; + if (videoCall != null) { + if (ui.isDisplayVideoSurfaceCreated()) { + Log.d(this, "Calling setDisplaySurface with " + ui.getDisplayVideoSurface()); + videoCall.setDisplaySurface(ui.getDisplayVideoSurface()); } - mPreviewSurfaceState = PreviewSurfaceState.CAMERA_SET; - InCallCameraManager cameraManager = InCallPresenter.getInstance(). - getInCallCameraManager(); - mVideoCall.setCamera(cameraManager.getActiveCameraId()); - mVideoCall.requestCameraCapabilities(); - if (DEBUG) { - Log.i(TAG, "isDisplayVideoSurfacedCreated: " + ui.isDisplayVideoSurfaceCreated()); - } - if (ui.isDisplayVideoSurfaceCreated()) { - mVideoCall.setDisplaySurface(ui.getDisplayVideoSurface()); + final int rotation = ui.getCurrentRotation(); + if (rotation != VideoCallFragment.ORIENTATION_UNKNOWN) { + videoCall.setDeviceOrientation(InCallPresenter.toRotationAngle(rotation)); } + + enableCamera(videoCall, isCameraRequired(newVideoState)); + } + mCurrentVideoState = newVideoState; + updateAudioMode(true); + + mIsVideoMode = true; + } + + //TODO: Move this into Telecom. InCallUI should not be this close to audio functionality. + private void updateAudioMode(boolean enableSpeaker) { + if (!isSpeakerEnabledForVideoCalls()) { + Log.d(this, "Speaker is disabled. Can't update audio mode"); + return; + } + + final TelecomAdapter telecomAdapter = TelecomAdapter.getInstance(); + final boolean isPrevAudioModeValid = + sPrevVideoAudioMode != AudioModeProvider.AUDIO_MODE_INVALID; + + Log.d(this, "Is previous audio mode valid = " + isPrevAudioModeValid + " enableSpeaker is " + + enableSpeaker); + + // Set audio mode to previous mode if enableSpeaker is false. + if (isPrevAudioModeValid && !enableSpeaker) { + telecomAdapter.setAudioRoute(sPrevVideoAudioMode); + sPrevVideoAudioMode = AudioModeProvider.AUDIO_MODE_INVALID; + return; + } + + int currentAudioMode = AudioModeProvider.getInstance().getAudioMode(); + + // Set audio mode to speaker if enableSpeaker is true and bluetooth or headset are not + // connected and it's a video call. + if (!isAudioRouteEnabled(currentAudioMode, + AudioState.ROUTE_BLUETOOTH | AudioState.ROUTE_WIRED_HEADSET) && + !isPrevAudioModeValid && enableSpeaker && CallUtils.isVideoCall(mPrimaryCall)) { + sPrevVideoAudioMode = currentAudioMode; + + Log.d(this, "Routing audio to speaker"); + telecomAdapter.setAudioRoute(AudioState.ROUTE_SPEAKER); + } + } + + private static boolean isSpeakerEnabledForVideoCalls() { + return (SystemProperties.getInt(TelephonyProperties.PROPERTY_VIDEOCALL_AUDIO_OUTPUT, + PhoneConstants.AUDIO_OUTPUT_DEFAULT) == + PhoneConstants.AUDIO_OUTPUT_ENABLE_SPEAKER); + } + + private void enableCamera(VideoCall videoCall, boolean isCameraRequired) { + Log.d(this, "enableCamera: VideoCall=" + videoCall + " enabling=" + isCameraRequired); + if (videoCall == null) { + Log.w(this, "enableCamera: VideoCall is null."); + return; } - mPreVideoAudioMode = AudioModeProvider.getInstance().getAudioMode(); - TelecomAdapter.getInstance().setAudioRoute(AudioState.ROUTE_SPEAKER); + if (isCameraRequired) { + InCallCameraManager cameraManager = InCallPresenter.getInstance(). + getInCallCameraManager(); + videoCall.setCamera(cameraManager.getActiveCameraId()); + mPreviewSurfaceState = PreviewSurfaceState.CAMERA_SET; + + videoCall.requestCameraCapabilities(); + } else { + mPreviewSurfaceState = PreviewSurfaceState.NONE; + videoCall.setCamera(null); + } } /** - * Exits video mode by hiding the video surfaces and making other adjustments (eg. audio). + * Exits video mode by hiding the video surfaces and making other adjustments (eg. audio). */ private void exitVideoMode() { + Log.d(this, "exitVideoMode"); + + InCallPresenter.getInstance().setInCallAllowsOrientationChange(false); + + showVideoUi(VideoProfile.VideoState.AUDIO_ONLY); + enableCamera(mVideoCall, false); + + Log.d(this, "exitVideoMode mIsFullScreen: " + mIsFullScreen); + if (mIsFullScreen) { + toggleFullScreen(); + } + + mIsVideoMode = false; + } + + /** + * Show video Ui depends on video state. + */ + private void showVideoUi(int videoState) { VideoCallUi ui = getUi(); if (ui == null) { + Log.e(this, "showVideoUi, VideoCallUi is null returning"); return; } - InCallPresenter.getInstance().setInCallAllowsOrientationChange(false); - ui.showVideoUi(false); - if (mVideoCall != null) { - // Also disable camera otherwise it will be already in use for next upgrade - mVideoCall.setCamera(null); + if (VideoProfile.VideoState.isBidirectional(videoState)) { + ui.showVideoViews(true, true); + } else if (VideoProfile.VideoState.isTransmissionEnabled(videoState)) { + ui.showVideoViews(true, false); + } else if (VideoProfile.VideoState.isReceptionEnabled(videoState)) { + ui.showVideoViews(false, true); + } else { + ui.hideVideoUi(); } - if (mPreVideoAudioMode != AudioModeProvider.AUDIO_MODE_INVALID) { - TelecomAdapter.getInstance().setAudioRoute(mPreVideoAudioMode); - mPreVideoAudioMode = AudioModeProvider.AUDIO_MODE_INVALID; - } + InCallPresenter.getInstance().enableScreenTimeout( + VideoProfile.VideoState.isAudioOnly(videoState)); } /** @@ -493,11 +757,43 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi */ @Override public void onUpdatePeerDimensions(Call call, int width, int height) { + Log.d(this, "onUpdatePeerDimensions: width= " + width + " height= " + height); + VideoCallUi ui = getUi(); + if (ui == null) { + Log.e(this, "VideoCallUi is null. Bail out"); + return; + } if (!call.equals(mPrimaryCall)) { + Log.e(this, "Current call is not equal to primary call. Bail out"); return; } - // TODO(vt): Change display surface aspect ratio. + // Change size of display surface to match the peer aspect ratio + if (width > 0 && height > 0) { + setDisplayVideoSize(width, height); + } + } + + /** + * Handles any video quality changes in the call. + * + * @param call The call which experienced a video quality change. + * @param videoQuality The new video call quality. + */ + @Override + public void onVideoQualityChanged(Call call, int videoQuality) { + if (!call.equals(mPrimaryCall)) { + return; + } + + VideoCallUi ui = getUi(); + if (ui == null) { + Log.e(this, "Error VideoCallUi is null. Return."); + return; + } + + // Display a video quality changed message on UI. + ui.showVideoQualityChanged(videoQuality); } /** @@ -510,16 +806,21 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi */ @Override public void onCameraDimensionsChange(Call call, int width, int height) { + Log.d(this, "onCameraDimensionsChange call=" + call + " width=" + width + " height=" + + height); VideoCallUi ui = getUi(); if (ui == null) { + Log.e(this, "onCameraDimensionsChange ui is null"); return; } if (!call.equals(mPrimaryCall)) { + Log.e(this, "Call is not primary call"); return; } mPreviewSurfaceState = PreviewSurfaceState.CAPABILITIES_RECEIVED; + ui.setPreviewSurfaceSize(width, height); // Configure the preview surface to the correct aspect ratio. float aspectRatio = 1.0f; @@ -537,45 +838,105 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi } /** - * Handles hanges to the device orientation. + * Called when call session event is raised. + * + * @param event The call session event. + */ + @Override + public void onCallSessionEvent(int event) { + Log.d(this, "onCallSessionEvent event =" + event); + VideoCallUi ui = getUi(); + if (ui == null) { + Log.e(this, "onCallSessionEvent: VideoCallUi is null"); + return; + } + ui.displayCallSessionEvent(event); + } + + /** + * Handles a change to the call data usage + * + * @param dataUsage call data usage value + */ + @Override + public void onCallDataUsageChange(long dataUsage) { + Log.d(this, "onCallDataUsageChange dataUsage=" + dataUsage); + VideoCallUi ui = getUi(); + if (ui == null) { + Log.e(this, "onCallDataUsageChange: VideoCallUi is null"); + return; + } + ui.setCallDataUsage(mContext, dataUsage); + } + + /** + * Handles changes to the device orientation. * See: {@link Configuration.ORIENTATION_LANDSCAPE}, {@link Configuration.ORIENTATION_PORTRAIT} * @param orientation The device orientation. */ @Override public void onDeviceOrientationChanged(int orientation) { + Log.d(this, "onDeviceOrientationChanged: orientation=" + orientation); mDeviceOrientation = orientation; } @Override - public void onUpgradeToVideoRequest(Call call) { - mPrimaryCall.setSessionModificationState( - Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST); + public void onUpgradeToVideoRequest(Call call, int videoState) { + Log.d(this, "onUpgradeToVideoRequest call = " + call + " new video state = " + videoState); + if (mPrimaryCall == null || !Call.areSame(mPrimaryCall, call)) { + Log.w(this, "UpgradeToVideoRequest received for non-primary call"); + } + + if (call == null) { + return; + } + + call.setSessionModificationTo(videoState); } @Override public void onUpgradeToVideoSuccess(Call call) { + Log.d(this, "onUpgradeToVideoSuccess call=" + call); if (mPrimaryCall == null || !Call.areSame(mPrimaryCall, call)) { + Log.w(this, "UpgradeToVideoSuccess received for non-primary call"); + } + + if (call == null) { return; } - mPrimaryCall.setSessionModificationState(Call.SessionModificationState.NO_REQUEST); + call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST); } @Override - public void onUpgradeToVideoFail(Call call) { + public void onUpgradeToVideoFail(int status, Call call) { + Log.d(this, "onUpgradeToVideoFail call=" + call); if (mPrimaryCall == null || !Call.areSame(mPrimaryCall, call)) { - return; + Log.w(this, "UpgradeToVideoFail received for non-primary call"); } - call.setSessionModificationState(Call.SessionModificationState.REQUEST_FAILED); + if (call == null) { + return; + } - // Start handler to change state from REQUEST_FAILED to NO_REQUEST after an interval. - mSessionModificationResetHandler.postDelayed(new Runnable() { - @Override - public void run() { - mPrimaryCall.setSessionModificationState(Call.SessionModificationState.NO_REQUEST); - } - }, SESSION_MODIFICATION_RESET_DELAY_MS); + if (status == VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT) { + call.setSessionModificationState( + Call.SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT); + } else { + call.setSessionModificationState(Call.SessionModificationState.REQUEST_FAILED); + + final Call modifyCall = call; + // Start handler to change state from REQUEST_FAILED to NO_REQUEST after an interval. + mSessionModificationResetHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (modifyCall != null) { + modifyCall + .setSessionModificationState(Call.SessionModificationState.NO_REQUEST); + } + } + }, SESSION_MODIFICATION_RESET_DELAY_MS); + } } @Override @@ -611,16 +972,140 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi } /** + * Sets the display video surface size based on peer width and height + * + * @param width peer width + * @param height peer height + */ + + private void setDisplayVideoSize(int width, int height) { + Log.d(this, "setDisplayVideoSize:Received peer width=" + width + " peer height=" + height); + VideoCallUi ui = getUi(); + if (ui == null) { + return; + } + + // Get current display size + Point size = ui.getScreenSize(); + Log.d("VideoCallPresenter", "setDisplayVideoSize: windowmgr width=" + size.x + + " windowmgr height=" + size.y); + if (size.y * width > size.x * height) { + // current display height is too much. Correct it + size.y = (int) (size.x * height / width); + } else if (size.y * width < size.x * height) { + // current display width is too much. Correct it + size.x = (int) (size.y * width / height); + } + ui.setDisplayVideoSize(size.x, size.y); + } + + private static boolean isAudioRouteEnabled(int audioRoute, int audioRouteMask) { + return ((audioRoute & audioRouteMask) != 0); + } + + private static void updateCameraSelection(Call call) { + Log.d(TAG, "updateCameraSelection: call=" + call); + Log.d(TAG, "updateCameraSelection: call=" + toSimpleString(call)); + + final Call activeCall = CallList.getInstance().getActiveCall(); + int cameraDir = Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN; + + // this function should never be called with null call object, however if it happens we + // should handle it gracefully. + if (call == null) { + cameraDir = Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN; + com.android.incallui.Log.e(TAG, "updateCameraSelection: Call object is null." + + " Setting camera direction to default value (CAMERA_DIRECTION_UNKNOWN)"); + } + + // Clear camera direction if this is not a video call. + else if (CallUtils.isAudioCall(call)) { + cameraDir = Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN; + call.getVideoSettings().setCameraDir(cameraDir); + } + + // If this is a waiting video call, default to active call's camera, + // since we don't want to change the current camera for waiting call + // without user's permission. + else if (CallUtils.isVideoCall(activeCall) && CallUtils.isIncomingVideoCall(call)) { + cameraDir = activeCall.getVideoSettings().getCameraDir(); + } + + // Infer the camera direction from the video state and store it, + // if this is an outgoing video call. + else if (CallUtils.isOutgoingVideoCall(call) && !isCameraDirectionSet(call) ) { + cameraDir = toCameraDirection(call.getVideoState()); + call.getVideoSettings().setCameraDir(cameraDir); + } + + // Use the stored camera dir if this is an outgoing video call for which camera direction + // is set. + else if (CallUtils.isOutgoingVideoCall(call)) { + cameraDir = call.getVideoSettings().getCameraDir(); + } + + // Infer the camera direction from the video state and store it, + // if this is an active video call and camera direction is not set. + else if (CallUtils.isActiveVideoCall(call) && !isCameraDirectionSet(call)) { + cameraDir = toCameraDirection(call.getVideoState()); + call.getVideoSettings().setCameraDir(cameraDir); + } + + // Use the stored camera dir if this is an active video call for which camera direction + // is set. + else if (CallUtils.isActiveVideoCall(call)) { + cameraDir = call.getVideoSettings().getCameraDir(); + } + + // For all other cases infer the camera direction but don't store it in the call object. + else { + cameraDir = toCameraDirection(call.getVideoState()); + } + + com.android.incallui.Log.d(TAG, "updateCameraSelection: Setting camera direction to " + + cameraDir + " Call=" + call); + final InCallCameraManager cameraManager = InCallPresenter.getInstance(). + getInCallCameraManager(); + cameraManager.setUseFrontFacingCamera(cameraDir == + Call.VideoSettings.CAMERA_DIRECTION_FRONT_FACING); + } + + private static int toCameraDirection(int videoState) { + return VideoProfile.VideoState.isTransmissionEnabled(videoState) && + !VideoProfile.VideoState.isBidirectional(videoState) + ? Call.VideoSettings.CAMERA_DIRECTION_BACK_FACING + : Call.VideoSettings.CAMERA_DIRECTION_FRONT_FACING; + } + + private static boolean isCameraDirectionSet(Call call) { + return CallUtils.isVideoCall(call) && call.getVideoSettings().getCameraDir() + != Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN; + } + + private static String toSimpleString(Call call) { + return call == null ? null : call.toSimpleString(); + } + + /** * Defines the VideoCallUI interactions. */ public interface VideoCallUi extends Ui { - void showVideoUi(boolean show); + void showVideoViews(boolean showPreview, boolean showIncoming); + void hideVideoUi(); + void showVideoQualityChanged(int videoQuality); boolean isDisplayVideoSurfaceCreated(); boolean isPreviewVideoSurfaceCreated(); Surface getDisplayVideoSurface(); Surface getPreviewVideoSurface(); + int getCurrentRotation(); void setPreviewSize(int width, int height); + void setPreviewSurfaceSize(int width, int height); + void setDisplayVideoSize(int width, int height); + void setCallDataUsage(Context context, long dataUsage); + void displayCallSessionEvent(int event); + Point getScreenSize(); void cleanupSurfaces(); boolean isActivityRestart(); + void showCallSubstateChanged(int callSubstate); } } |