From 600ad7adfd71592c753a5b66c426bc6747cfde9a Mon Sep 17 00:00:00 2001 From: Mildrette Date: Sat, 27 Jun 2026 02:19:32 +0100 Subject: [PATCH] feat(onboarding): rebrand 3-screen onboarding flow - WelcomeView: replaced single welcome screen with 3-page HorizontalPager hosting all onboarding screens in one place - SendingMessages: new ChooseMessageModeScreen (screen 2) showing Relay account vs online accounts options with informational cards - DefaultSmsAppView: new DefaultSmsAppScreen (screen 3) prompting user to set RelaySMS as default SMS app - All exit points (Skip, Done, navigate after set-default) - Added lock_open_right.xml and relay_to_account.png drawables used in screens 1 and 2 --- .../ui/viewModels/OnboardingViewModel.kt | 51 --- .../sw0b_001/ui/views/DefaultSmsAppView.kt | 205 ++++++++++++ .../sw0b_001/ui/views/SendingMessages.kt | 316 ++++++++++++++++++ .../example/sw0b_001/ui/views/WelcomeView.kt | 311 ++++++++++------- app/src/main/res/drawable/lock_open_right.xml | 10 + .../main/res/drawable/relay_to_account.png | Bin 0 -> 6065 bytes 6 files changed, 723 insertions(+), 170 deletions(-) create mode 100644 app/src/main/java/com/example/sw0b_001/ui/views/DefaultSmsAppView.kt create mode 100644 app/src/main/java/com/example/sw0b_001/ui/views/SendingMessages.kt create mode 100644 app/src/main/res/drawable/lock_open_right.xml create mode 100644 app/src/main/res/drawable/relay_to_account.png diff --git a/app/src/main/java/com/example/sw0b_001/ui/viewModels/OnboardingViewModel.kt b/app/src/main/java/com/example/sw0b_001/ui/viewModels/OnboardingViewModel.kt index f6366f67..a47070c6 100644 --- a/app/src/main/java/com/example/sw0b_001/ui/viewModels/OnboardingViewModel.kt +++ b/app/src/main/java/com/example/sw0b_001/ui/viewModels/OnboardingViewModel.kt @@ -96,57 +96,6 @@ class OnboardingViewModel @Inject constructor( } } ), - InteractiveOnboarding( - title = context.getString(R.string.save_your_accounts), - description = context.getString(R.string.you_can_also_use_sms_to_send_messages_from_your_online_accounts_saving_them_guarantees_you_can_use_them_without_an_internet_connection), - actionButtonText = context.getString(R.string.give_it_a_try), - image = R.drawable.vault_illus, - onClickCallToAction = { - showLoginSignupModal = true - } - ), - InteractiveOnboarding( - title = context.getString(R.string.start_messaging_now), - description = context.getString(R.string.you_can_now_send_messages_from_your_saved_accounts_you_can_also_save_more_accounts_later), - actionButtonText = context.getString(R.string.give_it_a_try), - image = R.drawable.try_sending_message_illus, - onClickCallToAction = { - showSendPlatformsModal = true - } - ), - InteractiveOnboarding( - title = context.getString(R.string.secure_your_app), - description = context.getString(R.string.from_locking_with_device_pin_code_to_other_secure_ways_of_making_sure_you_maintain_your_app_s_privacy), - actionButtonText = context.getString(R.string.let_s_lock_this_down), - image = R.drawable.undraw_fingerprint_kdwq, - onClickCallToAction = { - viewModelScope.launch { - _showBiometrics.emit { next() } - } - } - ), - InteractiveOnboarding( - title = context.getString(R.string.make_default_sms_app), - description = context.getString(R.string.you_can_manage_all_your_sms_messages_from_a_single_place), - subDescription = context.getString(R.string.this_also_unlocks_features_like_sending_images_with_sms_yes_not_mms), - actionButtonText = context.getString(R.string.set_as_default_sms_app), - image = R.drawable.try_sending_message_illus, - onClickCallToAction = { -// if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { -// val roleManager = context -// .getSystemService(RoleManager::class.java) -// val roleRequestIntent = roleManager -// .createRequestRoleIntent(RoleManager.ROLE_SMS) -// activity.startActivityForResult(roleRequestIntent, 12) -// } else { -// val intent = Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT) -// intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, -// context.packageName) -// context.startActivity(intent) -// } - showMakeDefaultRequest = true - } - ), ) } diff --git a/app/src/main/java/com/example/sw0b_001/ui/views/DefaultSmsAppView.kt b/app/src/main/java/com/example/sw0b_001/ui/views/DefaultSmsAppView.kt new file mode 100644 index 00000000..2ab39e47 --- /dev/null +++ b/app/src/main/java/com/example/sw0b_001/ui/views/DefaultSmsAppView.kt @@ -0,0 +1,205 @@ +package com.example.sw0b_001.ui.views + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.KeyboardArrowRight +import androidx.compose.material3.Button +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.afkanerd.smswithoutborders_libsmsmms.extensions.context.isDefault +import com.afkanerd.smswithoutborders_libsmsmms.ui.getSetDefaultBehaviour +import com.example.sw0b_001.R +import com.example.sw0b_001.ui.navigation.HomepageScreen +import com.example.sw0b_001.ui.theme.AppTheme +import com.example.sw0b_001.ui.views.threads.makeDefault + +@Composable +fun DefaultSmsAppScreen( + navController: NavController, + onSkip: () -> Unit, + onBack: () -> Unit, + onSetDefault: () -> Unit, + onDone: () -> Unit, +) { + val context = LocalContext.current + val previewMode = LocalInspectionMode.current + + var isDefault by remember { + mutableStateOf(previewMode || context.isDefault()) + } + + val getDefaultPermission = getSetDefaultBehaviour(context) { + isDefault = context.isDefault() + if (isDefault) { + navController.navigate(HomepageScreen) { + popUpTo(0) + launchSingleTop = true + } + } + } + + Column( + modifier = Modifier + .fillMaxSize() + .statusBarsPadding() + .padding(horizontal = 32.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + TextButton(onClick = onSkip) { + Text("Skip") + } + } + + Spacer(modifier = Modifier.weight(0.4f)) + + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + painter = painterResource(R.drawable.try_sending_message_illus), + contentDescription = null, + modifier = Modifier.size(150.dp) + ) + + Spacer(modifier = Modifier.height(40.dp)) + + Text( + text = "Make RelaySMS your\ndefault SMS app", + textAlign = TextAlign.Center, + style = MaterialTheme.typography.headlineMedium + ) + + Spacer(modifier = Modifier.height(20.dp)) + + Text( + text = "Manage your SMS messages from one place and send images through SMS!", + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + Spacer(modifier = Modifier.height(40.dp)) + + Button( + onClick = { + getDefaultPermission.launch(makeDefault(context)) + }, + shape = RoundedCornerShape(50.dp), + modifier = Modifier.height(48.dp) + ) { + Text("Set as Default SMS App") + } + } + + Spacer(modifier = Modifier.weight(1f)) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 24.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Row( + modifier = Modifier.weight(1f), + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + repeat(3) { index -> + Box( + modifier = Modifier + .size( + width = if (index == 2) 20.dp else 8.dp, + height = 8.dp + ) + .clip(CircleShape) + .background( + if (index == 2) + MaterialTheme.colorScheme.primary + else + MaterialTheme.colorScheme.outlineVariant + ) + ) + } + } + + TextButton(onClick = onBack) { + Text("Back") + } + + Spacer(modifier = Modifier.width(12.dp)) + + OutlinedButton( + onClick = onDone, + shape = RoundedCornerShape(50.dp), + border = BorderStroke(1.dp, MaterialTheme.colorScheme.primary) + ) { + Text("Done !") + Spacer(modifier = Modifier.width(12.dp)) + Box( + modifier = Modifier + .size(40.dp) + .background(MaterialTheme.colorScheme.primary, CircleShape), + contentAlignment = Alignment.Center + ) { + Icon( + Icons.Default.KeyboardArrowRight, + contentDescription = null, + tint = MaterialTheme.colorScheme.onPrimary + ) + } + } + } + } +} + +@Preview(showBackground = true) +@Composable +fun GetStartedPreviewLoggedIn() { + val context = LocalContext.current + AppTheme(darkTheme = false) { + DefaultSmsAppScreen( + navController = NavController(context), + onSkip = {}, + onBack = {}, + onSetDefault = {}, + onDone = {}, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/sw0b_001/ui/views/SendingMessages.kt b/app/src/main/java/com/example/sw0b_001/ui/views/SendingMessages.kt new file mode 100644 index 00000000..0771198b --- /dev/null +++ b/app/src/main/java/com/example/sw0b_001/ui/views/SendingMessages.kt @@ -0,0 +1,316 @@ +package com.example.sw0b_001.ui.views + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.KeyboardArrowRight +import androidx.compose.material3.* +import androidx.compose.runtime.* +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.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.example.sw0b_001.R +import com.example.sw0b_001.ui.theme.AppTheme + +@Composable +fun ChooseMessageModeScreen( + onSkip: () -> Unit, + onBack: () -> Unit, + onNext: () -> Unit, +) { + var selected by remember { mutableIntStateOf(0) } + + Column( + modifier = Modifier + .fillMaxSize() + .statusBarsPadding() + .padding(horizontal = 20.dp) + ) { + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Text( + text = "Skip", + modifier = Modifier.clickable { onSkip() }, + style = MaterialTheme.typography.bodyLarge + ) + } + + Spacer(modifier = Modifier.height(80.dp)) + + Text( + text = "Choose how you want to\nsend a message.", + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.headlineMedium.copy( + fontWeight = FontWeight.SemiBold + ) + ) + + Spacer(modifier = Modifier.height(40.dp)) + + MessageOptionCard( + selected = selected == 0, + title = "Relay account (Default)", + badge = "NO SETUP REQUIRED", + badgeContainerColor = MaterialTheme.colorScheme.errorContainer, + badgeContentColor = MaterialTheme.colorScheme.onErrorContainer, + description = "Send with your RelaySMS email alias created with your phone number, e.g. 12345689@relaysms.me.", + imageRes = R.drawable.relay_to_account, + imageCentered = true, + onClick = { selected = 0 } + ) + + Spacer(modifier = Modifier.height(20.dp)) + + Text( + text = "Or", + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleMedium + ) + + Spacer(modifier = Modifier.height(20.dp)) + + MessageOptionCard( + selected = selected == 1, + title = "Use your online accounts", + badge = "REQUIRES INTERNET", + badgeContainerColor = MaterialTheme.colorScheme.secondaryContainer, + badgeContentColor = MaterialTheme.colorScheme.onSecondaryContainer, + description = "Link your existing Gmail, Telegram, or X account(s) to send messages from your real identity.", + imageRes = R.drawable.relay_sms_save_vault, + imageCentered = false, + onClick = { selected = 1 } + ) + + Spacer(modifier = Modifier.weight(1f)) + + BottomNavigationRow( + currentStep = 1, + onBack = onBack, + onNext = onNext + ) + + Spacer(modifier = Modifier.height(24.dp)) + } +} + +@Composable +private fun MessageOptionCard( + selected: Boolean, + title: String, + badge: String, + badgeContainerColor: Color, + badgeContentColor: Color, + description: String, + imageRes: Int, + imageCentered: Boolean, + onClick: () -> Unit +) { + OutlinedCard( + modifier = Modifier + .fillMaxWidth() + .clickable(onClick = onClick), + shape = RoundedCornerShape(28.dp), + colors = CardDefaults.outlinedCardColors( + containerColor = MaterialTheme.colorScheme.surfaceBright + ), + border = BorderStroke( + width = if (selected) 2.dp else 1.dp, + color = if (selected) + MaterialTheme.colorScheme.primary + else + MaterialTheme.colorScheme.outline.copy(alpha = 0.4f) + ) + ) { + + Column( + modifier = Modifier.padding(20.dp) + ) { + + Row( + verticalAlignment = Alignment.CenterVertically + ) { + + Text( + text = title, + modifier = Modifier.weight(1f), + style = MaterialTheme.typography.titleMedium + ) + + Surface( + shape = RoundedCornerShape(8.dp), + color = badgeContainerColor + ) { + Text( + text = badge, + modifier = Modifier.padding( + horizontal = 10.dp, + vertical = 6.dp + ), + style = MaterialTheme.typography.labelSmall, + color = badgeContentColor + ) + } + } + + Spacer(modifier = Modifier.height(18.dp)) + + if (imageCentered) { + + Box( + modifier = Modifier + .fillMaxWidth() + .height(90.dp), + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(imageRes), + contentDescription = null, + modifier = Modifier.size(190.dp) + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = description, + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodyMedium + ) + + } else { + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + + Text( + text = description, + modifier = Modifier.weight(1f), + style = MaterialTheme.typography.bodyMedium + ) + + Spacer(modifier = Modifier.width(16.dp)) + + Image( + painter = painterResource(imageRes), + contentDescription = null, + modifier = Modifier.size(100.dp) + ) + } + } + } + } +} + +@Composable +private fun BottomNavigationRow( + currentStep: Int, + onBack: () -> Unit, + onNext: () -> Unit +) { + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + + Row( + modifier = Modifier.weight(1f), + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + + repeat(3) { index -> + + Box( + modifier = Modifier + .size( + width = if (index == currentStep) 22.dp else 8.dp, + height = 8.dp + ) + .clip(CircleShape) + .background( + if (index == currentStep) + MaterialTheme.colorScheme.primary + else + MaterialTheme.colorScheme.outlineVariant + ) + ) + } + } + + TextButton(onClick = onBack) { + Text("Back") + } + + Spacer(modifier = Modifier.width(12.dp)) + + OutlinedButton( + onClick = onNext, + shape = RoundedCornerShape(50.dp), + border = BorderStroke( + 1.dp, + MaterialTheme.colorScheme.primary + ), + contentPadding = PaddingValues( + start = 22.dp, + end = 6.dp, + top = 6.dp, + bottom = 6.dp + ) + ) { + + Text("Next") + + Spacer(modifier = Modifier.width(14.dp)) + + Box( + modifier = Modifier + .size(38.dp) + .background( + MaterialTheme.colorScheme.primary, + CircleShape + ), + contentAlignment = Alignment.Center + ) { + + Icon( + imageVector = Icons.Default.KeyboardArrowRight, + contentDescription = null, + tint = MaterialTheme.colorScheme.onPrimary + ) + } + } + } +} + + + +@Preview(showBackground = true) +@Composable +fun ChooseMessageModeScreenPreview() { + AppTheme(darkTheme = false) { + ChooseMessageModeScreen( + onSkip = {}, + onBack = {}, + onNext = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/sw0b_001/ui/views/WelcomeView.kt b/app/src/main/java/com/example/sw0b_001/ui/views/WelcomeView.kt index efc63030..8f820dd5 100644 --- a/app/src/main/java/com/example/sw0b_001/ui/views/WelcomeView.kt +++ b/app/src/main/java/com/example/sw0b_001/ui/views/WelcomeView.kt @@ -1,191 +1,264 @@ package com.example.sw0b_001.ui.views -import android.content.Intent import android.content.res.Configuration +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable +import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Language +import androidx.compose.material.icons.filled.KeyboardArrowRight import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringArrayResource -import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import com.example.sw0b_001.R -import androidx.core.net.toUri import androidx.navigation.compose.rememberNavController -import com.afkanerd.smswithoutborders_libsmsmms.extensions.context.getCurrentLocale -import com.afkanerd.smswithoutborders_libsmsmms.extensions.context.setLocale -import com.example.sw0b_001.data.Helpers -import com.example.sw0b_001.ui.navigation.OnboardingInteractiveScreen +import com.example.sw0b_001.R +import com.example.sw0b_001.extensions.context.settingsSetOnboardedCompletely +import com.example.sw0b_001.ui.navigation.HomepageScreen import com.example.sw0b_001.ui.theme.AppTheme +import kotlinx.coroutines.launch @Composable -fun WelcomeMainView( - navController: NavController -) { +fun WelcomeMainView(navController: NavController) { + + val pageCount = 3 + val pagerState = rememberPagerState(pageCount = { pageCount }) + val scope = rememberCoroutineScope() val context = LocalContext.current - var localeExpanded by remember { mutableStateOf(false) } - val localeArraysValues = stringArrayResource(R.array.language_values) - val localeArraysOptions= stringArrayResource(R.array.language_options) + fun finishOnboarding() { + context.settingsSetOnboardedCompletely(true) + navController.navigate(HomepageScreen) { + popUpTo(0) + launchSingleTop = true + } + } Scaffold { innerPadding -> Column( modifier = Modifier - .fillMaxHeight() + .fillMaxSize() .padding(innerPadding) - .padding(start = 16.dp, end = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center ) { - Image( - painter = painterResource(id = R.drawable.relay_sms_welcome,), - contentDescription = null, - modifier = Modifier.size(250.dp) - ) - Spacer(modifier = Modifier.height(32.dp)) + HorizontalPager( + state = pagerState, + modifier = Modifier.weight(1f) + ) { page -> - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - OutlinedButton( - onClick = { - localeExpanded = true - }, - shape = RoundedCornerShape(24.dp), - modifier = Modifier.padding(end = 8.dp), - ) { - Icon( - imageVector = Icons.Filled.Language, - contentDescription = stringResource(R.string.language), - modifier = Modifier.size(20.dp), + when (page) { + 0 -> PageOneContent() + + 1 -> ChooseMessageModeScreen( + onSkip = { finishOnboarding() }, + onBack = { + scope.launch { pagerState.animateScrollToPage(0) } + }, + onNext = { + scope.launch { pagerState.animateScrollToPage(2) } + } ) - Spacer(modifier = Modifier.width(4.dp)) - Text( - text = context.getCurrentLocale()?.displayName ?: - stringResource(R.string.english1), + + 2 -> DefaultSmsAppScreen( + navController = navController, + onSkip = { finishOnboarding() }, + onBack = { + scope.launch { pagerState.animateScrollToPage(1) } + }, + onSetDefault = { }, + onDone = { finishOnboarding() } ) } - Column( + } + + if (pagerState.currentPage == 0) { + Row( modifier = Modifier .fillMaxWidth() - .padding(start = 8.dp) + .padding(horizontal = 24.dp, vertical = 24.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically ) { - DropdownMenu( - expanded = localeExpanded, - onDismissRequest = { localeExpanded = false } + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + repeat(pageCount) { index -> + Box( + modifier = Modifier + .size( + width = if (pagerState.currentPage == index) 24.dp else 8.dp, + height = 8.dp + ) + .background( + color = if (pagerState.currentPage == index) + MaterialTheme.colorScheme.primary + else + MaterialTheme.colorScheme.outlineVariant, + shape = CircleShape + ) + ) + } + } + + Button( + onClick = { + scope.launch { + pagerState.animateScrollToPage(pagerState.currentPage + 1) + } + }, + shape = RoundedCornerShape(50.dp) ) { - localeArraysOptions.forEachIndexed { i, item -> - DropdownMenuItem( - text = { Text(item) }, - onClick = { - context.setLocale(localeArraysValues[i]) - localeExpanded = false - } + Text(text = "Get Started") + Spacer(modifier = Modifier.width(8.dp)) + + val isDark = isSystemInDarkTheme() + Box( + modifier = Modifier + .size(30.dp) + .background( + color = if (isDark) + MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.6f) + else + MaterialTheme.colorScheme.surface, + shape = CircleShape + ), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.Default.KeyboardArrowRight, + contentDescription = null, + tint = if (isDark) + MaterialTheme.colorScheme.onSurfaceVariant + else + MaterialTheme.colorScheme.primary ) } } } - } + Spacer(modifier = Modifier.height(5.dp)) + } + } +} +@Composable +fun PageOneContent() { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.weight(1f)) - Spacer(modifier = Modifier.height(64.dp)) + Image( + painter = painterResource(id = R.drawable.relay_sms_welcome), + contentDescription = null, + modifier = Modifier.size(200.dp) + ) - Text( - text = stringResource(R.string.welcome_to_relaysms_), - style = MaterialTheme.typography.headlineMedium, - textAlign = TextAlign.Center, - color = MaterialTheme.colorScheme.onSurface - ) + Spacer(modifier = Modifier.height(24.dp)) - Spacer(modifier = Modifier.height(16.dp)) + Text( + text = "Welcome to RelaySMS", + style = MaterialTheme.typography.headlineMedium.copy( + fontWeight = FontWeight.Bold + ), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) - Text( - text = stringResource(R.string.use_sms_to_make_a_post_send_emails_and_messages_with_no_internet_connection), - style = MaterialTheme.typography.bodyMedium, - textAlign = TextAlign.Center, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) + Spacer(modifier = Modifier.height(12.dp)) - Spacer(modifier = Modifier.weight(1f)) + Text( + text = "Send encrypted emails and online updates using SMS. No internet required.", + style = MaterialTheme.typography.bodyLarge, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.fillMaxWidth() + ) - Button( - onClick = { - navController.navigate(OnboardingInteractiveScreen) - }, - modifier = Modifier.fillMaxWidth(), - colors = ButtonDefaults.buttonColors( - MaterialTheme.colorScheme.primary) - ) { - Text(text = stringResource(R.string.learn_how_it_works_), - color = MaterialTheme.colorScheme.onPrimary) - } + Spacer(modifier = Modifier.height(50.dp)) - Spacer(modifier = Modifier.height(16.dp)) - Text( - text = stringResource(R.string.read_our_privacy_policy), - modifier = if(LocalInspectionMode.current) Modifier - else Modifier.clickable(onClick = { - val intent = Intent(Intent.ACTION_VIEW, - context.getString(R.string.https_smswithoutborders_com_privacy_policy).toUri()) - context.startActivity(intent) - }), - color = MaterialTheme.colorScheme.primary, - style = MaterialTheme.typography.bodySmall + OutlinedCard( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(16.dp), + border = BorderStroke(1.dp, MaterialTheme.colorScheme.primary), + colors = CardDefaults.outlinedCardColors( + containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.3f) ) - Spacer(modifier = Modifier.height(64.dp)) + ) { + Column( + modifier = Modifier + .padding(16.dp) + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxWidth() + ) { + Image( + painter = painterResource(id = R.drawable.lock_open_right), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(10.dp)) + Text( + text = "Zero Accounts, Zero Passwords", + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodyLarge.copy( + fontWeight = FontWeight.Bold + ) + ) + } + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "Your device is completely independent. We never ask you to register or log in.", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + } } + + Spacer(modifier = Modifier.height(80.dp)) } } -@Preview( - uiMode = Configuration.UI_MODE_NIGHT_YES, - name = "DefaultPreviewDark", - group = "Default" -) -@Preview( - uiMode = Configuration.UI_MODE_NIGHT_NO, - name = "DefaultPreviewLight", -) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "DefaultPreviewDark", group = "Default") +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, name = "DefaultPreviewLight") @Composable -fun OnboardingViewPreview() { +fun WelcomeMainViewPreview() { AppTheme { WelcomeMainView(rememberNavController()) } diff --git a/app/src/main/res/drawable/lock_open_right.xml b/app/src/main/res/drawable/lock_open_right.xml new file mode 100644 index 00000000..906a70d2 --- /dev/null +++ b/app/src/main/res/drawable/lock_open_right.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/relay_to_account.png b/app/src/main/res/drawable/relay_to_account.png new file mode 100644 index 0000000000000000000000000000000000000000..3055f538b0b988a73c220464d747f7e28f735fbd GIT binary patch literal 6065 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