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 \n 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. " )
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 )
}