diff options
Diffstat (limited to 'java/com/android/incallui/videotech')
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(); - } } |