From 88b895f5ea8960457fa7ee2769dfffc46f70b778 Mon Sep 17 00:00:00 2001 From: Rashad Sookram Date: Mon, 18 Jul 2022 18:24:38 -0400 Subject: [PATCH] Notify when calls start to be routed over cellular data. Only when the device thinks that it's also connected to a WiFi network. --- .../securesms/WebRtcCallActivity.java | 7 +++- .../webrtc/WebRtcCallViewModel.java | 12 ++++++ .../webrtc/WifiToCellularPopupWindow.kt | 39 +++++++++++++++++++ .../securesms/events/WebRtcViewModel.kt | 15 +++++++ .../service/webrtc/GroupActionProcessor.java | 12 ++++++ .../webrtc/GroupConnectedActionProcessor.java | 2 + .../webrtc/GroupJoiningActionProcessor.java | 2 + .../webrtc/GroupPreJoinActionProcessor.java | 2 + .../service/webrtc/SignalCallManager.java | 8 +--- .../service/webrtc/WebRtcActionProcessor.java | 17 ++++++++ .../service/webrtc/state/LocalDeviceState.kt | 4 +- .../state/WebRtcServiceStateBuilder.java | 5 +++ .../res/layout/wifi_to_cellular_popup.xml | 21 ++++++++++ app/src/main/res/values/strings.xml | 4 ++ 14 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WifiToCellularPopupWindow.kt create mode 100644 app/src/main/res/layout/wifi_to_cellular_popup.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java index 1bcf952aa7..c69e76dd15 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java @@ -62,6 +62,7 @@ import org.thoughtcrime.securesms.components.webrtc.WebRtcAudioOutput; import org.thoughtcrime.securesms.components.webrtc.WebRtcCallView; import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel; import org.thoughtcrime.securesms.components.webrtc.WebRtcControls; +import org.thoughtcrime.securesms.components.webrtc.WifiToCellularPopupWindow; import org.thoughtcrime.securesms.components.webrtc.participantslist.CallParticipantsListDialog; import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; @@ -108,6 +109,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan public static final String EXTRA_ENABLE_VIDEO_IF_AVAILABLE = WebRtcCallActivity.class.getCanonicalName() + ".ENABLE_VIDEO_IF_AVAILABLE"; private CallParticipantsListUpdatePopupWindow participantUpdateWindow; + private WifiToCellularPopupWindow wifiToCellularPopupWindow; private DeviceOrientationMonitor deviceOrientationMonitor; private FullscreenHelper fullscreenHelper; @@ -299,7 +301,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan callScreen = findViewById(R.id.callScreen); callScreen.setControlsListener(new ControlsListener()); - participantUpdateWindow = new CallParticipantsListUpdatePopupWindow(callScreen); + participantUpdateWindow = new CallParticipantsListUpdatePopupWindow(callScreen); + wifiToCellularPopupWindow = new WifiToCellularPopupWindow(callScreen); } private void initializeViewModel(boolean isLandscapeEnabled) { @@ -375,6 +378,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan videoTooltip.dismiss(); videoTooltip = null; } + } else if (event instanceof WebRtcCallViewModel.Event.ShowWifiToCellularPopup) { + wifiToCellularPopupWindow.show(); } else { throw new IllegalArgumentException("Unknown event: " + event); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java index 4331b64e41..76f50d234f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java @@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcEphemeralState; import org.thoughtcrime.securesms.util.DefaultValueLiveData; +import org.thoughtcrime.securesms.util.NetworkUtil; import org.thoughtcrime.securesms.util.SingleLiveEvent; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.livedata.LiveDataUtil; @@ -74,6 +75,7 @@ public class WebRtcCallViewModel extends ViewModel { private final Runnable stopOutgoingRingingMode = this::stopOutgoingRingingMode; private boolean canDisplayTooltipIfNeeded = true; + private boolean canDisplayPopupIfNeeded = true; private boolean hasEnabledLocalVideo = false; private boolean wasInOutgoingRingingMode = false; private long callConnectedTime = -1; @@ -292,6 +294,13 @@ public class WebRtcCallViewModel extends ViewModel { canDisplayTooltipIfNeeded = false; events.setValue(new Event.ShowVideoTooltip()); } + + if (canDisplayPopupIfNeeded && webRtcViewModel.isCellularConnection() && NetworkUtil.isConnectedWifi(ApplicationDependencies.getApplication())) { + canDisplayPopupIfNeeded = false; + events.setValue(new Event.ShowWifiToCellularPopup()); + } else if (!webRtcViewModel.isCellularConnection()) { + canDisplayPopupIfNeeded = true; + } } @MainThread @@ -481,6 +490,9 @@ public class WebRtcCallViewModel extends ViewModel { public static class DismissVideoTooltip extends Event { } + public static class ShowWifiToCellularPopup extends Event { + } + public static class StartCall extends Event { private final boolean isVideoCall; diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WifiToCellularPopupWindow.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WifiToCellularPopupWindow.kt new file mode 100644 index 0000000000..857110b40c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WifiToCellularPopupWindow.kt @@ -0,0 +1,39 @@ +package org.thoughtcrime.securesms.components.webrtc + +import android.view.Gravity +import android.view.LayoutInflater +import android.view.ViewGroup +import android.view.WindowManager +import android.widget.PopupWindow +import androidx.core.view.postDelayed +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.util.VibrateUtil +import java.util.concurrent.TimeUnit + +/** + * Popup shown when the device is connected to a WiFi and cellular network, and WiFi is unusable for + * RingRTC. + */ +class WifiToCellularPopupWindow(private val parent: ViewGroup) : PopupWindow( + LayoutInflater.from(parent.context).inflate(R.layout.wifi_to_cellular_popup, parent, false), + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.WRAP_CONTENT +) { + + init { + animationStyle = R.style.PopupAnimation + } + + fun show() { + showAtLocation(parent, Gravity.TOP or Gravity.START, 0, 0) + VibrateUtil.vibrate(parent.context, VIBRATE_DURATION_MS) + contentView.postDelayed(DISPLAY_DURATION_MS) { + dismiss() + } + } + + companion object { + private val DISPLAY_DURATION_MS = TimeUnit.SECONDS.toMillis(4) + private const val VIBRATE_DURATION_MS = 50 + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt index 569525f834..d30679041c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt @@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager +import org.webrtc.PeerConnection class WebRtcViewModel(state: WebRtcServiceState) { @@ -104,6 +105,20 @@ class WebRtcViewModel(state: WebRtcServiceState) { state.localDeviceState.isMicrophoneEnabled ) + val isCellularConnection: Boolean = when (state.localDeviceState.networkConnectionType) { + PeerConnection.AdapterType.UNKNOWN, + PeerConnection.AdapterType.ETHERNET, + PeerConnection.AdapterType.WIFI, + PeerConnection.AdapterType.VPN, + PeerConnection.AdapterType.LOOPBACK, + PeerConnection.AdapterType.ADAPTER_TYPE_ANY -> false + PeerConnection.AdapterType.CELLULAR, + PeerConnection.AdapterType.CELLULAR_2G, + PeerConnection.AdapterType.CELLULAR_3G, + PeerConnection.AdapterType.CELLULAR_4G, + PeerConnection.AdapterType.CELLULAR_5G -> true + } + val isRemoteVideoEnabled: Boolean get() = remoteParticipants.any(CallParticipant::isVideoEnabled) || groupState.isNotIdle && remoteParticipants.size > 1 diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java index 337ccd6c9f..2964936282 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java @@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.ringrtc.RemotePeer; import org.thoughtcrime.securesms.service.webrtc.state.VideoState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceStateBuilder; +import org.webrtc.PeerConnection; import org.webrtc.VideoTrack; import org.whispersystems.signalservice.api.messages.calls.OfferMessage; import org.whispersystems.signalservice.api.push.ServiceId; @@ -239,6 +240,17 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor { return currentState; } + @Override protected @NonNull WebRtcServiceState handleGroupLocalDeviceStateChanged(@NonNull WebRtcServiceState currentState) { + GroupCall groupCall = currentState.getCallInfoState().requireGroupCall(); + PeerConnection.AdapterType type = groupCall.getLocalDeviceState().getNetworkRoute().getLocalAdapterType(); + + return currentState.builder() + .changeLocalDeviceState() + .setNetworkConnectionType(type) + .commit() + .build(); + } + @Override protected @NonNull WebRtcServiceState handleGroupCallEnded(@NonNull WebRtcServiceState currentState, int groupCallHash, @NonNull GroupCall.GroupCallEndReason groupCallEndReason) { Log.i(tag, "handleGroupCallEnded(): reason: " + groupCallEndReason); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java index 01b81827be..2bc2d46dc7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java @@ -49,6 +49,8 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor { protected @NonNull WebRtcServiceState handleGroupLocalDeviceStateChanged(@NonNull WebRtcServiceState currentState) { Log.i(tag, "handleGroupLocalDeviceStateChanged():"); + currentState = super.handleGroupLocalDeviceStateChanged(currentState); + GroupCall groupCall = currentState.getCallInfoState().requireGroupCall(); GroupCall.LocalDeviceState device = groupCall.getLocalDeviceState(); GroupCall.ConnectionState connectionState = device.getConnectionState(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupJoiningActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupJoiningActionProcessor.java index d6eea555ea..f1d9e8bd00 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupJoiningActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupJoiningActionProcessor.java @@ -42,6 +42,8 @@ public class GroupJoiningActionProcessor extends GroupActionProcessor { protected @NonNull WebRtcServiceState handleGroupLocalDeviceStateChanged(@NonNull WebRtcServiceState currentState) { Log.i(tag, "handleGroupLocalDeviceStateChanged():"); + currentState = super.handleGroupLocalDeviceStateChanged(currentState); + GroupCall groupCall = currentState.getCallInfoState().requireGroupCall(); GroupCall.LocalDeviceState device = groupCall.getLocalDeviceState(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java index ea96df7d01..ecf0d9408f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java @@ -90,6 +90,8 @@ public class GroupPreJoinActionProcessor extends GroupActionProcessor { protected @NonNull WebRtcServiceState handleGroupLocalDeviceStateChanged(@NonNull WebRtcServiceState currentState) { Log.i(tag, "handleGroupLocalDeviceStateChanged():"); + currentState = super.handleGroupLocalDeviceStateChanged(currentState); + GroupCall groupCall = currentState.getCallInfoState().requireGroupCall(); GroupCall.LocalDeviceState device = groupCall.getLocalDeviceState(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java index 86c7641fca..8c8ef08641 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java @@ -51,7 +51,6 @@ import org.thoughtcrime.securesms.service.webrtc.state.WebRtcEphemeralState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState; import org.thoughtcrime.securesms.util.AppForegroundObserver; import org.thoughtcrime.securesms.util.BubbleUtil; -import org.thoughtcrime.securesms.util.NetworkUtil; import org.thoughtcrime.securesms.util.RecipientAccessList; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; @@ -513,12 +512,7 @@ private void processStateless(@NonNull Function1 p.handleNetworkRouteChanged(s, networkRoute)); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java index 1f7d45229f..a93e639f76 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java @@ -16,6 +16,7 @@ import org.signal.ringrtc.CallId; import org.signal.ringrtc.CallManager; import org.signal.ringrtc.CallManager.RingUpdate; import org.signal.ringrtc.GroupCall; +import org.signal.ringrtc.NetworkRoute; import org.thoughtcrime.securesms.components.sensors.Orientation; import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink; import org.thoughtcrime.securesms.components.webrtc.EglBaseWrapper; @@ -547,6 +548,22 @@ public abstract class WebRtcActionProcessor { return currentState; } + protected @NonNull WebRtcServiceState handleNetworkRouteChanged(@NonNull WebRtcServiceState currentState, @NonNull NetworkRoute networkRoute) { + Log.i(tag, "onNetworkRouteChanged: localAdapterType: " + networkRoute.getLocalAdapterType()); + try { + webRtcInteractor.getCallManager().updateBandwidthMode(NetworkUtil.getCallingBandwidthMode(context, networkRoute.getLocalAdapterType())); + } catch (CallException e) { + Log.w(tag, "Unable to update bandwidth mode on CallManager", e); + } + + PeerConnection.AdapterType type = networkRoute.getLocalAdapterType(); + return currentState.builder() + .changeLocalDeviceState() + .setNetworkConnectionType(type) + .commit() + .build(); + } + protected @NonNull WebRtcServiceState handleBandwidthModeUpdate(@NonNull WebRtcServiceState currentState) { try { webRtcInteractor.getCallManager().updateBandwidthMode(NetworkUtil.getCallingBandwidthMode(context)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/LocalDeviceState.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/LocalDeviceState.kt index bf53a7d63f..68b4b6b1aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/LocalDeviceState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/LocalDeviceState.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.service.webrtc.state import org.thoughtcrime.securesms.components.sensors.Orientation import org.thoughtcrime.securesms.ringrtc.CameraState import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager +import org.webrtc.PeerConnection /** * Local device specific state. @@ -15,7 +16,8 @@ data class LocalDeviceState constructor( var deviceOrientation: Orientation = Orientation.PORTRAIT_BOTTOM_EDGE, var activeDevice: SignalAudioManager.AudioDevice = SignalAudioManager.AudioDevice.NONE, var availableDevices: Set = emptySet(), - var bluetoothPermissionDenied: Boolean = false + var bluetoothPermissionDenied: Boolean = false, + var networkConnectionType: PeerConnection.AdapterType = PeerConnection.AdapterType.UNKNOWN, ) { fun duplicate(): LocalDeviceState { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java index f8f7a9f5d9..67f08b001b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java @@ -130,6 +130,11 @@ public class WebRtcServiceStateBuilder { toBuild.setBluetoothPermissionDenied(bluetoothPermissionDenied); return this; } + + public @NonNull LocalDeviceStateBuilder setNetworkConnectionType(@NonNull PeerConnection.AdapterType type) { + toBuild.setNetworkConnectionType(type); + return this; + } } public class CallSetupStateBuilder { diff --git a/app/src/main/res/layout/wifi_to_cellular_popup.xml b/app/src/main/res/layout/wifi_to_cellular_popup.xml new file mode 100644 index 0000000000..7849a8a94b --- /dev/null +++ b/app/src/main/res/layout/wifi_to_cellular_popup.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index da3de20477..d07e120973 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3539,6 +3539,10 @@ You (on another device) %1$s (on another device) + + + Weak Wi-Fi. Switched to cellular. + Deleting your account will: Enter your phone number