diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt index 2666f779f86d8ae439b483a79932356db03d6b2b..50b275f76f3ab085c5b5643a0eace842578f3f95 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt @@ -1,6 +1,8 @@ package org.briarproject.briar.desktop.login +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.InitialFocusState.AFTER_FIRST_FOCUSSED import androidx.compose.material.InitialFocusState.AFTER_FOCUS_LOST_ONCE @@ -9,6 +11,7 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment.Companion.Center import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusRequester @@ -19,6 +22,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.unit.dp import org.briarproject.briar.desktop.login.RegistrationViewModel.State.CREATED import org.briarproject.briar.desktop.login.RegistrationViewModel.State.CREATING import org.briarproject.briar.desktop.login.RegistrationViewModel.State.INSERT_NICKNAME @@ -113,6 +117,13 @@ fun PasswordForm( val initialFocusRequester = remember { FocusRequester() } val focusManager = LocalFocusManager.current + Box( + modifier = Modifier.fillMaxWidth().requiredHeight(24.dp), + contentAlignment = Center + ) { + if (password.isNotEmpty()) + StrengthMeter(passwordStrength, Modifier.fillMaxWidth()) + } OutlinedTextField( value = password, onValueChange = setPassword, diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/StrengthMeter.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/StrengthMeter.kt new file mode 100644 index 0000000000000000000000000000000000000000..0207c290cff1312566acbcaae88847fb96b795d2 --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/login/StrengthMeter.kt @@ -0,0 +1,51 @@ +package org.briarproject.briar.desktop.login + +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.material.LinearProgressIndicator +import androidx.compose.material.ProgressIndicatorDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRONG +import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK +import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.STRONG +import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.WEAK +import org.briarproject.briar.desktop.utils.PreviewUtils.preview + +val RED = Color(255, 0, 0) +val ORANGE = Color(255, 160, 0) +val YELLOW = Color(255, 255, 0) +val LIME = Color(180, 255, 0) +val GREEN = Color(0, 255, 0) + +fun main() = preview( + "strength" to 0f +) { + StrengthMeter(getFloatParameter("strength")) +} + +@Composable +fun StrengthMeter( + strength: Float, + modifier: Modifier = Modifier +) { + val color = when { + strength < WEAK -> RED + strength < QUITE_WEAK -> ORANGE + strength < QUITE_STRONG -> YELLOW + strength < STRONG -> GREEN + else -> LIME + } + val animatedProgress by animateFloatAsState( + targetValue = strength, + animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec + ) + val animatedColor by animateColorAsState(color) + LinearProgressIndicator( + progress = animatedProgress, + color = animatedColor, + modifier = modifier + ) +} diff --git a/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt b/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt index 967b0e4bb606c32e9f123642e1f3ef23abc84569..032a405e1adba1dc4d28844a5e66655878bb8dbf 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt @@ -64,6 +64,10 @@ object PreviewUtils { fun setLongParameter(name: String, value: Long) = setDatatype(name, value) + fun getFloatParameter(name: String) = getDatatype<Float>(name) + + fun setFloatParameter(name: String, value: Float) = setDatatype(name, value) + fun getRandomId() = random.nextBytes(UniqueId.LENGTH) @Composable @@ -110,6 +114,11 @@ object PreviewUtils { BasicTextField(value.value.toString(), { value.value = it.toLong() }) } + @Composable + private fun PreviewScope.addFloatParameter(name: String, initial: Float) = addParameter(name, initial) { value -> + BasicTextField(value.value.toString(), { value.value = it.toFloat() }) + } + /** * Open an interactive preview of the composable specified by [content]. * All [parameters] passed to this function will be changeable on the fly. @@ -132,6 +141,7 @@ object PreviewUtils { is Boolean -> scope.addBooleanParameter(name, initial) is Int -> scope.addIntParameter(name, initial) is Long -> scope.addLongParameter(name, initial) + is Float -> scope.addFloatParameter(name, initial) else -> throw IllegalArgumentException("Type ${initial::class.simpleName} is not supported for previewing.") } }