session-android/app/src/main/java/org/thoughtcrime/securesms/onboarding/recoverypassword/RecoveryPasswordActivity.kt

245 lines
9.5 KiB
Kotlin
Raw Normal View History

2023-10-19 17:15:33 +02:00
package org.thoughtcrime.securesms.onboarding.recoverypassword
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
2023-11-16 14:50:36 +01:00
import android.graphics.Bitmap
2023-10-19 17:15:33 +02:00
import android.os.Bundle
import android.widget.Toast
2023-11-16 14:50:36 +01:00
import androidx.activity.viewModels
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
2023-10-19 17:15:33 +02:00
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
2023-11-16 14:50:36 +01:00
import androidx.compose.foundation.layout.Box
2023-10-19 17:15:33 +02:00
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
2023-11-16 14:50:36 +01:00
import androidx.compose.foundation.layout.height
2023-10-19 17:15:33 +02:00
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
2023-11-16 14:50:36 +01:00
import androidx.compose.foundation.rememberScrollState
2023-10-19 17:15:33 +02:00
import androidx.compose.foundation.shape.RoundedCornerShape
2023-11-16 14:50:36 +01:00
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Card
import androidx.compose.material.Icon
2023-10-19 17:15:33 +02:00
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
2023-11-16 14:50:36 +01:00
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
2023-10-19 17:15:33 +02:00
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
2023-11-16 14:50:36 +01:00
import androidx.compose.ui.graphics.Color
2023-11-16 15:40:27 +01:00
import androidx.compose.ui.graphics.ColorFilter
2023-11-16 14:50:36 +01:00
import androidx.compose.ui.graphics.asImageBitmap
2023-10-19 17:15:33 +02:00
import androidx.compose.ui.platform.ComposeView
2023-11-16 14:50:36 +01:00
import androidx.compose.ui.res.painterResource
2023-10-19 17:15:33 +02:00
import androidx.compose.ui.res.stringResource
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 network.loki.messenger.R
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.BaseActionBarActivity
2023-11-29 15:15:53 +01:00
import org.thoughtcrime.securesms.showSessionDialog
2023-10-19 17:15:33 +02:00
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
2023-11-16 14:50:36 +01:00
import org.thoughtcrime.securesms.ui.LocalExtraColors
2023-10-19 17:15:33 +02:00
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionShieldIcon
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.classicDarkColors
import org.thoughtcrime.securesms.ui.colorDestructive
import org.thoughtcrime.securesms.ui.h8
2023-11-16 14:50:36 +01:00
import org.thoughtcrime.securesms.ui.small
2023-10-19 17:15:33 +02:00
class RecoveryPasswordActivity : BaseActionBarActivity() {
2023-11-16 14:50:36 +01:00
private val viewModel: RecoveryPasswordViewModel by viewModels()
2023-10-19 17:15:33 +02:00
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportActionBar!!.title = resources.getString(R.string.activity_recovery_password)
2023-11-16 14:50:36 +01:00
ComposeView(this).apply {
setContent {
2023-11-29 15:15:53 +01:00
RecoveryPassword(viewModel.seed, viewModel.qrBitmap, { copySeed() }) { onHide() }
2023-11-16 14:50:36 +01:00
}
}.let(::setContentView)
2023-10-19 17:15:33 +02:00
}
private fun revealSeed() {
TextSecurePreferences.setHasViewedSeed(this, true)
}
private fun copySeed() {
revealSeed()
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
2023-11-16 14:50:36 +01:00
val clip = ClipData.newPlainText("Seed", viewModel.seed)
2023-10-19 17:15:33 +02:00
clipboard.setPrimaryClip(clip)
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
}
2023-11-29 15:15:53 +01:00
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.")
destructiveButton(R.string.continue_2) { onHideConfirm() }
button(R.string.cancel) {}
}
}
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) {}
destructiveButton(R.string.yes) {
viewModel.permanentlyHidePassword()
finish()
}
}
}
2023-10-19 17:15:33 +02:00
}
@Preview
@Composable
fun PreviewMessageDetails(
@PreviewParameter(ThemeResPreviewParameterProvider::class) themeResId: Int
) {
PreviewTheme(themeResId) {
2023-11-16 14:50:36 +01:00
RecoveryPassword(seed = "Voyage urban toyed maverick peculiar tuxedo penguin tree grass building listen speak withdraw terminal plane")
2023-10-19 17:15:33 +02:00
}
}
@Composable
2023-11-29 15:15:53 +01:00
fun RecoveryPassword(
seed: String = "",
qrBitmap: Bitmap? = null,
copySeed:() -> Unit = {},
onHide:() -> Unit = {}
) {
2023-10-19 17:15:33 +02:00
AppTheme {
2023-11-16 14:50:36 +01:00
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.verticalScroll(rememberScrollState())
.padding(bottom = 16.dp)
) {
2023-11-16 15:40:27 +01:00
RecoveryPasswordCell(seed, qrBitmap, copySeed)
2023-11-29 15:15:53 +01:00
HideRecoveryPasswordCell(onHide)
2023-10-19 17:15:33 +02:00
}
}
}
@Composable
2023-11-16 15:40:27 +01:00
fun RecoveryPasswordCell(seed: String = "", qrBitmap: Bitmap? = null, copySeed:() -> Unit = {}) {
2023-11-16 14:50:36 +01:00
val showQr = remember {
mutableStateOf(false)
}
2023-10-19 17:15:33 +02:00
CellWithPaddingAndMargin {
Column {
Row {
Text("Recovery Password")
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.")
2023-11-16 14:50:36 +01:00
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),
style = MaterialTheme.typography.small.copy(fontFamily = FontFamily.Monospace),
color = LocalExtraColors.current.prominentButtonColor,
)
}
AnimatedVisibility(showQr.value, modifier = Modifier.align(Alignment.CenterHorizontally)) {
Card(
2023-11-16 15:40:27 +01:00
backgroundColor = LocalExtraColors.current.lightCell,
elevation = 0.dp,
2023-11-16 14:50:36 +01:00
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(vertical = 24.dp)
) {
Box {
2023-11-16 15:40:27 +01:00
qrBitmap?.let {
2023-11-16 14:50:36 +01:00
Image(
bitmap = it.asImageBitmap(),
2023-11-16 15:40:27 +01:00
contentDescription = "QR code of your recovery password",
colorFilter = ColorFilter.tint(LocalExtraColors.current.onLightCell)
2023-11-16 14:50:36 +01:00
)
}
Icon(
painter = painterResource(id = R.drawable.session_shield),
contentDescription = "",
2023-11-16 15:40:27 +01:00
tint = LocalExtraColors.current.onLightCell,
2023-11-16 14:50:36 +01:00
modifier = Modifier.align(Alignment.Center)
.width(46.dp)
.height(56.dp)
2023-11-16 15:40:27 +01:00
.background(color = LocalExtraColors.current.lightCell)
2023-11-16 14:50:36 +01:00
.padding(horizontal = 3.dp, vertical = 1.dp)
)
}
}
}
AnimatedVisibility(!showQr.value) {
Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) {
OutlineButton(text = stringResource(R.string.copy), modifier = Modifier.weight(1f), color = MaterialTheme.colors.onPrimary) { copySeed() }
OutlineButton(text = "View QR", modifier = Modifier.weight(1f), color = MaterialTheme.colors.onPrimary) { showQr.toggle() }
}
}
AnimatedVisibility(showQr.value, modifier = Modifier.align(Alignment.CenterHorizontally)) {
OutlineButton(
text = "View Password",
color = MaterialTheme.colors.onPrimary,
modifier = Modifier.align(Alignment.CenterHorizontally)
) { showQr.toggle() }
2023-10-19 17:15:33 +02:00
}
}
}
}
2023-11-16 14:50:36 +01:00
private fun MutableState<Boolean>.toggle() { value = !value }
2023-10-19 17:15:33 +02:00
@Composable
2023-11-29 15:15:53 +01:00
fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) {
2023-10-19 17:15:33 +02:00
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.")
}
OutlineButton(
"Hide",
modifier = Modifier.align(Alignment.CenterVertically),
color = colorDestructive
2023-11-29 15:15:53 +01:00
) { onHide() }
2023-10-19 17:15:33 +02:00
}
}
}
fun Context.startRecoveryPasswordActivity() {
Intent(this, RecoveryPasswordActivity::class.java).also(::startActivity)
}