mirror of
https://github.com/ProtonMail/android-mail.git
synced 2026-05-15 09:50:40 +00:00
Add unit tests to mail-pin-mock
ET-6548
This commit is contained in:
+40
@@ -1,9 +1,37 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="FunctionName" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="namePattern" value="[a-zA-Z][A-Za-z\d]*" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ObjectPropertyName" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="namePattern" value="[A-Za-z][- _A-Za-z\d]*" />
|
||||
</inspection_tool>
|
||||
@@ -11,6 +39,14 @@
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
@@ -35,6 +71,10 @@
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
|
||||
@@ -165,11 +165,6 @@
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" />
|
||||
|
||||
<meta-data
|
||||
android:name="ch.protonmail.android.initializer.AutoLockHandlerInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" />
|
||||
|
||||
<meta-data
|
||||
android:name="androidx.lifecycle.ProcessLifecycleInitializer"
|
||||
android:value="androidx.startup" />
|
||||
@@ -239,15 +234,6 @@
|
||||
|
||||
<receiver android:name=".mailnotifications.data.local.PushNotificationActionsBroadcastReceiver" />
|
||||
|
||||
<receiver
|
||||
android:name=".mailsettings.presentation.settings.autolock.broadcastreceiver.TimeSetBroadcastReceiver"
|
||||
android:exported="false">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.TIME_SET" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
||||
@@ -60,7 +60,6 @@ class MainInitializer : Initializer<Unit> {
|
||||
ThemeObserverInitializer::class.java,
|
||||
NotificationInitializer::class.java,
|
||||
NotificationHandlersInitializer::class.java,
|
||||
AutoLockHandlerInitializer::class.java,
|
||||
RustMailCommonInitializer::class.java,
|
||||
ChallengeInitializer::class.java,
|
||||
BackgroundExecutionInitializer::class.java,
|
||||
|
||||
@@ -140,13 +140,15 @@ internal fun NavGraphBuilder.addPrivacySettings(navController: NavHostController
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("ForbiddenComment")
|
||||
internal fun NavGraphBuilder.addAutoLockSettings(navController: NavHostController) {
|
||||
composable(route = Screen.AutoLockSettings.route) {
|
||||
ProtonInvertedTheme {
|
||||
AutoLockSettingsScreen(
|
||||
modifier = Modifier,
|
||||
actions = AutoLockSettingsScreen.Actions(
|
||||
onPinScreenNavigation = {},//{ navController.navigate(Screen.AutoLockPinScreen(it)) },
|
||||
// TODO ET-6548 { navController.navigate(Screen.AutoLockPinScreen(it)) },
|
||||
onPinScreenNavigation = {},
|
||||
onBackClick = { navController.navigateBack() },
|
||||
onChangeIntervalClick = { navController.navigate(Screen.AutolockInterval.route) }
|
||||
)
|
||||
|
||||
+1
-1
@@ -25,4 +25,4 @@ import uniffi.proton_mail_uniffi.MailSession
|
||||
interface AppLockDataSource {
|
||||
|
||||
suspend fun shouldAutoLock(mailSession: MailSession): Either<DataError, Boolean>
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -34,5 +34,4 @@ class RustAppLockDataSource @Inject constructor() : AppLockDataSource {
|
||||
is MailSessionShouldAutoLockResult.Error -> result.v1.toDataError().left()
|
||||
is MailSessionShouldAutoLockResult.Ok -> result.v1.right()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+13
-15
@@ -25,21 +25,19 @@ import ch.protonmail.android.mailpinlock.model.BiometricsSystemState
|
||||
import ch.protonmail.android.mailpinlock.model.Protection
|
||||
import ch.protonmail.android.mailsettings.domain.model.AppSettings
|
||||
|
||||
fun AppSettings.toAutolock(biometricsState: BiometricsSystemState) =
|
||||
Autolock(
|
||||
autolockInterval = autolockInterval,
|
||||
protectionType = autolockProtection,
|
||||
biometricsState = biometricsState.toAutolockBiometrics(autolockProtection == Protection.Biometrics)
|
||||
)
|
||||
fun AppSettings.toAutolock(biometricsState: BiometricsSystemState) = Autolock(
|
||||
autolockInterval = autolockInterval,
|
||||
protectionType = autolockProtection,
|
||||
biometricsState = biometricsState.toAutolockBiometrics(autolockProtection == Protection.Biometrics)
|
||||
)
|
||||
|
||||
fun BiometricsSystemState.toAutolockBiometrics(enrolled: Boolean) =
|
||||
when (this) {
|
||||
is BiometricsSystemState.BiometricNotAvailable ->
|
||||
AutoLockBiometricsState.BiometricsNotAvailable
|
||||
fun BiometricsSystemState.toAutolockBiometrics(enrolled: Boolean) = when (this) {
|
||||
is BiometricsSystemState.BiometricNotAvailable ->
|
||||
AutoLockBiometricsState.BiometricsNotAvailable
|
||||
|
||||
is BiometricsSystemState.BiometricEnrolled ->
|
||||
BiometricsAvailable.BiometricsEnrolled(enrolled)
|
||||
is BiometricsSystemState.BiometricEnrolled ->
|
||||
BiometricsAvailable.BiometricsEnrolled(enrolled)
|
||||
|
||||
is BiometricsSystemState.BiometricNotEnrolled ->
|
||||
BiometricsAvailable.BiometricsNotEnrolled
|
||||
}
|
||||
is BiometricsSystemState.BiometricNotEnrolled ->
|
||||
BiometricsAvailable.BiometricsNotEnrolled
|
||||
}
|
||||
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and Proton Mail.
|
||||
*
|
||||
* Proton Mail is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Mail is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.mailpinlock.data
|
||||
|
||||
import app.cash.turbine.test
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import ch.protonmail.android.mailcommon.domain.model.DataError
|
||||
import ch.protonmail.android.mailpinlock.domain.BiometricsSystemStateRepository
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockBiometricsState
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockInterval
|
||||
import ch.protonmail.android.mailpinlock.model.BiometricsSystemState
|
||||
import ch.protonmail.android.mailpinlock.model.Protection
|
||||
import ch.protonmail.android.mailsession.data.repository.MailSessionRepository
|
||||
import ch.protonmail.android.mailsession.data.wrapper.MailSessionWrapper
|
||||
import ch.protonmail.android.mailsettings.data.local.RustAppSettingsDataSource
|
||||
import ch.protonmail.android.mailsettings.data.repository.AppSettingsRepository
|
||||
import ch.protonmail.android.mailsettings.domain.model.AppLanguage
|
||||
import ch.protonmail.android.mailsettings.domain.model.AppSettings
|
||||
import ch.protonmail.android.mailsettings.domain.model.Theme
|
||||
import ch.protonmail.android.mailsettings.domain.repository.AppLanguageRepository
|
||||
import ch.protonmail.android.test.utils.rule.LoggingTestRule
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import uniffi.proton_mail_uniffi.AppAppearance
|
||||
import uniffi.proton_mail_uniffi.AppProtection
|
||||
import uniffi.proton_mail_uniffi.AutoLock
|
||||
import uniffi.proton_mail_uniffi.MailSession
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class AutolockRepositoryTest {
|
||||
|
||||
@get:Rule
|
||||
val loggingTestRule = LoggingTestRule()
|
||||
|
||||
private val mockMailSession = mockk<MailSession>()
|
||||
private val appSettingsDataSource = mockk<RustAppSettingsDataSource> {
|
||||
coEvery { this@mockk.updateAppSettings(mockMailSession, any()) } returns Unit.right()
|
||||
}
|
||||
private val appLockDataSource = mockk<AppLockDataSource>()
|
||||
private val appLanguageRepository = mockk<AppLanguageRepository> {
|
||||
every { this@mockk.observe() } returns flowOf(AppLanguage.FRENCH)
|
||||
}
|
||||
|
||||
private val mockMailSessionWrapper = mockk<MailSessionWrapper> {
|
||||
every { this@mockk.getRustMailSession() } returns mockMailSession
|
||||
}
|
||||
|
||||
private val mailSessionRepository = mockk<MailSessionRepository> {
|
||||
every { this@mockk.getMailSession() } returns mockMailSessionWrapper
|
||||
}
|
||||
|
||||
private val expectedBiometrics = AutoLockBiometricsState.BiometricsAvailable.BiometricsEnrolled(false)
|
||||
private val biometricsStateRepository = mockk<BiometricsSystemStateRepository> {
|
||||
every { this@mockk.observe() } returns flowOf(BiometricsSystemState.BiometricEnrolled)
|
||||
}
|
||||
|
||||
private val appSettingsRepository: AppSettingsRepository =
|
||||
AppSettingsRepository(
|
||||
mailSessionRepository = mailSessionRepository,
|
||||
rustAppSettingsDataSource = appSettingsDataSource,
|
||||
appLanguageRepository = appLanguageRepository
|
||||
)
|
||||
|
||||
private var autolockRepository: AutolockRepository =
|
||||
AutolockRepository(
|
||||
biometricsSystemStateRepository = biometricsStateRepository,
|
||||
appSettingsRepository = appSettingsRepository,
|
||||
mailSessionRepository = mailSessionRepository,
|
||||
appLockDataSource = appLockDataSource
|
||||
)
|
||||
|
||||
private val mockAppSettings = uniffi.proton_mail_uniffi.AppSettings(
|
||||
AppAppearance.LIGHT_MODE,
|
||||
AppProtection.PIN,
|
||||
AutoLock.Always,
|
||||
useCombineContacts = true,
|
||||
useAlternativeRouting = true
|
||||
)
|
||||
|
||||
private val expectedAppSettings = AppSettings(
|
||||
autolockInterval = AutoLockInterval.Immediately,
|
||||
autolockProtection = Protection.Pin,
|
||||
hasAlternativeRouting = true,
|
||||
customAppLanguage = AppLanguage.FRENCH.langName,
|
||||
hasDeviceContactsEnabled = true,
|
||||
theme = Theme.LIGHT
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `returns protection when observed`() = runTest {
|
||||
// Given
|
||||
coEvery {
|
||||
appSettingsDataSource.getAppSettings(any())
|
||||
} returns mockAppSettings.right()
|
||||
// When
|
||||
autolockRepository.observeAppLock().test {
|
||||
// Then
|
||||
assertEquals(expectedAppSettings.autolockProtection, awaitItem().protectionType)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns interval when observed`() = runTest {
|
||||
// Given
|
||||
coEvery {
|
||||
appSettingsDataSource.getAppSettings(any())
|
||||
} returns mockAppSettings.right()
|
||||
// When
|
||||
autolockRepository.observeAppLock().test {
|
||||
// Then
|
||||
assertEquals(expectedAppSettings.autolockInterval, awaitItem().autolockInterval)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns biometrics when observed`() = runTest {
|
||||
// Given
|
||||
coEvery { appLockDataSource.shouldAutoLock(mockMailSession) } returns false.right()
|
||||
coEvery {
|
||||
appSettingsDataSource.getAppSettings(any())
|
||||
} returns mockAppSettings.right()
|
||||
// When
|
||||
autolockRepository.observeAppLock().test {
|
||||
// Then
|
||||
assertEquals(expectedBiometrics, awaitItem().biometricsState)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when interval is updated then observer is also updated`() = runTest {
|
||||
val expectedUpdatedInterval = AutoLockInterval.FifteenMinutes
|
||||
val updatedAppSettings = mockAppSettings.copy(autoLock = AutoLock.Minutes(15L.toUByte()))
|
||||
// Given
|
||||
coEvery {
|
||||
appSettingsDataSource.getAppSettings(mockMailSession)
|
||||
} returns mockAppSettings.right() andThen updatedAppSettings.right()
|
||||
// When
|
||||
|
||||
autolockRepository.observeAppLock().test {
|
||||
// Then
|
||||
assertEquals(expectedAppSettings.autolockInterval, awaitItem().autolockInterval)
|
||||
|
||||
autolockRepository.updateAutolockInterval(expectedUpdatedInterval)
|
||||
|
||||
assertEquals(expectedUpdatedInterval, awaitItem().autolockInterval)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when shouldAutolock THEN return result`() = runTest {
|
||||
coEvery { appLockDataSource.shouldAutoLock(mockMailSession) } returns true.right()
|
||||
val result = autolockRepository.shouldAutolock()
|
||||
assert(result.isRight())
|
||||
assertTrue(result.getOrNull()!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when shouldAutolock is False THEN return result`() = runTest {
|
||||
coEvery { appLockDataSource.shouldAutoLock(mockMailSession) } returns false.right()
|
||||
val result = autolockRepository.shouldAutolock()
|
||||
|
||||
assert(result.isRight())
|
||||
assertFalse(result.getOrNull()!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when shouldAutolock fails then return error`() = runTest {
|
||||
coEvery { appLockDataSource.shouldAutoLock(mockMailSession) } returns DataError.Local.Unknown.left()
|
||||
val result = autolockRepository.shouldAutolock()
|
||||
|
||||
assert(result.isLeft())
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -105,4 +105,4 @@ class BiometricsSystemStateRepositoryImplTest {
|
||||
// Then
|
||||
assertEquals(BiometricsSystemState.BiometricEnrolled, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and Proton Mail.
|
||||
*
|
||||
* Proton Mail is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Mail is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.mailpinlock.data
|
||||
|
||||
import arrow.core.left
|
||||
import ch.protonmail.android.mailcommon.domain.model.DataError
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import uniffi.proton_mail_uniffi.MailSession
|
||||
import uniffi.proton_mail_uniffi.MailSessionShouldAutoLockResult
|
||||
import uniffi.proton_mail_uniffi.SessionErrorReason.UNKNOWN_LABEL
|
||||
import uniffi.proton_mail_uniffi.UserSessionError
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class RustAppLockDataSourceTest {
|
||||
|
||||
private val sut = RustAppLockDataSource()
|
||||
|
||||
@Test
|
||||
fun `when shouldAutoLock is TRUE then returns true right`() = runTest {
|
||||
val mailSession = mockk<MailSession> {
|
||||
coEvery { this@mockk.shouldAutoLock() } returns MailSessionShouldAutoLockResult.Ok(true)
|
||||
}
|
||||
val result = sut.shouldAutoLock(mailSession)
|
||||
assertTrue(result.getOrNull()!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when shouldAutoLock FALSE then returns false right`() = runTest {
|
||||
val mailSession = mockk<MailSession> {
|
||||
coEvery { this@mockk.shouldAutoLock() } returns MailSessionShouldAutoLockResult.Ok(false)
|
||||
}
|
||||
val result = sut.shouldAutoLock(mailSession)
|
||||
assertFalse(result.getOrNull()!!)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when shouldAutoLock and error then returns mapped error`() = runTest {
|
||||
val expectedError = DataError.Local.Unknown
|
||||
val mailSession = mockk<MailSession> {
|
||||
coEvery { this@mockk.shouldAutoLock() } returns
|
||||
MailSessionShouldAutoLockResult.Error(
|
||||
UserSessionError.Reason(
|
||||
UNKNOWN_LABEL
|
||||
)
|
||||
)
|
||||
}
|
||||
val result = sut.shouldAutoLock(mailSession)
|
||||
assertEquals(expectedError.left(), result)
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and Proton Mail.
|
||||
*
|
||||
* Proton Mail is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Mail is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.mailpinlock.data.mapper
|
||||
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockBiometricsState
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockInterval
|
||||
import ch.protonmail.android.mailpinlock.model.Autolock
|
||||
import ch.protonmail.android.mailpinlock.model.BiometricsSystemState
|
||||
import ch.protonmail.android.mailpinlock.model.Protection
|
||||
import ch.protonmail.android.mailsettings.domain.model.AppSettings
|
||||
import ch.protonmail.android.mailsettings.domain.model.Theme
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class AutolockMapperTest {
|
||||
|
||||
private val appSettings = AppSettings(
|
||||
theme = Theme.DARK,
|
||||
autolockInterval = AutoLockInterval.FifteenMinutes,
|
||||
autolockProtection = Protection.Biometrics,
|
||||
customAppLanguage = "en",
|
||||
hasDeviceContactsEnabled = true,
|
||||
hasAlternativeRouting = false
|
||||
)
|
||||
|
||||
private val biometricsSystemState = BiometricsSystemState.BiometricEnrolled
|
||||
private val biometricsState = AutoLockBiometricsState.BiometricsAvailable.BiometricsEnrolled(true)
|
||||
|
||||
private val expected = Autolock(
|
||||
autolockInterval = AutoLockInterval.FifteenMinutes,
|
||||
protectionType = Protection.Biometrics,
|
||||
biometricsState = biometricsState
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `map appSettings to autolock WHEN biometrics enrolled`() {
|
||||
val actual = appSettings.toAutolock(biometricsSystemState)
|
||||
assertEquals(actual.autolockInterval, expected.autolockInterval)
|
||||
assertEquals(actual.protectionType, expected.protectionType)
|
||||
assertEquals(actual.biometricsState, expected.biometricsState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `map appSettings to autolock WHEN biometrics not enrolled`() {
|
||||
appSettings.copy(autolockProtection = Protection.Pin)
|
||||
expected.copy(biometricsState = AutoLockBiometricsState.BiometricsAvailable.BiometricsEnrolled(false))
|
||||
val actual = appSettings.toAutolock(biometricsSystemState)
|
||||
assertEquals(actual.autolockInterval, expected.autolockInterval)
|
||||
assertEquals(actual.protectionType, expected.protectionType)
|
||||
assertEquals(actual.biometricsState, expected.biometricsState)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -24,4 +24,4 @@ import kotlinx.coroutines.flow.Flow
|
||||
interface BiometricsSystemStateRepository {
|
||||
fun getCurrentState(): BiometricsSystemState
|
||||
fun observe(): Flow<BiometricsSystemState>
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -20,13 +20,15 @@ package ch.protonmail.android.mailpinlock.domain.usecase
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO convert to rust
|
||||
@Suppress("ForbiddenComment")
|
||||
// TODO ET-6548 convert to rust
|
||||
class HasValidPinValue @Inject constructor(
|
||||
// private val observeAutoLockPinValue: ObserveAutoLockPinValue
|
||||
// private val observeAutoLockPinValue: ObserveAutoLockPinValue
|
||||
) {
|
||||
|
||||
// rust
|
||||
suspend operator fun invoke() = false /*observeAutoLockPinValue()
|
||||
private val isValid = false
|
||||
suspend operator fun invoke() = isValid
|
||||
/*observeAutoLockPinValue()
|
||||
.first()
|
||||
.getOrNull()
|
||||
?.value
|
||||
|
||||
+4
-2
@@ -20,9 +20,11 @@ package ch.protonmail.android.mailpinlock.domain.usecase
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO pin convert to rust
|
||||
@Suppress("ForbiddenComment")
|
||||
// TODO ET-6548 pin convert to rust
|
||||
class ToggleAutoLockAttemptPendingStatus @Inject constructor() {
|
||||
|
||||
suspend operator fun invoke(value: Boolean) = false
|
||||
val isPending = false
|
||||
suspend operator fun invoke(value: Boolean) = isPending
|
||||
// autoLockRepository.updateAutoLockAttemptPendingStatus(AutoLockAttemptPendingStatus(value))
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,4 +28,4 @@ sealed interface AutoLockBiometricsState {
|
||||
|
||||
data class BiometricsEnrolled(val enabled: Boolean) : BiometricsAvailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -19,13 +19,12 @@
|
||||
package ch.protonmail.android.mailpinlock.model
|
||||
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
data class Autolock(
|
||||
val autolockInterval: AutoLockInterval = AutoLockInterval.Never,
|
||||
val autolockInterval: AutoLockInterval = AutoLockInterval.Immediately,
|
||||
val protectionType: Protection = Protection.None,
|
||||
val biometricsState: AutoLockBiometricsState = AutoLockBiometricsState.BiometricsNotAvailable
|
||||
) {
|
||||
@@ -42,7 +41,7 @@ enum class AutoLockInterval(val duration: Duration) {
|
||||
FifteenMinutes(15.minutes),
|
||||
OneHour(1.hours),
|
||||
OneDay(24.hours),
|
||||
Never(36_000.days);
|
||||
Never(Duration.INFINITE);
|
||||
|
||||
companion object {
|
||||
|
||||
|
||||
+1
-1
@@ -22,4 +22,4 @@ sealed interface BiometricsSystemState {
|
||||
object BiometricEnrolled : BiometricsSystemState
|
||||
object BiometricNotEnrolled : BiometricsSystemState
|
||||
object BiometricNotAvailable : BiometricsSystemState
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -18,7 +18,9 @@
|
||||
|
||||
package ch.protonmail.android.mailpinlock.domain.autolock
|
||||
|
||||
// TODO convert to rust ET-648
|
||||
|
||||
// TODO ET-6548 pin convert to rust
|
||||
@Suppress("ForbiddenComment")
|
||||
internal class HasValidPinValueTest {
|
||||
|
||||
/* private val observeValidPinValue = mockk<ObserveAutoLockPinValue>()
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and Proton Mail.
|
||||
*
|
||||
* Proton Mail is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Mail is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.mailpinlock.domain.autolock.usecase
|
||||
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and Proton Mail.
|
||||
*
|
||||
* Proton Mail is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Mail is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import arrow.core.right
|
||||
import ch.protonmail.android.mailcommon.domain.AppInBackgroundState
|
||||
import ch.protonmail.android.mailpinlock.domain.AutolockRepository
|
||||
import ch.protonmail.android.mailpinlock.domain.usecase.ShouldPresentPinInsertionScreen
|
||||
import io.mockk.called
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.unmockkAll
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
internal class ShouldPresentPinInsertionScreenTest {
|
||||
|
||||
private val appInBackgroundState = mockk<AppInBackgroundState>()
|
||||
private val autolockRepository = mockk<AutolockRepository>()
|
||||
|
||||
private val shouldPresentPinInsertionScreen = ShouldPresentPinInsertionScreen(
|
||||
appInBackgroundState,
|
||||
autolockRepository
|
||||
)
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
unmockkAll()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not indicate to display pin screen and do nothing when the app is in the background`() = runTest {
|
||||
// Given
|
||||
expectAppInBackground()
|
||||
|
||||
// When
|
||||
val result = shouldPresentPinInsertionScreen().first()
|
||||
|
||||
// Then
|
||||
assertFalse(result)
|
||||
coVerify {
|
||||
autolockRepository wasNot called
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should indicate to display pin screen when the app is not background AND shouldShowPin is TRUE`() = runTest {
|
||||
// Given
|
||||
expectAppInForeground()
|
||||
coEvery { autolockRepository.shouldAutolock() } returns true.right()
|
||||
// When
|
||||
val result = shouldPresentPinInsertionScreen().first()
|
||||
|
||||
// Then
|
||||
assertTrue(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should NOT indicate to display pin screen when the app is not background AND shouldShowPin is FALSE`() =
|
||||
runTest {
|
||||
// Given
|
||||
expectAppInForeground()
|
||||
coEvery { autolockRepository.shouldAutolock() } returns false.right()
|
||||
|
||||
val result = shouldPresentPinInsertionScreen().first()
|
||||
|
||||
// Then
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
private fun expectAppInForeground() {
|
||||
every { appInBackgroundState.observe() } returns flowOf(false)
|
||||
}
|
||||
|
||||
private fun expectAppInBackground() {
|
||||
every { appInBackgroundState.observe() } returns flowOf(true)
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -19,12 +19,10 @@
|
||||
package ch.protonmail.android.mailpinlock.presentation.autolock
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import ch.protonmail.android.mailcommon.presentation.Effect
|
||||
import ch.protonmail.android.mailcommon.presentation.model.TextUiModel
|
||||
|
||||
@Stable
|
||||
data class AutoLockBiometricsUiModel(
|
||||
val enabled: Boolean,
|
||||
val biometricsEnrolled: Boolean,
|
||||
val biometricsHwAvailable: Boolean,
|
||||
val biometricsHwAvailable: Boolean
|
||||
)
|
||||
|
||||
+6
-2
@@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("RedundantSuspendModifier", "ControlFlowWithEmptyBody")
|
||||
@HiltViewModel
|
||||
class AutoLockSettingsViewModel @Inject constructor(
|
||||
private val autolockRepository: AutolockRepository
|
||||
@@ -48,13 +49,14 @@ class AutoLockSettingsViewModel @Inject constructor(
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis), AutolockSettingsUiState.Loading)
|
||||
|
||||
@Suppress("ForbiddenComment")
|
||||
internal fun submit(action: AutoLockSettingsViewAction) {
|
||||
viewModelScope.launch {
|
||||
when (action) {
|
||||
is AutoLockSettingsViewAction.ToggleAutoLockPreference -> updateAutoLockEnabledValue(action.newValue)
|
||||
|
||||
// TODO inegrate the biometrics
|
||||
//is AutoLockSettingsViewAction.ToggleAutoLockBiometricsPreference ->
|
||||
// TODO ET-6548 integrate the biometrics and pin
|
||||
// is AutoLockSettingsViewAction.ToggleAutoLockBiometricsPreference ->
|
||||
// updateBiometricsEnabledValue(action.autoLockBiometricsUiModel)
|
||||
else -> {}
|
||||
}
|
||||
@@ -66,7 +68,9 @@ class AutoLockSettingsViewModel @Inject constructor(
|
||||
// if turn on autolock - go to pin flow to set pin
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private suspend fun updateBiometricsEnabledValue(enabled: Boolean) {
|
||||
// ET-6548
|
||||
if (enabled) {
|
||||
// both go to pin flow, need to confirm pin if disabling
|
||||
// open pin verify flow
|
||||
|
||||
+2
-2
@@ -35,7 +35,6 @@ import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@HiltViewModel
|
||||
class AutolockIntervalViewModel @Inject constructor(
|
||||
private val autolockRepository: AutolockRepository
|
||||
@@ -50,7 +49,8 @@ class AutolockIntervalViewModel @Inject constructor(
|
||||
AutolockIntervalState.Data(
|
||||
autolock.autolockInterval,
|
||||
AutoLockInterval.entries.sortedBy { it.duration }
|
||||
.toMutableList().apply { this.removeAt(this.lastIndex) }
|
||||
.toMutableList()
|
||||
.apply { this.removeAt(this.lastIndex) } // we don't show never, that's autolock off
|
||||
.associateWith { it.toTextUiModel() }
|
||||
)
|
||||
}
|
||||
|
||||
+2
-1
@@ -16,13 +16,14 @@
|
||||
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package ch.protonmail.android.mailpinlock.presentation.autolock
|
||||
|
||||
import ch.protonmail.android.mailcommon.presentation.Effect
|
||||
import ch.protonmail.android.mailcommon.presentation.model.TextUiModel
|
||||
import ch.protonmail.android.mailpinlock.presentation.R
|
||||
|
||||
|
||||
sealed class AutolockSettingsUiState {
|
||||
data class Data(val settings: AutolockSettings) : AutolockSettingsUiState()
|
||||
data object Loading : AutolockSettingsUiState()
|
||||
|
||||
+13
-17
@@ -39,7 +39,6 @@ import ch.protonmail.android.design.compose.component.ProtonAppSettingsItemInver
|
||||
import ch.protonmail.android.design.compose.component.ProtonAppSettingsItemNorm
|
||||
import ch.protonmail.android.design.compose.component.ProtonCenteredProgress
|
||||
import ch.protonmail.android.design.compose.component.ProtonMainSettingsIcon
|
||||
import ch.protonmail.android.design.compose.component.ProtonSettingsDetailsAppBar
|
||||
import ch.protonmail.android.design.compose.component.ProtonSettingsToggleItem
|
||||
import ch.protonmail.android.design.compose.component.ProtonSettingsTopBar
|
||||
import ch.protonmail.android.design.compose.component.ProtonSnackbarHostState
|
||||
@@ -62,7 +61,6 @@ import ch.protonmail.android.mailpinlock.presentation.autolock.ProtectionType
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.ui.AutoLockSettingsScreen.Actions
|
||||
import ch.protonmail.android.uicomponents.snackbar.DismissableSnackbarHost
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AutoLockSettingsScreen(
|
||||
@@ -84,10 +82,10 @@ fun AutoLockSettingsScreen(
|
||||
effects.updateError
|
||||
)
|
||||
ConsumableLaunchedEffect(effects.forceOpenPinCreation) {
|
||||
//onPinScreenNavigation(AutoLockInsertionMode.CreatePin)
|
||||
// ET-6548 onPinScreenNavigation(AutoLockInsertionMode.CreatePin)
|
||||
}
|
||||
ConsumableLaunchedEffect(effects.forceOpenPinCreation) {
|
||||
//onPinScreenNavigation(AutoLockInsertionMode.ChangePin)
|
||||
// ET-6548 onPinScreenNavigation(AutoLockInsertionMode.ChangePin)
|
||||
}
|
||||
|
||||
when (val uiState = state) {
|
||||
@@ -109,8 +107,7 @@ fun AutoLockSettingsScreen(
|
||||
.padding(horizontal = ProtonDimens.Spacing.Large),
|
||||
settings = uiState.settings,
|
||||
submitAction = { viewModel.submit(it) },
|
||||
onChangeIntervalNavigation = actions.onChangeIntervalClick,
|
||||
onPinScreenNavigation = actions.onPinScreenNavigation
|
||||
actions = actions
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -122,9 +119,8 @@ fun AutoLockSettingsScreen(
|
||||
private fun AutolockSettingScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
settings: AutolockSettings,
|
||||
submitAction: (AutoLockSettingsViewAction) -> Unit,
|
||||
onPinScreenNavigation: () -> Unit = {},
|
||||
onChangeIntervalNavigation: () -> Unit = {}
|
||||
actions: Actions,
|
||||
submitAction: (AutoLockSettingsViewAction) -> Unit
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
AutolockOnOffToggle(
|
||||
@@ -142,10 +138,10 @@ private fun AutolockSettingScreen(
|
||||
containerColor = ProtonTheme.colors.backgroundInvertedSecondary
|
||||
)
|
||||
) {
|
||||
ChangePinOption(onClickChangePin = onPinScreenNavigation)
|
||||
ChangePinOption(onClickChangePin = actions.onPinScreenNavigation)
|
||||
ChangeIntervalOption(
|
||||
selectedChoice = settings.selectedUiInterval,
|
||||
onClickChangeInterval = onChangeIntervalNavigation
|
||||
onClickChangeInterval = actions.onChangeIntervalClick
|
||||
)
|
||||
if (settings.biometricsAvailable) {
|
||||
BiometricsOnOffToggle(
|
||||
@@ -278,7 +274,7 @@ fun PreviewAutolockSettingScreenEnabled() {
|
||||
protectionType = ProtectionType.Biometrics,
|
||||
biometricsAvailable = true
|
||||
),
|
||||
onPinScreenNavigation = {},
|
||||
actions = Actions(),
|
||||
submitAction = {}
|
||||
)
|
||||
}
|
||||
@@ -293,7 +289,7 @@ fun PreviewAutolockSettingScreenDisabled() {
|
||||
protectionType = ProtectionType.None,
|
||||
biometricsAvailable = false
|
||||
),
|
||||
onPinScreenNavigation = {},
|
||||
actions = Actions(),
|
||||
submitAction = {}
|
||||
)
|
||||
}
|
||||
@@ -301,8 +297,8 @@ fun PreviewAutolockSettingScreenDisabled() {
|
||||
object AutoLockSettingsScreen {
|
||||
|
||||
data class Actions(
|
||||
val onChangeIntervalClick: () -> Unit,
|
||||
val onBackClick: () -> Unit,
|
||||
val onPinScreenNavigation: () -> Unit = {},
|
||||
val onChangeIntervalClick: () -> Unit = {},
|
||||
val onBackClick: () -> Unit = {},
|
||||
val onPinScreenNavigation: () -> Unit = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+73
-68
@@ -22,9 +22,9 @@ import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import ch.protonmail.android.mailcommon.domain.model.autolock.AutoLockPin
|
||||
import ch.protonmail.android.mailpinlock.domain.usecase.ToggleAutoLockAttemptPendingStatus
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.AutoLockInsertionMode
|
||||
import ch.protonmail.android.mailpinlock.presentation.pin.ui.AutoLockPinScreen
|
||||
import ch.protonmail.android.mailpinlock.domain.usecase.ToggleAutoLockAttemptPendingStatus
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -32,15 +32,16 @@ import kotlinx.coroutines.launch
|
||||
import me.proton.core.util.kotlin.deserialize
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("unused", "UseExpressionBody", "RedundantSuspendModifier")
|
||||
@HiltViewModel
|
||||
class AutoLockPinViewModel @Inject constructor(
|
||||
// private val observeAutoLockPin: ObserveAutoLockPinValue,
|
||||
// private val toggleAutoLockEnabled: ToggleAutoLockEnabled,
|
||||
// private val updateRemainingAutoLockAttempts: UpdateRemainingAutoLockAttempts,
|
||||
// private val saveAutoLockPin: SaveAutoLockPin,
|
||||
// private val observeAutoLockPin: ObserveAutoLockPinValue,
|
||||
// private val toggleAutoLockEnabled: ToggleAutoLockEnabled,
|
||||
// private val updateRemainingAutoLockAttempts: UpdateRemainingAutoLockAttempts,
|
||||
// private val saveAutoLockPin: SaveAutoLockPin,
|
||||
private val clearPinDataAndForceLogout: ClearPinDataAndForceLogout,
|
||||
private val toggleAutoLockAttemptStatus: ToggleAutoLockAttemptPendingStatus,
|
||||
// private val updateAutoLockLastForegroundMillis: UpdateLastForegroundMillis,
|
||||
// private val updateAutoLockLastForegroundMillis: UpdateLastForegroundMillis,
|
||||
private val reducer: AutoLockPinReducer,
|
||||
savedStateHandle: SavedStateHandle
|
||||
) : ViewModel() {
|
||||
@@ -63,19 +64,19 @@ class AutoLockPinViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
/* val remainingAttempts = getRemainingAutoLockAttempts().getOrNull()?.value?.let {
|
||||
PinVerificationRemainingAttempts(it)
|
||||
} ?: PinVerificationRemainingAttempts.Default
|
||||
/* val remainingAttempts = getRemainingAutoLockAttempts().getOrNull()?.value?.let {
|
||||
PinVerificationRemainingAttempts(it)
|
||||
} ?: PinVerificationRemainingAttempts.Default
|
||||
|
||||
toggleAutoLockAttemptStatus(value = true)
|
||||
toggleAutoLockAttemptStatus(value = true)
|
||||
|
||||
val currentBiometricsState = getCurrentAutoLockBiometricState()
|
||||
emitNewStateFrom(AutoLockPinEvent.Data.Loaded(step, remainingAttempts, currentBiometricsState))
|
||||
val currentBiometricsState = getCurrentAutoLockBiometricState()
|
||||
emitNewStateFrom(AutoLockPinEvent.Data.Loaded(step, remainingAttempts, currentBiometricsState))
|
||||
|
||||
observeAutoLockBiometricsState()
|
||||
.onEach {
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.BiometricStateChanged(it))
|
||||
}*/
|
||||
observeAutoLockBiometricsState()
|
||||
.onEach {
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.BiometricStateChanged(it))
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,15 +124,15 @@ class AutoLockPinViewModel @Inject constructor(
|
||||
|
||||
val autoLockPin = AutoLockPin(insertedPin.toString())
|
||||
|
||||
/* saveAutoLockPin(autoLockPin).mapLeft {
|
||||
Timber.e("Unable to save auto pin lock value. - $it")
|
||||
return emitNewStateFrom(AutoLockPinEvent.Update.Error.UnknownError)
|
||||
}
|
||||
/* saveAutoLockPin(autoLockPin).mapLeft {
|
||||
Timber.e("Unable to save auto pin lock value. - $it")
|
||||
return emitNewStateFrom(AutoLockPinEvent.Update.Error.UnknownError)
|
||||
}
|
||||
|
||||
toggleAutoLockEnabled(newValue = true).mapLeft {
|
||||
Timber.e("Unable to enable pin lock. - $it")
|
||||
return emitNewStateFrom(AutoLockPinEvent.Update.Error.UnknownError)
|
||||
}*/
|
||||
toggleAutoLockEnabled(newValue = true).mapLeft {
|
||||
Timber.e("Unable to enable pin lock. - $it")
|
||||
return emitNewStateFrom(AutoLockPinEvent.Update.Error.UnknownError)
|
||||
}*/
|
||||
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.OperationCompleted)
|
||||
}
|
||||
@@ -155,24 +156,24 @@ class AutoLockPinViewModel @Inject constructor(
|
||||
remainingAttempts: PinVerificationRemainingAttempts,
|
||||
continuation: () -> Unit
|
||||
) {
|
||||
/* val storedPin = observeAutoLockPin().firstOrNull()?.getOrNull()
|
||||
?: return emitNewStateFrom(AutoLockPinEvent.Update.Error.UnknownError)
|
||||
/* val storedPin = observeAutoLockPin().firstOrNull()?.getOrNull()
|
||||
?: return emitNewStateFrom(AutoLockPinEvent.Update.Error.UnknownError)
|
||||
|
||||
if (!storedPin.matches(insertedPin)) {
|
||||
if (remainingAttempts.value <= 1) {
|
||||
clearPinDataAndForceLogout().await()
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.OperationAborted)
|
||||
return
|
||||
}
|
||||
if (!storedPin.matches(insertedPin)) {
|
||||
if (remainingAttempts.value <= 1) {
|
||||
clearPinDataAndForceLogout().await()
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.OperationAborted)
|
||||
return
|
||||
}
|
||||
|
||||
val decrementedRemainingAttempts = remainingAttempts.decrement()
|
||||
val decrementedRemainingAttempts = remainingAttempts.decrement()
|
||||
|
||||
updateRemainingAutoLockAttempts(decrementedRemainingAttempts.value).onLeft {
|
||||
Timber.e("Unable to update remaining auto lock attempts. - $it")
|
||||
}
|
||||
updateRemainingAutoLockAttempts(decrementedRemainingAttempts.value).onLeft {
|
||||
Timber.e("Unable to update remaining auto lock attempts. - $it")
|
||||
}
|
||||
|
||||
return emitNewStateFrom(AutoLockPinEvent.Update.Error.WrongPinCode(decrementedRemainingAttempts))*/
|
||||
}
|
||||
return emitNewStateFrom(AutoLockPinEvent.Update.Error.WrongPinCode(decrementedRemainingAttempts))*/
|
||||
}
|
||||
|
||||
/* updateRemainingAutoLockAttempts(PinVerificationRemainingAttempts.MaxAttempts).onLeft {
|
||||
Timber.e("Unable to reset remaining auto lock attempts. - $it")
|
||||
@@ -189,48 +190,48 @@ class AutoLockPinViewModel @Inject constructor(
|
||||
continuation()*/
|
||||
|
||||
private suspend fun onBiometricAuthenticationSucceeded() {
|
||||
/* updateRemainingAutoLockAttempts(PinVerificationRemainingAttempts.MaxAttempts).onLeft {
|
||||
Timber.e("Unable to reset remaining auto lock attempts. - $it")
|
||||
}
|
||||
/* updateRemainingAutoLockAttempts(PinVerificationRemainingAttempts.MaxAttempts).onLeft {
|
||||
Timber.e("Unable to reset remaining auto lock attempts. - $it")
|
||||
}
|
||||
|
||||
toggleAutoLockAttemptStatus(value = false).onLeft {
|
||||
Timber.e("Unable to reset pending lock attempt. - $it")
|
||||
}
|
||||
toggleAutoLockAttemptStatus(value = false).onLeft {
|
||||
Timber.e("Unable to reset pending lock attempt. - $it")
|
||||
}
|
||||
|
||||
updateAutoLockLastForegroundMillis(Long.MAX_VALUE).onLeft {
|
||||
Timber.e("Unable to update last foreground millis - $it")
|
||||
}*/
|
||||
updateAutoLockLastForegroundMillis(Long.MAX_VALUE).onLeft {
|
||||
Timber.e("Unable to update last foreground millis - $it")
|
||||
}*/
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.VerificationCompleted)
|
||||
}
|
||||
|
||||
private suspend fun onPerformConfirm() {
|
||||
/* val state = state.value as? AutoLockPinState.DataLoaded ?: return
|
||||
val currentStep = state.pinInsertionState.step
|
||||
val remainingAttempts = state.pinInsertionState.remainingAttempts
|
||||
val insertedPin = state.pinInsertionState.pinInsertionUiModel.currentPin
|
||||
/* val state = state.value as? AutoLockPinState.DataLoaded ?: return
|
||||
val currentStep = state.pinInsertionState.step
|
||||
val remainingAttempts = state.pinInsertionState.remainingAttempts
|
||||
val insertedPin = state.pinInsertionState.pinInsertionUiModel.currentPin
|
||||
|
||||
when (currentStep) {
|
||||
PinInsertionStep.PinChange -> handlePinChangeConfirmation(insertedPin, remainingAttempts)
|
||||
PinInsertionStep.PinInsertion -> handlePinInsertion(insertedPin)
|
||||
PinInsertionStep.PinConfirmation -> handlePinConfirmed(insertedPin)
|
||||
PinInsertionStep.PinVerification -> handlePinVerification(insertedPin, remainingAttempts)
|
||||
}*/
|
||||
when (currentStep) {
|
||||
PinInsertionStep.PinChange -> handlePinChangeConfirmation(insertedPin, remainingAttempts)
|
||||
PinInsertionStep.PinInsertion -> handlePinInsertion(insertedPin)
|
||||
PinInsertionStep.PinConfirmation -> handlePinConfirmed(insertedPin)
|
||||
PinInsertionStep.PinVerification -> handlePinVerification(insertedPin, remainingAttempts)
|
||||
}*/
|
||||
}
|
||||
|
||||
private fun onPinDigitRemoved() {
|
||||
/* val state = state.value as? AutoLockPinState.DataLoaded ?: return
|
||||
val currentPin =
|
||||
state.pinInsertionState.pinInsertionUiModel.currentPin.takeIf { it.isNotEmpty() } ?: return
|
||||
/* val state = state.value as? AutoLockPinState.DataLoaded ?: return
|
||||
val currentPin =
|
||||
state.pinInsertionState.pinInsertionUiModel.currentPin.takeIf { it.isNotEmpty() } ?: return
|
||||
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.PinValueChanged(currentPin.deleteLastDigit()))*/
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.PinValueChanged(currentPin.deleteLastDigit()))*/
|
||||
}
|
||||
|
||||
private fun onPinDigitAdded(action: AutoLockPinViewAction.AddPinDigit) {
|
||||
/* val state = state.value as? AutoLockPinState.DataLoaded ?: return
|
||||
val currentPin = state.pinInsertionState.pinInsertionUiModel.currentPin
|
||||
if (currentPin.isMaxLength()) return
|
||||
/* val state = state.value as? AutoLockPinState.DataLoaded ?: return
|
||||
val currentPin = state.pinInsertionState.pinInsertionUiModel.currentPin
|
||||
if (currentPin.isMaxLength()) return
|
||||
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.PinValueChanged(currentPin.appendDigit(action.addition)))*/
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.PinValueChanged(currentPin.appendDigit(action.addition)))*/
|
||||
}
|
||||
|
||||
private fun onSignOutRequested() {
|
||||
@@ -238,15 +239,19 @@ class AutoLockPinViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private suspend fun onSignOutConfirmed() {
|
||||
/* clearPinDataAndForceLogout().await()
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.SignOutConfirmed)*/
|
||||
/* clearPinDataAndForceLogout().await()
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.SignOutConfirmed)*/
|
||||
}
|
||||
|
||||
private fun onSignOutCanceled() {
|
||||
emitNewStateFrom(AutoLockPinEvent.Update.SignOutCanceled)
|
||||
}
|
||||
|
||||
private fun emitNewStateFrom(event: AutoLockPinEvent){}/* = mutableState.update {
|
||||
@Suppress("EmptyFunctionBlock")
|
||||
private fun emitNewStateFrom(event: AutoLockPinEvent) {
|
||||
}
|
||||
|
||||
/* ET-6548 = mutableState.update {
|
||||
reducer.newStateFrom(it, event)
|
||||
}
|
||||
*/
|
||||
|
||||
+1
-19
@@ -16,23 +16,5 @@
|
||||
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.initializer
|
||||
package protonmail.android.mailpinlock.presentation
|
||||
|
||||
import android.content.Context
|
||||
import androidx.startup.Initializer
|
||||
import ch.protonmail.android.di.AutoLockModule
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
|
||||
internal class AutoLockHandlerInitializer : Initializer<Unit> {
|
||||
|
||||
override fun create(context: Context) {
|
||||
EntryPointAccessors.fromApplication(
|
||||
context.applicationContext,
|
||||
AutoLockModule.EntryPointModule::class.java
|
||||
).autoLockHandler().handle()
|
||||
}
|
||||
|
||||
override fun dependencies(): List<Class<out Initializer<*>>> = listOf(
|
||||
AppInBackgroundCheckerInitializer::class.java
|
||||
)
|
||||
}
|
||||
+37
-8
@@ -19,21 +19,41 @@
|
||||
package protonmail.android.mailpinlock.presentation.autolock
|
||||
|
||||
import app.cash.turbine.test
|
||||
import arrow.core.right
|
||||
import ch.protonmail.android.mailcommon.presentation.model.TextUiModel
|
||||
import ch.protonmail.android.mailpinlock.domain.AutolockRepository
|
||||
import ch.protonmail.android.mailpinlock.model.Autolock
|
||||
import ch.protonmail.android.mailpinlock.presentation.R
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.AutoLockSettingsViewModel
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.AutolockSettings
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.AutolockSettingsUiState
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.ProtectionType
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import io.mockk.unmockkAll
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.After
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
internal class AutoLockSettingsViewModelTest {
|
||||
|
||||
private val autolockRepository: AutolockRepository = mockk()
|
||||
private val autoLockFlow = MutableSharedFlow<Autolock>()
|
||||
private val autolockRepository: AutolockRepository = mockk {
|
||||
coEvery {
|
||||
this@mockk.observeAppLock()
|
||||
} returns autoLockFlow
|
||||
|
||||
coEvery {
|
||||
this@mockk.updateAutolockInterval(any())
|
||||
} returns Unit.right()
|
||||
}
|
||||
|
||||
|
||||
private val viewModel by lazy {
|
||||
AutoLockSettingsViewModel(
|
||||
@@ -53,16 +73,25 @@ internal class AutoLockSettingsViewModelTest {
|
||||
|
||||
@Test
|
||||
fun `should return loading state when first launched`() = runTest {
|
||||
// Given
|
||||
// When + Then
|
||||
/* viewModel.state.test {
|
||||
viewModel.state.test {
|
||||
val loadingState = awaitItem()
|
||||
}*/
|
||||
Assert.assertEquals(AutolockSettingsUiState.Loading, loadingState)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return mapped data when flow emits value`() = runTest {
|
||||
viewModel.state.test {
|
||||
val loadingState = awaitItem()
|
||||
Assert.assertEquals(AutolockSettingsUiState.Loading, loadingState)
|
||||
autoLockFlow.tryEmit(Autolock())
|
||||
|
||||
private companion object {
|
||||
|
||||
|
||||
val expected = AutolockSettings(
|
||||
selectedUiInterval = TextUiModel(R.string.mail_pinlock_settings_autolock_never),
|
||||
protectionType = ProtectionType.None,
|
||||
biometricsAvailable = false
|
||||
)
|
||||
AutolockSettingsUiState.Data(settings = expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and Proton Mail.
|
||||
*
|
||||
* Proton Mail is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Mail is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package protonmail.android.mailpinlock.presentation.autolock
|
||||
|
||||
import app.cash.turbine.ReceiveTurbine
|
||||
import app.cash.turbine.test
|
||||
import arrow.core.right
|
||||
import ch.protonmail.android.mailcommon.presentation.Effect
|
||||
import ch.protonmail.android.mailcommon.presentation.model.TextUiModel
|
||||
import ch.protonmail.android.mailpinlock.domain.AutolockRepository
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockInterval.FifteenMinutes
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockInterval.FiveMinutes
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockInterval.Immediately
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockInterval.OneDay
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockInterval.OneHour
|
||||
import ch.protonmail.android.mailpinlock.model.Autolock
|
||||
import ch.protonmail.android.mailpinlock.presentation.R
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.AutolockIntervalEffects
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.AutolockIntervalState
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.AutolockIntervalViewModel
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class AutolockIntervalViewModelTest {
|
||||
|
||||
private val autoLockFlow = MutableSharedFlow<Autolock>()
|
||||
private val autolockRepository: AutolockRepository = mockk {
|
||||
coEvery {
|
||||
this@mockk.observeAppLock()
|
||||
} returns autoLockFlow
|
||||
|
||||
coEvery {
|
||||
this@mockk.updateAutolockInterval(any())
|
||||
} returns Unit.right()
|
||||
}
|
||||
|
||||
private lateinit var viewModel: AutolockIntervalViewModel
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
Dispatchers.setMain(UnconfinedTestDispatcher())
|
||||
|
||||
viewModel = AutolockIntervalViewModel(
|
||||
autolockRepository
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `close effect emitted when interval is updated`() = runTest {
|
||||
// Given
|
||||
coEvery { autolockRepository.updateAutolockInterval(any()) } returns Unit.right()
|
||||
|
||||
// When
|
||||
viewModel.onIntervalSelected(FifteenMinutes)
|
||||
// then
|
||||
Assert.assertEquals(
|
||||
AutolockIntervalEffects(close = Effect.Companion.of(Unit)),
|
||||
viewModel.effects.value
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `state returns interval with 15 minutes selected when saved interval is 15 minutes`() = runTest {
|
||||
viewModel.state.test {
|
||||
// Given
|
||||
initialStateEmitted()
|
||||
|
||||
// When
|
||||
autoLockFlow.emit(Autolock(autolockInterval = FifteenMinutes))
|
||||
|
||||
// Then
|
||||
Assert.assertEquals(
|
||||
AutolockIntervalState.Data(
|
||||
currentInterval = FifteenMinutes,
|
||||
intervalsToChoices = uiIntervals
|
||||
),
|
||||
awaitItem()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `state is updated when repository emits an updated interval`() = runTest {
|
||||
viewModel.state.test {
|
||||
// Given
|
||||
initialStateEmitted()
|
||||
// When
|
||||
autoLockFlow.emit(Autolock(autolockInterval = FifteenMinutes))
|
||||
// Then
|
||||
Assert.assertEquals(
|
||||
AutolockIntervalState.Data(
|
||||
currentInterval = FifteenMinutes,
|
||||
intervalsToChoices = uiIntervals
|
||||
),
|
||||
awaitItem()
|
||||
)
|
||||
|
||||
// When
|
||||
autoLockFlow.emit(Autolock(autolockInterval = OneDay))
|
||||
// Then
|
||||
Assert.assertEquals(
|
||||
AutolockIntervalState.Data(
|
||||
currentInterval = OneDay,
|
||||
intervalsToChoices = uiIntervals
|
||||
),
|
||||
awaitItem()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updates autolockRepository on repository when intervalSelected`() = runTest {
|
||||
// Given
|
||||
coEvery { autolockRepository.updateAutolockInterval(any()) } returns Unit.right()
|
||||
|
||||
// When
|
||||
viewModel.onIntervalSelected(FifteenMinutes)
|
||||
|
||||
// Then
|
||||
coVerify { autolockRepository.updateAutolockInterval(FifteenMinutes) }
|
||||
}
|
||||
|
||||
private suspend fun ReceiveTurbine<AutolockIntervalState>.initialStateEmitted() {
|
||||
awaitItem() as AutolockIntervalState.Loading
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val uiIntervals = mapOf(
|
||||
Immediately to TextUiModel(R.string.mail_pinlock_settings_autolock_immediately),
|
||||
FiveMinutes to TextUiModel(R.string.mail_pinlock_settings_autolock_description_five_minutes),
|
||||
FifteenMinutes to TextUiModel(R.string.mail_pinlock_settings_autolock_description_fifteen_minutes),
|
||||
OneHour to TextUiModel(R.string.mail_pinlock_settings_autolock_description_one_hour),
|
||||
OneDay to TextUiModel(R.string.mail_pinlock_settings_autolock_description_one_day)
|
||||
)
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and Proton Mail.
|
||||
*
|
||||
* Proton Mail is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Mail is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package protonmail.android.mailpinlock.presentation.autolock.mapper
|
||||
|
||||
import ch.protonmail.android.mailcommon.presentation.model.TextUiModel
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockInterval
|
||||
import ch.protonmail.android.mailpinlock.model.Autolock
|
||||
import ch.protonmail.android.mailpinlock.model.Protection
|
||||
import ch.protonmail.android.mailpinlock.presentation.R
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.AutolockSettings
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.ProtectionType
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.mapper.AutolockSettingsUiMapper
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class AutolockSettingsUIMapperTest(
|
||||
@Suppress("unused") private val testName: String,
|
||||
private val toMap: Autolock,
|
||||
private val expectedUiModel: AutolockSettings
|
||||
) {
|
||||
|
||||
@Test
|
||||
fun `should map to ui model`() {
|
||||
val uiModel = AutolockSettingsUiMapper.toUiModel(toMap)
|
||||
assertEquals(expectedUiModel, uiModel)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val baseAutoLock = Autolock()
|
||||
|
||||
val baseUiModel = AutolockSettings(
|
||||
TextUiModel(R.string.mail_pinlock_settings_autolock_immediately),
|
||||
protectionType = ProtectionType.None,
|
||||
biometricsAvailable = false
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
fun data(): Collection<Array<Any>> = listOf(
|
||||
arrayOf(
|
||||
"to autolock with default values",
|
||||
baseAutoLock,
|
||||
baseUiModel
|
||||
),
|
||||
arrayOf(
|
||||
"to autolock with pin",
|
||||
baseAutoLock.copy(protectionType = Protection.Pin),
|
||||
baseUiModel.copy(protectionType = ProtectionType.Pin)
|
||||
),
|
||||
arrayOf(
|
||||
"to autolock with biometrics",
|
||||
baseAutoLock.copy(protectionType = Protection.Biometrics),
|
||||
baseUiModel.copy(protectionType = ProtectionType.Biometrics)
|
||||
),
|
||||
arrayOf(
|
||||
"to autolock with never",
|
||||
baseAutoLock.copy(autolockInterval = AutoLockInterval.Never),
|
||||
baseUiModel.copy(selectedUiInterval = TextUiModel(R.string.mail_pinlock_settings_autolock_never))
|
||||
),
|
||||
arrayOf(
|
||||
"to autolock with biometrics Immediately",
|
||||
baseAutoLock.copy(autolockInterval = AutoLockInterval.Immediately),
|
||||
baseUiModel.copy(selectedUiInterval = TextUiModel(R.string.mail_pinlock_settings_autolock_immediately))
|
||||
),
|
||||
arrayOf(
|
||||
"to autolock with biometrics FifteenMinutes",
|
||||
baseAutoLock.copy(autolockInterval = AutoLockInterval.FifteenMinutes),
|
||||
baseUiModel.copy(
|
||||
selectedUiInterval =
|
||||
TextUiModel(R.string.mail_pinlock_settings_autolock_description_fifteen_minutes)
|
||||
)
|
||||
),
|
||||
arrayOf(
|
||||
"to autolock with biometrics One Hour",
|
||||
baseAutoLock.copy(autolockInterval = AutoLockInterval.OneHour),
|
||||
baseUiModel.copy(
|
||||
selectedUiInterval =
|
||||
TextUiModel(R.string.mail_pinlock_settings_autolock_description_one_hour)
|
||||
)
|
||||
),
|
||||
arrayOf(
|
||||
"to autolock with biometrics One Day",
|
||||
baseAutoLock.copy(autolockInterval = AutoLockInterval.OneDay),
|
||||
baseUiModel.copy(
|
||||
selectedUiInterval =
|
||||
TextUiModel(R.string.mail_pinlock_settings_autolock_description_one_day)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and Proton Mail.
|
||||
*
|
||||
* Proton Mail is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Mail is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package protonmail.android.mailpinlock.presentation.autolock.mapper
|
||||
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockBiometricsState
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.AutoLockBiometricsUiModel
|
||||
import ch.protonmail.android.mailpinlock.presentation.autolock.mapper.AutoLockBiometricsUiModelMapper
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class BiometricsUiModelMapperTest {
|
||||
|
||||
val sut = AutoLockBiometricsUiModelMapper()
|
||||
|
||||
@Test
|
||||
fun `map Biometrics Available AND Enrolled And Enabled`() {
|
||||
assertEquals(
|
||||
sut.toUiModel(AutoLockBiometricsState.BiometricsAvailable.BiometricsEnrolled(true)),
|
||||
AutoLockBiometricsUiModel(
|
||||
enabled = true,
|
||||
biometricsEnrolled = true,
|
||||
biometricsHwAvailable = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `map Biometrics Available AND Enrolled And NOT Enabled`() {
|
||||
assertEquals(
|
||||
sut.toUiModel(AutoLockBiometricsState.BiometricsAvailable.BiometricsEnrolled(false)),
|
||||
AutoLockBiometricsUiModel(
|
||||
enabled = false,
|
||||
biometricsEnrolled = true,
|
||||
biometricsHwAvailable = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `map Biometrics Available NOT Enrolled`() {
|
||||
assertEquals(
|
||||
sut.toUiModel(AutoLockBiometricsState.BiometricsAvailable.BiometricsNotEnrolled),
|
||||
AutoLockBiometricsUiModel(
|
||||
enabled = false,
|
||||
biometricsEnrolled = false,
|
||||
biometricsHwAvailable = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `map Biometrics NOT Available`() {
|
||||
assertEquals(
|
||||
sut.toUiModel(AutoLockBiometricsState.BiometricsNotAvailable),
|
||||
AutoLockBiometricsUiModel(
|
||||
enabled = false,
|
||||
biometricsEnrolled = false,
|
||||
biometricsHwAvailable = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+1
@@ -30,6 +30,7 @@ import ch.protonmail.android.mailpinlock.presentation.pin.mapper.AutoLockSuccess
|
||||
import io.mockk.mockk
|
||||
|
||||
// TODO currently being refactored ET-648
|
||||
@Suppress("ForbiddenComment", "unused")
|
||||
internal class AutoLockPinViewModelTest {
|
||||
|
||||
/* private val observeAutoLockBiometricsState = mockk<ObserveAutoLockBiometricsState>()
|
||||
|
||||
+15
-19
@@ -37,7 +37,6 @@ import uniffi.proton_mail_uniffi.AutoLock.Minutes
|
||||
import uniffi.proton_mail_uniffi.AutoLock.Never
|
||||
import uniffi.proton_mail_uniffi.AppSettingsDiff as LocalAppSettingsDiff
|
||||
|
||||
|
||||
fun AppSettingsDiff.toAppDiff(): LocalAppSettingsDiff {
|
||||
|
||||
fun setTheme(theme: Theme) = themeAppearanceLookup.getOrElse(theme, {
|
||||
@@ -45,12 +44,11 @@ fun AppSettingsDiff.toAppDiff(): LocalAppSettingsDiff {
|
||||
defaultThemeFallback
|
||||
})
|
||||
|
||||
fun setAutolockInteval(interval: AutoLockInterval) =
|
||||
when (interval) {
|
||||
AutoLockInterval.Immediately -> Always
|
||||
AutoLockInterval.Never -> Never
|
||||
else -> Minutes(interval.duration.inWholeMinutes.toUByte())
|
||||
}
|
||||
fun setAutolockInteval(interval: AutoLockInterval) = when (interval) {
|
||||
AutoLockInterval.Immediately -> Always
|
||||
AutoLockInterval.Never -> Never
|
||||
else -> Minutes(interval.duration.inWholeMinutes.toUByte())
|
||||
}
|
||||
|
||||
return LocalAppSettingsDiff(
|
||||
autoLock = interval?.let { setAutolockInteval(it) },
|
||||
@@ -73,19 +71,17 @@ private object LocalMapperThemeConstants {
|
||||
|
||||
fun AppAppearance.toTheme() = themeAppearanceLookup.entries.first { it.value == this }.key
|
||||
|
||||
fun LocalAutolock.toAutolockInterval() =
|
||||
when (this) {
|
||||
is Never -> AutoLockInterval.Never
|
||||
is Always -> AutoLockInterval.Immediately
|
||||
is Minutes -> AutoLockInterval.fromMinutes(this.v1.toLong())
|
||||
}
|
||||
fun LocalAutolock.toAutolockInterval() = when (this) {
|
||||
is Never -> AutoLockInterval.Never
|
||||
is Always -> AutoLockInterval.Immediately
|
||||
is Minutes -> AutoLockInterval.fromMinutes(this.v1.toLong())
|
||||
}
|
||||
|
||||
fun LocalProtection.toProtection() =
|
||||
when (this) {
|
||||
AppProtection.NONE -> Protection.None
|
||||
AppProtection.BIOMETRICS -> Protection.Biometrics
|
||||
AppProtection.PIN -> Protection.Pin
|
||||
}
|
||||
fun LocalProtection.toProtection() = when (this) {
|
||||
AppProtection.NONE -> Protection.None
|
||||
AppProtection.BIOMETRICS -> Protection.Biometrics
|
||||
AppProtection.PIN -> Protection.Pin
|
||||
}
|
||||
|
||||
fun LocalAppSettings.toAppSettings(customLanguage: AppLanguage? = null) = AppSettings(
|
||||
autolockProtection = protection.toProtection(),
|
||||
|
||||
+1
-1
@@ -15,8 +15,8 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.mailsettings.domain.model
|
||||
|
||||
import ch.protonmail.android.mailpinlock.model.AutoLockInterval
|
||||
|
||||
data class AppSettingsDiff(val theme: Theme? = null, val interval: AutoLockInterval? = null)
|
||||
|
||||
Reference in New Issue
Block a user