summaryrefslogtreecommitdiff
path: root/java/com/android/incallui/videotech
diff options
context:
space:
mode:
authorEric Erfanian <erfanian@google.com>2017-03-15 14:41:07 -0700
committerEric Erfanian <erfanian@google.com>2017-03-15 16:24:23 -0700
commitd5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9 (patch)
treeb54abbb51fb7d66e7755a1fbb5db023ff601090b /java/com/android/incallui/videotech
parent30436e7e6d3f2c8755a91b2b6222b74d465a9e87 (diff)
Update Dialer source from latest green build.
* Refactor voicemail component * Add new enriched calling components Test: treehugger, manual aosp testing Change-Id: I521a0f86327d4b42e14d93927c7d613044ed5942
Diffstat (limited to 'java/com/android/incallui/videotech')
-rw-r--r--java/com/android/incallui/videotech/VideoTech.java96
-rw-r--r--java/com/android/incallui/videotech/empty/EmptyVideoTech.java76
-rw-r--r--java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java201
-rw-r--r--java/com/android/incallui/videotech/ims/ImsVideoTech.java212
-rw-r--r--java/com/android/incallui/videotech/rcs/RcsVideoShare.java195
5 files changed, 780 insertions, 0 deletions
diff --git a/java/com/android/incallui/videotech/VideoTech.java b/java/com/android/incallui/videotech/VideoTech.java
new file mode 100644
index 000000000..fb2641793
--- /dev/null
+++ b/java/com/android/incallui/videotech/VideoTech.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.incallui.videotech;
+
+import android.support.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Video calling interface. */
+public interface VideoTech {
+
+ boolean isAvailable();
+
+ boolean isTransmittingOrReceiving();
+
+ void onCallStateChanged(int newState);
+
+ @SessionModificationState
+ int getSessionModificationState();
+
+ void upgradeToVideo();
+
+ void acceptVideoRequest();
+
+ void acceptVideoRequestAsAudio();
+
+ void declineVideoRequest();
+
+ boolean isTransmitting();
+
+ void stopTransmission();
+
+ void resumeTransmission();
+
+ void pause();
+
+ void unpause();
+
+ void setCamera(String cameraId);
+
+ void setDeviceOrientation(int rotation);
+
+ /** Listener for video call events. */
+ interface VideoTechListener {
+
+ void onVideoTechStateChanged();
+
+ void onSessionModificationStateChanged();
+
+ void onCameraDimensionsChanged(int width, int height);
+
+ void onPeerDimensionsChanged(int width, int height);
+
+ void onVideoUpgradeRequestReceived();
+ }
+
+ /**
+ * Defines different states of session modify requests, which are used to upgrade to video, or
+ * downgrade to audio.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ SESSION_MODIFICATION_STATE_NO_REQUEST,
+ SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE,
+ SESSION_MODIFICATION_STATE_REQUEST_FAILED,
+ SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST,
+ SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT,
+ SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_FAILED,
+ SESSION_MODIFICATION_STATE_REQUEST_REJECTED,
+ SESSION_MODIFICATION_STATE_WAITING_FOR_RESPONSE
+ })
+ @interface SessionModificationState {}
+
+ int SESSION_MODIFICATION_STATE_NO_REQUEST = 0;
+ int SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE = 1;
+ int SESSION_MODIFICATION_STATE_REQUEST_FAILED = 2;
+ int SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST = 3;
+ int SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT = 4;
+ int SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_FAILED = 5;
+ int SESSION_MODIFICATION_STATE_REQUEST_REJECTED = 6;
+ int SESSION_MODIFICATION_STATE_WAITING_FOR_RESPONSE = 7;
+}
diff --git a/java/com/android/incallui/videotech/empty/EmptyVideoTech.java b/java/com/android/incallui/videotech/empty/EmptyVideoTech.java
new file mode 100644
index 000000000..bc8db4c07
--- /dev/null
+++ b/java/com/android/incallui/videotech/empty/EmptyVideoTech.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.incallui.videotech.empty;
+
+import com.android.incallui.videotech.VideoTech;
+
+/** Default video tech that is always available but doesn't do anything. */
+public class EmptyVideoTech implements VideoTech {
+
+ @Override
+ public boolean isAvailable() {
+ return false;
+ }
+
+ @Override
+ public boolean isTransmittingOrReceiving() {
+ return false;
+ }
+
+ @Override
+ public void onCallStateChanged(int newState) {}
+
+ @Override
+ public int getSessionModificationState() {
+ return VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST;
+ }
+
+ @Override
+ public void upgradeToVideo() {}
+
+ @Override
+ public void acceptVideoRequest() {}
+
+ @Override
+ public void acceptVideoRequestAsAudio() {}
+
+ @Override
+ public void declineVideoRequest() {}
+
+ @Override
+ public boolean isTransmitting() {
+ return false;
+ }
+
+ @Override
+ public void stopTransmission() {}
+
+ @Override
+ public void resumeTransmission() {}
+
+ @Override
+ public void pause() {}
+
+ @Override
+ public void unpause() {}
+
+ @Override
+ public void setCamera(String cameraId) {}
+
+ @Override
+ public void setDeviceOrientation(int rotation) {}
+}
diff --git a/java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java b/java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java
new file mode 100644
index 000000000..0a15f7e65
--- /dev/null
+++ b/java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.incallui.videotech.ims;
+
+import android.os.Handler;
+import android.telecom.Call;
+import android.telecom.Connection;
+import android.telecom.Connection.VideoProvider;
+import android.telecom.InCallService.VideoCall;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.CameraCapabilities;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.videotech.VideoTech;
+import com.android.incallui.videotech.VideoTech.SessionModificationState;
+import com.android.incallui.videotech.VideoTech.VideoTechListener;
+
+/** Receives IMS video call state updates. */
+public class ImsVideoCallCallback extends VideoCall.Callback {
+ private static final int CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS = 4000;
+ private final Handler handler = new Handler();
+ private final Call call;
+ private final ImsVideoTech videoTech;
+ private final VideoTechListener listener;
+ private int requestedVideoState = VideoProfile.STATE_AUDIO_ONLY;
+
+ ImsVideoCallCallback(final Call call, ImsVideoTech videoTech, VideoTechListener listener) {
+ this.call = call;
+ this.videoTech = videoTech;
+ this.listener = listener;
+ }
+
+ @Override
+ public void onSessionModifyRequestReceived(VideoProfile videoProfile) {
+ LogUtil.i(
+ "ImsVideoCallCallback.onSessionModifyRequestReceived", "videoProfile: " + videoProfile);
+
+ int previousVideoState = ImsVideoTech.getUnpausedVideoState(call.getDetails().getVideoState());
+ int newVideoState = ImsVideoTech.getUnpausedVideoState(videoProfile.getVideoState());
+
+ boolean wasVideoCall = VideoProfile.isVideo(previousVideoState);
+ boolean isVideoCall = VideoProfile.isVideo(newVideoState);
+
+ if (wasVideoCall && !isVideoCall) {
+ LogUtil.i(
+ "ImsVideoTech.onSessionModifyRequestReceived", "call downgraded to %d", newVideoState);
+ } else if (previousVideoState != newVideoState) {
+ requestedVideoState = newVideoState;
+ videoTech.setSessionModificationState(
+ VideoTech.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST);
+ listener.onVideoUpgradeRequestReceived();
+ }
+ }
+
+ /**
+ * @param status Status of the session modify request. Valid values are {@link
+ * Connection.VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS}, {@link
+ * Connection.VideoProvider#SESSION_MODIFY_REQUEST_FAIL}, {@link
+ * Connection.VideoProvider#SESSION_MODIFY_REQUEST_INVALID}
+ * @param responseProfile The actual profile changes made by the peer device.
+ */
+ @Override
+ public void onSessionModifyResponseReceived(
+ int status, VideoProfile requestedProfile, VideoProfile responseProfile) {
+ LogUtil.i(
+ "ImsVideoCallCallback.onSessionModifyResponseReceived",
+ "status: %d, requestedProfile: %s, responseProfile: %s, session modification state: %d",
+ status,
+ requestedProfile,
+ responseProfile,
+ videoTech.getSessionModificationState());
+
+ if (videoTech.getSessionModificationState()
+ == VideoTech.SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE) {
+ handler.removeCallbacksAndMessages(null); // Clear everything
+
+ final int newSessionModificationState = getSessionModificationStateFromTelecomStatus(status);
+ if (status != VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) {
+ // This will update the video UI to display the error message.
+ videoTech.setSessionModificationState(newSessionModificationState);
+ }
+
+ // Wait for 4 seconds and then clean the session modification state. This allows the video UI
+ // to stay up so that the user can read the error message.
+ //
+ // If the other person accepted the upgrade request then this will keep the video UI up until
+ // the call's video state change. Without this we would switch to the voice call and then
+ // switch back to video UI.
+ handler.postDelayed(
+ () -> {
+ if (videoTech.getSessionModificationState() == newSessionModificationState) {
+ LogUtil.i("ImsVideoCallCallback.onSessionModifyResponseReceived", "clearing state");
+ videoTech.setSessionModificationState(
+ VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ } else {
+ LogUtil.i(
+ "ImsVideoCallCallback.onSessionModifyResponseReceived",
+ "session modification state has changed, not clearing state");
+ }
+ },
+ CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS);
+ } else if (videoTech.getSessionModificationState()
+ == VideoTech.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
+ videoTech.setSessionModificationState(VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ } else if (videoTech.getSessionModificationState()
+ == VideoTech.SESSION_MODIFICATION_STATE_WAITING_FOR_RESPONSE) {
+ videoTech.setSessionModificationState(getSessionModificationStateFromTelecomStatus(status));
+ } else {
+ LogUtil.i(
+ "ImsVideoCallCallback.onSessionModifyResponseReceived",
+ "call is not waiting for response, doing nothing");
+ }
+ }
+
+ @SessionModificationState
+ private int getSessionModificationStateFromTelecomStatus(int telecomStatus) {
+ switch (telecomStatus) {
+ case VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS:
+ return VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST;
+ case VideoProvider.SESSION_MODIFY_REQUEST_FAIL:
+ case VideoProvider.SESSION_MODIFY_REQUEST_INVALID:
+ // Check if it's already video call, which means the request is not video upgrade request.
+ if (VideoProfile.isVideo(call.getDetails().getVideoState())) {
+ return VideoTech.SESSION_MODIFICATION_STATE_REQUEST_FAILED;
+ } else {
+ return VideoTech.SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_FAILED;
+ }
+ case VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT:
+ return VideoTech.SESSION_MODIFICATION_STATE_UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT;
+ case VideoProvider.SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE:
+ return VideoTech.SESSION_MODIFICATION_STATE_REQUEST_REJECTED;
+ default:
+ LogUtil.e(
+ "ImsVideoCallCallback.getSessionModificationStateFromTelecomStatus",
+ "unknown status: %d",
+ telecomStatus);
+ return VideoTech.SESSION_MODIFICATION_STATE_REQUEST_FAILED;
+ }
+ }
+
+ @Override
+ public void onCallSessionEvent(int event) {
+ switch (event) {
+ case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE:
+ LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "rx_pause");
+ break;
+ case Connection.VideoProvider.SESSION_EVENT_RX_RESUME:
+ LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "rx_resume");
+ break;
+ case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE:
+ LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "camera_failure");
+ break;
+ case Connection.VideoProvider.SESSION_EVENT_CAMERA_READY:
+ LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "camera_ready");
+ break;
+ default:
+ LogUtil.i("ImsVideoCallCallback.onCallSessionEvent", "unknown event = : " + event);
+ break;
+ }
+ }
+
+ @Override
+ public void onPeerDimensionsChanged(int width, int height) {
+ listener.onPeerDimensionsChanged(width, height);
+ }
+
+ @Override
+ public void onVideoQualityChanged(int videoQuality) {
+ LogUtil.i("ImsVideoCallCallback.onVideoQualityChanged", "videoQuality: %d", videoQuality);
+ }
+
+ @Override
+ public void onCallDataUsageChanged(long dataUsage) {
+ LogUtil.i("ImsVideoCallCallback.onCallDataUsageChanged", "dataUsage: %d", dataUsage);
+ }
+
+ @Override
+ public void onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities) {
+ if (cameraCapabilities != null) {
+ listener.onCameraDimensionsChanged(
+ cameraCapabilities.getWidth(), cameraCapabilities.getHeight());
+ }
+ }
+
+ int getRequestedVideoState() {
+ return requestedVideoState;
+ }
+}
diff --git a/java/com/android/incallui/videotech/ims/ImsVideoTech.java b/java/com/android/incallui/videotech/ims/ImsVideoTech.java
new file mode 100644
index 000000000..890e5c80c
--- /dev/null
+++ b/java/com/android/incallui/videotech/ims/ImsVideoTech.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.incallui.videotech.ims;
+
+import android.os.Build;
+import android.telecom.Call;
+import android.telecom.Call.Details;
+import android.telecom.VideoProfile;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.videotech.VideoTech;
+
+/** ViLTE implementation */
+public class ImsVideoTech implements VideoTech {
+ private final Call call;
+ private final VideoTechListener listener;
+ private ImsVideoCallCallback callback;
+ private @SessionModificationState int sessionModificationState =
+ VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST;
+ private int previousVideoState = VideoProfile.STATE_AUDIO_ONLY;
+
+ public ImsVideoTech(VideoTechListener listener, Call call) {
+ this.listener = listener;
+ this.call = call;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ return false;
+ }
+
+ boolean hasCapabilities =
+ call.getDetails().can(Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX)
+ && call.getDetails().can(Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX);
+
+ return call.getVideoCall() != null
+ && (hasCapabilities || VideoProfile.isVideo(call.getDetails().getVideoState()));
+ }
+
+ @Override
+ public boolean isTransmittingOrReceiving() {
+ return VideoProfile.isVideo(call.getDetails().getVideoState());
+ }
+
+ @Override
+ public void onCallStateChanged(int newState) {
+ if (!isAvailable()) {
+ return;
+ }
+
+ if (callback == null) {
+ callback = new ImsVideoCallCallback(call, this, listener);
+ call.getVideoCall().registerCallback(callback);
+ }
+
+ if (getSessionModificationState()
+ == VideoTech.SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE
+ && isTransmittingOrReceiving()) {
+ // We don't clear the session modification state right away when we find out the video upgrade
+ // request was accepted to avoid having the UI switch from video to voice to video.
+ // Once the underlying telecom call updates to video mode it's safe to clear the state.
+ LogUtil.i(
+ "ImsVideoTech.onCallStateChanged",
+ "upgraded to video, clearing session modification state");
+ setSessionModificationState(VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+
+ // Determines if a received upgrade to video request should be cancelled. This can happen if
+ // another InCall UI responds to the upgrade to video request.
+ int newVideoState = call.getDetails().getVideoState();
+ if (newVideoState != previousVideoState
+ && sessionModificationState
+ == VideoTech.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
+ LogUtil.i("ImsVideoTech.onCallStateChanged", "cancelling upgrade notification");
+ setSessionModificationState(VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+ previousVideoState = newVideoState;
+ }
+
+ @Override
+ public int getSessionModificationState() {
+ return sessionModificationState;
+ }
+
+ void setSessionModificationState(@SessionModificationState int state) {
+ if (state != sessionModificationState) {
+ LogUtil.i(
+ "ImsVideoTech.setSessionModificationState", "%d -> %d", sessionModificationState, state);
+ sessionModificationState = state;
+ listener.onSessionModificationStateChanged();
+ }
+ }
+
+ @Override
+ public void upgradeToVideo() {
+ LogUtil.enterBlock("ImsVideoTech.upgradeToVideo");
+
+ int unpausedVideoState = getUnpausedVideoState(call.getDetails().getVideoState());
+ call.getVideoCall()
+ .sendSessionModifyRequest(
+ new VideoProfile(unpausedVideoState | VideoProfile.STATE_BIDIRECTIONAL));
+ setSessionModificationState(
+ VideoTech.SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE);
+ }
+
+ @Override
+ public void acceptVideoRequest() {
+ int requestedVideoState = callback.getRequestedVideoState();
+ Assert.checkArgument(requestedVideoState != VideoProfile.STATE_AUDIO_ONLY);
+ LogUtil.i("ImsVideoTech.acceptUpgradeRequest", "videoState: " + requestedVideoState);
+ call.getVideoCall().sendSessionModifyResponse(new VideoProfile(requestedVideoState));
+ setSessionModificationState(VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+
+ @Override
+ public void acceptVideoRequestAsAudio() {
+ LogUtil.enterBlock("ImsVideoTech.acceptVideoRequestAsAudio");
+ call.getVideoCall().sendSessionModifyResponse(new VideoProfile(VideoProfile.STATE_AUDIO_ONLY));
+ setSessionModificationState(VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+
+ @Override
+ public void declineVideoRequest() {
+ LogUtil.enterBlock("ImsVideoTech.declineUpgradeRequest");
+ call.getVideoCall()
+ .sendSessionModifyResponse(new VideoProfile(call.getDetails().getVideoState()));
+ setSessionModificationState(VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+
+ @Override
+ public boolean isTransmitting() {
+ return VideoProfile.isTransmissionEnabled(call.getDetails().getVideoState());
+ }
+
+ @Override
+ public void stopTransmission() {
+ LogUtil.enterBlock("ImsVideoTech.stopTransmission");
+
+ int unpausedVideoState = getUnpausedVideoState(call.getDetails().getVideoState());
+ call.getVideoCall()
+ .sendSessionModifyRequest(
+ new VideoProfile(unpausedVideoState & ~VideoProfile.STATE_TX_ENABLED));
+ }
+
+ @Override
+ public void resumeTransmission() {
+ LogUtil.enterBlock("ImsVideoTech.resumeTransmission");
+
+ int unpausedVideoState = getUnpausedVideoState(call.getDetails().getVideoState());
+ call.getVideoCall()
+ .sendSessionModifyRequest(
+ new VideoProfile(unpausedVideoState | VideoProfile.STATE_TX_ENABLED));
+ setSessionModificationState(VideoTech.SESSION_MODIFICATION_STATE_WAITING_FOR_RESPONSE);
+ }
+
+ @Override
+ public void pause() {
+ if (canPause()) {
+ LogUtil.i("ImsVideoTech.pause", "sending pause request");
+ int pausedVideoState = call.getDetails().getVideoState() | VideoProfile.STATE_PAUSED;
+ call.getVideoCall().sendSessionModifyRequest(new VideoProfile(pausedVideoState));
+ } else {
+ LogUtil.i("ImsVideoTech.pause", "not sending request: canPause: %b", canPause());
+ }
+ }
+
+ @Override
+ public void unpause() {
+ if (canPause()) {
+ LogUtil.i("ImsVideoTech.unpause", "sending unpause request");
+ int unpausedVideoState = getUnpausedVideoState(call.getDetails().getVideoState());
+ call.getVideoCall().sendSessionModifyRequest(new VideoProfile(unpausedVideoState));
+ } else {
+ LogUtil.i("ImsVideoTech.unpause", "not sending request: canPause: %b", canPause());
+ }
+ }
+
+ @Override
+ public void setCamera(String cameraId) {
+ call.getVideoCall().setCamera(cameraId);
+ call.getVideoCall().requestCameraCapabilities();
+ }
+
+ @Override
+ public void setDeviceOrientation(int rotation) {
+ call.getVideoCall().setDeviceOrientation(rotation);
+ }
+
+ private boolean canPause() {
+ return call.getDetails().can(Details.CAPABILITY_CAN_PAUSE_VIDEO)
+ && call.getState() == Call.STATE_ACTIVE;
+ }
+
+ static int getUnpausedVideoState(int videoState) {
+ return videoState & (~VideoProfile.STATE_PAUSED);
+ }
+}
diff --git a/java/com/android/incallui/videotech/rcs/RcsVideoShare.java b/java/com/android/incallui/videotech/rcs/RcsVideoShare.java
new file mode 100644
index 000000000..2cb43036f
--- /dev/null
+++ b/java/com/android/incallui/videotech/rcs/RcsVideoShare.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.incallui.videotech.rcs;
+
+import android.support.annotation.NonNull;
+import android.telecom.Call;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
+import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
+import com.android.dialer.enrichedcall.Session;
+import com.android.dialer.enrichedcall.videoshare.VideoShareListener;
+import com.android.incallui.videotech.VideoTech;
+
+/** Allows the in-call UI to make video calls over RCS. */
+public class RcsVideoShare implements VideoTech, CapabilitiesListener, VideoShareListener {
+ private final EnrichedCallManager enrichedCallManager;
+ private final VideoTechListener listener;
+ private final String callingNumber;
+ private int previousCallState = Call.STATE_NEW;
+ private long inviteSessionId = Session.NO_SESSION_ID;
+ private long transmittingSessionId = Session.NO_SESSION_ID;
+ private long receivingSessionId = Session.NO_SESSION_ID;
+
+ private @SessionModificationState int sessionModificationState =
+ VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST;
+
+ public RcsVideoShare(
+ @NonNull EnrichedCallManager enrichedCallManager,
+ @NonNull VideoTechListener listener,
+ @NonNull String callingNumber) {
+ this.enrichedCallManager = Assert.isNotNull(enrichedCallManager);
+ this.listener = Assert.isNotNull(listener);
+ this.callingNumber = Assert.isNotNull(callingNumber);
+
+ enrichedCallManager.registerCapabilitiesListener(this);
+ enrichedCallManager.registerVideoShareListener(this);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ EnrichedCallCapabilities capabilities = enrichedCallManager.getCapabilities(callingNumber);
+ return capabilities != null && capabilities.supportsVideoShare();
+ }
+
+ @Override
+ public boolean isTransmittingOrReceiving() {
+ return transmittingSessionId != Session.NO_SESSION_ID
+ || receivingSessionId != Session.NO_SESSION_ID;
+ }
+
+ @Override
+ public void onCallStateChanged(int newState) {
+ if (newState == Call.STATE_DISCONNECTING) {
+ enrichedCallManager.unregisterVideoShareListener(this);
+ enrichedCallManager.unregisterCapabilitiesListener(this);
+ }
+
+ if (newState != previousCallState && newState == Call.STATE_ACTIVE) {
+ // Per spec, request capabilities when the call becomes active
+ enrichedCallManager.requestCapabilities(callingNumber);
+ }
+
+ previousCallState = newState;
+ }
+
+ @Override
+ public int getSessionModificationState() {
+ return sessionModificationState;
+ }
+
+ private void setSessionModificationState(@SessionModificationState int state) {
+ if (state != sessionModificationState) {
+ LogUtil.i(
+ "RcsVideoShare.setSessionModificationState", "%d -> %d", sessionModificationState, state);
+ sessionModificationState = state;
+ listener.onSessionModificationStateChanged();
+ }
+ }
+
+ @Override
+ public void upgradeToVideo() {
+ LogUtil.enterBlock("RcsVideoShare.upgradeToVideo");
+ transmittingSessionId = enrichedCallManager.startVideoShareSession(callingNumber);
+ if (transmittingSessionId != Session.NO_SESSION_ID) {
+ setSessionModificationState(
+ VideoTech.SESSION_MODIFICATION_STATE_WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE);
+ }
+ }
+
+ @Override
+ public void acceptVideoRequest() {
+ LogUtil.enterBlock("RcsVideoShare.acceptVideoRequest");
+ if (enrichedCallManager.acceptVideoShareSession(inviteSessionId)) {
+ receivingSessionId = inviteSessionId;
+ }
+ inviteSessionId = Session.NO_SESSION_ID;
+ setSessionModificationState(VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+
+ @Override
+ public void acceptVideoRequestAsAudio() {
+ throw Assert.createUnsupportedOperationFailException();
+ }
+
+ @Override
+ public void declineVideoRequest() {
+ LogUtil.enterBlock("RcsVideoTech.declineUpgradeRequest");
+ enrichedCallManager.endVideoShareSession(
+ enrichedCallManager.getVideoShareInviteSessionId(callingNumber));
+ inviteSessionId = Session.NO_SESSION_ID;
+ setSessionModificationState(VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+
+ @Override
+ public boolean isTransmitting() {
+ return transmittingSessionId != Session.NO_SESSION_ID;
+ }
+
+ @Override
+ public void stopTransmission() {
+ LogUtil.enterBlock("RcsVideoTech.stopTransmission");
+ }
+
+ @Override
+ public void resumeTransmission() {
+ LogUtil.enterBlock("RcsVideoTech.resumeTransmission");
+ }
+
+ @Override
+ public void pause() {}
+
+ @Override
+ public void unpause() {}
+
+ @Override
+ public void setCamera(String cameraId) {}
+
+ @Override
+ public void setDeviceOrientation(int rotation) {}
+
+ @Override
+ public void onCapabilitiesUpdated() {
+ listener.onVideoTechStateChanged();
+ }
+
+ @Override
+ public void onVideoShareChanged() {
+ long existingInviteSessionId = inviteSessionId;
+
+ inviteSessionId = enrichedCallManager.getVideoShareInviteSessionId(callingNumber);
+ if (inviteSessionId != Session.NO_SESSION_ID) {
+ if (existingInviteSessionId == Session.NO_SESSION_ID) {
+ // This is a new invite
+ setSessionModificationState(
+ VideoTech.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST);
+ listener.onVideoUpgradeRequestReceived();
+ }
+ } else {
+ setSessionModificationState(VideoTech.SESSION_MODIFICATION_STATE_NO_REQUEST);
+ }
+
+ if (sessionIsClosed(transmittingSessionId)) {
+ LogUtil.i("RcsVideoShare.onSessionClosed", "transmitting session closed");
+ transmittingSessionId = Session.NO_SESSION_ID;
+ }
+
+ if (sessionIsClosed(receivingSessionId)) {
+ LogUtil.i("RcsVideoShare.onSessionClosed", "receiving session closed");
+ receivingSessionId = Session.NO_SESSION_ID;
+ }
+
+ listener.onVideoTechStateChanged();
+ }
+
+ private boolean sessionIsClosed(long sessionId) {
+ return sessionId != Session.NO_SESSION_ID
+ && enrichedCallManager.getVideoShareSession(sessionId) == null;
+ }
+}