From cf56dd67934fe24b065dc8427e7644aa3618bd84 Mon Sep 17 00:00:00 2001 From: Jonathan Nobels Date: Wed, 27 Mar 2024 17:19:13 -0400 Subject: [PATCH] android/ui: add one-time intro screen (#253) * android/ui: add one-time intro screen fixes ENG-2910 adds a one-time intro screen mostly identical to the one presented in the legacy app. Signed-off-by: Jonathan Nobels * android/ui: string change tailscale -> Tailscale Co-authored-by: Andrea Gottardo Signed-off-by: Jonathan Nobels --------- Signed-off-by: Jonathan Nobels Signed-off-by: Jonathan Nobels Co-authored-by: Andrea Gottardo --- .../java/com/tailscale/ipn/MainActivity.kt | 19 ++++++ .../com/tailscale/ipn/ui/view/IntroView.kt | 62 +++++++++++++++++++ android/src/main/res/values/strings.xml | 5 ++ 3 files changed, 86 insertions(+) create mode 100644 android/src/main/java/com/tailscale/ipn/ui/view/IntroView.kt diff --git a/android/src/main/java/com/tailscale/ipn/MainActivity.kt b/android/src/main/java/com/tailscale/ipn/MainActivity.kt index 9d98083..fc68ef6 100644 --- a/android/src/main/java/com/tailscale/ipn/MainActivity.kt +++ b/android/src/main/java/com/tailscale/ipn/MainActivity.kt @@ -33,6 +33,7 @@ import com.tailscale.ipn.ui.view.BackNavigation import com.tailscale.ipn.ui.view.BugReportView import com.tailscale.ipn.ui.view.DNSSettingsView import com.tailscale.ipn.ui.view.ExitNodePicker +import com.tailscale.ipn.ui.view.IntroView import com.tailscale.ipn.ui.view.MDMSettingsDebugView import com.tailscale.ipn.ui.view.MainView import com.tailscale.ipn.ui.view.MainViewNavigation @@ -136,6 +137,13 @@ class MainActivity : ComponentActivity() { composable("permissions") { PermissionsView(nav = backNav, openApplicationSettings = ::openApplicationSettings) } + composable("intro") { IntroView { navController.popBackStack() } } + } + + // Show the intro screen one time + if (!introScreenViewed()) { + navController.navigate("intro") + setIntroScreenViewed(true) } } } @@ -209,6 +217,17 @@ class MainActivity : ComponentActivity() { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) startActivity(intent) } + + private fun introScreenViewed(): Boolean { + return getSharedPreferences("introScreen", Context.MODE_PRIVATE).getBoolean("seen", false) + } + + private fun setIntroScreenViewed(seen: Boolean) { + getSharedPreferences("introScreen", Context.MODE_PRIVATE) + .edit() + .putBoolean("seen", seen) + .apply() + } } class VpnPermissionContract : ActivityResultContract() { diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/IntroView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/IntroView.kt new file mode 100644 index 0000000..3712377 --- /dev/null +++ b/android/src/main/java/com/tailscale/ipn/ui/view/IntroView.kt @@ -0,0 +1,62 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package com.tailscale.ipn.ui.view + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.tailscale.ipn.R + +@Composable +fun IntroView(onContinue: () -> Unit) { + Surface { + Column( + modifier = Modifier.fillMaxHeight().padding(20.dp), + horizontalAlignment = Alignment.CenterHorizontally) { + Spacer(modifier = Modifier.height(30.dp)) + Image( + modifier = + Modifier.width(140.dp) + .height(140.dp) + .clip(RoundedCornerShape(50)) + .background(Color.Black) + .padding(15.dp), + painter = painterResource(id = R.drawable.ic_tile), + contentDescription = stringResource(R.string.app_icon_content_description)) + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = stringResource(R.string.tailscale), + style = MaterialTheme.typography.headlineLarge) + Spacer(modifier = Modifier.height(20.dp)) + Text( + text = stringResource(R.string.welcome1), + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center) + Spacer(modifier = Modifier.height(20.dp)) + PrimaryActionButton(onClick = onContinue) { + Text( + text = stringResource(id = R.string.getStarted), + fontSize = MaterialTheme.typography.titleMedium.fontSize) + } + } + } +} diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml index 3fe26a3..1348bad 100644 --- a/android/src/main/res/values/strings.xml +++ b/android/src/main/res/values/strings.xml @@ -200,4 +200,9 @@ Not Connected Taildrop Failed + + Tailscale + Get Started + Tailscale is a mesh VPN for securely connecting your devices. All connections are device-to-device, so we never see your data.\n\nWe collect and use your email address and name, as well as your device name, OS version, and IP address in order to help you to connect your devices and manage your settings. We log when you are connected to your network.\n +