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
|
showSafetyScreen = true
|
||||||
},
|
},
|
||||||
onVerifyPassword = { password ->
|
onVerifyPassword = { password ->
|
||||||
// TODO: Implement password verification
|
// Verify password by trying to decrypt the private key
|
||||||
if (password == "test") {
|
try {
|
||||||
"word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12"
|
val publicKey = account?.publicKey ?: return@BackupScreen null
|
||||||
} else 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.layout.windowInsetsPadding
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
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.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
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.Visibility
|
||||||
import androidx.compose.material.icons.filled.VisibilityOff
|
import androidx.compose.material.icons.filled.VisibilityOff
|
||||||
import androidx.compose.material.icons.filled.Warning
|
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.text.input.VisualTransformation
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import com.airbnb.lottie.compose.*
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BackupScreen(
|
fun BackupScreen(
|
||||||
isDarkTheme: Boolean,
|
isDarkTheme: Boolean,
|
||||||
onBack: () -> Unit,
|
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 backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF)
|
||||||
val surfaceColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color(0xFFF2F2F7)
|
val surfaceColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color(0xFFF2F2F7)
|
||||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||||
|
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
var password by remember { mutableStateOf("") }
|
var password by remember { mutableStateOf("") }
|
||||||
var seedPhrase by remember { mutableStateOf<String?>(null) }
|
var seedPhrase by remember { mutableStateOf<String?>(null) }
|
||||||
var passwordVisible by remember { mutableStateOf(false) }
|
var passwordVisible by remember { mutableStateOf(false) }
|
||||||
@@ -91,11 +98,15 @@ fun BackupScreen(
|
|||||||
modifier = Modifier.padding(16.dp),
|
modifier = Modifier.padding(16.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Icon(
|
val composition by rememberLottieComposition(LottieCompositionSpec.Asset("lottie/shush.json"))
|
||||||
imageVector = Icons.Filled.Warning,
|
val progress by animateLottieCompositionAsState(
|
||||||
contentDescription = null,
|
composition = composition,
|
||||||
tint = Color(0xFFFFAC00),
|
iterations = 1
|
||||||
modifier = Modifier.size(48.dp)
|
)
|
||||||
|
LottieAnimation(
|
||||||
|
composition = composition,
|
||||||
|
progress = { progress },
|
||||||
|
modifier = Modifier.size(80.dp)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
Text(
|
Text(
|
||||||
@@ -128,16 +139,19 @@ fun BackupScreen(
|
|||||||
onValueChange = { newValue ->
|
onValueChange = { newValue ->
|
||||||
password = newValue
|
password = newValue
|
||||||
// Try to verify password
|
// Try to verify password
|
||||||
|
scope.launch {
|
||||||
val result = onVerifyPassword(newValue)
|
val result = onVerifyPassword(newValue)
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
seedPhrase = result
|
seedPhrase = result
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
textStyle = TextStyle(
|
textStyle = TextStyle(
|
||||||
color = textColor,
|
color = textColor,
|
||||||
fontSize = 15.sp
|
fontSize = 15.sp
|
||||||
),
|
),
|
||||||
|
cursorBrush = SolidColor(textColor),
|
||||||
visualTransformation = if (passwordVisible)
|
visualTransformation = if (passwordVisible)
|
||||||
VisualTransformation.None
|
VisualTransformation.None
|
||||||
else
|
else
|
||||||
@@ -182,33 +196,80 @@ fun BackupScreen(
|
|||||||
lineHeight = 20.sp
|
lineHeight = 20.sp
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Seed Phrase Display
|
// Seed Phrase Display with colored words
|
||||||
Surface(
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
color = surfaceColor,
|
|
||||||
shape = RoundedCornerShape(10.dp)
|
|
||||||
) {
|
|
||||||
Column(modifier = Modifier.padding(20.dp)) {
|
|
||||||
val words = seedPhrase!!.split(" ")
|
val words = seedPhrase!!.split(" ")
|
||||||
words.chunked(3).forEach { rowWords ->
|
|
||||||
|
// Two column layout like in SeedPhraseScreen
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
) {
|
) {
|
||||||
rowWords.forEachIndexed { index, word ->
|
// Left column (words 1-6)
|
||||||
Text(
|
Column(
|
||||||
text = "${words.indexOf(word) + 1}. $word",
|
modifier = Modifier.weight(1f),
|
||||||
fontSize = 14.sp,
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
fontWeight = FontWeight.Medium,
|
) {
|
||||||
color = textColor,
|
for (i in 0..5) {
|
||||||
modifier = Modifier.padding(vertical = 4.dp)
|
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(
|
||||||
text = "Please don't share your seed phrase! The administration will never ask you for it.",
|
text = "Please don't share your seed phrase! The administration will never ask you for it.",
|
||||||
fontSize = 14.sp,
|
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