mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
BIT-1076 Requesting Camera Permission (#415)
This commit is contained in:
parent
f2f3a6a386
commit
39e285fff8
11 changed files with 247 additions and 19 deletions
|
@ -2,6 +2,11 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.camera"
|
||||||
|
android:required="false" />
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".BitwardenApplication"
|
android:name=".BitwardenApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.base.util
|
||||||
|
|
||||||
|
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for managing permissions.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
interface PermissionsManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for creating and returning a permission launcher.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun getLauncher(onResult: (Boolean) -> Unit): ManagedActivityResultLauncher<String, Boolean>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for checking whether the permission is granted.
|
||||||
|
*/
|
||||||
|
fun checkPermission(permission: String): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for checking if an informative UI should be shown the user.
|
||||||
|
*/
|
||||||
|
fun shouldShouldRequestPermissionRationale(
|
||||||
|
permission: String,
|
||||||
|
): Boolean
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.base.util
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary implementation of [PermissionsManager].
|
||||||
|
*/
|
||||||
|
class PermissionsManagerImpl(
|
||||||
|
private val activity: Activity,
|
||||||
|
) : PermissionsManager {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun getLauncher(
|
||||||
|
onResult: (Boolean) -> Unit,
|
||||||
|
): ManagedActivityResultLauncher<String, Boolean> =
|
||||||
|
rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.RequestPermission(),
|
||||||
|
onResult,
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun checkPermission(permission: String): Boolean =
|
||||||
|
ContextCompat.checkSelfPermission(
|
||||||
|
activity,
|
||||||
|
permission,
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
|
||||||
|
override fun shouldShouldRequestPermissionRationale(
|
||||||
|
permission: String,
|
||||||
|
): Boolean =
|
||||||
|
activity.shouldShowRequestPermissionRationale(permission)
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package com.x8bit.bitwarden.ui.vault.feature.additem
|
package com.x8bit.bitwarden.ui.vault.feature.additem
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
@ -11,6 +12,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.PermissionsManager
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenListHeaderText
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenListHeaderText
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
@ -19,6 +21,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||||
* The top level content UI state for the [VaultAddItemScreen].
|
* The top level content UI state for the [VaultAddItemScreen].
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
|
@Suppress("LongMethod")
|
||||||
fun AddEditItemContent(
|
fun AddEditItemContent(
|
||||||
state: VaultAddItemState.ViewState.Content,
|
state: VaultAddItemState.ViewState.Content,
|
||||||
isAddItemMode: Boolean,
|
isAddItemMode: Boolean,
|
||||||
|
@ -26,7 +29,23 @@ fun AddEditItemContent(
|
||||||
commonTypeHandlers: VaultAddItemCommonTypeHandlers,
|
commonTypeHandlers: VaultAddItemCommonTypeHandlers,
|
||||||
loginItemTypeHandlers: VaultAddLoginItemTypeHandlers,
|
loginItemTypeHandlers: VaultAddLoginItemTypeHandlers,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
permissionsManager: PermissionsManager,
|
||||||
) {
|
) {
|
||||||
|
val launcher = permissionsManager.getLauncher(
|
||||||
|
onResult = { isGranted ->
|
||||||
|
when (state.type) {
|
||||||
|
is VaultAddItemState.ViewState.Content.ItemType.SecureNotes -> Unit
|
||||||
|
// TODO: Create UI for card-type item creation BIT-507
|
||||||
|
is VaultAddItemState.ViewState.Content.ItemType.Card -> Unit
|
||||||
|
// TODO: Create UI for identity-type item creation BIT-667
|
||||||
|
is VaultAddItemState.ViewState.Content.ItemType.Identity -> Unit
|
||||||
|
is VaultAddItemState.ViewState.Content.ItemType.Login -> {
|
||||||
|
loginItemTypeHandlers.onSetupTotpClick(isGranted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
|
@ -57,6 +76,13 @@ fun AddEditItemContent(
|
||||||
isAddItemMode = isAddItemMode,
|
isAddItemMode = isAddItemMode,
|
||||||
commonActionHandler = commonTypeHandlers,
|
commonActionHandler = commonTypeHandlers,
|
||||||
loginItemTypeHandlers = loginItemTypeHandlers,
|
loginItemTypeHandlers = loginItemTypeHandlers,
|
||||||
|
onTotpSetupClick = {
|
||||||
|
if (permissionsManager.checkPermission(Manifest.permission.CAMERA)) {
|
||||||
|
loginItemTypeHandlers.onSetupTotpClick(true)
|
||||||
|
} else {
|
||||||
|
launcher.launch(Manifest.permission.CAMERA)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,13 +32,14 @@ import kotlinx.collections.immutable.toImmutableList
|
||||||
/**
|
/**
|
||||||
* The UI for adding and editing a login cipher.
|
* The UI for adding and editing a login cipher.
|
||||||
*/
|
*/
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod", "LongParameterList")
|
||||||
fun LazyListScope.addEditLoginItems(
|
fun LazyListScope.addEditLoginItems(
|
||||||
commonState: VaultAddItemState.ViewState.Content.Common,
|
commonState: VaultAddItemState.ViewState.Content.Common,
|
||||||
loginState: VaultAddItemState.ViewState.Content.ItemType.Login,
|
loginState: VaultAddItemState.ViewState.Content.ItemType.Login,
|
||||||
isAddItemMode: Boolean,
|
isAddItemMode: Boolean,
|
||||||
commonActionHandler: VaultAddItemCommonTypeHandlers,
|
commonActionHandler: VaultAddItemCommonTypeHandlers,
|
||||||
loginItemTypeHandlers: VaultAddLoginItemTypeHandlers,
|
loginItemTypeHandlers: VaultAddLoginItemTypeHandlers,
|
||||||
|
onTotpSetupClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
@ -112,7 +113,7 @@ fun LazyListScope.addEditLoginItems(
|
||||||
BitwardenFilledTonalButtonWithIcon(
|
BitwardenFilledTonalButtonWithIcon(
|
||||||
label = stringResource(id = R.string.setup_totp),
|
label = stringResource(id = R.string.setup_totp),
|
||||||
icon = painterResource(id = R.drawable.ic_light_bulb),
|
icon = painterResource(id = R.drawable.ic_light_bulb),
|
||||||
onClick = loginItemTypeHandlers.onSetupTotpClick,
|
onClick = onTotpSetupClick,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.x8bit.bitwarden.ui.vault.feature.additem
|
package com.x8bit.bitwarden.ui.vault.feature.additem
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.imePadding
|
import androidx.compose.foundation.layout.imePadding
|
||||||
|
@ -27,6 +28,8 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||||
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
|
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.PermissionsManager
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.PermissionsManagerImpl
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top level composable for the vault add item screen.
|
* Top level composable for the vault add item screen.
|
||||||
|
@ -37,6 +40,8 @@ import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
|
||||||
fun VaultAddItemScreen(
|
fun VaultAddItemScreen(
|
||||||
onNavigateBack: () -> Unit,
|
onNavigateBack: () -> Unit,
|
||||||
viewModel: VaultAddItemViewModel = hiltViewModel(),
|
viewModel: VaultAddItemViewModel = hiltViewModel(),
|
||||||
|
permissionsManager: PermissionsManager =
|
||||||
|
PermissionsManagerImpl(LocalContext.current as Activity),
|
||||||
) {
|
) {
|
||||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
@ -101,6 +106,7 @@ fun VaultAddItemScreen(
|
||||||
},
|
},
|
||||||
loginItemTypeHandlers = loginItemTypeHandlers,
|
loginItemTypeHandlers = loginItemTypeHandlers,
|
||||||
commonTypeHandlers = commonTypeHandlers,
|
commonTypeHandlers = commonTypeHandlers,
|
||||||
|
permissionsManager = permissionsManager,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.imePadding()
|
.imePadding()
|
||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
|
|
|
@ -15,8 +15,8 @@ import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.concat
|
import com.x8bit.bitwarden.ui.platform.base.util.concat
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.additem.model.CustomFieldType
|
import com.x8bit.bitwarden.ui.vault.feature.additem.model.CustomFieldType
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.additem.util.toViewState
|
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.additem.model.toCustomField
|
import com.x8bit.bitwarden.ui.vault.feature.additem.model.toCustomField
|
||||||
|
import com.x8bit.bitwarden.ui.vault.feature.additem.util.toViewState
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.vault.util.toCipherView
|
import com.x8bit.bitwarden.ui.vault.feature.vault.util.toCipherView
|
||||||
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
|
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
|
||||||
import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType
|
import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType
|
||||||
|
@ -332,7 +332,7 @@ class VaultAddItemViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
is VaultAddItemAction.ItemType.LoginType.SetupTotpClick -> {
|
is VaultAddItemAction.ItemType.LoginType.SetupTotpClick -> {
|
||||||
handleLoginSetupTotpClick()
|
handleLoginSetupTotpClick(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
is VaultAddItemAction.ItemType.LoginType.UriSettingsClick -> {
|
is VaultAddItemAction.ItemType.LoginType.UriSettingsClick -> {
|
||||||
|
@ -399,11 +399,18 @@ class VaultAddItemViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleLoginSetupTotpClick() {
|
private fun handleLoginSetupTotpClick(
|
||||||
|
action: VaultAddItemAction.ItemType.LoginType.SetupTotpClick,
|
||||||
|
) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
val message = if (action.isGranted) {
|
||||||
|
"Permission Granted, QR Code Scanner Not Implemented"
|
||||||
|
} else {
|
||||||
|
"Permission Not Granted, Manual QR Code Entry Not Implemented"
|
||||||
|
}
|
||||||
sendEvent(
|
sendEvent(
|
||||||
event = VaultAddItemEvent.ShowToast(
|
event = VaultAddItemEvent.ShowToast(
|
||||||
message = "Setup TOTP",
|
message = message,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -972,6 +979,13 @@ sealed class VaultAddItemAction {
|
||||||
*/
|
*/
|
||||||
data class UriTextChange(val uri: String) : LoginType()
|
data class UriTextChange(val uri: String) : LoginType()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the action to set up TOTP.
|
||||||
|
*
|
||||||
|
* @property isGranted the status of the camera permission
|
||||||
|
*/
|
||||||
|
data class SetupTotpClick(val isGranted: Boolean) : LoginType()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the action to open the username generator.
|
* Represents the action to open the username generator.
|
||||||
*/
|
*/
|
||||||
|
@ -987,11 +1001,6 @@ sealed class VaultAddItemAction {
|
||||||
*/
|
*/
|
||||||
data object OpenPasswordGeneratorClick : LoginType()
|
data object OpenPasswordGeneratorClick : LoginType()
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the action to set up TOTP.
|
|
||||||
*/
|
|
||||||
data object SetupTotpClick : LoginType()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the action of clicking TOTP settings
|
* Represents the action of clicking TOTP settings
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -26,7 +26,7 @@ class VaultAddLoginItemTypeHandlers(
|
||||||
val onOpenUsernameGeneratorClick: () -> Unit,
|
val onOpenUsernameGeneratorClick: () -> Unit,
|
||||||
val onPasswordCheckerClick: () -> Unit,
|
val onPasswordCheckerClick: () -> Unit,
|
||||||
val onOpenPasswordGeneratorClick: () -> Unit,
|
val onOpenPasswordGeneratorClick: () -> Unit,
|
||||||
val onSetupTotpClick: () -> Unit,
|
val onSetupTotpClick: (Boolean) -> Unit,
|
||||||
val onUriSettingsClick: () -> Unit,
|
val onUriSettingsClick: () -> Unit,
|
||||||
val onAddNewUriClick: () -> Unit,
|
val onAddNewUriClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
@ -72,8 +72,10 @@ class VaultAddLoginItemTypeHandlers(
|
||||||
VaultAddItemAction.ItemType.LoginType.OpenPasswordGeneratorClick,
|
VaultAddItemAction.ItemType.LoginType.OpenPasswordGeneratorClick,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onSetupTotpClick = {
|
onSetupTotpClick = { isGranted ->
|
||||||
viewModel.trySendAction(VaultAddItemAction.ItemType.LoginType.SetupTotpClick)
|
viewModel.trySendAction(
|
||||||
|
VaultAddItemAction.ItemType.LoginType.SetupTotpClick(isGranted),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
onUriSettingsClick = {
|
onUriSettingsClick = {
|
||||||
viewModel.trySendAction(VaultAddItemAction.ItemType.LoginType.UriSettingsClick)
|
viewModel.trySendAction(VaultAddItemAction.ItemType.LoginType.UriSettingsClick)
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.base.util
|
||||||
|
|
||||||
|
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class used to test permissions
|
||||||
|
*/
|
||||||
|
class FakePermissionManager : PermissionsManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value returned when we check if we have the permission.
|
||||||
|
*/
|
||||||
|
var checkPermissionResult: Boolean = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value returned when the user is asked for permission.
|
||||||
|
*/
|
||||||
|
var getPermissionsResult: Boolean = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * The value for whether a rationale should be shown to the user.
|
||||||
|
*/
|
||||||
|
var shouldShowRequestRationale: Boolean = false
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun getLauncher(
|
||||||
|
onResult: (Boolean) -> Unit,
|
||||||
|
): ManagedActivityResultLauncher<String, Boolean> {
|
||||||
|
return mockk {
|
||||||
|
every { launch(any()) } answers { onResult.invoke(getPermissionsResult) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkPermission(permission: String): Boolean {
|
||||||
|
return checkPermissionResult
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldShouldRequestPermissionRationale(
|
||||||
|
permission: String,
|
||||||
|
): Boolean {
|
||||||
|
return shouldShowRequestRationale
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ import com.x8bit.bitwarden.ui.util.onAllNodesWithTextAfterScroll
|
||||||
import com.x8bit.bitwarden.ui.util.onNodeWithContentDescriptionAfterScroll
|
import com.x8bit.bitwarden.ui.util.onNodeWithContentDescriptionAfterScroll
|
||||||
import com.x8bit.bitwarden.ui.util.onNodeWithTextAfterScroll
|
import com.x8bit.bitwarden.ui.util.onNodeWithTextAfterScroll
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.additem.model.CustomFieldType
|
import com.x8bit.bitwarden.ui.vault.feature.additem.model.CustomFieldType
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.FakePermissionManager
|
||||||
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
|
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
@ -49,6 +50,8 @@ class VaultAddItemScreenTest : BaseComposeTest() {
|
||||||
private val mutableEventFlow = MutableSharedFlow<VaultAddItemEvent>(Int.MAX_VALUE)
|
private val mutableEventFlow = MutableSharedFlow<VaultAddItemEvent>(Int.MAX_VALUE)
|
||||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE_LOGIN)
|
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE_LOGIN)
|
||||||
|
|
||||||
|
private val fakePermissionManager: FakePermissionManager = FakePermissionManager()
|
||||||
|
|
||||||
private val viewModel = mockk<VaultAddItemViewModel>(relaxed = true) {
|
private val viewModel = mockk<VaultAddItemViewModel>(relaxed = true) {
|
||||||
every { eventFlow } returns mutableEventFlow
|
every { eventFlow } returns mutableEventFlow
|
||||||
every { stateFlow } returns mutableStateFlow
|
every { stateFlow } returns mutableStateFlow
|
||||||
|
@ -60,6 +63,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
|
||||||
VaultAddItemScreen(
|
VaultAddItemScreen(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
onNavigateBack = { onNavigateBackCalled = true },
|
onNavigateBack = { onNavigateBackCalled = true },
|
||||||
|
permissionsManager = fakePermissionManager,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -315,15 +319,52 @@ class VaultAddItemScreenTest : BaseComposeTest() {
|
||||||
.assertTextContains("•••••••••••")
|
.assertTextContains("•••••••••••")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `in ItemType_Login state clicking Set up TOTP button should trigger SetupTotpClick`() {
|
fun `in ItemType_Login state clicking SetupTOTP button with a positive result should send true if permission check returns true`() {
|
||||||
|
fakePermissionManager.checkPermissionResult = true
|
||||||
|
|
||||||
composeTestRule
|
composeTestRule
|
||||||
.onNodeWithTextAfterScroll(text = "Set up TOTP")
|
.onNodeWithTextAfterScroll(text = "Set up TOTP")
|
||||||
.performClick()
|
.performClick()
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
viewModel.trySendAction(
|
viewModel.trySendAction(
|
||||||
VaultAddItemAction.ItemType.LoginType.SetupTotpClick,
|
VaultAddItemAction.ItemType.LoginType.SetupTotpClick(true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `in ItemType_Login state clicking SetupTOTP button with a positive result should send true`() {
|
||||||
|
fakePermissionManager.checkPermissionResult = false
|
||||||
|
fakePermissionManager.getPermissionsResult = true
|
||||||
|
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithTextAfterScroll(text = "Set up TOTP")
|
||||||
|
.performClick()
|
||||||
|
|
||||||
|
verify {
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddItemAction.ItemType.LoginType.SetupTotpClick(true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `in ItemType_Login state clicking Set up TOTP button with a negative result should send false`() {
|
||||||
|
fakePermissionManager.checkPermissionResult = false
|
||||||
|
fakePermissionManager.getPermissionsResult = false
|
||||||
|
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithTextAfterScroll(text = "Set up TOTP")
|
||||||
|
.performClick()
|
||||||
|
|
||||||
|
verify {
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddItemAction.ItemType.LoginType.SetupTotpClick(false),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -474,12 +474,37 @@ class VaultAddItemViewModelTest : BaseViewModelTest() {
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `SetupTotpClick should emit ShowToast with 'Setup TOTP' message`() = runTest {
|
fun `SetupTotpClick should emit ShowToast with permission granted when isGranted is true`() = runTest {
|
||||||
val viewModel = createAddVaultItemViewModel()
|
val viewModel = createAddVaultItemViewModel()
|
||||||
|
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
viewModel.actionChannel.trySend(VaultAddItemAction.ItemType.LoginType.SetupTotpClick)
|
viewModel.actionChannel.trySend(
|
||||||
assertEquals(VaultAddItemEvent.ShowToast("Setup TOTP"), awaitItem())
|
VaultAddItemAction.ItemType.LoginType.SetupTotpClick(
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
VaultAddItemEvent.ShowToast("Permission Granted, QR Code Scanner Not Implemented"),
|
||||||
|
awaitItem(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `SetupTotpClick should emit ShowToast with permission not granted when isGranted is false`() = runTest {
|
||||||
|
val viewModel = createAddVaultItemViewModel()
|
||||||
|
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.actionChannel.trySend(
|
||||||
|
VaultAddItemAction.ItemType.LoginType.SetupTotpClick(
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
VaultAddItemEvent.ShowToast("Permission Not Granted, Manual QR Code Entry Not Implemented"),
|
||||||
|
awaitItem(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue