Pin: add LockScreenConfig and address PR reviews
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -25,20 +25,20 @@ open class SetupPinStateProvider : PreviewParameterProvider<SetupPinState> {
|
||||
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<SetupPinState> {
|
||||
}
|
||||
|
||||
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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ open class PinUnlockStateProvider : PreviewParameterProvider<PinUnlockState> {
|
||||
override val values: Sequence<PinUnlockState>
|
||||
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<PinUnlockState> {
|
||||
}
|
||||
|
||||
fun aPinUnlockState(
|
||||
pinEntry: PinEntry = PinEntry.empty(4),
|
||||
pinEntry: PinEntry = PinEntry.createEmpty(4),
|
||||
remainingAttempts: Int = 3,
|
||||
showWrongPinTitle: Boolean = false,
|
||||
showSignOutPrompt: Boolean = false,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user