Skip to content

Commit

Permalink
feat: observe team app lock config (WPB-4476) (#2380)
Browse files Browse the repository at this point in the history
  • Loading branch information
ohassine committed Nov 2, 2023
1 parent 0584961 commit 9e51b65
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 103 deletions.
Expand Up @@ -18,33 +18,59 @@
package com.wire.android.feature

import com.wire.android.datastore.GlobalDataStore
import com.wire.android.di.KaliumCoreLogic
import com.wire.kalium.logic.CoreLogic
import com.wire.kalium.logic.feature.applock.AppLockTeamFeatureConfigObserverImpl.Companion.DEFAULT_TIMEOUT
import com.wire.kalium.logic.feature.session.CurrentSessionResult
import com.wire.kalium.logic.feature.session.CurrentSessionUseCase
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combineTransform
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds

@Singleton
class ObserveAppLockConfigUseCase @Inject constructor(
private val globalDataStore: GlobalDataStore,
@KaliumCoreLogic private val coreLogic: CoreLogic,
private val currentSession: CurrentSessionUseCase
) {

operator fun invoke(): Flow<AppLockConfig> =
globalDataStore.isAppLockPasscodeSetFlow().map { // TODO: include checking if any logged account does not enforce app-lock
when {
it -> AppLockConfig.Enabled
else -> AppLockConfig.Disabled
operator fun invoke(): Flow<AppLockConfig> = channelFlow {
when (val currentSession = currentSession()) {
is CurrentSessionResult.Failure -> {
send(AppLockConfig.Disabled(DEFAULT_TIMEOUT))
}

is CurrentSessionResult.Success -> {
val userId = currentSession.accountInfo.userId
val appLockTeamFeatureConfigFlow =
coreLogic.getSessionScope(userId).appLockTeamFeatureConfigObserver

appLockTeamFeatureConfigFlow().combineTransform(
globalDataStore.isAppLockPasscodeSetFlow()
) { isAppLockedByTeam, isAppLocked ->
if (isAppLockedByTeam.isEnabled) {
emit(AppLockConfig.EnforcedByTeam(isAppLockedByTeam.timeout))
} else {
if (isAppLocked) {
emit(AppLockConfig.Enabled(isAppLockedByTeam.timeout))
} else {
emit(AppLockConfig.Disabled(isAppLockedByTeam.timeout))
}
}
}.collectLatest {
send(it)
}
}
}
}
}

sealed class AppLockConfig(open val timeout: Duration = DEFAULT_TIMEOUT) {
data object Disabled : AppLockConfig()
data object Enabled : AppLockConfig()
data class Disabled(override val timeout: Duration) : AppLockConfig(timeout)
data class Enabled(override val timeout: Duration) : AppLockConfig(timeout)
data class EnforcedByTeam(override val timeout: Duration) : AppLockConfig(timeout)

companion object {
val DEFAULT_TIMEOUT = 60.seconds
}
}
Expand Up @@ -18,14 +18,14 @@
package com.wire.android.ui.home.appLock

import androidx.compose.ui.text.input.TextFieldValue
import com.wire.android.feature.AppLockConfig
import com.wire.kalium.logic.feature.applock.AppLockTeamFeatureConfigObserverImpl.Companion.DEFAULT_TIMEOUT
import com.wire.kalium.logic.feature.auth.ValidatePasswordResult
import kotlin.time.Duration

data class SetLockCodeViewState(
val continueEnabled: Boolean = false,
val password: TextFieldValue = TextFieldValue(),
val passwordValidation: ValidatePasswordResult = ValidatePasswordResult.Invalid(),
val timeout: Duration = AppLockConfig.DEFAULT_TIMEOUT,
val timeout: Duration = DEFAULT_TIMEOUT,
val done: Boolean = false
)
Expand Up @@ -114,9 +114,20 @@ fun SettingsScreenContent(
add(SettingsItem.NetworkSettings)
add(SettingsItem.AppLock(
when (settingsState.appLockConfig) {
AppLockConfig.Disabled -> SwitchState.Enabled(false, true, onAppLockSwitchChanged)
AppLockConfig.Enabled -> SwitchState.Enabled(true, true) { turnAppLockOffDialogState.show(Unit) }
is AppLockConfig.EnforcedByTeam -> SwitchState.TextOnly(true)
is AppLockConfig.Disabled -> SwitchState.Enabled(
value = false,
isOnOffVisible = true,
onCheckedChange = onAppLockSwitchChanged
)
is AppLockConfig.Enabled -> SwitchState.Enabled(
value = true,
isOnOffVisible = true
) {
turnAppLockOffDialogState.show(Unit)
}
is AppLockConfig.EnforcedByTeam -> {
SwitchState.TextOnly(true)
}
}
))
},
Expand Down
Expand Up @@ -18,7 +18,8 @@
package com.wire.android.ui.home.settings

import com.wire.android.feature.AppLockConfig
import com.wire.kalium.logic.feature.applock.AppLockTeamFeatureConfigObserverImpl.Companion.DEFAULT_TIMEOUT

data class SettingsState(
val appLockConfig: AppLockConfig = AppLockConfig.Disabled,
val appLockConfig: AppLockConfig = AppLockConfig.Disabled(DEFAULT_TIMEOUT),
)
Expand Up @@ -17,44 +17,125 @@
*/
package com.wire.android.feature

import app.cash.turbine.test
import com.wire.android.datastore.GlobalDataStore
import com.wire.kalium.logic.CoreLogic
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.UserSessionScope
import com.wire.kalium.logic.feature.applock.AppLockTeamConfig
import com.wire.kalium.logic.feature.applock.AppLockTeamFeatureConfigObserver
import com.wire.kalium.logic.feature.auth.AccountInfo
import com.wire.kalium.logic.feature.session.CurrentSessionResult
import com.wire.kalium.logic.feature.session.CurrentSessionUseCase
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.every
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.internal.assertEquals
import org.junit.jupiter.api.Test
import kotlin.time.Duration.Companion.seconds

class ObserveAppLockConfigUseCaseTest {

// TODO: include checking if any logged account does not enforce app-lock
@Test
fun givenPasscodeIsSet_whenObservingAppLockConfig_thenReturnEnabled() = runTest {
fun givenNoValidSession_whenObservingAppLock_thenSendDisabledStatus() = runTest {
val (_, useCase) = Arrangement()
.withAppLockPasscodeSet(true)
.withNonValidSession()
.arrange()

val result = useCase.invoke().firstOrNull()
val result = useCase.invoke()

assert(result is AppLockConfig.Enabled)
result.test {
val appLockStatus = awaitItem()

assertEquals(AppLockConfig.Disabled(timeout), appLockStatus)
awaitComplete()
}
}

@Test
fun givenPasscodeIsNotSet_whenObservingAppLockConfig_thenReturnDisabled() = runTest {
val (_, useCase) = Arrangement()
.withAppLockPasscodeSet(false)
.arrange()
fun givenValidSessionAndAppLockedByTeam_whenObservingAppLock_thenSendEnforcedByTeamStatus() =
runTest {
val (_, useCase) = Arrangement()
.withValidSession()
.withTeamAppLockEnabled()
.withAppLockedByCurrentUser()
.arrange()

val result = useCase.invoke().firstOrNull()
val result = useCase.invoke()

assert(result is AppLockConfig.Disabled)
}
result.test {
val appLockStatus = awaitItem()

assertEquals(AppLockConfig.EnforcedByTeam(timeout), appLockStatus)
awaitComplete()
}
}

@Test
fun givenValidSessionAndAppLockedByUserOnly_whenObservingAppLock_thenSendEnabledStatus() =
runTest {
val (_, useCase) = Arrangement()
.withValidSession()
.withTeamAppLockDisabled()
.withAppLockedByCurrentUser()
.arrange()

val result = useCase.invoke()

result.test {
val appLockStatus = awaitItem()

assertEquals(AppLockConfig.Enabled(timeout), appLockStatus)
awaitComplete()
}
}

@Test
fun givenValidSessionAndAppNotLockedByUserNorTeam_whenObservingAppLock_thenSendDisabledStatus() =
runTest {
val (_, useCase) = Arrangement()
.withValidSession()
.withTeamAppLockDisabled()
.withAppNonLockedByCurrentUser()
.arrange()

val result = useCase.invoke()

result.test {
val appLockStatus = awaitItem()

assertEquals(AppLockConfig.Disabled(timeout), appLockStatus)
awaitComplete()
}
}

inner class Arrangement {

@MockK
lateinit var globalDataStore: GlobalDataStore
val useCase by lazy { ObserveAppLockConfigUseCase(globalDataStore) }

@MockK
lateinit var currentSession: CurrentSessionUseCase

@MockK
lateinit var coreLogic: CoreLogic

@MockK
lateinit var userSessionScope: UserSessionScope

@MockK
lateinit var appLockTeamFeatureConfigObserver: AppLockTeamFeatureConfigObserver

val useCase by lazy {
ObserveAppLockConfigUseCase(
globalDataStore = globalDataStore,
coreLogic = coreLogic,
currentSession = currentSession
)
}

init {
MockKAnnotations.init(this, relaxUnitFun = true)
Expand All @@ -65,5 +146,46 @@ class ObserveAppLockConfigUseCaseTest {
}

fun arrange() = this to useCase

fun withNonValidSession() = apply {
coEvery { currentSession() } returns CurrentSessionResult.Failure.SessionNotFound
}

fun withValidSession() = apply {
coEvery { currentSession() } returns CurrentSessionResult.Success(accountInfo)
}

fun withTeamAppLockEnabled() = apply {
every { coreLogic.getSessionScope(any()) } returns userSessionScope
every {
userSessionScope.appLockTeamFeatureConfigObserver
} returns appLockTeamFeatureConfigObserver
every {
appLockTeamFeatureConfigObserver.invoke()
} returns flowOf(AppLockTeamConfig(true, timeout))
}

fun withTeamAppLockDisabled() = apply {
every { coreLogic.getSessionScope(any()) } returns userSessionScope
every {
userSessionScope.appLockTeamFeatureConfigObserver
} returns appLockTeamFeatureConfigObserver
every {
appLockTeamFeatureConfigObserver.invoke()
} returns flowOf(AppLockTeamConfig(false, timeout))
}

fun withAppLockedByCurrentUser() = apply {
every { globalDataStore.isAppLockPasscodeSetFlow() } returns flowOf(true)
}

fun withAppNonLockedByCurrentUser() = apply {
every { globalDataStore.isAppLockPasscodeSetFlow() } returns flowOf(false)
}
}

companion object {
private val accountInfo = AccountInfo.Valid(UserId("userId", "domain"))
private val timeout = 60.seconds
}
}

0 comments on commit 9e51b65

Please sign in to comment.