diff --git a/android/src/main/java/com/tailscale/ipn/App.kt b/android/src/main/java/com/tailscale/ipn/App.kt index 249a42d..fdbd295 100644 --- a/android/src/main/java/com/tailscale/ipn/App.kt +++ b/android/src/main/java/com/tailscale/ipn/App.kt @@ -10,6 +10,7 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.content.RestrictionsManager import android.content.SharedPreferences import android.content.pm.PackageManager import android.net.ConnectivityManager @@ -48,7 +49,6 @@ import kotlinx.coroutines.launch import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import libtailscale.Libtailscale -import java.io.File import java.io.IOException import java.net.NetworkInterface import java.security.GeneralSecurityException @@ -157,13 +157,16 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner { if (storedUri != null && storedUri.toString().startsWith("content://")) { startLibtailscale(storedUri.toString()) } else { - startLibtailscale(this.getFilesDir().absolutePath) + startLibtailscale(this.filesDir.absolutePath) } healthNotifier = HealthNotifier(Notifier.health, Notifier.state, applicationScope) connectivityManager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager NetworkChangeCallback.monitorDnsChanges(connectivityManager, dns) initViewModels() applicationScope.launch { + val rm = getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager + MDMSettings.update(get(), rm) + Notifier.state.collect { _ -> combine(Notifier.state, MDMSettings.forceEnabled.flow, Notifier.prefs, Notifier.netmap) { state, diff --git a/android/src/main/java/com/tailscale/ipn/MainActivity.kt b/android/src/main/java/com/tailscale/ipn/MainActivity.kt index a8549ce..dbcbc3f 100644 --- a/android/src/main/java/com/tailscale/ipn/MainActivity.kt +++ b/android/src/main/java/com/tailscale/ipn/MainActivity.kt @@ -137,6 +137,11 @@ class MainActivity : ComponentActivity() { val rm = getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager MDMSettings.update(App.get(), rm) + if (MDMSettings.onboardingFlow.flow.value.value == ShowHide.Hide || + MDMSettings.authKey.flow.value.value != null) { + setIntroScreenViewed(true) + } + // (jonathan) TODO: Force the app to be portrait on small screens until we have // proper landscape layout support if (!isLandscapeCapable()) { @@ -367,7 +372,7 @@ class MainActivity : ComponentActivity() { onNavigateHome = backTo("main"), backTo("userSwitcher")) } } - if (shouldDisplayOnboarding()) { + if (isIntroScreenViewedSet()) { navController.navigate("intro") setIntroScreenViewed(true) } @@ -505,10 +510,6 @@ class MainActivity : ComponentActivity() { lifecycleScope.launch(Dispatchers.IO) { MDMSettings.update(App.get(), restrictionsManager) } } - override fun onStart() { - super.onStart() - } - override fun onStop() { super.onStop() val restrictionsManager = @@ -525,11 +526,8 @@ class MainActivity : ComponentActivity() { startActivity(intent) } - private fun shouldDisplayOnboarding(): Boolean { - val onboardingFlowShowHide = MDMSettings.onboardingFlow.flow.value.value - val introSeen = - getSharedPreferences("introScreen", Context.MODE_PRIVATE).getBoolean("seen", false) - return (onboardingFlowShowHide == ShowHide.Show && !introSeen) + private fun isIntroScreenViewedSet(): Boolean { + return !getSharedPreferences("introScreen", Context.MODE_PRIVATE).getBoolean("seen", false) } private fun setIntroScreenViewed(seen: Boolean) { diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt index 71f87f5..0a848a4 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt @@ -215,13 +215,15 @@ fun MainView( when (state) { Ipn.State.Running -> { - - PromptPermissionsIfNecessary() viewModel.maybeRequestVpnPermission() LaunchVpnPermissionIfNeeded(viewModel) - LaunchedEffect(state) { - if (state == Ipn.State.Running && !isAndroidTV()) { - viewModel.checkIfTaildropDirectorySelected() + PromptForMissingPermissions(viewModel) + + if (!viewModel.skipPromptsForAuthKeyLogin()) { + LaunchedEffect(state) { + if (state == Ipn.State.Running && !isAndroidTV()) { + viewModel.checkIfTaildropDirectorySelected() + } } } @@ -795,7 +797,11 @@ fun ExpiryNotification(netmap: Netmap.NetworkMap?, action: () -> Unit = {}) { @OptIn(ExperimentalPermissionsApi::class) @Composable -fun PromptPermissionsIfNecessary() { +fun PromptForMissingPermissions(viewModel: MainViewModel) { + if (viewModel.skipPromptsForAuthKeyLogin()) { + return + } + Permissions.prompt.forEach { (permission, state) -> ErrorDialog( title = permission.title, 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 9a6f3e7..9634a86 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 @@ -126,6 +126,13 @@ class MainViewModel(private val vpnViewModel: VpnViewModel) : IpnViewModel() { this.pingViewModel.handleDismissal() } + // Returns true if we should skip all of the user-interactive permissions prompts + // (with the exception of the VPN permission prompt) + fun skipPromptsForAuthKeyLogin(): Boolean { + val v = MDMSettings.authKey.flow.value.value + return v != null && v != "" + } + private val peerCategorizer = PeerCategorizer() init { @@ -219,6 +226,10 @@ class MainViewModel(private val vpnViewModel: VpnViewModel) : IpnViewModel() { } fun checkIfTaildropDirectorySelected() { + if (skipPromptsForAuthKeyLogin()) { + return + } + val app = App.get() val storedUri = app.getStoredDirectoryUri() if (storedUri == null) {