diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index 9633e2b2bd..872963a302 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -57,6 +57,30 @@
+
+
+
+
+
+
+
+
+
+
+
connect()
+ IPNReceiver.INTENT_DISCONNECT_VPN -> UninitializedApp.get().stopVPN()
+ else -> TSLog.w(TAG, "unknown action: ${intent?.action}")
+ }
+
+ // Never show any UI: finish before this activity becomes visible.
+ finish()
+ }
+
+ /**
+ * Starts the VPN directly when it is ready and consent is already granted. Otherwise (not set up,
+ * or consent not yet granted) opens the app, which handles login and the consent prompt.
+ */
+ private fun connect() {
+ val app = UninitializedApp.get()
+ if (app.isAbleToStartVPN() && VpnService.prepare(this) == null) {
+ app.startVPN()
+ } else {
+ launchMainActivity()
+ }
+ }
+
+ private fun launchMainActivity() {
+ val intent =
+ Intent(this, MainActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }
+ try {
+ startActivity(intent)
+ } catch (e: Exception) {
+ TSLog.e(TAG, "Failed to launch MainActivity: $e")
+ }
+ }
+
+ companion object {
+ private const val TAG = "AutomationActivity"
+ }
+}
diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml
index 608f1f2a2e..1e3f1fb664 100644
--- a/android/src/main/res/values/strings.xml
+++ b/android/src/main/res/values/strings.xml
@@ -378,4 +378,10 @@
Enable hardware attestation
Use hardware-backed keys to bind node identity to the device
+
+ Connect
+ Connect to Tailscale
+ Disconnect
+ Disconnect from Tailscale
+
diff --git a/android/src/main/res/xml/shortcuts.xml b/android/src/main/res/xml/shortcuts.xml
new file mode 100644
index 0000000000..a4a1a3164f
--- /dev/null
+++ b/android/src/main/res/xml/shortcuts.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+