summaryrefslogtreecommitdiff
path: root/java/com/android/incallui/videotech
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/incallui/videotech')
-rw-r--r--java/com/android/incallui/videotech/VideoTech.java14
-rw-r--r--java/com/android/incallui/videotech/empty/EmptyVideoTech.java9
-rw-r--r--java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java7
-rw-r--r--java/com/android/incallui/videotech/ims/ImsVideoTech.java122
-rw-r--r--java/com/android/incallui/videotech/lightbringer/LightbringerTech.java59
-rw-r--r--java/com/android/incallui/videotech/utils/VideoUtils.java20
6 files changed, 180 insertions, 51 deletions
diff --git a/java/com/android/incallui/videotech/VideoTech.java b/java/com/android/incallui/videotech/VideoTech.java
index 7da74c057..79a8c605c 100644
--- a/java/com/android/incallui/videotech/VideoTech.java
+++ b/java/com/android/incallui/videotech/VideoTech.java
@@ -17,6 +17,8 @@
package com.android.incallui.videotech;
import android.content.Context;
+import android.support.annotation.Nullable;
+import com.android.dialer.logging.DialerImpression;
import com.android.incallui.video.protocol.VideoCallScreen;
import com.android.incallui.video.protocol.VideoCallScreenDelegate;
import com.android.incallui.videotech.utils.SessionModificationState;
@@ -41,6 +43,8 @@ public interface VideoTech {
void onCallStateChanged(Context context, int newState);
+ void onRemovedFromCallList();
+
@SessionModificationState
int getSessionModificationState();
@@ -62,10 +66,16 @@ public interface VideoTech {
void unpause();
- void setCamera(String cameraId);
+ void setCamera(@Nullable String cameraId);
void setDeviceOrientation(int rotation);
+ /**
+ * Called on {@code VideoTechManager.savedTech} when it's first selected and it will always be
+ * used.
+ */
+ void becomePrimary();
+
/** Listener for video call events. */
interface VideoTechListener {
@@ -80,5 +90,7 @@ public interface VideoTech {
void onVideoUpgradeRequestReceived();
void onUpgradedToVideo(boolean switchToSpeaker);
+
+ void onImpressionLoggingNeeded(DialerImpression.Type impressionType);
}
}
diff --git a/java/com/android/incallui/videotech/empty/EmptyVideoTech.java b/java/com/android/incallui/videotech/empty/EmptyVideoTech.java
index d0a111d62..34dd1bf8b 100644
--- a/java/com/android/incallui/videotech/empty/EmptyVideoTech.java
+++ b/java/com/android/incallui/videotech/empty/EmptyVideoTech.java
@@ -17,6 +17,7 @@
package com.android.incallui.videotech.empty;
import android.content.Context;
+import android.support.annotation.Nullable;
import com.android.dialer.common.Assert;
import com.android.incallui.video.protocol.VideoCallScreen;
import com.android.incallui.video.protocol.VideoCallScreenDelegate;
@@ -56,6 +57,9 @@ public class EmptyVideoTech implements VideoTech {
public void onCallStateChanged(Context context, int newState) {}
@Override
+ public void onRemovedFromCallList() {}
+
+ @Override
public int getSessionModificationState() {
return SessionModificationState.NO_REQUEST;
}
@@ -90,8 +94,11 @@ public class EmptyVideoTech implements VideoTech {
public void unpause() {}
@Override
- public void setCamera(String cameraId) {}
+ public void setCamera(@Nullable String cameraId) {}
@Override
public void setDeviceOrientation(int rotation) {}
+
+ @Override
+ public void becomePrimary() {}
}
diff --git a/java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java b/java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java
index 17c2e6518..b83929304 100644
--- a/java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java
+++ b/java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java
@@ -161,6 +161,13 @@ public class ImsVideoCallCallback extends VideoCall.Callback {
}
}
+ // In the vendor code rx_pause and rx_resume get triggered when the video player starts or stops
+ // playing the incoming video stream. For the case where you're resuming a held call, its
+ // definitely a good signal to use to know that the video is resuming (though the video state
+ // should change to indicate its not paused in this case as well). However, keep in mind you'll
+ // get these signals as well on carriers that don't support the video pause signalling (like TMO)
+ // so you want to ensure you don't send sessionModifyRequests with pause/resume based on these
+ // signals. Also, its technically possible to have a pause/resume if the video signal degrades.
@Override
public void onCallSessionEvent(int event) {
switch (event) {
diff --git a/java/com/android/incallui/videotech/ims/ImsVideoTech.java b/java/com/android/incallui/videotech/ims/ImsVideoTech.java
index 01e4bb84c..fec05dc55 100644
--- a/java/com/android/incallui/videotech/ims/ImsVideoTech.java
+++ b/java/com/android/incallui/videotech/ims/ImsVideoTech.java
@@ -18,6 +18,7 @@ package com.android.incallui.videotech.ims;
import android.content.Context;
import android.os.Build;
+import android.support.annotation.Nullable;
import android.telecom.Call;
import android.telecom.Call.Details;
import android.telecom.VideoProfile;
@@ -25,6 +26,7 @@ import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.LoggingBindings;
+import com.android.dialer.util.CallUtil;
import com.android.incallui.video.protocol.VideoCallScreen;
import com.android.incallui.video.protocol.VideoCallScreenDelegate;
import com.android.incallui.videotech.VideoTech;
@@ -40,6 +42,13 @@ public class ImsVideoTech implements VideoTech {
SessionModificationState.NO_REQUEST;
private int previousVideoState = VideoProfile.STATE_AUDIO_ONLY;
private boolean paused = false;
+ private String savedCameraId;
+
+ // Hold onto a flag of whether or not stopTransmission was called but resumeTransmission has not
+ // been. This is needed because there is time between calling stopTransmission and
+ // call.getDetails().getVideoState() reflecting the change. During that time, pause() and
+ // unpause() will send the incorrect VideoProfile.
+ private boolean transmissionStopped = false;
public ImsVideoTech(LoggingBindings logger, VideoTechListener listener, Call call) {
this.logger = logger;
@@ -53,12 +62,31 @@ public class ImsVideoTech implements VideoTech {
return false;
}
- boolean hasCapabilities =
- call.getDetails().can(Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX)
- && call.getDetails().can(Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX);
+ if (call.getVideoCall() == null) {
+ return false;
+ }
+
+ // We are already in an IMS video call
+ if (VideoProfile.isVideo(call.getDetails().getVideoState())) {
+ return true;
+ }
+
+ // The user has disabled IMS video calling in system settings
+ if (!CallUtil.isVideoEnabled(context)) {
+ return false;
+ }
+
+ // The current call doesn't support transmitting video
+ if (!call.getDetails().can(Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
+ return false;
+ }
- return call.getVideoCall() != null
- && (hasCapabilities || VideoProfile.isVideo(call.getDetails().getVideoState()));
+ // The current call remote device doesn't support receiving video
+ if (!call.getDetails().can(Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
+ return false;
+ }
+
+ return true;
}
@Override
@@ -120,6 +148,9 @@ public class ImsVideoTech implements VideoTech {
}
@Override
+ public void onRemovedFromCallList() {}
+
+ @Override
public int getSessionModificationState() {
return sessionModificationState;
}
@@ -183,7 +214,7 @@ public class ImsVideoTech implements VideoTech {
public void stopTransmission() {
LogUtil.enterBlock("ImsVideoTech.stopTransmission");
- setCamera(null);
+ transmissionStopped = true;
int unpausedVideoState = getUnpausedVideoState(call.getDetails().getVideoState());
call.getVideoCall()
@@ -195,6 +226,8 @@ public class ImsVideoTech implements VideoTech {
public void resumeTransmission() {
LogUtil.enterBlock("ImsVideoTech.resumeTransmission");
+ transmissionStopped = false;
+
int unpausedVideoState = getUnpausedVideoState(call.getDetails().getVideoState());
call.getVideoCall()
.sendSessionModifyRequest(
@@ -204,38 +237,75 @@ public class ImsVideoTech implements VideoTech {
@Override
public void pause() {
- if (canPause() && !paused) {
+ if (call.getState() != Call.STATE_ACTIVE) {
+ LogUtil.i("ImsVideoTech.pause", "not pausing because call is not active");
+ return;
+ }
+
+ if (!isTransmittingOrReceiving()) {
+ LogUtil.i("ImsVideoTech.pause", "not pausing because this is not a video call");
+ return;
+ }
+
+ if (paused) {
+ LogUtil.i("ImsVideoTech.pause", "already paused");
+ return;
+ }
+
+ paused = true;
+
+ if (canPause()) {
LogUtil.i("ImsVideoTech.pause", "sending pause request");
- paused = true;
int pausedVideoState = call.getDetails().getVideoState() | VideoProfile.STATE_PAUSED;
+ if (transmissionStopped && VideoProfile.isTransmissionEnabled(pausedVideoState)) {
+ LogUtil.i("ImsVideoTech.pause", "overriding TX to false due to user request");
+ pausedVideoState &= ~VideoProfile.STATE_TX_ENABLED;
+ }
call.getVideoCall().sendSessionModifyRequest(new VideoProfile(pausedVideoState));
} else {
- LogUtil.i(
- "ImsVideoTech.pause",
- "not sending request: canPause: %b, paused: %b",
- canPause(),
- paused);
+ // This video call does not support pause so we fall back to disabling the camera
+ LogUtil.i("ImsVideoTech.pause", "disabling camera");
+ call.getVideoCall().setCamera(null);
}
}
@Override
public void unpause() {
- if (canPause() && paused) {
+ if (call.getState() != Call.STATE_ACTIVE) {
+ LogUtil.i("ImsVideoTech.unpause", "not unpausing because call is not active");
+ return;
+ }
+
+ if (!isTransmittingOrReceiving()) {
+ LogUtil.i("ImsVideoTech.unpause", "not unpausing because this is not a video call");
+ return;
+ }
+
+ if (!paused) {
+ LogUtil.i("ImsVideoTech.unpause", "already unpaused");
+ return;
+ }
+
+ paused = false;
+
+ if (canPause()) {
LogUtil.i("ImsVideoTech.unpause", "sending unpause request");
- paused = false;
int unpausedVideoState = getUnpausedVideoState(call.getDetails().getVideoState());
+ if (transmissionStopped && VideoProfile.isTransmissionEnabled(unpausedVideoState)) {
+ LogUtil.i("ImsVideoTech.unpause", "overriding TX to false due to user request");
+ unpausedVideoState &= ~VideoProfile.STATE_TX_ENABLED;
+ }
call.getVideoCall().sendSessionModifyRequest(new VideoProfile(unpausedVideoState));
} else {
- LogUtil.i(
- "ImsVideoTech.unpause",
- "not sending request: canPause: %b, paused: %b",
- canPause(),
- paused);
+ // This video call does not support pause so we fall back to re-enabling the camera
+ LogUtil.i("ImsVideoTech.pause", "re-enabling camera");
+ setCamera(savedCameraId);
}
}
@Override
- public void setCamera(String cameraId) {
+ public void setCamera(@Nullable String cameraId) {
+ savedCameraId = cameraId;
call.getVideoCall().setCamera(cameraId);
call.getVideoCall().requestCameraCapabilities();
}
@@ -245,10 +315,14 @@ public class ImsVideoTech implements VideoTech {
call.getVideoCall().setDeviceOrientation(rotation);
}
+ @Override
+ public void becomePrimary() {
+ listener.onImpressionLoggingNeeded(
+ DialerImpression.Type.UPGRADE_TO_VIDEO_CALL_BUTTON_SHOWN_FOR_IMS);
+ }
+
private boolean canPause() {
- return call.getDetails().can(Details.CAPABILITY_CAN_PAUSE_VIDEO)
- && call.getState() == Call.STATE_ACTIVE
- && isTransmitting();
+ return call.getDetails().can(Details.CAPABILITY_CAN_PAUSE_VIDEO);
}
static int getUnpausedVideoState(int videoState) {
diff --git a/java/com/android/incallui/videotech/lightbringer/LightbringerTech.java b/java/com/android/incallui/videotech/lightbringer/LightbringerTech.java
index e49d0b84d..4882ba879 100644
--- a/java/com/android/incallui/videotech/lightbringer/LightbringerTech.java
+++ b/java/com/android/incallui/videotech/lightbringer/LightbringerTech.java
@@ -17,11 +17,17 @@
package com.android.incallui.videotech.lightbringer;
import android.content.Context;
+import android.os.Build;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.telecom.Call;
+import com.android.contacts.common.compat.telecom.TelecomManagerCompat;
import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.configprovider.ConfigProviderBindings;
import com.android.dialer.lightbringer.Lightbringer;
import com.android.dialer.lightbringer.LightbringerListener;
+import com.android.dialer.logging.DialerImpression;
import com.android.incallui.video.protocol.VideoCallScreen;
import com.android.incallui.video.protocol.VideoCallScreenDelegate;
import com.android.incallui.videotech.VideoTech;
@@ -30,15 +36,18 @@ import com.android.incallui.videotech.utils.SessionModificationState;
public class LightbringerTech implements VideoTech, LightbringerListener {
private final Lightbringer lightbringer;
private final VideoTechListener listener;
+ private final Call call;
private final String callingNumber;
private int callState = Call.STATE_NEW;
public LightbringerTech(
@NonNull Lightbringer lightbringer,
@NonNull VideoTechListener listener,
+ @NonNull Call call,
@NonNull String callingNumber) {
this.lightbringer = Assert.isNotNull(lightbringer);
this.listener = Assert.isNotNull(listener);
+ this.call = Assert.isNotNull(call);
this.callingNumber = Assert.isNotNull(callingNumber);
lightbringer.registerListener(this);
@@ -46,7 +55,33 @@ public class LightbringerTech implements VideoTech, LightbringerListener {
@Override
public boolean isAvailable(Context context) {
- return callState == Call.STATE_ACTIVE && lightbringer.isReachable(context, callingNumber);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+ LogUtil.v("LightbringerTech.isAvailable", "upgrade unavailable, only supported on O+");
+ return false;
+ }
+
+ if (!ConfigProviderBindings.get(context)
+ .getBoolean("enable_lightbringer_video_upgrade", true)) {
+ LogUtil.v("LightbringerTech.isAvailable", "upgrade disabled by flag");
+ return false;
+ }
+
+ if (callState != Call.STATE_ACTIVE) {
+ LogUtil.v("LightbringerTech.isAvailable", "upgrade unavailable, call must be active");
+ return false;
+ }
+
+ if (!TelecomManagerCompat.supportsHandover()) {
+ LogUtil.v("LightbringerTech.isAvailable", "upgrade unavailable, telephony support missing");
+ return false;
+ }
+
+ if (!lightbringer.supportsUpgrade(context, callingNumber)) {
+ LogUtil.v("LightbringerTech.isAvailable", "upgrade unavailable, number does not support it");
+ return false;
+ }
+
+ return true;
}
@Override
@@ -80,13 +115,19 @@ public class LightbringerTech implements VideoTech, LightbringerListener {
}
@Override
+ public void onRemovedFromCallList() {
+ lightbringer.unregisterListener(this);
+ }
+
+ @Override
public int getSessionModificationState() {
return SessionModificationState.NO_REQUEST;
}
@Override
public void upgradeToVideo() {
- // TODO: upgrade to a video call
+ listener.onImpressionLoggingNeeded(DialerImpression.Type.LIGHTBRINGER_UPGRADE_REQUESTED);
+ lightbringer.requestUpgrade(call);
}
@Override
@@ -120,18 +161,20 @@ public class LightbringerTech implements VideoTech, LightbringerListener {
}
@Override
- public void pause() {
- throw Assert.createUnsupportedOperationFailException();
- }
+ public void pause() {}
+
+ @Override
+ public void unpause() {}
@Override
- public void unpause() {
+ public void setCamera(@Nullable String cameraId) {
throw Assert.createUnsupportedOperationFailException();
}
@Override
- public void setCamera(String cameraId) {
- throw Assert.createUnsupportedOperationFailException();
+ public void becomePrimary() {
+ listener.onImpressionLoggingNeeded(
+ DialerImpression.Type.UPGRADE_TO_VIDEO_CALL_BUTTON_SHOWN_FOR_LIGHTBRINGER);
}
@Override
diff --git a/java/com/android/incallui/videotech/utils/VideoUtils.java b/java/com/android/incallui/videotech/utils/VideoUtils.java
index 527654030..9dfc68728 100644
--- a/java/com/android/incallui/videotech/utils/VideoUtils.java
+++ b/java/com/android/incallui/videotech/utils/VideoUtils.java
@@ -20,12 +20,10 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
-import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.PermissionsUtil;
public class VideoUtils {
- private static final String PREFERENCE_CAMERA_ALLOWED_BY_USER = "camera_allowed_by_user";
-
public static boolean hasSentVideoUpgradeRequest(@SessionModificationState int state) {
return state == SessionModificationState.WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE
|| state == SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_FAILED
@@ -37,24 +35,12 @@ public class VideoUtils {
return state == SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
}
- public static boolean hasCameraPermissionAndAllowedByUser(@NonNull Context context) {
- return isCameraAllowedByUser(context) && hasCameraPermission(context);
+ public static boolean hasCameraPermissionAndShownPrivacyToast(@NonNull Context context) {
+ return PermissionsUtil.hasCameraPrivacyToastShown(context) && hasCameraPermission(context);
}
public static boolean hasCameraPermission(@NonNull Context context) {
return ContextCompat.checkSelfPermission(context, android.Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED;
}
-
- public static boolean isCameraAllowedByUser(@NonNull Context context) {
- return DialerUtils.getDefaultSharedPreferenceForDeviceProtectedStorageContext(context)
- .getBoolean(PREFERENCE_CAMERA_ALLOWED_BY_USER, false);
- }
-
- public static void setCameraAllowedByUser(@NonNull Context context) {
- DialerUtils.getDefaultSharedPreferenceForDeviceProtectedStorageContext(context)
- .edit()
- .putBoolean(PREFERENCE_CAMERA_ALLOWED_BY_USER, true)
- .apply();
- }
}