diff options
| -rw-r--r-- | android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt | 113 |
1 files changed, 109 insertions, 4 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt index c34983ae03..35714d1ce5 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt @@ -1,34 +1,139 @@ package net.mullvad.mullvadvpn.ui.serviceconnection import android.os.Messenger +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.ipc.DispatchingHandler import net.mullvad.mullvadvpn.ipc.Event import net.mullvad.mullvadvpn.ipc.Request import net.mullvad.mullvadvpn.model.TunnelState +import net.mullvad.talpid.tunnel.ActionAfterDisconnect import net.mullvad.talpid.util.EventNotifier +val ANTICIPATED_STATE_TIMEOUT_MS = 1500L + class ConnectionProxy(val connection: Messenger, eventDispatcher: DispatchingHandler<Event>) { + private var resetAnticipatedStateJob: Job? = null + val onStateChange = EventNotifier<TunnelState>(TunnelState.Disconnected) + val onUiStateChange = EventNotifier<TunnelState>(TunnelState.Disconnected) + + var state by onStateChange.notifiable() + private set + var uiState by onUiStateChange.notifiable() + private set init { eventDispatcher.registerHandler(Event.TunnelStateChange::class) { event -> - onStateChange.notify(event.tunnelState) + handleNewState(event.tunnelState) } } fun connect() { - connection.send(Request.Connect.message) + if (anticipateConnectingState()) { + connection.send(Request.Connect.message) + } } fun disconnect() { - connection.send(Request.Disconnect.message) + if (anticipateReconnectingState()) { + connection.send(Request.Disconnect.message) + } } fun reconnect() { - connection.send(Request.Reconnect.message) + if (anticipateDisconnectingState()) { + connection.send(Request.Reconnect.message) + } } fun onDestroy() { onStateChange.unsubscribeAll() + onUiStateChange.unsubscribeAll() + } + + private fun handleNewState(newState: TunnelState) { + synchronized(this) { + resetAnticipatedStateJob?.cancel() + state = newState + uiState = newState + } + } + + private fun anticipateConnectingState(): Boolean { + synchronized(this) { + val currentState = uiState + + if (currentState is TunnelState.Connecting || currentState is TunnelState.Connected) { + return false + } else { + scheduleToResetAnticipatedState() + uiState = TunnelState.Connecting(null, null) + return true + } + } + } + + private fun anticipateReconnectingState(): Boolean { + synchronized(this) { + val currentState = uiState + + val willReconnect = when (currentState) { + is TunnelState.Disconnected -> false + is TunnelState.Disconnecting -> { + when (currentState.actionAfterDisconnect) { + ActionAfterDisconnect.Nothing -> false + ActionAfterDisconnect.Reconnect -> true + ActionAfterDisconnect.Block -> true + } + } + is TunnelState.Connecting -> true + is TunnelState.Connected -> true + is TunnelState.Error -> true + } + + if (willReconnect) { + scheduleToResetAnticipatedState() + uiState = TunnelState.Disconnecting(ActionAfterDisconnect.Reconnect) + } + + return willReconnect + } + } + + private fun anticipateDisconnectingState(): Boolean { + synchronized(this) { + val currentState = uiState + + if (currentState is TunnelState.Disconnected) { + return false + } else { + scheduleToResetAnticipatedState() + uiState = TunnelState.Disconnecting(ActionAfterDisconnect.Nothing) + return true + } + } + } + + private fun scheduleToResetAnticipatedState() { + resetAnticipatedStateJob?.cancel() + + var currentJob: Job? = null + + val newJob = GlobalScope.launch(Dispatchers.Default) { + delay(ANTICIPATED_STATE_TIMEOUT_MS) + + synchronized(this@ConnectionProxy) { + if (!currentJob!!.isCancelled) { + uiState = state + } + } + } + + currentJob = newJob + resetAnticipatedStateJob = newJob } } |
