Implement password verification and enhance BackupScreen UI
- Added password verification logic in MainActivity to decrypt the private key and seed phrase. - Updated BackupScreen to handle password verification asynchronously using coroutines. - Improved UI layout for displaying seed phrases in two columns with colored words. - Added a copy button for the seed phrase with clipboard functionality. - Introduced a new composable function, WordItem, for better visual representation of seed phrase words.
This commit is contained in:
1
app/src/main/assets/lottie/shush.json
Normal file
1
app/src/main/assets/lottie/shush.json
Normal file
File diff suppressed because one or more lines are too long
@@ -542,10 +542,35 @@ fun MainScreen(
|
||||
showSafetyScreen = true
|
||||
},
|
||||
onVerifyPassword = { password ->
|
||||
// TODO: Implement password verification
|
||||
if (password == "test") {
|
||||
"word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12"
|
||||
} else null
|
||||
// Verify password by trying to decrypt the private key
|
||||
try {
|
||||
val publicKey = account?.publicKey ?: return@BackupScreen null
|
||||
val accountManager = AccountManager(context)
|
||||
val encryptedAccount = accountManager.getAccount(publicKey)
|
||||
|
||||
if (encryptedAccount != null) {
|
||||
// Try to decrypt private key with password
|
||||
val decryptedPrivateKey = com.rosetta.messenger.crypto.CryptoManager.decryptWithPassword(
|
||||
encryptedAccount.encryptedPrivateKey,
|
||||
password
|
||||
)
|
||||
|
||||
if (decryptedPrivateKey != null) {
|
||||
// Password is correct, decrypt seed phrase
|
||||
com.rosetta.messenger.crypto.CryptoManager.decryptWithPassword(
|
||||
encryptedAccount.encryptedSeedPhrase,
|
||||
password
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("MainActivity", "Error verifying password", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,9 +9,13 @@ import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ContentCopy
|
||||
import androidx.compose.material.icons.filled.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
@@ -26,18 +30,21 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.coroutines.launch
|
||||
import com.airbnb.lottie.compose.*
|
||||
|
||||
@Composable
|
||||
fun BackupScreen(
|
||||
isDarkTheme: Boolean,
|
||||
onBack: () -> Unit,
|
||||
onVerifyPassword: (String) -> String? // Returns seed phrase if password is correct
|
||||
onVerifyPassword: suspend (String) -> String? // Returns seed phrase if password is correct
|
||||
) {
|
||||
val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF)
|
||||
val surfaceColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color(0xFFF2F2F7)
|
||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
var password by remember { mutableStateOf("") }
|
||||
var seedPhrase by remember { mutableStateOf<String?>(null) }
|
||||
var passwordVisible by remember { mutableStateOf(false) }
|
||||
@@ -91,11 +98,15 @@ fun BackupScreen(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Warning,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFFFFAC00),
|
||||
modifier = Modifier.size(48.dp)
|
||||
val composition by rememberLottieComposition(LottieCompositionSpec.Asset("lottie/shush.json"))
|
||||
val progress by animateLottieCompositionAsState(
|
||||
composition = composition,
|
||||
iterations = 1
|
||||
)
|
||||
LottieAnimation(
|
||||
composition = composition,
|
||||
progress = { progress },
|
||||
modifier = Modifier.size(80.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Text(
|
||||
@@ -128,9 +139,11 @@ fun BackupScreen(
|
||||
onValueChange = { newValue ->
|
||||
password = newValue
|
||||
// Try to verify password
|
||||
val result = onVerifyPassword(newValue)
|
||||
if (result != null) {
|
||||
seedPhrase = result
|
||||
scope.launch {
|
||||
val result = onVerifyPassword(newValue)
|
||||
if (result != null) {
|
||||
seedPhrase = result
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
@@ -138,6 +151,7 @@ fun BackupScreen(
|
||||
color = textColor,
|
||||
fontSize = 15.sp
|
||||
),
|
||||
cursorBrush = SolidColor(textColor),
|
||||
visualTransformation = if (passwordVisible)
|
||||
VisualTransformation.None
|
||||
else
|
||||
@@ -182,33 +196,80 @@ fun BackupScreen(
|
||||
lineHeight = 20.sp
|
||||
)
|
||||
} else {
|
||||
// Seed Phrase Display
|
||||
Surface(
|
||||
// Seed Phrase Display with colored words
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
val words = seedPhrase!!.split(" ")
|
||||
|
||||
// Two column layout like in SeedPhraseScreen
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = surfaceColor,
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(20.dp)) {
|
||||
val words = seedPhrase!!.split(" ")
|
||||
words.chunked(3).forEach { rowWords ->
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
rowWords.forEachIndexed { index, word ->
|
||||
Text(
|
||||
text = "${words.indexOf(word) + 1}. $word",
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = textColor,
|
||||
modifier = Modifier.padding(vertical = 4.dp)
|
||||
)
|
||||
}
|
||||
// Left column (words 1-6)
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
for (i in 0..5) {
|
||||
if (i < words.size) {
|
||||
WordItem(
|
||||
number = i + 1,
|
||||
word = words[i],
|
||||
isDarkTheme = isDarkTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Right column (words 7-12)
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
for (i in 6..11) {
|
||||
if (i < words.size) {
|
||||
WordItem(
|
||||
number = i + 1,
|
||||
word = words[i],
|
||||
isDarkTheme = isDarkTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// Copy button
|
||||
val context = androidx.compose.ui.platform.LocalContext.current
|
||||
Button(
|
||||
onClick = {
|
||||
val clipboard = context.getSystemService(android.content.Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
|
||||
val clip = android.content.ClipData.newPlainText("Seed Phrase", seedPhrase)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color(0xFF248AE6)
|
||||
),
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ContentCopy,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Copy Seed Phrase",
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = "Please don't share your seed phrase! The administration will never ask you for it.",
|
||||
fontSize = 14.sp,
|
||||
@@ -222,3 +283,59 @@ fun BackupScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WordItem(
|
||||
number: Int,
|
||||
word: String,
|
||||
isDarkTheme: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val numberColor = if (isDarkTheme) Color(0xFF888888) else Color(0xFF999999)
|
||||
|
||||
// Beautiful solid colors that fit the theme
|
||||
val wordColors = listOf(
|
||||
Color(0xFF5E9FFF), // Soft blue
|
||||
Color(0xFFFF7EB3), // Soft pink
|
||||
Color(0xFF7B68EE), // Medium purple
|
||||
Color(0xFF50C878), // Emerald green
|
||||
Color(0xFFFF6B6B), // Coral red
|
||||
Color(0xFF4ECDC4), // Teal
|
||||
Color(0xFFFFB347), // Pastel orange
|
||||
Color(0xFFBA55D3), // Medium orchid
|
||||
Color(0xFF87CEEB), // Sky blue
|
||||
Color(0xFFDDA0DD), // Plum
|
||||
Color(0xFF98D8C8), // Mint
|
||||
Color(0xFFF7DC6F) // Soft yellow
|
||||
)
|
||||
|
||||
val wordColor = wordColors[(number - 1) % wordColors.size]
|
||||
val bgColor = if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFFF5F5F5)
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(bgColor)
|
||||
.padding(horizontal = 16.dp, vertical = 14.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = "$number.",
|
||||
fontSize = 15.sp,
|
||||
color = numberColor,
|
||||
modifier = Modifier.width(28.dp)
|
||||
)
|
||||
Text(
|
||||
text = word,
|
||||
fontSize = 17.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = wordColor,
|
||||
fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user