summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-06-25 00:01:55 +0000
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-06-26 20:47:40 +0000
commit38e543f1aa326b549364c898957c59919f9587cb (patch)
tree38350b4a343a1b77fe300c404b7ced3ada9c191c /android
parent6fdd8251230d97bf4be6b5323267d245761d12d0 (diff)
downloadmullvadvpn-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')
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectFragment.kt141
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt29
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/ConnectionProxy.kt93
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
+ }
+}