diff --git a/android/src/main/java/com/tailscale/ipn/MainActivity.kt b/android/src/main/java/com/tailscale/ipn/MainActivity.kt
index 98b046a..336edd9 100644
--- a/android/src/main/java/com/tailscale/ipn/MainActivity.kt
+++ b/android/src/main/java/com/tailscale/ipn/MainActivity.kt
@@ -5,12 +5,15 @@ package com.tailscale.ipn
import android.annotation.SuppressLint
import android.app.Activity
+import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.content.RestrictionsManager
import android.content.pm.ActivityInfo
import android.content.res.Configuration.SCREENLAYOUT_SIZE_LARGE
import android.content.res.Configuration.SCREENLAYOUT_SIZE_MASK
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
import android.os.Bundle
import android.provider.Settings
import android.util.Log
@@ -129,8 +132,13 @@ class MainActivity : ComponentActivity() {
vpnViewModel.setVpnPrepared(true)
App.get().startVPN()
} else {
- Log.d("VpnPermission", "VPN permission denied")
- vpnViewModel.setVpnPrepared(false)
+ if (isAnotherVpnActive(this)) {
+ Log.d("VpnPermission", "Another VPN is likely active")
+ showOtherVPNConflictDialog()
+ } else {
+ Log.d("VpnPermission", "Permission was denied by the user")
+ viewModel.setVpnPrepared(false)
+ }
}
}
viewModel.setVpnPermissionLauncher(vpnPermissionLauncher)
@@ -285,6 +293,34 @@ class MainActivity : ComponentActivity() {
lifecycleScope.launch { Notifier.loginFinished.collect { _ -> loginQRCode.set(null) } }
}
+ private fun showOtherVPNConflictDialog() {
+ AlertDialog.Builder(this)
+ .setTitle(R.string.vpn_permission_denied)
+ .setMessage(R.string.multiple_vpn_explainer)
+ .setPositiveButton(R.string.go_to_settings) { _, _ ->
+ // Intent to open the VPN settings
+ val intent = Intent(Settings.ACTION_VPN_SETTINGS)
+ startActivity(intent)
+ }
+ .setNegativeButton(R.string.cancel, null)
+ .show()
+ }
+
+ fun isAnotherVpnActive(context: Context): Boolean {
+ val connectivityManager =
+ context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+
+ val activeNetwork = connectivityManager.activeNetwork
+ if (activeNetwork != null) {
+ val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
+ if (networkCapabilities != null &&
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
+ return true
+ }
+ }
+ return false
+ }
+
// Returns true if we should render a QR code instead of launching a browser
// for login requests
private fun useQRCodeLogin(): Boolean {
diff --git a/android/src/main/java/com/tailscale/ipn/ui/viewModel/MainViewModel.kt b/android/src/main/java/com/tailscale/ipn/ui/viewModel/MainViewModel.kt
index 8bf07c0..e583c1a 100644
--- a/android/src/main/java/com/tailscale/ipn/ui/viewModel/MainViewModel.kt
+++ b/android/src/main/java/com/tailscale/ipn/ui/viewModel/MainViewModel.kt
@@ -100,8 +100,10 @@ class MainViewModel(private val vpnViewModel: VpnViewModel) : IpnViewModel() {
val isOn =
when {
- currentState == State.Running || currentState == State.Starting -> true
- previousState == State.NoState && currentState == State.Starting -> true
+ prepared && currentState == State.Running || currentState == State.Starting ->
+ true
+ previousState == State.NoState && currentState == State.Starting ->
+ true
else -> false
}
@@ -182,7 +184,7 @@ private fun userStringRes(currentState: State?, previousState: State?, vpnPrepar
currentState == State.NeedsMachineAuth -> R.string.needs_machine_auth
currentState == State.Stopped -> R.string.stopped
currentState == State.Starting -> R.string.starting
- currentState == State.Running -> R.string.connected
+ currentState == State.Running -> if (vpnPrepared) R.string.connected else R.string.placeholder
else -> R.string.placeholder
}
}
diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml
index 1f9a033..6d79df3 100644
--- a/android/src/main/res/values/strings.xml
+++ b/android/src/main/res/values/strings.xml
@@ -292,4 +292,11 @@
Health warnings
No issues found
Tailscale is operating normally.
+
+
+ VPN permission denied
+ Only one VPN can be active, and it appears another is already running. Before starting Tailscale, disable the other VPN.
+ Go to Settings
+ Cancel
+