From 53feff04a184a1591d11b0cf3438c0b0c0efe10b Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 18 Oct 2023 17:04:23 +0200 Subject: [PATCH 01/16] Pin : start create pin view --- .../lockscreen/impl/create/CreatePinView.kt | 71 +++++++++++++++---- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt index 120c0b6079..d86c2f296d 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt @@ -14,31 +14,77 @@ * limitations under the License. */ +@file:OptIn(ExperimentalMaterial3Api::class) + package io.element.android.features.lockscreen.impl.create -import androidx.compose.foundation.layout.Box -import androidx.compose.material3.MaterialTheme +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter +import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule +import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage +import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.Text -import timber.log.Timber +import io.element.android.libraries.designsystem.theme.components.Button +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.TopAppBar @Composable fun CreatePinView( state: CreatePinState, + onBackClicked: () -> Unit, modifier: Modifier = Modifier, ) { - Timber.d("CreatePinView: $state") - Box(modifier, contentAlignment = Alignment.Center) { - Text( - "CreatePin feature view", - color = MaterialTheme.colorScheme.primary, - ) - } + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + navigationIcon = { + BackButton(onClick = onBackClicked) + }, + title = {} + ) + }, + content = { padding -> + HeaderFooterPage( + modifier = Modifier + .padding(padding) + .consumeWindowInsets(padding), + header = { CreatePinHeader() }, + footer = { CreatePinFooter() }, + ) + } + ) +} + +@Composable +private fun CreatePinHeader( + modifier: Modifier = Modifier, +) { + IconTitleSubtitleMolecule( + modifier = modifier, + title = "Choose 4 digit PIN", + subTitle = "Lock Element to add extra security to your chats.\n\nChoose something memorable. If you forget this PIN, you will be logged out of the app", + iconImageVector = Icons.Default.Lock, + ) +} + +@Composable +private fun CreatePinFooter() { + Button( + modifier = Modifier.fillMaxWidth(), + text = "Continue", + onClick = { + + } + ) } @Composable @@ -47,6 +93,7 @@ internal fun CreatePinViewPreview(@PreviewParameter(CreatePinStateProvider::clas ElementPreview { CreatePinView( state = state, + onBackClicked = {}, ) } } From f07a687630534a4a733ebe204385d83d8a4e55f4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 18 Oct 2023 21:20:47 +0200 Subject: [PATCH 02/16] Create pin : start handling the text field --- .../lockscreen/impl/create/CreatePinEvents.kt | 2 +- .../lockscreen/impl/create/CreatePinNode.kt | 1 + .../impl/create/CreatePinPresenter.kt | 12 ++- .../lockscreen/impl/create/CreatePinState.kt | 3 + .../impl/create/CreatePinStateProvider.kt | 11 ++ .../lockscreen/impl/create/CreatePinView.kt | 102 ++++++++++++++++++ .../lockscreen/impl/create/model/PinDigit.kt | 29 +++++ .../lockscreen/impl/create/model/PinEntry.kt | 62 +++++++++++ .../designsystem/theme/ColorAliases.kt | 10 ++ 9 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinDigit.kt create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt index deb3095e69..f492f5ab05 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt @@ -17,5 +17,5 @@ package io.element.android.features.lockscreen.impl.create sealed interface CreatePinEvents { - object MyEvent : CreatePinEvents + data class OnPinEntryChanged(val entryAsText: String) : CreatePinEvents } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinNode.kt index 3689c0cc76..331d6ada84 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinNode.kt @@ -38,6 +38,7 @@ class CreatePinNode @AssistedInject constructor( val state = presenter.present() CreatePinView( state = state, + onBackClicked = { }, modifier = modifier ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt index 08ba24e074..4599a06fc9 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt @@ -17,6 +17,10 @@ package io.element.android.features.lockscreen.impl.create import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import io.element.android.features.lockscreen.impl.create.model.PinEntry import io.element.android.libraries.architecture.Presenter import javax.inject.Inject @@ -24,14 +28,20 @@ class CreatePinPresenter @Inject constructor() : Presenter { @Composable override fun present(): CreatePinState { + val pinEntry by remember { + mutableStateOf(PinEntry.empty(4)) + } fun handleEvents(event: CreatePinEvents) { when (event) { - CreatePinEvents.MyEvent -> Unit + is CreatePinEvents.OnPinEntryChanged -> { + pinEntry.fillWith(event.entryAsText) + } } } return CreatePinState( + pinEntry = pinEntry, eventSink = ::handleEvents ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt index 67311639ad..9b3835193f 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt @@ -16,6 +16,9 @@ package io.element.android.features.lockscreen.impl.create +import io.element.android.features.lockscreen.impl.create.model.PinEntry + data class CreatePinState( + val pinEntry: PinEntry, val eventSink: (CreatePinEvents) -> Unit ) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt index a918b5193e..dbce5dddc5 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt @@ -17,6 +17,9 @@ package io.element.android.features.lockscreen.impl.create import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.lockscreen.impl.create.model.PinDigit +import io.element.android.features.lockscreen.impl.create.model.PinEntry +import kotlinx.collections.immutable.persistentListOf open class CreatePinStateProvider : PreviewParameterProvider { override val values: Sequence @@ -27,5 +30,13 @@ open class CreatePinStateProvider : PreviewParameterProvider { } fun aCreatePinState() = CreatePinState( + pinEntry = PinEntry( + digits = persistentListOf( + PinDigit.Filled('1'), + PinDigit.Filled('2'), + PinDigit.Empty, + PinDigit.Empty, + ) + ), eventSink = {} ) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt index d86c2f296d..6035f9d5d6 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt @@ -18,15 +18,30 @@ package io.element.android.features.lockscreen.impl.create +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Lock import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.features.lockscreen.impl.create.model.PinDigit +import io.element.android.features.lockscreen.impl.create.model.PinEntry import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.button.BackButton @@ -34,7 +49,10 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.designsystem.theme.pinDigitBg +import io.element.android.libraries.theme.ElementTheme @Composable fun CreatePinView( @@ -59,6 +77,7 @@ fun CreatePinView( .consumeWindowInsets(padding), header = { CreatePinHeader() }, footer = { CreatePinFooter() }, + content = { CreatePinContent(state) } ) } ) @@ -87,6 +106,89 @@ private fun CreatePinFooter() { ) } +@Composable +private fun CreatePinContent( + state: CreatePinState, + modifier: Modifier = Modifier, +) { + + PinEntryTextField( + state.pinEntry, + onValueChange = { + state.eventSink(CreatePinEvents.OnPinEntryChanged(it)) + }, + modifier = modifier + .padding(top = 36.dp) + .fillMaxWidth() + ) +} + +@Composable +fun PinEntryTextField( + pinEntry: PinEntry, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, +) { + BasicTextField( + modifier = modifier, + value = TextFieldValue(pinEntry.toText()), + onValueChange = { + onValueChange(it.text) + }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword), + decorationBox = { + PinEntryRow(pinEntry = pinEntry) + } + ) +} + +@Composable +private fun PinEntryRow( + pinEntry: PinEntry, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(8.dp, alignment = Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically, + ) { + for (digit in pinEntry.digits) { + PinDigitView(digit = digit) + } + } +} + +@Composable +private fun PinDigitView( + digit: PinDigit, + modifier: Modifier = Modifier, +) { + val shape = RoundedCornerShape(8.dp) + val appearanceModifier = when (digit) { + PinDigit.Empty -> { + Modifier.border(1.dp, ElementTheme.colors.iconPrimary, shape) + } + is PinDigit.Filled -> { + Modifier.background(ElementTheme.colors.pinDigitBg, shape) + } + } + Box( + modifier = modifier + .size(40.dp, 50.dp) + .then(appearanceModifier), + contentAlignment = Alignment.Center, + + ) { + if (digit is PinDigit.Filled) { + Text( + text = digit.toText(), + style = ElementTheme.typography.fontHeadingMdBold + ) + } + + } +} + @Composable @PreviewsDayNight internal fun CreatePinViewPreview(@PreviewParameter(CreatePinStateProvider::class) state: CreatePinState) { diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinDigit.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinDigit.kt new file mode 100644 index 0000000000..741a61cafe --- /dev/null +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinDigit.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.lockscreen.impl.create.model + +sealed interface PinDigit { + data object Empty : PinDigit + data class Filled(val value: Char) : PinDigit + + fun toText(): String { + return when (this) { + is Empty -> "" + is Filled -> value.toString() + } + } +} diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt new file mode 100644 index 0000000000..587fde955d --- /dev/null +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.lockscreen.impl.create.model + +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toPersistentList + +data class PinEntry( + val digits: ImmutableList, +) { + + companion object { + fun empty(size: Int): PinEntry { + val digits = List(size) { PinDigit.Empty } + return PinEntry( + digits = digits.toPersistentList() + ) + } + } + + private val size = digits.size + + /** + * Fill the first digits with the given text. + * Can't be more than the size of the PinEntry + * Keep the Empty digits at the end + * @return the new PinEntry + */ + fun fillWith(text: String): PinEntry { + val newDigits = digits.toMutableList() + text.forEachIndexed { index, char -> + if (index < size) { + newDigits[index] = PinDigit.Filled(char) + } + } + return copy(digits = newDigits.toPersistentList()) + } + + fun isPinComplete(): Boolean { + return digits.all { it is PinDigit.Filled } + } + + fun toText(): String { + return digits.joinToString("") { + it.toText() + } + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt index b347402b41..e85abb396b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt @@ -98,6 +98,16 @@ val SemanticColors.bgSubtleTertiary val SemanticColors.temporaryColorBgSpecial get() = if (isLight) Color(0xFFE4E8F0) else Color(0xFF3A4048) +// This color is not present in Semantic color, so put hard-coded value for now +val SemanticColors.pinDigitBg + get() = if (isLight) { + // We want LightDesignTokens.colorGray300 + Color(0xFFF0F2F5) + } else { + // We want DarkDesignTokens.colorGray400 + Color(0xFF26282D) + } + @PreviewsDayNight @Composable internal fun ColorAliasesPreview() = ElementPreview { From 63fbb4412b894a13011d50054ea5df3a7a3b8e58 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 19 Oct 2023 12:11:14 +0200 Subject: [PATCH 03/16] Pin create : add some more states to manage validation and confirmation --- .../lockscreen/impl/create/CreatePinEvents.kt | 1 + .../impl/create/CreatePinPresenter.kt | 55 +++++++++++++++++-- .../lockscreen/impl/create/CreatePinState.kt | 14 ++++- .../impl/create/CreatePinStateProvider.kt | 31 +++++++---- .../lockscreen/impl/create/CreatePinView.kt | 10 ++-- .../lockscreen/impl/create/model/PinEntry.kt | 4 ++ .../create/validation/PinCreationFailure.kt | 22 ++++++++ .../impl/create/validation/PinValidator.kt | 40 ++++++++++++++ 8 files changed, 153 insertions(+), 24 deletions(-) create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinCreationFailure.kt create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt index f492f5ab05..9e53762c07 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt @@ -18,4 +18,5 @@ package io.element.android.features.lockscreen.impl.create sealed interface CreatePinEvents { data class OnPinEntryChanged(val entryAsText: String) : CreatePinEvents + data object OnClearValidationFailure : CreatePinEvents } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt index 4599a06fc9..18a17acb62 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt @@ -20,28 +20,73 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import io.element.android.features.lockscreen.impl.create.model.PinEntry +import io.element.android.features.lockscreen.impl.create.validation.PinCreationFailure +import io.element.android.features.lockscreen.impl.create.validation.PinValidator +import io.element.android.features.lockscreen.impl.pin.PinCodeManager import io.element.android.libraries.architecture.Presenter import javax.inject.Inject -class CreatePinPresenter @Inject constructor() : Presenter { +private const val PIN_SIZE = 4 + +class CreatePinPresenter @Inject constructor( + private val pinValidator: PinValidator, + private val pinCodeManager: PinCodeManager, +) : Presenter { @Composable override fun present(): CreatePinState { - val pinEntry by remember { - mutableStateOf(PinEntry.empty(4)) + var choosePinEntry by remember { + mutableStateOf(PinEntry.empty(PIN_SIZE)) + } + var confirmPinEntry by remember { + mutableStateOf(PinEntry.empty(PIN_SIZE)) + } + var isConfirmationStep by remember { + mutableStateOf(false) + } + var creationFailure by remember { + mutableStateOf(null) } fun handleEvents(event: CreatePinEvents) { when (event) { is CreatePinEvents.OnPinEntryChanged -> { - pinEntry.fillWith(event.entryAsText) + if (isConfirmationStep) { + confirmPinEntry = confirmPinEntry.fillWith(event.entryAsText) + if (confirmPinEntry.isPinComplete()) { + if (confirmPinEntry == choosePinEntry) { + //pinCodeManager.savePin(confirmPinEntry.toText()) + } else { + confirmPinEntry = PinEntry.empty(PIN_SIZE) + creationFailure = PinCreationFailure.ConfirmationPinNotMatching + } + } + } else { + choosePinEntry = choosePinEntry.fillWith(event.entryAsText) + if (choosePinEntry.isPinComplete()) { + when (val pinValidationResult = pinValidator.isPinValid(choosePinEntry)) { + is PinValidator.Result.Invalid -> { + choosePinEntry = PinEntry.empty(PIN_SIZE) + creationFailure = pinValidationResult.failure + } + PinValidator.Result.Valid -> isConfirmationStep = true + } + } + } + } + CreatePinEvents.OnClearValidationFailure -> { + creationFailure = null } } } return CreatePinState( - pinEntry = pinEntry, + choosePinEntry = choosePinEntry, + confirmPinEntry = confirmPinEntry, + isConfirmationStep = isConfirmationStep, + creationFailure = creationFailure, eventSink = ::handleEvents ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt index 9b3835193f..799d4b20a8 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt @@ -17,8 +17,18 @@ package io.element.android.features.lockscreen.impl.create import io.element.android.features.lockscreen.impl.create.model.PinEntry +import io.element.android.features.lockscreen.impl.create.validation.PinCreationFailure data class CreatePinState( - val pinEntry: PinEntry, + val choosePinEntry: PinEntry, + val confirmPinEntry: PinEntry, + val isConfirmationStep: Boolean, + val creationFailure: PinCreationFailure?, val eventSink: (CreatePinEvents) -> Unit -) +) { + val activePinEntry = if (isConfirmationStep) { + confirmPinEntry + } else { + choosePinEntry + } +} diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt index dbce5dddc5..f4d778a296 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt @@ -17,26 +17,33 @@ package io.element.android.features.lockscreen.impl.create import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.lockscreen.impl.create.model.PinDigit import io.element.android.features.lockscreen.impl.create.model.PinEntry -import kotlinx.collections.immutable.persistentListOf +import io.element.android.features.lockscreen.impl.create.validation.PinCreationFailure open class CreatePinStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aCreatePinState(), - // Add other states here + aCreatePinState( + choosePinEntry = PinEntry.empty(4).fillWith("12") + ), + aCreatePinState( + choosePinEntry = PinEntry.empty(4).fillWith("1789"), + isConfirmationStep = true, + ), ) } -fun aCreatePinState() = CreatePinState( - pinEntry = PinEntry( - digits = persistentListOf( - PinDigit.Filled('1'), - PinDigit.Filled('2'), - PinDigit.Empty, - PinDigit.Empty, - ) - ), +fun aCreatePinState( + choosePinEntry: PinEntry = PinEntry.empty(4), + confirmPinEntry: PinEntry = PinEntry.empty(4), + isConfirmationStep: Boolean = false, + creationFailure: PinCreationFailure? = null, +) = CreatePinState( + choosePinEntry = choosePinEntry, + confirmPinEntry = confirmPinEntry, + isConfirmationStep = isConfirmationStep, + creationFailure = creationFailure, eventSink = {} ) + diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt index 6035f9d5d6..f5b2e49df8 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt @@ -75,7 +75,7 @@ fun CreatePinView( modifier = Modifier .padding(padding) .consumeWindowInsets(padding), - header = { CreatePinHeader() }, + header = { CreatePinHeader(state.isConfirmationStep) }, footer = { CreatePinFooter() }, content = { CreatePinContent(state) } ) @@ -85,11 +85,12 @@ fun CreatePinView( @Composable private fun CreatePinHeader( + isValidationStep: Boolean, modifier: Modifier = Modifier, ) { IconTitleSubtitleMolecule( modifier = modifier, - title = "Choose 4 digit PIN", + title = if (isValidationStep) "Confirm PIN" else "Choose 4 digit PIN", subTitle = "Lock Element to add extra security to your chats.\n\nChoose something memorable. If you forget this PIN, you will be logged out of the app", iconImageVector = Icons.Default.Lock, ) @@ -111,9 +112,8 @@ private fun CreatePinContent( state: CreatePinState, modifier: Modifier = Modifier, ) { - PinEntryTextField( - state.pinEntry, + state.activePinEntry, onValueChange = { state.eventSink(CreatePinEvents.OnPinEntryChanged(it)) }, @@ -135,7 +135,7 @@ fun PinEntryTextField( onValueChange = { onValueChange(it.text) }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), decorationBox = { PinEntryRow(pinEntry = pinEntry) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt index 587fde955d..2228110156 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt @@ -50,6 +50,10 @@ data class PinEntry( return copy(digits = newDigits.toPersistentList()) } + fun clear(): PinEntry { + return fillWith("") + } + fun isPinComplete(): Boolean { return digits.all { it is PinDigit.Filled } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinCreationFailure.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinCreationFailure.kt new file mode 100644 index 0000000000..26b1eb5fd8 --- /dev/null +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinCreationFailure.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.lockscreen.impl.create.validation + +sealed interface PinCreationFailure { + data object ChosenPinBlacklisted : PinCreationFailure + data object ConfirmationPinNotMatching : PinCreationFailure +} diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt new file mode 100644 index 0000000000..1d97cda60d --- /dev/null +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.lockscreen.impl.create.validation + +import io.element.android.features.lockscreen.impl.create.model.PinEntry +import javax.inject.Inject + +private val BLACKLIST = listOf("0000", "1234") + +class PinValidator @Inject constructor() { + + sealed interface Result { + data object Valid : Result + data class Invalid(val failure: PinCreationFailure) : Result + } + + fun isPinValid(pinEntry: PinEntry): Result { + val pinAsText = pinEntry.toText() + val isBlacklisted = BLACKLIST.any { it == pinAsText } + return if (isBlacklisted) { + Result.Invalid(PinCreationFailure.ChosenPinBlacklisted) + } else { + Result.Valid + } + } +} From cbd2ba50e72b7ea9071297ff977579463a253812 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 19 Oct 2023 12:16:30 +0200 Subject: [PATCH 04/16] Pin create : improve clear validation --- .../lockscreen/impl/create/CreatePinPresenter.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt index 18a17acb62..a39c199256 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt @@ -59,7 +59,6 @@ class CreatePinPresenter @Inject constructor( if (confirmPinEntry == choosePinEntry) { //pinCodeManager.savePin(confirmPinEntry.toText()) } else { - confirmPinEntry = PinEntry.empty(PIN_SIZE) creationFailure = PinCreationFailure.ConfirmationPinNotMatching } } @@ -68,7 +67,6 @@ class CreatePinPresenter @Inject constructor( if (choosePinEntry.isPinComplete()) { when (val pinValidationResult = pinValidator.isPinValid(choosePinEntry)) { is PinValidator.Result.Invalid -> { - choosePinEntry = PinEntry.empty(PIN_SIZE) creationFailure = pinValidationResult.failure } PinValidator.Result.Valid -> isConfirmationStep = true @@ -77,6 +75,17 @@ class CreatePinPresenter @Inject constructor( } } CreatePinEvents.OnClearValidationFailure -> { + when (creationFailure) { + is PinCreationFailure.ConfirmationPinNotMatching -> { + choosePinEntry = PinEntry.empty(PIN_SIZE) + confirmPinEntry = PinEntry.empty(PIN_SIZE) + } + is PinCreationFailure.ChosenPinBlacklisted -> { + choosePinEntry = PinEntry.empty(PIN_SIZE) + } + null -> Unit + } + isConfirmationStep = false creationFailure = null } } From e5bcfb393633edd8f24992d71ba80ea789f19a3c Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 19 Oct 2023 12:18:26 +0200 Subject: [PATCH 05/16] Create pin : remove PinCodeManager and add TODO --- .../features/lockscreen/impl/create/CreatePinPresenter.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt index a39c199256..435de0ebe0 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt @@ -24,7 +24,6 @@ import androidx.compose.runtime.setValue import io.element.android.features.lockscreen.impl.create.model.PinEntry import io.element.android.features.lockscreen.impl.create.validation.PinCreationFailure import io.element.android.features.lockscreen.impl.create.validation.PinValidator -import io.element.android.features.lockscreen.impl.pin.PinCodeManager import io.element.android.libraries.architecture.Presenter import javax.inject.Inject @@ -32,7 +31,6 @@ private const val PIN_SIZE = 4 class CreatePinPresenter @Inject constructor( private val pinValidator: PinValidator, - private val pinCodeManager: PinCodeManager, ) : Presenter { @Composable @@ -57,7 +55,7 @@ class CreatePinPresenter @Inject constructor( confirmPinEntry = confirmPinEntry.fillWith(event.entryAsText) if (confirmPinEntry.isPinComplete()) { if (confirmPinEntry == choosePinEntry) { - //pinCodeManager.savePin(confirmPinEntry.toText()) + //TODO save in db and navigate to next screen } else { creationFailure = PinCreationFailure.ConfirmationPinNotMatching } From 7ba9a0af77fd24af9b0b8082515237ce16433118 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 19 Oct 2023 12:34:29 +0200 Subject: [PATCH 06/16] Create pin : render failures --- .../lockscreen/impl/create/CreatePinEvents.kt | 2 +- .../impl/create/CreatePinPresenter.kt | 22 +++++----- .../lockscreen/impl/create/CreatePinState.kt | 4 +- .../impl/create/CreatePinStateProvider.kt | 17 ++++++-- .../lockscreen/impl/create/CreatePinView.kt | 41 +++++++++++++------ ...CreationFailure.kt => CreatePinFailure.kt} | 6 +-- .../impl/create/validation/PinValidator.kt | 4 +- 7 files changed, 61 insertions(+), 35 deletions(-) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/{PinCreationFailure.kt => CreatePinFailure.kt} (80%) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt index 9e53762c07..78ce529325 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinEvents.kt @@ -18,5 +18,5 @@ package io.element.android.features.lockscreen.impl.create sealed interface CreatePinEvents { data class OnPinEntryChanged(val entryAsText: String) : CreatePinEvents - data object OnClearValidationFailure : CreatePinEvents + data object ClearFailure : CreatePinEvents } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt index 435de0ebe0..525b80314b 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt @@ -22,7 +22,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import io.element.android.features.lockscreen.impl.create.model.PinEntry -import io.element.android.features.lockscreen.impl.create.validation.PinCreationFailure +import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure import io.element.android.features.lockscreen.impl.create.validation.PinValidator import io.element.android.libraries.architecture.Presenter import javax.inject.Inject @@ -44,8 +44,8 @@ class CreatePinPresenter @Inject constructor( var isConfirmationStep by remember { mutableStateOf(false) } - var creationFailure by remember { - mutableStateOf(null) + var createPinFailure by remember { + mutableStateOf(null) } fun handleEvents(event: CreatePinEvents) { @@ -57,7 +57,7 @@ class CreatePinPresenter @Inject constructor( if (confirmPinEntry == choosePinEntry) { //TODO save in db and navigate to next screen } else { - creationFailure = PinCreationFailure.ConfirmationPinNotMatching + createPinFailure = CreatePinFailure.ConfirmationPinNotMatching } } } else { @@ -65,26 +65,26 @@ class CreatePinPresenter @Inject constructor( if (choosePinEntry.isPinComplete()) { when (val pinValidationResult = pinValidator.isPinValid(choosePinEntry)) { is PinValidator.Result.Invalid -> { - creationFailure = pinValidationResult.failure + createPinFailure = pinValidationResult.failure } PinValidator.Result.Valid -> isConfirmationStep = true } } } } - CreatePinEvents.OnClearValidationFailure -> { - when (creationFailure) { - is PinCreationFailure.ConfirmationPinNotMatching -> { + CreatePinEvents.ClearFailure -> { + when (createPinFailure) { + is CreatePinFailure.ConfirmationPinNotMatching -> { choosePinEntry = PinEntry.empty(PIN_SIZE) confirmPinEntry = PinEntry.empty(PIN_SIZE) } - is PinCreationFailure.ChosenPinBlacklisted -> { + is CreatePinFailure.ChosenPinBlacklisted -> { choosePinEntry = PinEntry.empty(PIN_SIZE) } null -> Unit } isConfirmationStep = false - creationFailure = null + createPinFailure = null } } } @@ -93,7 +93,7 @@ class CreatePinPresenter @Inject constructor( choosePinEntry = choosePinEntry, confirmPinEntry = confirmPinEntry, isConfirmationStep = isConfirmationStep, - creationFailure = creationFailure, + createPinFailure = createPinFailure, eventSink = ::handleEvents ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt index 799d4b20a8..914e12ca96 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt @@ -17,13 +17,13 @@ package io.element.android.features.lockscreen.impl.create import io.element.android.features.lockscreen.impl.create.model.PinEntry -import io.element.android.features.lockscreen.impl.create.validation.PinCreationFailure +import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure data class CreatePinState( val choosePinEntry: PinEntry, val confirmPinEntry: PinEntry, val isConfirmationStep: Boolean, - val creationFailure: PinCreationFailure?, + val createPinFailure: CreatePinFailure?, val eventSink: (CreatePinEvents) -> Unit ) { val activePinEntry = if (isConfirmationStep) { diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt index f4d778a296..40287622fd 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt @@ -18,7 +18,7 @@ package io.element.android.features.lockscreen.impl.create import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.lockscreen.impl.create.model.PinEntry -import io.element.android.features.lockscreen.impl.create.validation.PinCreationFailure +import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure open class CreatePinStateProvider : PreviewParameterProvider { override val values: Sequence @@ -31,6 +31,17 @@ open class CreatePinStateProvider : PreviewParameterProvider { choosePinEntry = PinEntry.empty(4).fillWith("1789"), isConfirmationStep = true, ), + aCreatePinState( + choosePinEntry = PinEntry.empty(4).fillWith("1789"), + confirmPinEntry = PinEntry.empty(4).fillWith("1788"), + isConfirmationStep = true, + creationFailure = CreatePinFailure.ConfirmationPinNotMatching + ), + aCreatePinState( + choosePinEntry = PinEntry.empty(4).fillWith("1111"), + creationFailure = CreatePinFailure.ChosenPinBlacklisted + ), + ) } @@ -38,12 +49,12 @@ fun aCreatePinState( choosePinEntry: PinEntry = PinEntry.empty(4), confirmPinEntry: PinEntry = PinEntry.empty(4), isConfirmationStep: Boolean = false, - creationFailure: PinCreationFailure? = null, + creationFailure: CreatePinFailure? = null, ) = CreatePinState( choosePinEntry = choosePinEntry, confirmPinEntry = confirmPinEntry, isConfirmationStep = isConfirmationStep, - creationFailure = creationFailure, + createPinFailure = creationFailure, eventSink = {} ) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt index f5b2e49df8..fdce08c229 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt @@ -42,12 +42,13 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.lockscreen.impl.create.model.PinDigit import io.element.android.features.lockscreen.impl.create.model.PinEntry +import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar @@ -76,7 +77,6 @@ fun CreatePinView( .padding(padding) .consumeWindowInsets(padding), header = { CreatePinHeader(state.isConfirmationStep) }, - footer = { CreatePinFooter() }, content = { CreatePinContent(state) } ) } @@ -96,17 +96,6 @@ private fun CreatePinHeader( ) } -@Composable -private fun CreatePinFooter() { - Button( - modifier = Modifier.fillMaxWidth(), - text = "Continue", - onClick = { - - } - ) -} - @Composable private fun CreatePinContent( state: CreatePinState, @@ -121,6 +110,32 @@ private fun CreatePinContent( .padding(top = 36.dp) .fillMaxWidth() ) + if (state.createPinFailure != null) { + ErrorDialog( + modifier = modifier, + title = state.createPinFailure.title(), + content = state.createPinFailure.content(), + onDismiss = { + state.eventSink(CreatePinEvents.ClearFailure) + } + ) + } +} + +@Composable +private fun CreatePinFailure.content(): String { + return when (this) { + CreatePinFailure.ChosenPinBlacklisted -> "You cannot choose this as your PIN code for security reasons" + CreatePinFailure.ConfirmationPinNotMatching -> "Please enter the same PIN twice" + } +} + +@Composable +private fun CreatePinFailure.title(): String { + return when (this) { + CreatePinFailure.ChosenPinBlacklisted -> "Choose a different PIN" + CreatePinFailure.ConfirmationPinNotMatching -> "PINs don't match" + } } @Composable diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinCreationFailure.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/CreatePinFailure.kt similarity index 80% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinCreationFailure.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/CreatePinFailure.kt index 26b1eb5fd8..96c0de0056 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinCreationFailure.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/CreatePinFailure.kt @@ -16,7 +16,7 @@ package io.element.android.features.lockscreen.impl.create.validation -sealed interface PinCreationFailure { - data object ChosenPinBlacklisted : PinCreationFailure - data object ConfirmationPinNotMatching : PinCreationFailure +sealed interface CreatePinFailure { + data object ChosenPinBlacklisted : CreatePinFailure + data object ConfirmationPinNotMatching : CreatePinFailure } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt index 1d97cda60d..8c1854ecee 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt @@ -25,14 +25,14 @@ class PinValidator @Inject constructor() { sealed interface Result { data object Valid : Result - data class Invalid(val failure: PinCreationFailure) : Result + data class Invalid(val failure: CreatePinFailure) : Result } fun isPinValid(pinEntry: PinEntry): Result { val pinAsText = pinEntry.toText() val isBlacklisted = BLACKLIST.any { it == pinAsText } return if (isBlacklisted) { - Result.Invalid(PinCreationFailure.ChosenPinBlacklisted) + Result.Invalid(CreatePinFailure.ChosenPinBlacklisted) } else { Result.Valid } From 537fc68778f1922e21cb09af3d85ccf841479447 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 19 Oct 2023 10:47:03 +0000 Subject: [PATCH 07/16] Update screenshots --- ...create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png | 3 +++ ...create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png | 3 +++ ...create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png | 3 +++ ...create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png | 3 +++ ...create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png | 3 +++ ...create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png | 3 +++ ...create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png | 3 +++ ...create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png | 3 +++ 10 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png index e8dd1a28d7..03b059496a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9914a33ba23544bdfce1e21b52ad247024392730fb22b60bc9b6fa6440f004d4 -size 9216 +oid sha256:df5f2cc45255cec07dc99de8df0f2e8dd06fdc3afded3ba43c8deec2bb7c1d0b +size 34529 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ffcb20390e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c5f338938adeeb280be0b68f57b5ba0b4c9a904e8660e99fb6e57cd6b4774fc +size 34534 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5e82145176 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a412e9c38549341f707e9a6379e165d554294c0f452fec82ab783fa9e0533ad7 +size 32374 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2fdd3802d4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2fbba51aa681041018dd41f9f2e69159e229411b2cf438a62367a7f1edbb857a +size 28781 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..26305ae91e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:843141e92c23f9cbba98296fc543c365d33a708daaabe0ff9549ccb8d6f69af4 +size 36969 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png index 157a7c52c3..c3a42144c6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ad524a918e499fcea6fd5293358167ff52f8877cc31b778c8def01925fa662f -size 8582 +oid sha256:be489eaf748f7d79ee7d8dd3f0177ab47626728f5b4dae9851b98e708f31e97c +size 32962 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a37f885bb6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb6369c1574670c81f2d0f5a541cc86da708cf087539250b50a1bb86fee33894 +size 33146 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f5c161a105 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72836a8215d294ccb10dd961c5bde18342f50ab4aedf23c6bd5e655edd733a4a +size 31348 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e78f91910a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86591b97276d7379ae68c500a81a977ee0adb2f8b28e0896d0b32a658fb359bf +size 25430 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..968d9fdff5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:110139dd6177e5e1bd126bcd60e4442394867c2e0a3656bee7f15cc5c58c6a6e +size 32584 From bde270565418ba53e41e66d286f132420f3c081a Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 19 Oct 2023 15:45:11 +0200 Subject: [PATCH 08/16] Pin create: add test for presenter --- build.gradle.kts | 1 - features/lockscreen/impl/build.gradle.kts | 1 + .../impl/create/CreatePinPresenter.kt | 6 +- .../impl/create/CreatePinStateProvider.kt | 4 +- .../lockscreen/impl/create/CreatePinView.kt | 8 +- .../create/validation/CreatePinFailure.kt | 4 +- .../impl/create/validation/PinValidator.kt | 10 +- .../impl/create/CreatePinPresenterTest.kt | 113 ++++++++++++++++++ .../android/tests/testutils/ReceiveTurbine.kt | 10 ++ 9 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 487776d948..e14ad71981 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -253,7 +253,6 @@ koverMerged { // Temporary until we have actually something to test. excludes += "io.element.android.features.lockscreen.impl.auth.PinAuthenticationPresenter" excludes += "io.element.android.features.lockscreen.impl.auth.PinAuthenticationPresenter$*" - excludes += "io.element.android.features.lockscreen.impl.create.CreatePinPresenter" } bound { minValue = 85 diff --git a/features/lockscreen/impl/build.gradle.kts b/features/lockscreen/impl/build.gradle.kts index af63538db5..028d8bee3c 100644 --- a/features/lockscreen/impl/build.gradle.kts +++ b/features/lockscreen/impl/build.gradle.kts @@ -47,6 +47,7 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.tests.testutils) testImplementation(projects.libraries.cryptography.test) testImplementation(projects.libraries.cryptography.impl) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt index 525b80314b..e72e636ed4 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt @@ -57,7 +57,7 @@ class CreatePinPresenter @Inject constructor( if (confirmPinEntry == choosePinEntry) { //TODO save in db and navigate to next screen } else { - createPinFailure = CreatePinFailure.ConfirmationPinNotMatching + createPinFailure = CreatePinFailure.PinsDontMatch } } } else { @@ -74,11 +74,11 @@ class CreatePinPresenter @Inject constructor( } CreatePinEvents.ClearFailure -> { when (createPinFailure) { - is CreatePinFailure.ConfirmationPinNotMatching -> { + is CreatePinFailure.PinsDontMatch -> { choosePinEntry = PinEntry.empty(PIN_SIZE) confirmPinEntry = PinEntry.empty(PIN_SIZE) } - is CreatePinFailure.ChosenPinBlacklisted -> { + is CreatePinFailure.PinBlacklisted -> { choosePinEntry = PinEntry.empty(PIN_SIZE) } null -> Unit diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt index 40287622fd..543360f91e 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt @@ -35,11 +35,11 @@ open class CreatePinStateProvider : PreviewParameterProvider { choosePinEntry = PinEntry.empty(4).fillWith("1789"), confirmPinEntry = PinEntry.empty(4).fillWith("1788"), isConfirmationStep = true, - creationFailure = CreatePinFailure.ConfirmationPinNotMatching + creationFailure = CreatePinFailure.PinsDontMatch ), aCreatePinState( choosePinEntry = PinEntry.empty(4).fillWith("1111"), - creationFailure = CreatePinFailure.ChosenPinBlacklisted + creationFailure = CreatePinFailure.PinBlacklisted ), ) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt index fdce08c229..915bd2b4b0 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt @@ -125,16 +125,16 @@ private fun CreatePinContent( @Composable private fun CreatePinFailure.content(): String { return when (this) { - CreatePinFailure.ChosenPinBlacklisted -> "You cannot choose this as your PIN code for security reasons" - CreatePinFailure.ConfirmationPinNotMatching -> "Please enter the same PIN twice" + CreatePinFailure.PinBlacklisted -> "You cannot choose this as your PIN code for security reasons" + CreatePinFailure.PinsDontMatch -> "Please enter the same PIN twice" } } @Composable private fun CreatePinFailure.title(): String { return when (this) { - CreatePinFailure.ChosenPinBlacklisted -> "Choose a different PIN" - CreatePinFailure.ConfirmationPinNotMatching -> "PINs don't match" + CreatePinFailure.PinBlacklisted -> "Choose a different PIN" + CreatePinFailure.PinsDontMatch -> "PINs don't match" } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/CreatePinFailure.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/CreatePinFailure.kt index 96c0de0056..8c0cb78921 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/CreatePinFailure.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/CreatePinFailure.kt @@ -17,6 +17,6 @@ package io.element.android.features.lockscreen.impl.create.validation sealed interface CreatePinFailure { - data object ChosenPinBlacklisted : CreatePinFailure - data object ConfirmationPinNotMatching : CreatePinFailure + data object PinBlacklisted : CreatePinFailure + data object PinsDontMatch : CreatePinFailure } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt index 8c1854ecee..7353ec47d0 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/validation/PinValidator.kt @@ -16,13 +16,17 @@ package io.element.android.features.lockscreen.impl.create.validation +import androidx.annotation.VisibleForTesting import io.element.android.features.lockscreen.impl.create.model.PinEntry import javax.inject.Inject -private val BLACKLIST = listOf("0000", "1234") - class PinValidator @Inject constructor() { + companion object { + @VisibleForTesting + val BLACKLIST = listOf("0000", "1234") + } + sealed interface Result { data object Valid : Result data class Invalid(val failure: CreatePinFailure) : Result @@ -32,7 +36,7 @@ class PinValidator @Inject constructor() { val pinAsText = pinEntry.toText() val isBlacklisted = BLACKLIST.any { it == pinAsText } return if (isBlacklisted) { - Result.Invalid(CreatePinFailure.ChosenPinBlacklisted) + Result.Invalid(CreatePinFailure.PinBlacklisted) } else { Result.Valid } diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt new file mode 100644 index 0000000000..9c86039fe1 --- /dev/null +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.lockscreen.impl.create + +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.features.lockscreen.impl.create.model.PinDigit +import io.element.android.features.lockscreen.impl.create.model.PinEntry +import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure +import io.element.android.features.lockscreen.impl.create.validation.PinValidator +import io.element.android.tests.testutils.awaitLastSequentialItem +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class CreatePinPresenterTest { + + private val blacklistedPin = PinValidator.BLACKLIST.first() + private val halfCompletePin = "12" + private val completePin = "1235" + private val mismatchedPin = "1236" + + @Test + fun `present - complete flow`() = runTest { + + val presenter = createPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().also { state -> + state.choosePinEntry.assertEmpty() + state.confirmPinEntry.assertEmpty() + assertThat(state.createPinFailure).isNull() + assertThat(state.isConfirmationStep).isFalse() + state.eventSink(CreatePinEvents.OnPinEntryChanged(halfCompletePin)) + } + awaitItem().also { state -> + state.choosePinEntry.assertText(halfCompletePin) + state.confirmPinEntry.assertEmpty() + assertThat(state.createPinFailure).isNull() + assertThat(state.isConfirmationStep).isFalse() + state.eventSink(CreatePinEvents.OnPinEntryChanged(blacklistedPin)) + } + awaitLastSequentialItem().also { state -> + state.choosePinEntry.assertText(blacklistedPin) + assertThat(state.createPinFailure).isEqualTo(CreatePinFailure.PinBlacklisted) + state.eventSink(CreatePinEvents.ClearFailure) + } + awaitLastSequentialItem().also { state -> + state.choosePinEntry.assertEmpty() + assertThat(state.createPinFailure).isNull() + state.eventSink(CreatePinEvents.OnPinEntryChanged(completePin)) + } + awaitLastSequentialItem().also { state -> + state.choosePinEntry.assertText(completePin) + state.confirmPinEntry.assertEmpty() + assertThat(state.isConfirmationStep).isTrue() + state.eventSink(CreatePinEvents.OnPinEntryChanged(mismatchedPin)) + } + awaitLastSequentialItem().also { state -> + state.choosePinEntry.assertText(completePin) + state.confirmPinEntry.assertText(mismatchedPin) + assertThat(state.createPinFailure).isEqualTo(CreatePinFailure.PinsDontMatch) + state.eventSink(CreatePinEvents.ClearFailure) + } + awaitLastSequentialItem().also { state -> + state.choosePinEntry.assertEmpty() + state.confirmPinEntry.assertEmpty() + assertThat(state.isConfirmationStep).isFalse() + assertThat(state.createPinFailure).isNull() + state.eventSink(CreatePinEvents.OnPinEntryChanged(completePin)) + } + awaitLastSequentialItem().also { state -> + state.choosePinEntry.assertText(completePin) + state.confirmPinEntry.assertEmpty() + assertThat(state.isConfirmationStep).isTrue() + state.eventSink(CreatePinEvents.OnPinEntryChanged(completePin)) + } + awaitItem().also { state -> + state.choosePinEntry.assertText(completePin) + state.confirmPinEntry.assertText(completePin) + } + } + } + + private fun PinEntry.assertText(text: String) { + assertThat(toText()).isEqualTo(text) + } + + private fun PinEntry.assertEmpty() { + val isEmpty = digits.all { it is PinDigit.Empty } + assertThat(isEmpty).isTrue() + } + + private fun createPresenter(): CreatePinPresenter { + return CreatePinPresenter(PinValidator()) + } +} diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt index 06b6b3d3ea..3e47dd63ce 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt @@ -32,6 +32,16 @@ suspend fun ReceiveTurbine.consumeItemsUntilTimeout(timeout: Durati return consumeItemsUntilPredicate(timeout) { false } } +/** + * Consume all items which are emitted sequentially. + * Use the smallest timeout possible internally to avoid wasting time. + * Same as calling skipItems(x) and then awaitItem() but without assumption on the number of items. + * @return the last item emitted. + */ +suspend fun ReceiveTurbine.awaitLastSequentialItem(): T { + return consumeItemsUntilTimeout(1.milliseconds).last() +} + /** * Consume items until predicate is true, or timeout is reached waiting for an event, or we receive terminal event. * The timeout is applied for each event. From c08cd13e0ecc107ccfb607df7ccd48f64164bb38 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 19 Oct 2023 16:28:20 +0200 Subject: [PATCH 09/16] Pin create: use localazy strings --- .../lockscreen/impl/create/CreatePinState.kt | 1 + .../lockscreen/impl/create/CreatePinView.kt | 42 +++++++++++---- .../lockscreen/impl/create/model/PinEntry.kt | 2 +- .../impl/src/main/res/values/localazy.xml | 24 +++++++++ .../api/src/main/res/values/localazy.xml | 4 ++ .../src/main/res/values/localazy.xml | 54 ++++++++++++++++++- tools/localazy/config.json | 6 +++ 7 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 features/lockscreen/impl/src/main/res/values/localazy.xml diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt index 914e12ca96..5bb632f04e 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt @@ -26,6 +26,7 @@ data class CreatePinState( val createPinFailure: CreatePinFailure?, val eventSink: (CreatePinEvents) -> Unit ) { + val pinSize = choosePinEntry.size val activePinEntry = if (isConfirmationStep) { confirmPinEntry } else { diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt index 915bd2b4b0..dc9956abaa 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt @@ -22,6 +22,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxWidth @@ -33,13 +34,17 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Lock import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.features.lockscreen.impl.R import io.element.android.features.lockscreen.impl.create.model.PinDigit import io.element.android.features.lockscreen.impl.create.model.PinEntry import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure @@ -76,7 +81,7 @@ fun CreatePinView( modifier = Modifier .padding(padding) .consumeWindowInsets(padding), - header = { CreatePinHeader(state.isConfirmationStep) }, + header = { CreatePinHeader(state.isConfirmationStep, state.pinSize) }, content = { CreatePinContent(state) } ) } @@ -86,14 +91,31 @@ fun CreatePinView( @Composable private fun CreatePinHeader( isValidationStep: Boolean, + pinSize: Int, modifier: Modifier = Modifier, ) { - IconTitleSubtitleMolecule( + Column( modifier = modifier, - title = if (isValidationStep) "Confirm PIN" else "Choose 4 digit PIN", - subTitle = "Lock Element to add extra security to your chats.\n\nChoose something memorable. If you forget this PIN, you will be logged out of the app", - iconImageVector = Icons.Default.Lock, - ) + horizontalAlignment = Alignment.CenterHorizontally, + ) { + IconTitleSubtitleMolecule( + modifier = Modifier.padding(top = 60.dp, bottom = 12.dp), + title = if (isValidationStep) { + stringResource(id = R.string.screen_app_lock_setup_confirm_pin) + } else { + stringResource(id = R.string.screen_app_lock_setup_choose_pin, pinSize) + }, + subTitle = stringResource(id = R.string.screen_app_lock_setup_pin_context), + iconImageVector = Icons.Filled.Lock, + ) + Text( + text = stringResource(id = R.string.screen_app_lock_setup_pin_context_warning), + modifier = Modifier.padding(8.dp), + textAlign = TextAlign.Center, + style = ElementTheme.typography.fontBodyMdRegular, + color = MaterialTheme.colorScheme.secondary, + ) + } } @Composable @@ -125,16 +147,16 @@ private fun CreatePinContent( @Composable private fun CreatePinFailure.content(): String { return when (this) { - CreatePinFailure.PinBlacklisted -> "You cannot choose this as your PIN code for security reasons" - CreatePinFailure.PinsDontMatch -> "Please enter the same PIN twice" + CreatePinFailure.PinBlacklisted -> stringResource(id = R.string.screen_app_lock_setup_pin_blacklisted_dialog_content) + CreatePinFailure.PinsDontMatch -> stringResource(id = R.string.screen_app_lock_setup_pin_mismatch_dialog_content) } } @Composable private fun CreatePinFailure.title(): String { return when (this) { - CreatePinFailure.PinBlacklisted -> "Choose a different PIN" - CreatePinFailure.PinsDontMatch -> "PINs don't match" + CreatePinFailure.PinBlacklisted -> stringResource(id = R.string.screen_app_lock_setup_pin_blacklisted_dialog_title) + CreatePinFailure.PinsDontMatch -> stringResource(id = R.string.screen_app_lock_setup_pin_mismatch_dialog_title) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt index 2228110156..a97315f2e8 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/model/PinEntry.kt @@ -32,7 +32,7 @@ data class PinEntry( } } - private val size = digits.size + val size = digits.size /** * Fill the first digits with the given text. diff --git a/features/lockscreen/impl/src/main/res/values/localazy.xml b/features/lockscreen/impl/src/main/res/values/localazy.xml new file mode 100644 index 0000000000..fb5c2be73c --- /dev/null +++ b/features/lockscreen/impl/src/main/res/values/localazy.xml @@ -0,0 +1,24 @@ + + + + "Wrong PIN. You have %1$d more chance" + "Wrong PIN. You have %1$d more chances" + + "Forgot PIN?" + "Change PIN code" + "Allow biometric unlock" + "Remove PIN" + "Are you sure you want to remove PIN?" + "Remove PIN?" + "Choose %1$d digit PIN" + "Confirm PIN" + "You cannot choose this as your PIN code for security reasons" + "Choose a different PIN" + "Lock Element to add extra security to your chats." + "Choose something memorable. If you forget this PIN, you will be logged out of the app." + "Please enter the same PIN twice" + "PINs don\'t match" + "You’ll need to re-login and create a new PIN to proceed" + "You are being signed out" + "You have 3 attempts to unlock" + diff --git a/features/logout/api/src/main/res/values/localazy.xml b/features/logout/api/src/main/res/values/localazy.xml index 9ea4bb77fd..5a5c9c64ca 100644 --- a/features/logout/api/src/main/res/values/localazy.xml +++ b/features/logout/api/src/main/res/values/localazy.xml @@ -1,8 +1,12 @@ + "Please wait for this to complete before signing out." + "Your keys are still being backed up" "Are you sure you want to sign out?" "Sign out" "Signing out…" + "You are about to sign out of your last session. If you sign out now, you might lose access to your encrypted messages." + "Have you saved your recovery key?" "Sign out" "Sign out" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index b9a6460bec..c7bd2f2ca9 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -5,6 +5,7 @@ "Mentions only" "Muted" "Pause" + "PIN field" "Play" "Poll" "Ended poll" @@ -69,6 +70,8 @@ "Share" "Share link" "Sign in again" + "Sign out" + "Sign out anyway" "Skip" "Start" "Start chat" @@ -84,6 +87,7 @@ "Analytics" "Audio" "Bubbles" + "Chat backup" "Copyright" "Creating room…" "Left room" @@ -93,6 +97,7 @@ "Editing" "* %1$s %2$s" "Encryption enabled" + "Enter your PIN" "Error" "Everyone" "File" @@ -122,6 +127,7 @@ "Privacy policy" "Reaction" "Reactions" + "Recovery key" "Refreshing…" "Replying to %1$s" "Report a bug" @@ -129,9 +135,9 @@ "Rich text editor" "Room name" "e.g. your project name" + "Screen lock" "Search for someone" "Search results" - "Secure backup" "Security" "Sending…" "Server not supported" @@ -151,6 +157,7 @@ "Unable to decrypt" "Invites couldn\'t be sent to one or more users." "Unable to send invite(s)" + "Unlock" "Unmute" "Unsupported event" "Username" @@ -177,6 +184,7 @@ "%1$s could not access your location. Please try again later." "%1$s does not have permission to access your location. You can enable access in Settings." "%1$s does not have permission to access your location. Enable access below." + "%1$s does not have permission to access your microphone. Enable access to record a voice message." "Some messages have not been sent" "Sorry, an error occurred" "🔐️ Join me on %1$s" @@ -185,6 +193,10 @@ "Are you sure that you want to leave this room? This room is not public and you won\'t be able to rejoin without an invite." "Are you sure that you want to leave the room?" "%1$s Android" + + "%1$d digit entered" + "%1$d digits entered" + "%1$d member" "%1$d members" @@ -199,7 +211,26 @@ "This is the beginning of %1$s." "This is the beginning of this conversation." "New" + "Custom Element Call base URL" + "Set a custom base URL for Element Call." + "Invalid URL, please make sure you include the protocol (http/https) and the correct address." "Share analytics data" + "Turn off backup" + "Turn on backup" + "Backup ensures that you don\'t lose your message history. %1$s." + "Backup" + "Change recovery key" + "Confirm recovery key" + "Your chat backup is currently out of sync." + "Set up recovery" + "Get access to your encrypted messages if you lose all your devices or are signed out of %1$s everywhere." + "Turn off" + "You will lose your encrypted messages if you are signed out of all devices." + "Are you sure you want to turn off backup?" + "Turning off backup will remove your current encryption key backup and turn off other security features. In this case, you will:" + "Not have encrypted message history on new devices" + "Lose access to your encrypted messages if you are signed out of %1$s everywhere" + "Are you sure you want to turn off backup?" "Failed selecting media, please try again." "Failed processing media to upload, please try again." "Failed uploading media, please try again." @@ -228,6 +259,27 @@ If you proceed, some of your settings may change." "system settings" "System notifications turned off" "Notifications" + "Get a new recovery key if you\'ve lost your existing one. After changing your recovery key, your old one will no longer work." + "Generate a new recovery key" + "Make sure you can store your recovery key somewhere safe" + "Recovery key changed" + "Change recovery key?" + "Enter your recovery key to confirm access to your chat backup." + "Enter the 48 character code." + "Enter…" + "Recovery key confirmed" + "Confirm your recovery key" + "Save recovery key" + "Write down your recovery key somewhere safe or save it in a password manager." + "Tap to copy recovery key" + "Save your recovery key" + "You will not be able to access your new recovery key after this step." + "Have you saved your recovery key?" + "Your chat backup is protected by a recovery key. If you need a new recovery key after setup you can recreate by selecting ‘Change recovery key’." + "Generate your recovery key" + "Make sure you can store your recovery key somewhere safe" + "Recovery setup successful" + "Set up recovery" "Check if you want to hide all current and future messages from this user" "Share location" "Share my location" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 7e07d269c0..fd35feb36e 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -153,6 +153,12 @@ "includeRegex": [ "call_.*" ] + }, + { + "name": ":features:lockscreen:impl", + "includeRegex": [ + "screen_app_lock_.*" + ] } ] } From 6c8e0bd86e24cfed03605aeebc9dd74b1f3edba8 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 19 Oct 2023 16:29:16 +0200 Subject: [PATCH 10/16] Create pin : change digit size box --- .../android/features/lockscreen/impl/create/CreatePinView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt index dc9956abaa..fa79016472 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt @@ -211,7 +211,7 @@ private fun PinDigitView( } Box( modifier = modifier - .size(40.dp, 50.dp) + .size(48.dp) .then(appearanceModifier), contentAlignment = Alignment.Center, From 8ae07ba74b89dec02ca9a1ba0505ce00699cd837 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 19 Oct 2023 16:32:58 +0200 Subject: [PATCH 11/16] Create pin : fix konsist --- .../android/features/lockscreen/impl/create/CreatePinView.kt | 2 +- .../features/lockscreen/impl/create/CreatePinPresenterTest.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt index fa79016472..4e01de8c2f 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt @@ -161,7 +161,7 @@ private fun CreatePinFailure.title(): String { } @Composable -fun PinEntryTextField( +private fun PinEntryTextField( pinEntry: PinEntry, onValueChange: (String) -> Unit, modifier: Modifier = Modifier, diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt index 9c86039fe1..c1af14f519 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt @@ -38,7 +38,7 @@ class CreatePinPresenterTest { @Test fun `present - complete flow`() = runTest { - val presenter = createPresenter() + val presenter = createCreatePinPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -107,7 +107,7 @@ class CreatePinPresenterTest { assertThat(isEmpty).isTrue() } - private fun createPresenter(): CreatePinPresenter { + private fun createCreatePinPresenter(): CreatePinPresenter { return CreatePinPresenter(PinValidator()) } } From 1c1db6a444ebd9f1f0afc4c65f857d1679c99e20 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 19 Oct 2023 15:24:30 +0000 Subject: [PATCH 12/16] Update screenshots --- ...create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png index 03b059496a..f110f617f8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df5f2cc45255cec07dc99de8df0f2e8dd06fdc3afded3ba43c8deec2bb7c1d0b -size 34529 +oid sha256:4e2d58ea747cf87fd6e4f70b3ebb1f1e638a4ec3f7ac8cb712566a889167c3e1 +size 35045 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png index ffcb20390e..e5a3fe28af 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c5f338938adeeb280be0b68f57b5ba0b4c9a904e8660e99fb6e57cd6b4774fc -size 34534 +oid sha256:4be560206e4fc9101d8a793ad5f5f9cc2057fb47e60f37f91ce6dae8e2b995e2 +size 35147 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png index 5e82145176..87cefa723f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a412e9c38549341f707e9a6379e165d554294c0f452fec82ab783fa9e0533ad7 -size 32374 +oid sha256:da83f8b634c8217636b08792498704d9ffc1623c8d4aedfb15265d2f5e085e86 +size 32805 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png index 2fdd3802d4..d3d3ba4038 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2fbba51aa681041018dd41f9f2e69159e229411b2cf438a62367a7f1edbb857a -size 28781 +oid sha256:598214e07c2545ac4dd061f6237a737e91e9c47eeea01a63ab064b3398e39b68 +size 24046 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png index 26305ae91e..7476004b1f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:843141e92c23f9cbba98296fc543c365d33a708daaabe0ff9549ccb8d6f69af4 -size 36969 +oid sha256:0ec4a6f05bfb15e018c19205cb919f6619440e25392ed0a74fb2896ddb1decb6 +size 28266 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png index c3a42144c6..41a349426e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be489eaf748f7d79ee7d8dd3f0177ab47626728f5b4dae9851b98e708f31e97c -size 32962 +oid sha256:b7c84953e8c1c3b2384078be4ca8cd5c763c032bfa22bb2ba034dcbee8ef9c72 +size 33594 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png index a37f885bb6..8f6c3fdbba 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb6369c1574670c81f2d0f5a541cc86da708cf087539250b50a1bb86fee33894 -size 33146 +oid sha256:0917206ffe964311097f05de3fb838e0cc23f6fdbb2cd106d3015a751b378cfc +size 33864 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png index f5c161a105..64bc9a11cd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72836a8215d294ccb10dd961c5bde18342f50ab4aedf23c6bd5e655edd733a4a -size 31348 +oid sha256:32b2c6514d9a23da09c932e198f29ec112b5afa872a0fa42cd5de85689660891 +size 31658 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png index e78f91910a..6d10aeb87d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86591b97276d7379ae68c500a81a977ee0adb2f8b28e0896d0b32a658fb359bf -size 25430 +oid sha256:4bdedf444797479efa73d6ca7d3d6545f4bd01faa80d50980b0ad6ea427b09ee +size 20828 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png index 968d9fdff5..7c2736059e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:110139dd6177e5e1bd126bcd60e4442394867c2e0a3656bee7f15cc5c58c6a6e -size 32584 +oid sha256:51774321b68ee4d1f5a605edf1ce630e0f7651191e93cab6a885fc90f1639061 +size 24493 From 6dfba0f0c4d47ac531df97464e485fd50bf9cfeb Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 19 Oct 2023 22:23:19 +0200 Subject: [PATCH 13/16] Create pin : fix some spacing --- .../lockscreen/impl/create/CreatePinView.kt | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt index 4e01de8c2f..cddadbbae9 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt @@ -25,12 +25,16 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState 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.material.icons.Icons import androidx.compose.material.icons.filled.Lock import androidx.compose.material3.ExperimentalMaterial3Api @@ -49,7 +53,6 @@ import io.element.android.features.lockscreen.impl.create.model.PinDigit import io.element.android.features.lockscreen.impl.create.model.PinEntry import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule -import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreview @@ -77,13 +80,18 @@ fun CreatePinView( ) }, content = { padding -> - HeaderFooterPage( + val scrollState = rememberScrollState() + Column( modifier = Modifier + .imePadding() .padding(padding) - .consumeWindowInsets(padding), - header = { CreatePinHeader(state.isConfirmationStep, state.pinSize) }, - content = { CreatePinContent(state) } - ) + .consumeWindowInsets(padding) + .verticalScroll(state = scrollState) + .padding(vertical = 16.dp, horizontal = 20.dp), + ) { + CreatePinHeader(state.isConfirmationStep, state.pinSize) + CreatePinContent(state) + } } ) } @@ -99,7 +107,6 @@ private fun CreatePinHeader( horizontalAlignment = Alignment.CenterHorizontally, ) { IconTitleSubtitleMolecule( - modifier = Modifier.padding(top = 60.dp, bottom = 12.dp), title = if (isValidationStep) { stringResource(id = R.string.screen_app_lock_setup_confirm_pin) } else { @@ -110,7 +117,7 @@ private fun CreatePinHeader( ) Text( text = stringResource(id = R.string.screen_app_lock_setup_pin_context_warning), - modifier = Modifier.padding(8.dp), + modifier = Modifier.padding(top = 24.dp), textAlign = TextAlign.Center, style = ElementTheme.typography.fontBodyMdRegular, color = MaterialTheme.colorScheme.secondary, From e38ce7d9e0733971685ff137c7feba858e38af57 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 19 Oct 2023 20:39:07 +0000 Subject: [PATCH 14/16] Update screenshots --- ...create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png index f110f617f8..6d36e3b71b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e2d58ea747cf87fd6e4f70b3ebb1f1e638a4ec3f7ac8cb712566a889167c3e1 -size 35045 +oid sha256:8ddeca63feb6f81e2db0da909657f1f90cfb13facb8ce854cc79e4eb33e329ee +size 34635 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png index e5a3fe28af..3981b8e841 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4be560206e4fc9101d8a793ad5f5f9cc2057fb47e60f37f91ce6dae8e2b995e2 -size 35147 +oid sha256:28dbde9da91754c0377667bc852b8df3f17d2c6d91f0be4b04a5762cda7995e6 +size 34703 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png index 87cefa723f..a054b68cf1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da83f8b634c8217636b08792498704d9ffc1623c8d4aedfb15265d2f5e085e86 -size 32805 +oid sha256:e06d34c115e1e3ecadf73a78701f8e565f0bc9cd38d1eb3f007c0ec4486cbf3e +size 32319 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png index d3d3ba4038..fede94e2a3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:598214e07c2545ac4dd061f6237a737e91e9c47eeea01a63ab064b3398e39b68 -size 24046 +oid sha256:9368e0cae34cde6e8559702c8de4cb42d684cea5f13cbd62d9ab0b7de918e684 +size 28624 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png index 7476004b1f..9f40f229df 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ec4a6f05bfb15e018c19205cb919f6619440e25392ed0a74fb2896ddb1decb6 -size 28266 +oid sha256:3a948fedb7df6d6cd25793d9aab2a96487bd1a7bd31ba8bacb44026d0c97582c +size 36925 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png index 41a349426e..e1cff5f4ef 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7c84953e8c1c3b2384078be4ca8cd5c763c032bfa22bb2ba034dcbee8ef9c72 -size 33594 +oid sha256:e17eaf4585825f04fb121296d4b1370d652fc64d28149c285ac28849267ea9f0 +size 33051 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png index 8f6c3fdbba..b67c6d82ed 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0917206ffe964311097f05de3fb838e0cc23f6fdbb2cd106d3015a751b378cfc -size 33864 +oid sha256:9b0b7a756d5195a5397ef4eeba5da8b1cbdc8a4a182e935d03ef9d258d921b5f +size 33278 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png index 64bc9a11cd..f6c53f382a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32b2c6514d9a23da09c932e198f29ec112b5afa872a0fa42cd5de85689660891 -size 31658 +oid sha256:d42ed3cb545fc96e1840db73c8efdc6782a545572539855ccceceda50663f284 +size 31445 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png index 6d10aeb87d..2047696c67 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bdedf444797479efa73d6ca7d3d6545f4bd01faa80d50980b0ad6ea427b09ee -size 20828 +oid sha256:75785d23eafb006cbfbbab3e6824364a419f56d0a7d2d2a734b6f0947ca7843b +size 25358 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png index 7c2736059e..21e905b2df 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51774321b68ee4d1f5a605edf1ce630e0f7651191e93cab6a885fc90f1639061 -size 24493 +oid sha256:624d62ed7dd52617d8c02bcf5be4ded2707500e930d56f4362c834226e6108bd +size 32514 From 78d264c6fc51d3ab4331f9cc7d9977ec1165821f Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 20 Oct 2023 10:54:59 +0200 Subject: [PATCH 15/16] Create PIN : hopefully fix remaining issues --- .../impl/create/CreatePinPresenter.kt | 3 +++ .../lockscreen/impl/create/CreatePinState.kt | 1 + .../impl/create/CreatePinStateProvider.kt | 1 + .../lockscreen/impl/create/CreatePinView.kt | 18 ++++-------------- .../impl/src/main/res/values/localazy.xml | 7 ++++--- .../impl/create/CreatePinPresenterTest.kt | 3 ++- 6 files changed, 15 insertions(+), 18 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt index e72e636ed4..957594c0f7 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenter.kt @@ -25,12 +25,14 @@ import io.element.android.features.lockscreen.impl.create.model.PinEntry import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure import io.element.android.features.lockscreen.impl.create.validation.PinValidator import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.core.meta.BuildMeta import javax.inject.Inject private const val PIN_SIZE = 4 class CreatePinPresenter @Inject constructor( private val pinValidator: PinValidator, + private val buildMeta: BuildMeta, ) : Presenter { @Composable @@ -94,6 +96,7 @@ class CreatePinPresenter @Inject constructor( confirmPinEntry = confirmPinEntry, isConfirmationStep = isConfirmationStep, createPinFailure = createPinFailure, + appName = buildMeta.applicationName, eventSink = ::handleEvents ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt index 5bb632f04e..020076a2ab 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinState.kt @@ -24,6 +24,7 @@ data class CreatePinState( val confirmPinEntry: PinEntry, val isConfirmationStep: Boolean, val createPinFailure: CreatePinFailure?, + val appName: String, val eventSink: (CreatePinEvents) -> Unit ) { val pinSize = choosePinEntry.size diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt index 543360f91e..c9dcce018d 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinStateProvider.kt @@ -55,6 +55,7 @@ fun aCreatePinState( confirmPinEntry = confirmPinEntry, isConfirmationStep = isConfirmationStep, createPinFailure = creationFailure, + appName = "Element", eventSink = {} ) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt index cddadbbae9..063d65f41f 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinView.kt @@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding @@ -38,14 +37,12 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Lock import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.lockscreen.impl.R @@ -89,7 +86,7 @@ fun CreatePinView( .verticalScroll(state = scrollState) .padding(vertical = 16.dp, horizontal = 20.dp), ) { - CreatePinHeader(state.isConfirmationStep, state.pinSize) + CreatePinHeader(state.isConfirmationStep, state.appName) CreatePinContent(state) } } @@ -99,7 +96,7 @@ fun CreatePinView( @Composable private fun CreatePinHeader( isValidationStep: Boolean, - pinSize: Int, + appName: String, modifier: Modifier = Modifier, ) { Column( @@ -110,18 +107,11 @@ private fun CreatePinHeader( title = if (isValidationStep) { stringResource(id = R.string.screen_app_lock_setup_confirm_pin) } else { - stringResource(id = R.string.screen_app_lock_setup_choose_pin, pinSize) + stringResource(id = R.string.screen_app_lock_setup_choose_pin) }, - subTitle = stringResource(id = R.string.screen_app_lock_setup_pin_context), + subTitle = stringResource(id = R.string.screen_app_lock_setup_pin_context, appName), iconImageVector = Icons.Filled.Lock, ) - Text( - text = stringResource(id = R.string.screen_app_lock_setup_pin_context_warning), - modifier = Modifier.padding(top = 24.dp), - textAlign = TextAlign.Center, - style = ElementTheme.typography.fontBodyMdRegular, - color = MaterialTheme.colorScheme.secondary, - ) } } diff --git a/features/lockscreen/impl/src/main/res/values/localazy.xml b/features/lockscreen/impl/src/main/res/values/localazy.xml index fb5c2be73c..6b12eac427 100644 --- a/features/lockscreen/impl/src/main/res/values/localazy.xml +++ b/features/lockscreen/impl/src/main/res/values/localazy.xml @@ -10,12 +10,13 @@ "Remove PIN" "Are you sure you want to remove PIN?" "Remove PIN?" - "Choose %1$d digit PIN" + "Choose PIN" "Confirm PIN" "You cannot choose this as your PIN code for security reasons" "Choose a different PIN" - "Lock Element to add extra security to your chats." - "Choose something memorable. If you forget this PIN, you will be logged out of the app." + "Lock %1$s to add extra security to your chats. + +Choose something memorable. If you forget this PIN, you will be logged out of the app." "Please enter the same PIN twice" "PINs don\'t match" "You’ll need to re-login and create a new PIN to proceed" diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt index c1af14f519..78536bb693 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/create/CreatePinPresenterTest.kt @@ -24,6 +24,7 @@ import io.element.android.features.lockscreen.impl.create.model.PinDigit import io.element.android.features.lockscreen.impl.create.model.PinEntry import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure import io.element.android.features.lockscreen.impl.create.validation.PinValidator +import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.tests.testutils.awaitLastSequentialItem import kotlinx.coroutines.test.runTest import org.junit.Test @@ -108,6 +109,6 @@ class CreatePinPresenterTest { } private fun createCreatePinPresenter(): CreatePinPresenter { - return CreatePinPresenter(PinValidator()) + return CreatePinPresenter(PinValidator(), aBuildMeta()) } } From 1b9cc7ded53d17ad8a384842057bb37b161cccb6 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 20 Oct 2023 09:12:23 +0000 Subject: [PATCH 16/16] Update screenshots --- ...create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png index 6d36e3b71b..c3d918b1c2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ddeca63feb6f81e2db0da909657f1f90cfb13facb8ce854cc79e4eb33e329ee -size 34635 +oid sha256:c9f1f9f900cea024cdf777867c33c1d1bbe4744a5b91cd994a03aa6cbf4f8815 +size 33186 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png index 3981b8e841..0c72d54e99 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28dbde9da91754c0377667bc852b8df3f17d2c6d91f0be4b04a5762cda7995e6 -size 34703 +oid sha256:d25ab8d3f53a5139b265a1ddef43ef3755b539a695aa8f43615e9197bd93471d +size 33231 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png index a054b68cf1..61c45c675c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e06d34c115e1e3ecadf73a78701f8e565f0bc9cd38d1eb3f007c0ec4486cbf3e -size 32319 +oid sha256:13f2b0ac39e3e5448916beea6f7a7183c8a205811343d3ff5e8a21a4d2d3f641 +size 32447 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png index fede94e2a3..f96a3743f3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9368e0cae34cde6e8559702c8de4cb42d684cea5f13cbd62d9ab0b7de918e684 -size 28624 +oid sha256:f031423cd98572a729f54e0bc4cdb50778aadd732aa93cf48d063075e9da00be +size 28636 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png index 9f40f229df..8a290a2303 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-D-1_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a948fedb7df6d6cd25793d9aab2a96487bd1a7bd31ba8bacb44026d0c97582c -size 36925 +oid sha256:2818a043ea06af263b1442c6e90727509da2ce2da036d68dce89de31fa8b013f +size 35614 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png index e1cff5f4ef..c1fb8f128f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e17eaf4585825f04fb121296d4b1370d652fc64d28149c285ac28849267ea9f0 -size 33051 +oid sha256:75145d74e0bebbad427626711d5bc86911bd89350e73c968c8ca9448e939cdbb +size 31954 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png index b67c6d82ed..b22bfb46dd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b0b7a756d5195a5397ef4eeba5da8b1cbdc8a4a182e935d03ef9d258d921b5f -size 33278 +oid sha256:2d5037348c8b27714e54ebec53aac8a546f14b11e3195434f681aad95e416847 +size 32181 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png index f6c53f382a..4b0a1988c5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d42ed3cb545fc96e1840db73c8efdc6782a545572539855ccceceda50663f284 -size 31445 +oid sha256:d0a34e1c3c047806e4304791664c25cd8d7c9e7850c24b2bb17119e0d83fd5d4 +size 31492 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png index 2047696c67..d0c1ab3c32 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75785d23eafb006cbfbbab3e6824364a419f56d0a7d2d2a734b6f0947ca7843b -size 25358 +oid sha256:d8c09be0dbf07d8593fd2f93881febdf3494bd439cb6f62adb78beee34b6c7e7 +size 25357 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png index 21e905b2df..05e4d69148 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.create_null_CreatePinView-N-1_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:624d62ed7dd52617d8c02bcf5be4ded2707500e930d56f4362c834226e6108bd -size 32514 +oid sha256:68a54676b1f82425df6db8cb56b161950584ce9d433fc59630a16ac6288fc7d8 +size 31204