diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/LockScreenConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/LockScreenConfig.kt new file mode 100644 index 0000000000..5930f53428 --- /dev/null +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/LockScreenConfig.kt @@ -0,0 +1,35 @@ +/* + * 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.appconfig + +object LockScreenConfig { + + /** + * Whether the LockScreen is mandatory or not. + */ + const val IS_MANDATORY: Boolean = false + + /** + * Some PINs are blacklisted. + */ + val PIN_BLACKLIST = listOf("0000", "1234") + + /** + * The size of the PIN. + */ + const val PIN_SIZE = 4 +} diff --git a/features/lockscreen/impl/build.gradle.kts b/features/lockscreen/impl/build.gradle.kts index a3f5b27949..a3657ccff3 100644 --- a/features/lockscreen/impl/build.gradle.kts +++ b/features/lockscreen/impl/build.gradle.kts @@ -30,9 +30,11 @@ anvil { } dependencies { + ksp(libs.showkase.processor) implementation(projects.anvilannotations) anvil(projects.anvilcodegen) api(projects.features.lockscreen.api) + implementation(projects.appconfig) implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) @@ -52,7 +54,4 @@ dependencies { testImplementation(projects.libraries.cryptography.test) testImplementation(projects.libraries.cryptography.impl) testImplementation(projects.libraries.featureflag.test) - - - ksp(libs.showkase.processor) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt index fceaf59f2a..91f6d435c5 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/components/PinEntryTextField.kt @@ -110,7 +110,7 @@ private fun PinDigitView( internal fun PinEntryTextFieldPreview() { ElementPreview { PinEntryTextField( - pinEntry = PinEntry.empty(4).fillWith("12"), + pinEntry = PinEntry.createEmpty(4).fillWith("12"), onValueChange = {}, ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinEntry.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinEntry.kt index 92dda869a6..eaca592de9 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinEntry.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinEntry.kt @@ -24,7 +24,7 @@ data class PinEntry( ) { companion object { - fun empty(size: Int): PinEntry { + fun createEmpty(size: Int): PinEntry { val digits = List(size) { PinDigit.Empty } return PinEntry( digits = digits.toPersistentList() @@ -69,7 +69,7 @@ data class PinEntry( } fun clear(): PinEntry { - return fillWith("") + return createEmpty(size) } fun isComplete(): Boolean { diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinPresenter.kt index 6a880a6967..3c380e6be7 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinPresenter.kt @@ -21,15 +21,14 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import io.element.android.appconfig.LockScreenConfig import io.element.android.features.lockscreen.impl.pin.model.PinEntry -import io.element.android.features.lockscreen.impl.setup.validation.SetupPinFailure import io.element.android.features.lockscreen.impl.setup.validation.PinValidator +import io.element.android.features.lockscreen.impl.setup.validation.SetupPinFailure 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 SetupPinPresenter @Inject constructor( private val pinValidator: PinValidator, private val buildMeta: BuildMeta, @@ -38,10 +37,10 @@ class SetupPinPresenter @Inject constructor( @Composable override fun present(): SetupPinState { var choosePinEntry by remember { - mutableStateOf(PinEntry.empty(PIN_SIZE)) + mutableStateOf(PinEntry.createEmpty(LockScreenConfig.PIN_SIZE)) } var confirmPinEntry by remember { - mutableStateOf(PinEntry.empty(PIN_SIZE)) + mutableStateOf(PinEntry.createEmpty(LockScreenConfig.PIN_SIZE)) } var isConfirmationStep by remember { mutableStateOf(false) @@ -77,11 +76,11 @@ class SetupPinPresenter @Inject constructor( SetupPinEvents.ClearFailure -> { when (setupPinFailure) { is SetupPinFailure.PinsDontMatch -> { - choosePinEntry = PinEntry.empty(PIN_SIZE) - confirmPinEntry = PinEntry.empty(PIN_SIZE) + choosePinEntry = choosePinEntry.clear() + confirmPinEntry = confirmPinEntry.clear() } is SetupPinFailure.PinBlacklisted -> { - choosePinEntry = PinEntry.empty(PIN_SIZE) + choosePinEntry = choosePinEntry.clear() } null -> Unit } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinStateProvider.kt index 1a177b4a83..bb0a46d10c 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinStateProvider.kt @@ -25,20 +25,20 @@ open class SetupPinStateProvider : PreviewParameterProvider { get() = sequenceOf( aSetupPinState(), aSetupPinState( - choosePinEntry = PinEntry.empty(4).fillWith("12") + choosePinEntry = PinEntry.createEmpty(4).fillWith("12") ), aSetupPinState( - choosePinEntry = PinEntry.empty(4).fillWith("1789"), + choosePinEntry = PinEntry.createEmpty(4).fillWith("1789"), isConfirmationStep = true, ), aSetupPinState( - choosePinEntry = PinEntry.empty(4).fillWith("1789"), - confirmPinEntry = PinEntry.empty(4).fillWith("1788"), + choosePinEntry = PinEntry.createEmpty(4).fillWith("1789"), + confirmPinEntry = PinEntry.createEmpty(4).fillWith("1788"), isConfirmationStep = true, creationFailure = SetupPinFailure.PinsDontMatch ), aSetupPinState( - choosePinEntry = PinEntry.empty(4).fillWith("1111"), + choosePinEntry = PinEntry.createEmpty(4).fillWith("1111"), creationFailure = SetupPinFailure.PinBlacklisted ), @@ -46,8 +46,8 @@ open class SetupPinStateProvider : PreviewParameterProvider { } fun aSetupPinState( - choosePinEntry: PinEntry = PinEntry.empty(4), - confirmPinEntry: PinEntry = PinEntry.empty(4), + choosePinEntry: PinEntry = PinEntry.createEmpty(4), + confirmPinEntry: PinEntry = PinEntry.createEmpty(4), isConfirmationStep: Boolean = false, creationFailure: SetupPinFailure? = null, ) = SetupPinState( diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/validation/PinValidator.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/validation/PinValidator.kt index c7435120aa..20ad023b1c 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/validation/PinValidator.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/validation/PinValidator.kt @@ -16,17 +16,12 @@ package io.element.android.features.lockscreen.impl.setup.validation -import androidx.annotation.VisibleForTesting +import io.element.android.appconfig.LockScreenConfig import io.element.android.features.lockscreen.impl.pin.model.PinEntry import javax.inject.Inject class PinValidator @Inject constructor() { - companion object { - @VisibleForTesting - val BLACKLIST = listOf("0000", "1234") - } - sealed interface Result { data object Valid : Result data class Invalid(val failure: SetupPinFailure) : Result @@ -34,7 +29,7 @@ class PinValidator @Inject constructor() { fun isPinValid(pinEntry: PinEntry): Result { val pinAsText = pinEntry.toText() - val isBlacklisted = BLACKLIST.any { it == pinAsText } + val isBlacklisted = LockScreenConfig.PIN_BLACKLIST.any { it == pinAsText } return if (isBlacklisted) { Result.Invalid(SetupPinFailure.PinBlacklisted) } else { diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvents.kt index 5560dedd24..30ee16df02 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvents.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvents.kt @@ -16,7 +16,7 @@ package io.element.android.features.lockscreen.impl.unlock -import io.element.android.features.lockscreen.impl.unlock.numpad.PinKeypadModel +import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel sealed interface PinUnlockEvents { data class OnPinKeypadPressed(val pinKeypadModel: PinKeypadModel) : PinUnlockEvents diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt index 783e03c92a..e189a2ab39 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt @@ -23,9 +23,10 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import io.element.android.appconfig.LockScreenConfig import io.element.android.features.lockscreen.api.LockScreenStateService import io.element.android.features.lockscreen.impl.pin.model.PinEntry -import io.element.android.features.lockscreen.impl.unlock.numpad.PinKeypadModel +import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel import io.element.android.libraries.architecture.Presenter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -39,9 +40,11 @@ class PinUnlockPresenter @Inject constructor( @Composable override fun present(): PinUnlockState { var pinEntry by remember { - mutableStateOf(PinEntry.empty(4)) + //TODO fetch size from db + mutableStateOf(PinEntry.createEmpty(LockScreenConfig.PIN_SIZE)) } var remainingAttempts by rememberSaveable { + //TODO fetch from db mutableIntStateOf(3) } var showWrongPinTitle by rememberSaveable { @@ -56,6 +59,7 @@ class PinUnlockPresenter @Inject constructor( is PinUnlockEvents.OnPinKeypadPressed -> { pinEntry = pinEntry.process(event.pinKeypadModel) if (pinEntry.isComplete()) { + //TODO check pin with PinCodeManager coroutineScope.launch { pinStateService.unlock() } } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt index 4f269d2f5a..8ddc942e25 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt @@ -23,7 +23,7 @@ open class PinUnlockStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aPinUnlockState(), - aPinUnlockState(pinEntry = PinEntry.empty(4).fillWith("12")), + aPinUnlockState(pinEntry = PinEntry.createEmpty(4).fillWith("12")), aPinUnlockState(showWrongPinTitle = true), aPinUnlockState(showSignOutPrompt = true), aPinUnlockState(showSignOutPrompt = true, remainingAttempts = 0), @@ -31,7 +31,7 @@ open class PinUnlockStateProvider : PreviewParameterProvider { } fun aPinUnlockState( - pinEntry: PinEntry = PinEntry.empty(4), + pinEntry: PinEntry = PinEntry.createEmpty(4), remainingAttempts: Int = 3, showWrongPinTitle: Boolean = false, showSignOutPrompt: Boolean = false, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt index e2cf522a07..37cb591007 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt @@ -47,7 +47,7 @@ import androidx.compose.ui.unit.dp import io.element.android.features.lockscreen.impl.R import io.element.android.features.lockscreen.impl.pin.model.PinDigit import io.element.android.features.lockscreen.impl.pin.model.PinEntry -import io.element.android.features.lockscreen.impl.unlock.numpad.PinKeypad +import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypad import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreview diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/numpad/PinKeypad.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypad.kt similarity index 99% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/numpad/PinKeypad.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypad.kt index 46b9ca1b82..1056718dd0 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/numpad/PinKeypad.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypad.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.lockscreen.impl.unlock.numpad +package io.element.android.features.lockscreen.impl.unlock.keypad import androidx.compose.foundation.background import androidx.compose.foundation.clickable diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/numpad/PinKeypadModel.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypadModel.kt similarity index 92% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/numpad/PinKeypadModel.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypadModel.kt index f1430dcaa5..8d232cb21b 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/numpad/PinKeypadModel.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypadModel.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.lockscreen.impl.unlock.numpad +package io.element.android.features.lockscreen.impl.unlock.keypad import androidx.compose.runtime.Immutable diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinPresenterTest.kt index 94877fbff5..21005cc722 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinPresenterTest.kt @@ -20,6 +20,7 @@ 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.appconfig.LockScreenConfig import io.element.android.features.lockscreen.impl.pin.model.assertEmpty import io.element.android.features.lockscreen.impl.pin.model.assertText import io.element.android.features.lockscreen.impl.setup.validation.PinValidator @@ -31,7 +32,7 @@ import org.junit.Test class SetupPinPresenterTest { - private val blacklistedPin = PinValidator.BLACKLIST.first() + private val blacklistedPin = LockScreenConfig.PIN_BLACKLIST private val halfCompletePin = "12" private val completePin = "1235" private val mismatchedPin = "1236" diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt index 6839fe65c7..02919edce0 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt @@ -23,7 +23,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.lockscreen.impl.pin.model.assertEmpty import io.element.android.features.lockscreen.impl.pin.model.assertText import io.element.android.features.lockscreen.impl.state.DefaultLockScreenStateService -import io.element.android.features.lockscreen.impl.unlock.numpad.PinKeypadModel +import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.tests.testutils.awaitLastSequentialItem