diff --git a/app/build.gradle b/app/build.gradle index 7fa2d15fd5..85a3206e6a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath "com.android.tools.build:gradle:$gradlePluginVersion" classpath files('libs/gradle-witness.jar') classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion" diff --git a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt index a99b40f36f..d5db6ae041 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt @@ -14,6 +14,7 @@ import androidx.annotation.LayoutRes import androidx.annotation.StringRes import androidx.annotation.StyleRes import androidx.appcompat.app.AlertDialog +import androidx.core.text.HtmlCompat import androidx.core.view.setMargins import androidx.core.view.setPadding import androidx.core.view.updateMargins @@ -21,7 +22,6 @@ import androidx.fragment.app.Fragment import network.loki.messenger.R import org.thoughtcrime.securesms.util.toPx - @DslMarker @Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) annotation class DialogDsl @@ -64,7 +64,6 @@ class SessionDialogBuilder(val context: Context) { } } - private fun text(text: CharSequence?, @StyleRes style: Int, modify: TextView.() -> Unit) { text ?: return TextView(context, null, 0, style) @@ -75,6 +74,8 @@ class SessionDialogBuilder(val context: Context) { }.let(topView::addView) } + fun htmlText(@StringRes id: Int, @StyleRes style: Int = 0, modify: TextView.() -> Unit = {}) { text(context.resources.getText(id)) } + fun view(view: View) = contentView.addView(view) fun view(@LayoutRes layout: Int): View = LayoutInflater.from(context).inflate(layout, contentView) diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/LoadingActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LoadingActivity.kt index e68fcb541a..523bc17888 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/LoadingActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LoadingActivity.kt @@ -18,9 +18,11 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.res.stringResource import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch +import network.loki.messenger.R import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.dependencies.ConfigFactory @@ -99,8 +101,8 @@ class LoadingActivity: BaseActionBarActivity() { Column { Spacer(modifier = Modifier.weight(1f)) ProgressArc(animatable.value, modifier = Modifier.align(Alignment.CenterHorizontally)) - Text("One moment please..", modifier = Modifier.align(Alignment.CenterHorizontally), style = MaterialTheme.typography.h6) - Text("Loading your account", modifier = Modifier.align(Alignment.CenterHorizontally)) + Text(stringResource(R.string.waitOneMoment), modifier = Modifier.align(Alignment.CenterHorizontally), style = MaterialTheme.typography.h6) + Text(stringResource(R.string.loadAccountProgressMessage), modifier = Modifier.align(Alignment.CenterHorizontally)) Spacer(modifier = Modifier.weight(2f)) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt index 42fc83f05e..00b6e6c3e0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt @@ -61,16 +61,16 @@ class MessageNotificationsActivity : BaseActionBarActivity() { TextSecurePreferences.setHasSeenWelcomeScreen(this, true) ComposeView(this) - .apply { setContent { MessageNotifications() } } + .apply { setContent { MessageNotificationsScreen() } } .let(::setContentView) } @Composable - private fun MessageNotifications() { + private fun MessageNotificationsScreen() { val state by viewModel.stateFlow.collectAsState() AppTheme { - MessageNotifications(state, viewModel::setEnabled, ::register) + MessageNotificationsScreen(state, viewModel::setEnabled, ::register) } } @@ -87,25 +87,25 @@ class MessageNotificationsActivity : BaseActionBarActivity() { @Preview @Composable -fun MessageNotificationsPreview( +fun MessageNotificationsScreenPreview( @PreviewParameter(ThemeResPreviewParameterProvider::class) themeResId: Int ) { PreviewTheme(themeResId) { - MessageNotifications() + MessageNotificationsScreen() } } @Composable -fun MessageNotifications( +fun MessageNotificationsScreen( state: MessageNotificationsState = MessageNotificationsState(), setEnabled: (Boolean) -> Unit = {}, onContinue: () -> Unit = {} ) { Column(Modifier.padding(horizontal = 32.dp)) { Spacer(Modifier.weight(1f)) - Text("Message notifications", style = MaterialTheme.typography.h4) + Text(stringResource(R.string.notificationsMessage), style = MaterialTheme.typography.h4) Spacer(Modifier.height(16.dp)) - Text("There are two ways Session can notify you of new messages.") + Text(stringResource(R.string.onboardingMessageNotificationExplaination)) Spacer(Modifier.height(16.dp)) NotificationRadioButton( R.string.activity_pn_mode_fast_mode, @@ -125,8 +125,8 @@ fun MessageNotifications( OutlineButton( stringResource(R.string.continue_2), modifier = Modifier - .align(Alignment.CenterHorizontally) - .width(262.dp), + .align(Alignment.CenterHorizontally) + .width(262.dp), onClick = onContinue ) Spacer(modifier = Modifier.height(12.dp)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPasswordActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPasswordActivity.kt index a95cde5261..4c70cfe455 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPasswordActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPasswordActivity.kt @@ -7,6 +7,8 @@ import android.os.Bundle import androidx.activity.viewModels import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.Crossfade +import androidx.compose.animation.core.animateValue +import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -27,9 +29,11 @@ import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter @@ -41,6 +45,9 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import network.loki.messenger.R import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.showSessionDialog @@ -55,6 +62,7 @@ import org.thoughtcrime.securesms.ui.classicDarkColors import org.thoughtcrime.securesms.ui.colorDestructive import org.thoughtcrime.securesms.ui.h8 import org.thoughtcrime.securesms.ui.small +import kotlin.time.Duration.Companion.seconds class RecoveryPasswordActivity : BaseActionBarActivity() { @@ -73,20 +81,18 @@ class RecoveryPasswordActivity : BaseActionBarActivity() { private fun onHide() { showSessionDialog { - title("Hide Recovery Password Permanently") - text("Without your recovery password, you cannot load your account on new devices.\n" + - "\n" + - "We strongly recommend you save your recovery password in a safe and secure place before continuing.") + title(R.string.recoveryPasswordHidePermanently) + htmlText(R.string.recoveryPasswordHidePermanentlyDescription1) destructiveButton(R.string.continue_2) { onHideConfirm() } - button(R.string.cancel) {} + cancelButton() } } private fun onHideConfirm() { showSessionDialog { - title("Hide Recovery Password Permanently") - text("Are you sure you want to permanently hide your recovery password on this device? This cannot be undone.") - button(R.string.cancel) {} + title(R.string.recoveryPasswordHidePermanently) + text(R.string.recoveryPasswordHidePermanentlyDescription2) + cancelButton() destructiveButton(R.string.yes) { viewModel.permanentlyHidePassword() finish() @@ -131,31 +137,27 @@ fun RecoveryPasswordCell(seed: String = "", qrBitmap: Bitmap? = null, copySeed:( mutableStateOf(false) } - val copied = remember { - mutableStateOf(false) - } - CellWithPaddingAndMargin { Column { Row { - Text("Recovery Password") + Text(stringResource(R.string.sessionRecoveryPassword)) Spacer(Modifier.width(8.dp)) SessionShieldIcon() } - Text("Use your recovery password to load your account on new devices.\n\nYour account cannot be recovered without your recovery password. Make sure it's stored somewhere safe and secure — and don't share it with anyone.") + Text(stringResource(R.string.recoveryPasswordDescription)) AnimatedVisibility(!showQr.value) { Text( seed, modifier = Modifier - .padding(vertical = 24.dp) - .border( - width = 1.dp, - color = classicDarkColors[3], - shape = RoundedCornerShape(11.dp) - ) - .padding(24.dp), + .padding(vertical = 24.dp) + .border( + width = 1.dp, + color = classicDarkColors[3], + shape = RoundedCornerShape(11.dp) + ) + .padding(24.dp), style = MaterialTheme.typography.small.copy(fontFamily = FontFamily.Monospace), color = LocalExtraColors.current.prominentButtonColor, ) @@ -166,8 +168,8 @@ fun RecoveryPasswordCell(seed: String = "", qrBitmap: Bitmap? = null, copySeed:( backgroundColor = LocalExtraColors.current.lightCell, elevation = 0.dp, modifier = Modifier - .align(Alignment.CenterHorizontally) - .padding(vertical = 24.dp) + .align(Alignment.CenterHorizontally) + .padding(vertical = 24.dp) ) { Box { qrBitmap?.let { @@ -183,11 +185,11 @@ fun RecoveryPasswordCell(seed: String = "", qrBitmap: Bitmap? = null, copySeed:( contentDescription = "", tint = LocalExtraColors.current.onLightCell, modifier = Modifier - .align(Alignment.Center) - .width(46.dp) - .height(56.dp) - .background(color = LocalExtraColors.current.lightCell) - .padding(horizontal = 3.dp, vertical = 1.dp) + .align(Alignment.Center) + .width(46.dp) + .height(56.dp) + .background(color = LocalExtraColors.current.lightCell) + .padding(horizontal = 3.dp, vertical = 1.dp) ) } } @@ -195,16 +197,36 @@ fun RecoveryPasswordCell(seed: String = "", qrBitmap: Bitmap? = null, copySeed:( AnimatedVisibility(!showQr.value) { Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) { - Crossfade(targetState = if (copied.value) R.string.copied else R.string.copy, modifier = Modifier.weight(1f), label = "Copy to Copied CrossFade") { - OutlineButton(text = stringResource(it), modifier = Modifier.fillMaxWidth(), color = MaterialTheme.colors.onPrimary) { copySeed(); copied.value = true } + val scope = rememberCoroutineScope() + val revertCopiedTextJob = remember { mutableStateOf(null) } + val copied = remember { mutableStateOf(false) } + OutlineButton( + modifier = Modifier.weight(1f), + color = MaterialTheme.colors.onPrimary, + onClick = { + copySeed() + revertCopiedTextJob.value?.cancel() + revertCopiedTextJob.value = scope.launch { + copied.value = true + delay(2.seconds) + copied.value = false + } + } + ) { + AnimatedVisibility(!copied.value) { + Text(stringResource(R.string.copy)) + } + AnimatedVisibility(copied.value) { + Text(stringResource(R.string.copied)) + } } - OutlineButton(text = "View QR", modifier = Modifier.weight(1f), color = MaterialTheme.colors.onPrimary) { showQr.toggle() } + OutlineButton(text = stringResource(R.string.qrView), modifier = Modifier.weight(1f), color = MaterialTheme.colors.onPrimary) { showQr.toggle() } } } AnimatedVisibility(showQr.value, modifier = Modifier.align(Alignment.CenterHorizontally)) { OutlineButton( - text = "View Password", + text = stringResource(R.string.recoveryPasswordView), color = MaterialTheme.colors.onPrimary, modifier = Modifier.align(Alignment.CenterHorizontally) ) { showQr.toggle() } @@ -220,11 +242,11 @@ fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) { CellWithPaddingAndMargin { Row { Column(Modifier.weight(1f)) { - Text(text = "Hide Recovery Password", style = MaterialTheme.typography.h8) - Text(text = "Permanently hide your recovery password on this device.") + Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPassword), style = MaterialTheme.typography.h8) + Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription)) } OutlineButton( - "Hide", + stringResource(R.string.hide), modifier = Modifier.align(Alignment.CenterVertically), color = colorDestructive ) { onHide() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt index 023353477a..262b2e6e59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt @@ -84,6 +84,27 @@ fun OutlineButton( } } +@Composable +fun OutlineButton( + modifier: Modifier = Modifier, + color: Color = LocalExtraColors.current.prominentButtonColor, + onClick: () -> Unit = {}, + content: @Composable () -> Unit = {} +) { + OutlinedButton( + modifier = modifier, + onClick = onClick, + border = BorderStroke(1.dp, color), + shape = RoundedCornerShape(50), // = 50% percent + colors = ButtonDefaults.outlinedButtonColors( + contentColor = color, + backgroundColor = Color.Unspecified + ) + ) { + content() + } +} + @Composable fun FilledButton(text: String, modifier: Modifier = Modifier, onClick: () -> Unit) { OutlinedButton( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5b4a7edeed..4d476d02af 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1081,4 +1081,17 @@ Load Account Camera Permission permanently denied. Configure in settings. Enter your recovery password to load your account. If you haven\'t saved it, you can find it in your app settings. + One moment please.. + Loading your account + Message notifications + There are two ways Session can notify you of new messages. + Hide Recovery Password Permanently +
We strongly recommend you save your recovery password in a safe and secure place before continuing.]]>
+ Are you sure you want to permanently hide your recovery password on this device? This cannot be undone. + Use your recovery password to load your account on new devices. Your account cannot be recovered without your recovery password. Make sure it\'s stored somewhere safe and secure — and don\'t share it with anyone. + Hide + Hide Recovery Password + View QR + View Password + Permanently hide your recovery password on this device. diff --git a/gradle.properties b/gradle.properties index e6dd30852e..66ea0fcba8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ android.enableJetifier=true org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" org.gradle.unsafe.configuration-cache=true -gradlePluginVersion=7.3.1 +gradlePluginVersion=7.4.2 googleServicesVersion=4.3.12 kotlinVersion=1.8.21 android.useAndroidX=true