Spruce up the welcome and permission screens in regV5.
@@ -578,6 +578,7 @@ dependencies {
|
||||
implementation(project(":core:models"))
|
||||
implementation(project(":core:models-jvm"))
|
||||
implementation(project(":feature:camera"))
|
||||
implementation(project(":feature:registration"))
|
||||
|
||||
implementation(libs.androidx.fragment.ktx)
|
||||
implementation(libs.androidx.appcompat) {
|
||||
|
||||
@@ -25,7 +25,7 @@ import androidx.compose.ui.unit.dp
|
||||
import org.signal.core.ui.compose.Buttons
|
||||
import org.signal.core.ui.compose.DayNightPreviews
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.signal.registration.R
|
||||
import org.thoughtcrime.securesms.registration.ui.shared.RegistrationScreen
|
||||
|
||||
/**
|
||||
|
||||
@@ -2770,8 +2770,6 @@
|
||||
<string name="RegistrationActivity_i_understand">I understand</string>
|
||||
<string name="RegistrationActivity_play_services_error">Play Services Error</string>
|
||||
<string name="RegistrationActivity_google_play_services_is_updating_or_unavailable">Google Play Services is updating or temporarily unavailable. Please try again.</string>
|
||||
<!-- Non-English translations should use "Signal is a nonprofit" instead, dropping the 501c3 reference. -->
|
||||
<string name="RegistrationActivity_terms_and_privacy">Signal is a 501c3 nonprofit\nTerms & Privacy Policy</string>
|
||||
<string name="RegistrationActivity_signal_needs_access_to_your_contacts_and_media_in_order_to_connect_with_friends">Signal needs the contacts and media permissions to help you connect with friends and send messages. Your contacts are uploaded using Signal\'s private contact discovery, which means they are end-to-end encrypted and never visible to the Signal service.</string>
|
||||
<string name="RegistrationActivity_signal_needs_access_to_your_contacts_in_order_to_connect_with_friends">Signal needs the contacts permission to help you connect with friends. Your contacts are uploaded using Signal\'s private contact discovery, which means they are end-to-end encrypted and never visible to the Signal service.</string>
|
||||
<string name="RegistrationActivity_rate_limited_to_service">You\'ve made too many attempts to register this number. Please try again later.</string>
|
||||
@@ -2804,8 +2802,6 @@
|
||||
<!-- Dialog button to cancel the pending action and return to the previous state. -->
|
||||
<string name="RegistrationActivity_cancel">Cancel</string>
|
||||
<string name="RegistrationActivity_next">Next</string>
|
||||
<string name="RegistrationActivity_continue">Continue</string>
|
||||
<string name="RegistrationActivity_take_privacy_with_you_be_yourself_in_every_message">Take privacy with you.\nBe yourself in every message.</string>
|
||||
<!-- Title of registration screen when asking for the users phone number -->
|
||||
<string name="RegistrationActivity_phone_number">Phone number</string>
|
||||
<!-- Subtitle of registration screen when asking for the users phone number -->
|
||||
@@ -4341,31 +4337,6 @@
|
||||
<!-- Alert dialog button to update now -->
|
||||
<string name="PaymentsHomeFragment__update_now">Update now</string>
|
||||
|
||||
<!-- GrantPermissionsFragment -->
|
||||
<!-- Displayed as a text-only action button at the bottom start of the screen -->
|
||||
<string name="GrantPermissionsFragment__not_now">Not now</string>
|
||||
<!-- Displayed as an action button at the bottom end of the screen -->
|
||||
<string name="GrantPermissionsFragment__next">Next</string>
|
||||
<!-- Displayed as a title at the top of the screen -->
|
||||
<string name="GrantPermissionsFragment__allow_permissions">Allow permissions</string>
|
||||
<!-- Displayed as a subtitle at the top of the screen -->
|
||||
<string name="GrantPermissionsFragment__to_help_you_message_people_you_know">To help you message people you know, Signal will request these permissions. </string>
|
||||
<!-- Notifications permission row title -->
|
||||
<string name="GrantPermissionsFragment__notifications">Notifications</string>
|
||||
<!-- Notifications permission row description -->
|
||||
<string name="GrantPermissionsFragment__get_notified_when">Get notified when new messages arrive.</string>
|
||||
<!-- Contacts permission row title -->
|
||||
<string name="GrantPermissionsFragment__contacts">Contacts</string>
|
||||
<!-- Contacts permission row description -->
|
||||
<string name="GrantPermissionsFragment__find_people_you_know">Find people you know. Your contacts are encrypted and not visible to the Signal service.</string>
|
||||
<!-- Phone calls permission row title -->
|
||||
<string name="GrantPermissionsFragment__phone_calls">Phone calls</string>
|
||||
<!-- Phone calls permission row description -->
|
||||
<string name="GrantPermissionsFragment__make_registering_easier">Make registering easier and enable additional calling features.</string>
|
||||
<!-- Storage permission row title -->
|
||||
<string name="GrantPermissionsFragment__storage">Storage</string>
|
||||
<!-- Storage permission row description -->
|
||||
<string name="GrantPermissionsFragment__send_photos_videos_and_files">Send photos, videos and files from your device.</string>
|
||||
|
||||
|
||||
<!-- PaymentsSecuritySetupFragment -->
|
||||
@@ -4828,8 +4799,6 @@
|
||||
<string name="backup_enable_dialog__i_have_written_down_this_passphrase">I have written down this passphrase. Without it, I will be unable to restore a backup.</string>
|
||||
<string name="registration_activity__restore_backup">Restore backup</string>
|
||||
<string name="registration_activity__transfer_or_restore_account">Transfer or restore account</string>
|
||||
<!-- Screen title for restoring or transfering account -->
|
||||
<string name="registration_activity__restore_or_transfer">Restore or transfer</string>
|
||||
<string name="registration_activity__transfer_account">Transfer account</string>
|
||||
<string name="registration_activity__skip">Skip</string>
|
||||
<!-- Text label button to skip restoring -->
|
||||
|
||||
@@ -8,27 +8,31 @@
|
||||
package org.signal.registration.screens.permissions
|
||||
|
||||
import android.Manifest
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.material3.Button
|
||||
import androidx.compose.foundation.layout.size
|
||||
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.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.MultiplePermissionsState
|
||||
import org.signal.core.ui.compose.Buttons
|
||||
import org.signal.core.ui.compose.DayNightPreviews
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.signal.registration.R
|
||||
import org.signal.registration.screens.shared.RegistrationScreen
|
||||
import org.signal.registration.screens.util.MockMultiplePermissionsState
|
||||
import org.signal.registration.screens.util.MockPermissionsState
|
||||
import org.signal.registration.test.TestTags
|
||||
@@ -38,7 +42,7 @@ import org.signal.registration.test.TestTags
|
||||
* Requests necessary runtime permissions before continuing.
|
||||
*
|
||||
* @param permissionsState The permissions state managed at the activity level.
|
||||
* @param onEvent Callback for screen events.
|
||||
* @param onProceed Callback invoked when the user proceeds (either granting or skipping).
|
||||
* @param modifier Modifier to be applied to the root container.
|
||||
*/
|
||||
@Composable
|
||||
@@ -47,102 +51,60 @@ fun PermissionsScreen(
|
||||
onProceed: () -> Unit = {},
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
val permissions = permissionsState.permissions.map { it.permission }
|
||||
|
||||
RegistrationScreen(
|
||||
title = stringResource(id = R.string.GrantPermissionsFragment__allow_permissions),
|
||||
subtitle = stringResource(id = R.string.GrantPermissionsFragment__to_help_you_message_people_you_know),
|
||||
modifier = modifier.testTag(TestTags.PERMISSIONS_SCREEN),
|
||||
bottomContent = {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
TextButton(
|
||||
modifier = Modifier
|
||||
.weight(weight = 1f, fill = false)
|
||||
.testTag(TestTags.PERMISSIONS_NOT_NOW_BUTTON),
|
||||
onClick = onProceed
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.GrantPermissionsFragment__not_now)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.size(24.dp))
|
||||
|
||||
Buttons.LargeTonal(
|
||||
modifier = Modifier.testTag(TestTags.PERMISSIONS_NEXT_BUTTON),
|
||||
onClick = {
|
||||
permissionsState.launchMultiplePermissionRequest()
|
||||
onProceed()
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.GrantPermissionsFragment__next)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = "Permissions",
|
||||
style = MaterialTheme.typography.headlineLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = "Signal needs the following permissions to provide the best experience:",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
PermissionsList(permissions = permissionsState.permissions.map { it.permission })
|
||||
|
||||
Spacer(modifier = Modifier.height(48.dp))
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
permissionsState.launchMultiplePermissionRequest()
|
||||
onProceed()
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(TestTags.PERMISSIONS_NEXT_BUTTON)
|
||||
) {
|
||||
Text("Next")
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
onClick = { onProceed() },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(TestTags.PERMISSIONS_NOT_NOW_BUTTON)
|
||||
) {
|
||||
Text("Not now")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a list of permission explanations.
|
||||
*/
|
||||
@Composable
|
||||
private fun PermissionsList(
|
||||
permissions: List<String>,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
val permissionDescriptions = getPermissionDescriptions(permissions)
|
||||
permissionDescriptions.forEach { description ->
|
||||
PermissionItem(description = description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual permission item with description.
|
||||
*/
|
||||
@Composable
|
||||
private fun PermissionItem(
|
||||
description: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Text(
|
||||
text = "• $description",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts permission names to user-friendly descriptions.
|
||||
*/
|
||||
private fun getPermissionDescriptions(permissions: List<String>): List<String> {
|
||||
return buildList {
|
||||
if (permissions.any { it == Manifest.permission.POST_NOTIFICATIONS }) {
|
||||
add("Notifications - Stay updated with new messages")
|
||||
PermissionRow(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.permission_notification),
|
||||
title = stringResource(id = R.string.GrantPermissionsFragment__notifications),
|
||||
subtitle = stringResource(id = R.string.GrantPermissionsFragment__get_notified_when)
|
||||
)
|
||||
}
|
||||
|
||||
if (permissions.any { it == Manifest.permission.READ_CONTACTS || it == Manifest.permission.WRITE_CONTACTS }) {
|
||||
add("Contacts - Find friends who use Signal")
|
||||
PermissionRow(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.permission_contact),
|
||||
title = stringResource(id = R.string.GrantPermissionsFragment__contacts),
|
||||
subtitle = stringResource(id = R.string.GrantPermissionsFragment__find_people_you_know)
|
||||
)
|
||||
}
|
||||
|
||||
if (permissions.any {
|
||||
it == Manifest.permission.READ_EXTERNAL_STORAGE ||
|
||||
it == Manifest.permission.WRITE_EXTERNAL_STORAGE ||
|
||||
@@ -151,14 +113,54 @@ private fun getPermissionDescriptions(permissions: List<String>): List<String> {
|
||||
it == Manifest.permission.READ_MEDIA_AUDIO
|
||||
}
|
||||
) {
|
||||
add("Photos and media - Share images and videos")
|
||||
PermissionRow(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.permission_file),
|
||||
title = stringResource(id = R.string.GrantPermissionsFragment__storage),
|
||||
subtitle = stringResource(id = R.string.GrantPermissionsFragment__send_photos_videos_and_files)
|
||||
)
|
||||
}
|
||||
|
||||
if (permissions.any { it == Manifest.permission.READ_PHONE_STATE || it == Manifest.permission.READ_PHONE_NUMBERS }) {
|
||||
add("Phone - Verify your phone number")
|
||||
PermissionRow(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.permission_phone),
|
||||
title = stringResource(id = R.string.GrantPermissionsFragment__phone_calls),
|
||||
subtitle = stringResource(id = R.string.GrantPermissionsFragment__make_registering_easier)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PermissionRow(
|
||||
imageVector: ImageVector,
|
||||
title: String,
|
||||
subtitle: String
|
||||
) {
|
||||
Row(modifier = Modifier.padding(bottom = 32.dp)) {
|
||||
Image(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(48.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.size(16.dp))
|
||||
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
)
|
||||
|
||||
Text(
|
||||
text = subtitle,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.size(32.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
private fun PermissionsScreenPreview() {
|
||||
|
||||
@@ -76,7 +76,7 @@ fun PhoneNumberScreen(
|
||||
)
|
||||
}
|
||||
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
Box(modifier = modifier.fillMaxSize().testTag(TestTags.PHONE_NUMBER_SCREEN)) {
|
||||
ScreenContent(state, onEvent)
|
||||
|
||||
if (state.showFullScreenSpinner) {
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.registration.screens.shared
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.signal.core.ui.compose.DayNightPreviews
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.signal.core.ui.compose.horizontalGutters
|
||||
|
||||
/**
|
||||
* A base framework for rendering the various registration screens.
|
||||
*/
|
||||
@Composable
|
||||
fun RegistrationScreen(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
bottomContent: @Composable (BoxScope.() -> Unit),
|
||||
modifier: Modifier = Modifier,
|
||||
mainContent: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
Surface(modifier = modifier) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(scrollState)
|
||||
.weight(weight = 1f, fill = false)
|
||||
.padding(bottom = 16.dp)
|
||||
.horizontalGutters()
|
||||
) {
|
||||
Spacer(Modifier.height(40.dp))
|
||||
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Text(
|
||||
text = subtitle,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
|
||||
mainContent()
|
||||
}
|
||||
|
||||
Surface(
|
||||
shadowElevation = if (scrollState.canScrollForward) 8.dp else 0.dp,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp, bottom = 24.dp)
|
||||
.horizontalGutters()
|
||||
) {
|
||||
bottomContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
private fun RegistrationScreenPreview() {
|
||||
Previews.Preview {
|
||||
RegistrationScreen(
|
||||
title = "Title",
|
||||
subtitle = "Subtitle",
|
||||
bottomContent = {
|
||||
TextButton(onClick = {}) {
|
||||
Text("Bottom Button")
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text("Main content")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,19 +7,19 @@
|
||||
|
||||
package org.signal.registration.screens.welcome
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -29,14 +29,20 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.testTag
|
||||
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 kotlinx.coroutines.CoroutineScope
|
||||
import org.signal.core.ui.compose.BottomSheets
|
||||
import org.signal.core.ui.compose.Buttons
|
||||
import org.signal.core.ui.compose.DayNightPreviews
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.signal.core.ui.compose.dismissWithAnimation
|
||||
import org.signal.core.ui.compose.theme.SignalTheme
|
||||
import org.signal.registration.R
|
||||
import org.signal.registration.test.TestTags
|
||||
|
||||
/**
|
||||
@@ -53,34 +59,70 @@ fun WelcomeScreen(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
.testTag(TestTags.WELCOME_SCREEN),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = "Welcome to Signal",
|
||||
style = MaterialTheme.typography.headlineLarge,
|
||||
textAlign = TextAlign.Center
|
||||
Image(
|
||||
painter = painterResource(R.drawable.welcome),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
contentScale = ContentScale.Fit
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(48.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.RegistrationActivity_take_privacy_with_you_be_yourself_in_every_message),
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 32.dp)
|
||||
.testTag(TestTags.WELCOME_HEADLINE)
|
||||
)
|
||||
|
||||
Button(
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
|
||||
TextButton(
|
||||
onClick = { /* Terms & Privacy link */ },
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.RegistrationActivity_terms_and_privacy),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Buttons.LargeTonal(
|
||||
onClick = { onEvent(WelcomeScreenEvents.Continue) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 32.dp)
|
||||
.testTag(TestTags.WELCOME_GET_STARTED_BUTTON)
|
||||
) {
|
||||
Text("Get Started")
|
||||
Text(stringResource(R.string.RegistrationActivity_continue))
|
||||
}
|
||||
OutlinedButton(
|
||||
|
||||
Spacer(modifier = Modifier.height(17.dp))
|
||||
|
||||
Buttons.LargeTonal(
|
||||
onClick = { showBottomSheet = true },
|
||||
colors = ButtonDefaults.filledTonalButtonColors(
|
||||
containerColor = SignalTheme.colors.colorSurface2
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 32.dp)
|
||||
.testTag(TestTags.WELCOME_RESTORE_OR_TRANSFER_BUTTON)
|
||||
) {
|
||||
Text("Restore or transfer")
|
||||
Text(stringResource(R.string.registration_activity__restore_or_transfer))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(48.dp))
|
||||
}
|
||||
|
||||
if (showBottomSheet) {
|
||||
@@ -130,9 +172,9 @@ private fun RestoreOrTransferBottomSheetContent(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp, vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Button(
|
||||
Buttons.LargeTonal(
|
||||
onClick = {
|
||||
sheetState.dismissWithAnimation(scope) {
|
||||
onEvent(WelcomeScreenEvents.HasOldPhone)
|
||||
@@ -145,9 +187,10 @@ private fun RestoreOrTransferBottomSheetContent(
|
||||
Text("I have my old phone")
|
||||
}
|
||||
|
||||
Button(
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Buttons.LargeTonal(
|
||||
onClick = {
|
||||
onEvent(WelcomeScreenEvents.DoesNotHaveOldPhone)
|
||||
sheetState.dismissWithAnimation(scope) {
|
||||
onEvent(WelcomeScreenEvents.DoesNotHaveOldPhone)
|
||||
}
|
||||
|
||||
@@ -11,16 +11,20 @@ package org.signal.registration.test
|
||||
object TestTags {
|
||||
|
||||
// Welcome Screen
|
||||
const val WELCOME_SCREEN = "welcome_screen"
|
||||
const val WELCOME_HEADLINE = "welcome_headline"
|
||||
const val WELCOME_GET_STARTED_BUTTON = "welcome_get_started_button"
|
||||
const val WELCOME_RESTORE_OR_TRANSFER_BUTTON = "welcome_restore_or_transfer_button"
|
||||
const val WELCOME_RESTORE_HAS_OLD_PHONE_BUTTON = "welcome_restore_has_old_phone_button"
|
||||
const val WELCOME_RESTORE_NO_OLD_PHONE_BUTTON = "welcome_restore_no_old_phone_button"
|
||||
|
||||
// Permissions Screen
|
||||
const val PERMISSIONS_SCREEN = "permissions_screen"
|
||||
const val PERMISSIONS_NEXT_BUTTON = "permissions_next_button"
|
||||
const val PERMISSIONS_NOT_NOW_BUTTON = "permissions_not_now_button"
|
||||
|
||||
// Phone Number Screen
|
||||
const val PHONE_NUMBER_SCREEN = "phone_number_screen"
|
||||
const val PHONE_NUMBER_COUNTRY_PICKER = "phone_number_country_picker"
|
||||
const val PHONE_NUMBER_COUNTRY_CODE_FIELD = "phone_number_country_code_field"
|
||||
const val PHONE_NUMBER_PHONE_FIELD = "phone_number_phone_field"
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
35
feature/registration/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="RegistrationActivity_take_privacy_with_you_be_yourself_in_every_message">Take privacy with you.\nBe yourself in every message.</string>
|
||||
<!-- Non-English translations should use "Signal is a nonprofit" instead, dropping the 501c3 reference. -->
|
||||
<string name="RegistrationActivity_terms_and_privacy">Signal is a 501c3 nonprofit\nTerms & Privacy Policy</string>
|
||||
<string name="RegistrationActivity_continue">Continue</string>
|
||||
<!-- Screen title for restoring or transfering account -->
|
||||
<string name="registration_activity__restore_or_transfer">Restore or transfer</string>
|
||||
|
||||
<!-- GrantPermissionsFragment -->
|
||||
<!-- Displayed as a title at the top of the screen -->
|
||||
<string name="GrantPermissionsFragment__allow_permissions">Allow permissions</string>
|
||||
<!-- Displayed as a subtitle at the top of the screen -->
|
||||
<string name="GrantPermissionsFragment__to_help_you_message_people_you_know">To help you message people you know, Signal will request these permissions. </string>
|
||||
<!-- Displayed as a text-only action button at the bottom start of the screen -->
|
||||
<string name="GrantPermissionsFragment__not_now">Not now</string>
|
||||
<!-- Displayed as an action button at the bottom end of the screen -->
|
||||
<string name="GrantPermissionsFragment__next">Next</string>
|
||||
<!-- Notifications permission row title -->
|
||||
<string name="GrantPermissionsFragment__notifications">Notifications</string>
|
||||
<!-- Notifications permission row description -->
|
||||
<string name="GrantPermissionsFragment__get_notified_when">Get notified when new messages arrive.</string>
|
||||
<!-- Contacts permission row title -->
|
||||
<string name="GrantPermissionsFragment__contacts">Contacts</string>
|
||||
<!-- Contacts permission row description -->
|
||||
<string name="GrantPermissionsFragment__find_people_you_know">Find people you know. Your contacts are encrypted and not visible to the Signal service.</string>
|
||||
<!-- Phone calls permission row title -->
|
||||
<string name="GrantPermissionsFragment__phone_calls">Phone calls</string>
|
||||
<!-- Phone calls permission row description -->
|
||||
<string name="GrantPermissionsFragment__make_registering_easier">Make registering easier and enable additional calling features.</string>
|
||||
<!-- Storage permission row title -->
|
||||
<string name="GrantPermissionsFragment__storage">Storage</string>
|
||||
<!-- Storage permission row description -->
|
||||
<string name="GrantPermissionsFragment__send_photos_videos_and_files">Send photos, videos and files from your device.</string>
|
||||
</resources>
|
||||
@@ -10,7 +10,6 @@ import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
@@ -67,7 +66,7 @@ class RegistrationNavigationTest {
|
||||
}
|
||||
|
||||
// Then - verify Welcome screen is displayed
|
||||
composeTestRule.onNodeWithText("Welcome to Signal").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithTag(TestTags.WELCOME_SCREEN).assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,7 +88,7 @@ class RegistrationNavigationTest {
|
||||
composeTestRule.onNodeWithTag(TestTags.WELCOME_GET_STARTED_BUTTON).performClick()
|
||||
|
||||
// Then - verify Permissions screen is displayed
|
||||
composeTestRule.onNodeWithText("Permissions").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithTag(TestTags.PERMISSIONS_SCREEN).assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -114,7 +113,7 @@ class RegistrationNavigationTest {
|
||||
composeTestRule.onNodeWithTag(TestTags.PERMISSIONS_NEXT_BUTTON).performClick()
|
||||
|
||||
// Then - verify PhoneNumber screen is displayed
|
||||
composeTestRule.onNodeWithText("You will receive a verification code").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithTag(TestTags.PHONE_NUMBER_SCREEN).assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -139,7 +138,7 @@ class RegistrationNavigationTest {
|
||||
composeTestRule.onNodeWithTag(TestTags.PERMISSIONS_NOT_NOW_BUTTON).performClick()
|
||||
|
||||
// Then - verify PhoneNumber screen is displayed
|
||||
composeTestRule.onNodeWithText("You will receive a verification code").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithTag(TestTags.PHONE_NUMBER_SCREEN).assertIsDisplayed()
|
||||
}
|
||||
|
||||
// Note: Back navigation testing in Navigation 3 requires testing through
|
||||
@@ -168,7 +167,7 @@ class RegistrationNavigationTest {
|
||||
|
||||
// Then - verify Permissions screen is displayed
|
||||
// (After permissions, user would go to RestoreViaQr screen)
|
||||
composeTestRule.onNodeWithText("Permissions").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithTag(TestTags.PERMISSIONS_SCREEN).assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -10,7 +10,6 @@ import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -130,7 +129,7 @@ class WelcomeScreenTest {
|
||||
}
|
||||
|
||||
// Then
|
||||
composeTestRule.onNodeWithText("Welcome to Signal").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithTag(TestTags.WELCOME_HEADLINE).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithTag(TestTags.WELCOME_GET_STARTED_BUTTON).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithTag(TestTags.WELCOME_RESTORE_OR_TRANSFER_BUTTON).assertIsDisplayed()
|
||||
}
|
||||
|
||||