diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-06-25 00:01:55 +0000 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-06-26 20:47:40 +0000 |
| commit | 38e543f1aa326b549364c898957c59919f9587cb (patch) | |
| tree | 38350b4a343a1b77fe300c404b7ced3ada9c191c /android | |
| parent | 6fdd8251230d97bf4be6b5323267d245761d12d0 (diff) | |
| download | mullvadvpn-38e543f1aa326b549364c898957c59919f9587cb.tar.xz mullvadvpn-38e543f1aa326b549364c898957c59919f9587cb.zip | |
Refactor to create `ConnectionProxy`
Refactor how connection requests are made, so that it is uncoupled from
the `ConnectFragment`.
Diffstat (limited to 'android')
3 files changed, 145 insertions, 118 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectFragment.kt index c31b1691dc..53e00a4c31 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectFragment.kt @@ -1,28 +1,21 @@ package net.mullvad.mullvadvpn import kotlinx.coroutines.launch -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job -import android.app.Activity import android.content.Context -import android.content.Intent -import android.net.VpnService import android.os.Bundle -import android.os.Handler import android.support.v4.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Button import android.widget.ImageButton +import net.mullvad.mullvadvpn.dataproxy.ConnectionProxy import net.mullvad.mullvadvpn.dataproxy.LocationInfoCache import net.mullvad.mullvadvpn.dataproxy.RelayListListener -import net.mullvad.mullvadvpn.model.GeoIpLocation import net.mullvad.mullvadvpn.model.TunnelStateTransition class ConnectFragment : Fragment() { @@ -34,27 +27,19 @@ class ConnectFragment : Fragment() { private lateinit var locationInfo: LocationInfo private lateinit var parentActivity: MainActivity + private lateinit var connectionProxy: ConnectionProxy private lateinit var locationInfoCache: LocationInfoCache private lateinit var relayListListener: RelayListListener - private var daemon = CompletableDeferred<MullvadDaemon>() - private var vpnPermission = CompletableDeferred<Unit>() - - private var fetchInitialStateJob = fetchInitialState() - private var generateWireguardKeyJob = generateWireguardKey() - - private var activeAction: Job? = null - private var attachListenerJob: Job? = null private var updateViewJob: Job? = null - private var waitForDaemonJob: Job? = null override fun onAttach(context: Context) { super.onAttach(context) parentActivity = context as MainActivity + connectionProxy = parentActivity.connectionProxy locationInfoCache = parentActivity.locationInfoCache relayListListener = parentActivity.relayListListener - waitForDaemonJob = waitForDaemon(parentActivity.daemon) } override fun onCreateView( @@ -75,15 +60,22 @@ class ConnectFragment : Fragment() { actionButton = ConnectActionButton(view) actionButton.apply { - onConnect = { connect() } - onCancel = { disconnect() } - onDisconnect = { disconnect() } + onConnect = { connectionProxy.connect() } + onCancel = { connectionProxy.disconnect() } + onDisconnect = { connectionProxy.disconnect() } } switchLocationButton = SwitchLocationButton(view) switchLocationButton.onClick = { openSwitchLocationScreen() } - attachListenerJob = attachListener() + updateView(connectionProxy.uiState) + + connectionProxy.onUiStateChange = { uiState -> + updateViewJob?.cancel() + updateViewJob = GlobalScope.launch(Dispatchers.Main) { + updateView(uiState) + } + } return view } @@ -106,110 +98,23 @@ class ConnectFragment : Fragment() { locationInfo.onDestroy() switchLocationButton.onDestroy() - waitForDaemonJob?.cancel() - attachListenerJob?.cancel() - - detachListener() - - generateWireguardKeyJob.cancel() + connectionProxy.onUiStateChange = null updateViewJob?.cancel() super.onDestroyView() } - override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { - if (resultCode == Activity.RESULT_OK) { - vpnPermission.complete(Unit) - } - } - - private fun waitForDaemon(originalDaemon: Deferred<MullvadDaemon>) = - GlobalScope.launch(Dispatchers.Default) { - daemon.complete(originalDaemon.await()) - } - - private fun attachListener() = GlobalScope.launch(Dispatchers.Default) { - daemon.await().onTunnelStateChange = { state -> - synchronized(this@ConnectFragment) { - updateViewJob = updateView(state) - } - } - } - - private fun detachListener() = GlobalScope.launch(Dispatchers.Default) { - daemon.await().onTunnelStateChange = null - } - - private fun fetchInitialState() = GlobalScope.launch(Dispatchers.Default) { - val state = daemon.await().getState() - - synchronized(this@ConnectFragment) { - updateViewJob = updateViewJob ?: updateView(state) - } - } - - private fun generateWireguardKey() = GlobalScope.launch(Dispatchers.Default) { - val daemon = this@ConnectFragment.daemon.await() - val key = daemon.getWireguardKey() - - if (key == null) { - daemon.generateWireguardKey() - } - } - - private fun requestVpnPermission() { - val intent = VpnService.prepare(parentActivity) - - vpnPermission = CompletableDeferred<Unit>() - - if (intent != null) { - startActivityForResult(intent, 0) - } else { - onActivityResult(0, Activity.RESULT_OK, null) - } - } - - private fun connect() { - updateViewToPreConnecting() - activeAction?.cancel() - - requestVpnPermission() + private fun updateView(uiState: TunnelStateTransition) { + val realState = connectionProxy.state - activeAction = GlobalScope.launch(Dispatchers.Default) { - vpnPermission.await() - generateWireguardKeyJob.join() - daemon.await().connect() - } - } - - private fun disconnect() { - updateView(TunnelStateTransition.Disconnecting()) - activeAction?.cancel() - - activeAction = GlobalScope.launch(Dispatchers.Default) { - daemon.await().disconnect() - } - } - - private fun updateViewToPreConnecting() { - val connecting = TunnelStateTransition.Connecting() - val disconnected = TunnelStateTransition.Disconnected() - - headerBar.setState(disconnected) - - actionButton.state = connecting - notificationBanner.setState(connecting) - status.setState(connecting) - } + locationInfoCache.state = realState + headerBar.setState(realState) - private fun updateView(state: TunnelStateTransition) = GlobalScope.launch(Dispatchers.Main) { - actionButton.state = state - switchLocationButton.state = state + actionButton.state = uiState + switchLocationButton.state = uiState - headerBar.setState(state) - notificationBanner.setState(state) - status.setState(state) - locationInfoCache.setState(state) + notificationBanner.setState(uiState) + status.setState(uiState) } private fun openSwitchLocationScreen() { diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt index 457cefa3dc..b75a408ece 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt @@ -9,14 +9,17 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job +import android.app.Activity import android.content.ComponentName import android.content.Intent import android.content.ServiceConnection +import android.net.VpnService import android.os.Bundle import android.os.IBinder import android.support.v4.app.FragmentActivity import net.mullvad.mullvadvpn.dataproxy.AccountCache +import net.mullvad.mullvadvpn.dataproxy.ConnectionProxy import net.mullvad.mullvadvpn.dataproxy.LocationInfoCache import net.mullvad.mullvadvpn.dataproxy.MullvadProblemReport import net.mullvad.mullvadvpn.dataproxy.RelayListListener @@ -27,9 +30,12 @@ import net.mullvad.mullvadvpn.relaylist.RelayItem import net.mullvad.mullvadvpn.relaylist.RelayList class MainActivity : FragmentActivity() { + private var vpnPermission: CompletableDeferred<Boolean>? = null + var daemon = CompletableDeferred<MullvadDaemon>() private set + val connectionProxy = ConnectionProxy(this) val locationInfoCache = LocationInfoCache(daemon) val problemReport = MullvadProblemReport() var settingsListener = SettingsListener(this) @@ -71,6 +77,14 @@ class MainActivity : FragmentActivity() { bindService(intent, serviceConnection, 0) } + override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { + if (resultCode == Activity.RESULT_OK) { + vpnPermission?.complete(true) + } else { + vpnPermission?.complete(false) + } + } + override fun onStop() { unbindService(serviceConnection) @@ -102,6 +116,21 @@ class MainActivity : FragmentActivity() { } } + fun requestVpnPermission(): Deferred<Boolean> { + val intent = VpnService.prepare(this) + val request = CompletableDeferred<Boolean>() + + vpnPermission = request + + if (intent != null) { + startActivityForResult(intent, 0) + } else { + request.complete(true) + } + + return request + } + private fun addInitialFragment() { supportFragmentManager?.beginTransaction()?.apply { add(R.id.main_fragment, LaunchFragment()) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/ConnectionProxy.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/ConnectionProxy.kt new file mode 100644 index 0000000000..fb74e70160 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/ConnectionProxy.kt @@ -0,0 +1,93 @@ +package net.mullvad.mullvadvpn.dataproxy + +import kotlinx.coroutines.launch +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job + +import net.mullvad.mullvadvpn.MainActivity +import net.mullvad.mullvadvpn.model.TunnelStateTransition + +class ConnectionProxy(val parentActivity: MainActivity) { + val daemon = parentActivity.daemon + + private var activeAction: Job? = null + + private val attachListenerJob = attachListener() + private val fetchInitialStateJob = fetchInitialState() + + private var realState: TunnelStateTransition? = null + set(value) { + field = value + uiState = value ?: TunnelStateTransition.Disconnected() + } + + val state: TunnelStateTransition + get() { + return realState ?: TunnelStateTransition.Disconnected() + } + + var uiState: TunnelStateTransition = TunnelStateTransition.Disconnected() + private set(value) { + field = value + onUiStateChange?.invoke(value) + } + + var onUiStateChange: ((TunnelStateTransition) -> Unit)? = null + + fun connect() { + uiState = TunnelStateTransition.Connecting() + + cancelActiveAction() + + val vpnPermission = parentActivity.requestVpnPermission() + + activeAction = GlobalScope.launch(Dispatchers.Default) { + if (vpnPermission.await()) { + daemon.await().connect() + } + } + } + + fun disconnect() { + uiState = TunnelStateTransition.Disconnecting() + + cancelActiveAction() + activeAction = GlobalScope.launch(Dispatchers.Default) { + daemon.await().disconnect() + } + } + + fun cancelActiveAction() { + activeAction?.cancel() + } + + fun onDestroy() { + attachListenerJob.cancel() + detachListener() + fetchInitialStateJob.cancel() + cancelActiveAction() + } + + private fun fetchInitialState() = GlobalScope.launch(Dispatchers.Default) { + val initialState = daemon.await().getState() + + synchronized(this) { + if (realState == null) { + realState = initialState + } + } + } + + private fun attachListener() = GlobalScope.launch(Dispatchers.Default) { + daemon.await().onTunnelStateChange = { newState -> + synchronized(this) { + realState = newState + } + } + } + + private fun detachListener() = GlobalScope.launch(Dispatchers.Default) { + daemon.await().onTunnelStateChange = null + } +} |
