diff --git a/android/build.gradle b/android/build.gradle index a311789f2d..a1accd7e24 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -39,6 +39,7 @@ android { targetSdkVersion 34 versionCode 242 versionName getVersionProperty("VERSION_LONG") + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // This setting, which defaults to 'true', will cause Tailscale to fall // back to the Google DNS servers if it cannot determine what the diff --git a/android/src/androidTest/kotlin/com/tailscale/ipn/MainActivityTest.kt b/android/src/androidTest/kotlin/com/tailscale/ipn/MainActivityTest.kt index 0a7fe5c6b7..be76734b20 100644 --- a/android/src/androidTest/kotlin/com/tailscale/ipn/MainActivityTest.kt +++ b/android/src/androidTest/kotlin/com/tailscale/ipn/MainActivityTest.kt @@ -17,6 +17,11 @@ import androidx.test.uiautomator.UiSelector import dev.turingcomplete.kotlinonetimepassword.HmacAlgorithm import dev.turingcomplete.kotlinonetimepassword.TimeBasedOneTimePasswordConfig import dev.turingcomplete.kotlinonetimepassword.TimeBasedOneTimePasswordGenerator +import java.net.HttpURLConnection +import java.net.URL +import java.util.concurrent.TimeUnit +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds import org.apache.commons.codec.binary.Base32 import org.junit.After import org.junit.Assert @@ -24,11 +29,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import java.net.HttpURLConnection -import java.net.URL -import java.util.concurrent.TimeUnit -import kotlin.time.Duration.Companion.minutes -import kotlin.time.Duration.Companion.seconds @RunWith(AndroidJUnit4::class) @LargeTest @@ -59,18 +59,18 @@ class MainActivityTest { timeStep = 30, timeStepUnit = TimeUnit.SECONDS) val githubTOTP = TimeBasedOneTimePasswordGenerator(github2FASecret, config) - val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - Log.d(TAG, "Wait for VPN permission prompt and accept") - device.find(By.text("Connection request")) - device.find(By.text("OK")).click() Log.d(TAG, "Click through Get Started screen") device.find(By.text("Get Started")) device.find(By.text("Get Started")).click() + Log.d(TAG, "Wait for VPN permission prompt and accept") + device.find(By.text("Connection request")) + device.find(By.text("OK")).click() + asNecessary( - timeout = 2.minutes, + 2.minutes, { Log.d(TAG, "Log in") device.find(By.text("Log in")).click() @@ -93,7 +93,6 @@ class MainActivityTest { }, { Log.d(TAG, "Make sure GitHub page has loaded") - device.find(By.text("New to GitHub")) device.find(By.text("Username or email address")) device.find(By.text("Sign in")) }, @@ -115,10 +114,15 @@ class MainActivityTest { .setText(githubTOTP.generate()) device.find(UiSelector().instance(0).className(Button::class.java)).click() }, + { + Log.d(TAG, "Authorizing Tailscale") + device.find(By.text("Authorize tailscale")).click() + }, { Log.d(TAG, "Accept Tailscale app") device.find(By.text("Learn more about OAuth")) // Sleep a little to give button time to activate + Thread.sleep(5.seconds.inWholeMilliseconds) device.find(UiSelector().instance(1).className(Button::class.java)).click() }, @@ -126,8 +130,7 @@ class MainActivityTest { Log.d(TAG, "Connect device") device.find(By.text("Connect device")) device.find(UiSelector().instance(0).className(Button::class.java)).click() - }, - ) + }) try { Log.d(TAG, "Accept Permission (Either Storage or Notifications)") diff --git a/android/src/main/java/com/tailscale/ipn/App.kt b/android/src/main/java/com/tailscale/ipn/App.kt index 5126626036..131a730990 100644 --- a/android/src/main/java/com/tailscale/ipn/App.kt +++ b/android/src/main/java/com/tailscale/ipn/App.kt @@ -7,8 +7,10 @@ import android.app.Application import android.app.Notification import android.app.NotificationChannel import android.app.PendingIntent +import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.content.SharedPreferences import android.content.pm.PackageManager import android.net.ConnectivityManager @@ -423,10 +425,27 @@ open class UninitializedApp : Application() { } } - // Calls stopVPN() followed by startVPN() to restart the VPN. fun restartVPN() { + // Register a receiver to listen for the completion of stopVPN + TSLog.d("KARI", "hi") + val stopReceiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + // Ensure stop intent is complete + if (intent?.action == IPNService.ACTION_STOP_VPN) { + // Unregister receiver after receiving the broadcast + context?.unregisterReceiver(this) + // Now start the VPN + startVPN() + } + } + } + + // Register the receiver before stopping VPN + val intentFilter = IntentFilter(IPNService.ACTION_STOP_VPN) + this.registerReceiver(stopReceiver, intentFilter) + stopVPN() - startVPN() } fun createNotificationChannel(id: String, name: String, description: String, importance: Int) {