diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-07-22 14:09:44 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-07-22 14:09:44 -0300 |
| commit | 05fcc36628b7fd47ce348c038e9f19d9587352b1 (patch) | |
| tree | 8ed2afd13e9d5e68a192e04db27f377e0b3ee89d | |
| parent | d6871530f4875ac9f4f644589cc0a69b328911f9 (diff) | |
| parent | fb04fe4a164af0289c0434443e6255ebe726ce57 (diff) | |
| download | mullvadvpn-05fcc36628b7fd47ce348c038e9f19d9587352b1.tar.xz mullvadvpn-05fcc36628b7fd47ce348c038e9f19d9587352b1.zip | |
Merge branch 'android-split-tunnelling-persistence'
5 files changed, 73 insertions, 19 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 73ca0d857f..8af2de1e27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,9 @@ Line wrap the file at 100 chars. Th - Reconnect with a new key when WireGuard key is rotated automatically, previously the tunnel would time out before reconnecting. +#### Android +- Add split-tunnelling, allowing apps to be configured to be excluded from the tunnel. + ### Changed - Upgrade from Electron 7 to Electron 8. - Change version string parsing to never suggest the user to upgrade to an older version. diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListAdapter.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListAdapter.kt index 820ef12fb2..0fb1d94d47 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListAdapter.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/applist/AppListAdapter.kt @@ -35,7 +35,7 @@ class AppListAdapter( init { jobTracker.newBackgroundJob("populateAppList") { - populateAppList(context) + populateAppList() } } @@ -52,7 +52,7 @@ class AppListAdapter( holder.appInfo = appList.get(position) } - private fun populateAppList(context: Context) { + private fun populateAppList() { val applications = packageManager .getInstalledApplications(0) .filter { info -> info.packageName != thisPackageName } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt index 317ca72eed..f4fb25a895 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt @@ -242,7 +242,7 @@ class MullvadVpnService : TalpidVpnService() { pendingAction = null } - val splitTunnelling = SplitTunnelling().apply { + val splitTunnelling = SplitTunnelling(this).apply { onChange = { excludedApps -> disallowedApps = excludedApps markTunAsStale() diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/SplitTunnelling.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/SplitTunnelling.kt index 0722c2fc56..13f6b242f1 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/SplitTunnelling.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/SplitTunnelling.kt @@ -1,9 +1,16 @@ package net.mullvad.mullvadvpn.service +import android.content.Context +import java.io.File import kotlin.properties.Delegates.observable -class SplitTunnelling { +private const val SHARED_PREFERENCES = "split_tunnelling" +private const val KEY_ENABLED = "enabled" + +class SplitTunnelling(context: Context) { + private val appListFile = File(context.filesDir, "split-tunnelling.txt") private val excludedApps = HashSet<String>() + private val preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE) val excludedAppList get() = if (enabled) { @@ -12,9 +19,18 @@ class SplitTunnelling { emptyList() } - var enabled by observable(false) { _, _, _ -> update() } + var enabled by observable(preferences.getBoolean(KEY_ENABLED, false)) { _, _, _ -> + enabledChanged() + } + var onChange: ((List<String>) -> Unit)? = null + init { + if (appListFile.exists()) { + excludedApps.addAll(appListFile.readLines()) + } + } + fun isAppExcluded(appPackageName: String) = excludedApps.contains(appPackageName) fun excludeApp(appPackageName: String) { @@ -27,6 +43,19 @@ class SplitTunnelling { update() } + fun persist() { + appListFile.writeText(excludedApps.joinToString(separator = "\n")) + } + + private fun enabledChanged() { + preferences.edit().apply { + putBoolean(KEY_ENABLED, enabled) + apply() + } + + update() + } + private fun update() { onChange?.invoke(excludedAppList) } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt index b94883798d..dc905abdbf 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/SplitTunnellingFragment.kt @@ -84,6 +84,12 @@ class SplitTunnellingFragment : ServiceDependentFragment(OnNoService.GoToLaunchS return view } + override fun onSafelyPause() { + jobTracker.newBackgroundJob("persistExcludedApps") { + splitTunnelling.persist() + } + } + override fun onSafelyDestroyView() { titleController.onDestroy() } @@ -104,7 +110,20 @@ class SplitTunnellingFragment : ServiceDependentFragment(OnNoService.GoToLaunchS setDuration(200) } + if (configureSpinner()) { + jobTracker.newUiJob("enableAdapter") { + loadingSpinner.visibility = View.GONE + appListAdapter.enabled = true + } + } + enabledToggle = header.findViewById<CellSwitch>(R.id.enabled_toggle).apply { + if (splitTunnelling.enabled) { + forcefullySetState(CellSwitch.State.ON) + } else { + forcefullySetState(CellSwitch.State.OFF) + } + listener = { toggleState -> when (toggleState) { CellSwitch.State.ON -> enable() @@ -119,29 +138,32 @@ class SplitTunnellingFragment : ServiceDependentFragment(OnNoService.GoToLaunchS } private fun enable() { - appListAdapter.apply { - if (!isListReady) { - enabled = false - showLoadingSpinner() - onListReady = { - hideLoadingSpinner() - } - } else { - enabled = true - } - } - + splitTunnelling.enabled = true + appListAdapter.enabled = configureSpinner() excludeApplications.visibility = View.VISIBLE excludeApplicationsFadeOut.reverse() - splitTunnelling.enabled = true } private fun disable() { - appListAdapter.enabled = false splitTunnelling.enabled = false + appListAdapter.enabled = false excludeApplicationsFadeOut.start() } + private fun configureSpinner(): Boolean { + if (splitTunnelling.enabled && !appListAdapter.isListReady) { + showLoadingSpinner() + + appListAdapter.onListReady = { + hideLoadingSpinner() + } + + return false + } else { + return splitTunnelling.enabled + } + } + private fun showLoadingSpinner() { loadingSpinner.visibility = View.VISIBLE loadingSpinnerFadeIn.start() |
