mirror of
https://github.com/bitwarden/android.git
synced 2025-02-16 11:59:57 +03:00
PM-14458: Update notifications permissions request (#4229)
This commit is contained in:
parent
202b4de5ca
commit
4930c1032e
13 changed files with 369 additions and 89 deletions
|
@ -9,6 +9,7 @@ import androidx.compose.material3.SheetState
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
@ -19,6 +20,7 @@ import com.x8bit.bitwarden.ui.platform.components.appbar.NavigationIcon
|
||||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reusable modal bottom sheet that applies provides a bottom sheet layout with the
|
* A reusable modal bottom sheet that applies provides a bottom sheet layout with the
|
||||||
|
@ -28,11 +30,12 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||||
* @param sheetTitle The title to display in the [BitwardenTopAppBar]
|
* @param sheetTitle The title to display in the [BitwardenTopAppBar]
|
||||||
* @param onDismiss The action to perform when the bottom sheet is dismissed will also be performed
|
* @param onDismiss The action to perform when the bottom sheet is dismissed will also be performed
|
||||||
* when the "close" icon is clicked, caller must handle any desired animation or hiding of the
|
* when the "close" icon is clicked, caller must handle any desired animation or hiding of the
|
||||||
* bottom sheet.
|
* bottom sheet. This will be invoked _after_ the sheet has been animated away.
|
||||||
* @param showBottomSheet Whether or not to show the bottom sheet, by default this is true assuming
|
* @param showBottomSheet Whether or not to show the bottom sheet, by default this is true assuming
|
||||||
* the showing/hiding will be handled by the caller.
|
* the showing/hiding will be handled by the caller.
|
||||||
* @param sheetContent Content to display in the bottom sheet. The content is passed the padding
|
* @param sheetContent Content to display in the bottom sheet. The content is passed the padding
|
||||||
* from the containing [BitwardenScaffold].
|
* from the containing [BitwardenScaffold] and a `onDismiss` lambda to be used for manual dismissal
|
||||||
|
* that will include the dismissal animation.
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -42,7 +45,10 @@ fun BitwardenModalBottomSheet(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
showBottomSheet: Boolean = true,
|
showBottomSheet: Boolean = true,
|
||||||
sheetState: SheetState = rememberModalBottomSheetState(),
|
sheetState: SheetState = rememberModalBottomSheetState(),
|
||||||
sheetContent: @Composable (PaddingValues) -> Unit,
|
sheetContent: @Composable (
|
||||||
|
paddingValues: PaddingValues,
|
||||||
|
animatedOnDismiss: () -> Unit,
|
||||||
|
) -> Unit,
|
||||||
) {
|
) {
|
||||||
if (!showBottomSheet) return
|
if (!showBottomSheet) return
|
||||||
ModalBottomSheet(
|
ModalBottomSheet(
|
||||||
|
@ -56,13 +62,14 @@ fun BitwardenModalBottomSheet(
|
||||||
shape = BitwardenTheme.shapes.bottomSheet,
|
shape = BitwardenTheme.shapes.bottomSheet,
|
||||||
) {
|
) {
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||||
|
val animatedOnDismiss = sheetState.createAnimatedDismissAction(onDismiss = onDismiss)
|
||||||
BitwardenScaffold(
|
BitwardenScaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
BitwardenTopAppBar(
|
BitwardenTopAppBar(
|
||||||
title = sheetTitle,
|
title = sheetTitle,
|
||||||
navigationIcon = NavigationIcon(
|
navigationIcon = NavigationIcon(
|
||||||
navigationIcon = rememberVectorPainter(R.drawable.ic_close),
|
navigationIcon = rememberVectorPainter(R.drawable.ic_close),
|
||||||
onNavigationIconClick = onDismiss,
|
onNavigationIconClick = animatedOnDismiss,
|
||||||
navigationIconContentDescription = stringResource(R.string.close),
|
navigationIconContentDescription = stringResource(R.string.close),
|
||||||
),
|
),
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
|
@ -73,7 +80,18 @@ fun BitwardenModalBottomSheet(
|
||||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
sheetContent(paddingValues)
|
sheetContent(paddingValues, animatedOnDismiss)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun SheetState.createAnimatedDismissAction(onDismiss: () -> Unit): () -> Unit {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
return {
|
||||||
|
scope
|
||||||
|
.launch { this@createAnimatedDismissAction.hide() }
|
||||||
|
.invokeOnCompletion { onDismiss() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.pendingrequests
|
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.pendingrequests
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.os.Build
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
@ -14,6 +17,7 @@ import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
@ -21,6 +25,7 @@ import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.material3.ripple
|
import androidx.compose.material3.ripple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -40,11 +45,15 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
|
import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
|
||||||
|
import com.x8bit.bitwarden.data.platform.util.isFdroid
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.LivecycleEventEffect
|
import com.x8bit.bitwarden.ui.platform.base.util.LivecycleEventEffect
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.bottomDivider
|
import com.x8bit.bitwarden.ui.platform.base.util.bottomDivider
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.bottomsheet.BitwardenModalBottomSheet
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||||
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenErrorContent
|
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenErrorContent
|
||||||
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenLoadingContent
|
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenLoadingContent
|
||||||
|
@ -52,6 +61,8 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialo
|
||||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.rememberBitwardenPullToRefreshState
|
import com.x8bit.bitwarden.ui.platform.components.scaffold.rememberBitwardenPullToRefreshState
|
||||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||||
|
import com.x8bit.bitwarden.ui.platform.composition.LocalPermissionsManager
|
||||||
|
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,6 +73,7 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||||
@Composable
|
@Composable
|
||||||
fun PendingRequestsScreen(
|
fun PendingRequestsScreen(
|
||||||
viewModel: PendingRequestsViewModel = hiltViewModel(),
|
viewModel: PendingRequestsViewModel = hiltViewModel(),
|
||||||
|
permissionsManager: PermissionsManager = LocalPermissionsManager.current,
|
||||||
onNavigateBack: () -> Unit,
|
onNavigateBack: () -> Unit,
|
||||||
onNavigateToLoginApproval: (fingerprint: String) -> Unit,
|
onNavigateToLoginApproval: (fingerprint: String) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
@ -98,6 +110,29 @@ fun PendingRequestsScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val hideBottomSheet = state.hideBottomSheet ||
|
||||||
|
isFdroid ||
|
||||||
|
isBuildVersionBelow(Build.VERSION_CODES.TIRAMISU) ||
|
||||||
|
permissionsManager.checkPermission(Manifest.permission.POST_NOTIFICATIONS) ||
|
||||||
|
!permissionsManager.shouldShowRequestPermissionRationale(
|
||||||
|
permission = Manifest.permission.POST_NOTIFICATIONS,
|
||||||
|
)
|
||||||
|
BitwardenModalBottomSheet(
|
||||||
|
showBottomSheet = !hideBottomSheet,
|
||||||
|
sheetTitle = stringResource(R.string.enable_notifications),
|
||||||
|
onDismiss = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(PendingRequestsAction.HideBottomSheet) }
|
||||||
|
},
|
||||||
|
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
|
||||||
|
modifier = Modifier.statusBarsPadding(),
|
||||||
|
) { paddingValues, animatedOnDismiss ->
|
||||||
|
PendingRequestsBottomSheetContent(
|
||||||
|
modifier = Modifier.padding(paddingValues),
|
||||||
|
permissionsManager = permissionsManager,
|
||||||
|
onDismiss = animatedOnDismiss,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
BitwardenScaffold(
|
BitwardenScaffold(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -338,3 +373,68 @@ private fun PendingRequestsEmpty(
|
||||||
Spacer(modifier = Modifier.height(64.dp))
|
Spacer(modifier = Modifier.height(64.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun PendingRequestsBottomSheetContent(
|
||||||
|
permissionsManager: PermissionsManager,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val notificationPermissionLauncher = permissionsManager.getLauncher {
|
||||||
|
onDismiss()
|
||||||
|
}
|
||||||
|
Column(modifier = modifier.verticalScroll(rememberScrollState())) {
|
||||||
|
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||||
|
Image(
|
||||||
|
painter = rememberVectorPainter(id = R.drawable.img_2fa),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.standardHorizontalMargin()
|
||||||
|
.size(size = 132.dp)
|
||||||
|
.align(alignment = Alignment.CenterHorizontally),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.log_in_quickly_and_easily_across_devices),
|
||||||
|
style = BitwardenTheme.typography.titleMedium,
|
||||||
|
color = BitwardenTheme.colorScheme.text.primary,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.standardHorizontalMargin(),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
Text(
|
||||||
|
text = stringResource(
|
||||||
|
id = R.string.bitwarden_can_notify_you_each_time_you_receive_a_new_login_request_from_another_device,
|
||||||
|
),
|
||||||
|
style = BitwardenTheme.typography.bodyMedium,
|
||||||
|
color = BitwardenTheme.colorScheme.text.primary,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.standardHorizontalMargin(),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||||
|
BitwardenFilledButton(
|
||||||
|
label = stringResource(id = R.string.enable_notifications),
|
||||||
|
onClick = {
|
||||||
|
@SuppressLint("InlinedApi")
|
||||||
|
notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.standardHorizontalMargin(),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||||
|
BitwardenOutlinedButton(
|
||||||
|
label = stringResource(id = R.string.skip_for_now),
|
||||||
|
onClick = onDismiss,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.standardHorizontalMargin(),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ private const val KEY_STATE = "state"
|
||||||
/**
|
/**
|
||||||
* View model for the pending login requests screen.
|
* View model for the pending login requests screen.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("TooManyFunctions")
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class PendingRequestsViewModel @Inject constructor(
|
class PendingRequestsViewModel @Inject constructor(
|
||||||
private val clock: Clock,
|
private val clock: Clock,
|
||||||
|
@ -39,6 +40,7 @@ class PendingRequestsViewModel @Inject constructor(
|
||||||
viewState = PendingRequestsState.ViewState.Loading,
|
viewState = PendingRequestsState.ViewState.Loading,
|
||||||
isPullToRefreshSettingEnabled = settingsRepository.getPullToRefreshEnabledFlow().value,
|
isPullToRefreshSettingEnabled = settingsRepository.getPullToRefreshEnabledFlow().value,
|
||||||
isRefreshing = false,
|
isRefreshing = false,
|
||||||
|
hideBottomSheet = false,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
private var authJob: Job = Job().apply { complete() }
|
private var authJob: Job = Job().apply { complete() }
|
||||||
|
@ -56,6 +58,7 @@ class PendingRequestsViewModel @Inject constructor(
|
||||||
when (action) {
|
when (action) {
|
||||||
PendingRequestsAction.CloseClick -> handleCloseClicked()
|
PendingRequestsAction.CloseClick -> handleCloseClicked()
|
||||||
PendingRequestsAction.DeclineAllRequestsConfirm -> handleDeclineAllRequestsConfirmed()
|
PendingRequestsAction.DeclineAllRequestsConfirm -> handleDeclineAllRequestsConfirmed()
|
||||||
|
PendingRequestsAction.HideBottomSheet -> handleHideBottomSheet()
|
||||||
PendingRequestsAction.LifecycleResume -> handleOnLifecycleResumed()
|
PendingRequestsAction.LifecycleResume -> handleOnLifecycleResumed()
|
||||||
PendingRequestsAction.RefreshPull -> handleRefreshPull()
|
PendingRequestsAction.RefreshPull -> handleRefreshPull()
|
||||||
is PendingRequestsAction.PendingRequestRowClick -> {
|
is PendingRequestsAction.PendingRequestRowClick -> {
|
||||||
|
@ -89,6 +92,10 @@ class PendingRequestsViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleHideBottomSheet() {
|
||||||
|
mutableStateFlow.update { it.copy(hideBottomSheet = true) }
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleOnLifecycleResumed() {
|
private fun handleOnLifecycleResumed() {
|
||||||
updateAuthRequestList()
|
updateAuthRequestList()
|
||||||
}
|
}
|
||||||
|
@ -193,6 +200,7 @@ data class PendingRequestsState(
|
||||||
val viewState: ViewState,
|
val viewState: ViewState,
|
||||||
private val isPullToRefreshSettingEnabled: Boolean,
|
private val isPullToRefreshSettingEnabled: Boolean,
|
||||||
val isRefreshing: Boolean,
|
val isRefreshing: Boolean,
|
||||||
|
val hideBottomSheet: Boolean,
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
/**
|
/**
|
||||||
* Indicates that the pull-to-refresh should be enabled in the UI.
|
* Indicates that the pull-to-refresh should be enabled in the UI.
|
||||||
|
@ -297,6 +305,11 @@ sealed class PendingRequestsAction {
|
||||||
*/
|
*/
|
||||||
data object DeclineAllRequestsConfirm : PendingRequestsAction()
|
data object DeclineAllRequestsConfirm : PendingRequestsAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user has dismissed the bottom sheet.
|
||||||
|
*/
|
||||||
|
data object HideBottomSheet : PendingRequestsAction()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The screen has been re-opened and should be updated.
|
* The screen has been re-opened and should be updated.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -23,7 +23,6 @@ import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
@ -65,7 +64,6 @@ import com.x8bit.bitwarden.ui.vault.feature.importlogins.handlers.ImportLoginHan
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.importlogins.handlers.rememberImportLoginHandler
|
import com.x8bit.bitwarden.ui.vault.feature.importlogins.handlers.rememberImportLoginHandler
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.importlogins.model.InstructionStep
|
import com.x8bit.bitwarden.ui.vault.feature.importlogins.model.InstructionStep
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
private const val IMPORT_HELP_URL = "https://bitwarden.com/help/import-data/"
|
private const val IMPORT_HELP_URL = "https://bitwarden.com/help/import-data/"
|
||||||
|
|
||||||
|
@ -100,27 +98,15 @@ fun ImportLoginsScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
val hideSheetAndExecuteCompleteImportLogins: () -> Unit = {
|
|
||||||
// This pattern mirrors the onDismissRequest handling in the material ModalBottomSheet
|
|
||||||
scope
|
|
||||||
.launch {
|
|
||||||
sheetState.hide()
|
|
||||||
}
|
|
||||||
.invokeOnCompletion {
|
|
||||||
handler.onSuccessfulSyncAcknowledged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BitwardenModalBottomSheet(
|
BitwardenModalBottomSheet(
|
||||||
showBottomSheet = state.showBottomSheet,
|
showBottomSheet = state.showBottomSheet,
|
||||||
sheetTitle = stringResource(R.string.bitwarden_tools),
|
sheetTitle = stringResource(R.string.bitwarden_tools),
|
||||||
onDismiss = hideSheetAndExecuteCompleteImportLogins,
|
onDismiss = handler.onSuccessfulSyncAcknowledged,
|
||||||
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
|
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
|
||||||
modifier = Modifier.statusBarsPadding(),
|
modifier = Modifier.statusBarsPadding(),
|
||||||
) { paddingValues ->
|
) { paddingValues, animatedOnDismiss ->
|
||||||
ImportLoginsSuccessBottomSheetContent(
|
ImportLoginsSuccessBottomSheetContent(
|
||||||
onCompleteImportLogins = hideSheetAndExecuteCompleteImportLogins,
|
onCompleteImportLogins = animatedOnDismiss,
|
||||||
modifier = Modifier.padding(paddingValues),
|
modifier = Modifier.padding(paddingValues),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package com.x8bit.bitwarden.ui.vault.feature.vault
|
package com.x8bit.bitwarden.ui.vault.feature.vault
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.scaleIn
|
import androidx.compose.animation.scaleIn
|
||||||
|
@ -15,7 +13,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
@ -61,11 +58,9 @@ import com.x8bit.bitwarden.ui.platform.components.snackbar.rememberBitwardenSnac
|
||||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||||
import com.x8bit.bitwarden.ui.platform.composition.LocalExitManager
|
import com.x8bit.bitwarden.ui.platform.composition.LocalExitManager
|
||||||
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||||
import com.x8bit.bitwarden.ui.platform.composition.LocalPermissionsManager
|
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.search.model.SearchType
|
import com.x8bit.bitwarden.ui.platform.feature.search.model.SearchType
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
|
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelay
|
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelay
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.vault.handlers.VaultHandlers
|
import com.x8bit.bitwarden.ui.vault.feature.vault.handlers.VaultHandlers
|
||||||
|
@ -90,7 +85,6 @@ fun VaultScreen(
|
||||||
onNavigateToImportLogins: (SnackbarRelay) -> Unit,
|
onNavigateToImportLogins: (SnackbarRelay) -> Unit,
|
||||||
exitManager: ExitManager = LocalExitManager.current,
|
exitManager: ExitManager = LocalExitManager.current,
|
||||||
intentManager: IntentManager = LocalIntentManager.current,
|
intentManager: IntentManager = LocalIntentManager.current,
|
||||||
permissionsManager: PermissionsManager = LocalPermissionsManager.current,
|
|
||||||
) {
|
) {
|
||||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
@ -137,10 +131,6 @@ fun VaultScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val vaultHandlers = remember(viewModel) { VaultHandlers.create(viewModel) }
|
val vaultHandlers = remember(viewModel) { VaultHandlers.create(viewModel) }
|
||||||
VaultScreenPushNotifications(
|
|
||||||
hideNotificationsDialog = state.hideNotificationsDialog,
|
|
||||||
permissionsManager = permissionsManager,
|
|
||||||
)
|
|
||||||
VaultScreenScaffold(
|
VaultScreenScaffold(
|
||||||
state = state,
|
state = state,
|
||||||
pullToRefreshState = pullToRefreshState,
|
pullToRefreshState = pullToRefreshState,
|
||||||
|
@ -150,28 +140,6 @@ fun VaultScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the notifications permission request.
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
private fun VaultScreenPushNotifications(
|
|
||||||
hideNotificationsDialog: Boolean,
|
|
||||||
permissionsManager: PermissionsManager,
|
|
||||||
) {
|
|
||||||
if (hideNotificationsDialog) return
|
|
||||||
val launcher = permissionsManager.getLauncher {
|
|
||||||
// We do not actually care what the response is, we just need
|
|
||||||
// to give the user a chance to give us the permission.
|
|
||||||
}
|
|
||||||
LaunchedEffect(key1 = Unit) {
|
|
||||||
@SuppressLint("InlinedApi")
|
|
||||||
// We check the version code as part of the 'hideNotificationsDialog' property.
|
|
||||||
if (!permissionsManager.checkPermission(Manifest.permission.POST_NOTIFICATIONS)) {
|
|
||||||
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scaffold for the [VaultScreen]
|
* Scaffold for the [VaultScreen]
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.x8bit.bitwarden.ui.vault.feature.vault
|
package com.x8bit.bitwarden.ui.vault.feature.vault
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
@ -19,8 +18,6 @@ import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent
|
||||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl
|
import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl
|
||||||
import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
|
|
||||||
import com.x8bit.bitwarden.data.platform.util.isFdroid
|
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
|
||||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||||
|
@ -102,7 +99,6 @@ class VaultViewModel @Inject constructor(
|
||||||
isPullToRefreshSettingEnabled = settingsRepository.getPullToRefreshEnabledFlow().value,
|
isPullToRefreshSettingEnabled = settingsRepository.getPullToRefreshEnabledFlow().value,
|
||||||
baseIconUrl = userState.activeAccount.environment.environmentUrlData.baseIconUrl,
|
baseIconUrl = userState.activeAccount.environment.environmentUrlData.baseIconUrl,
|
||||||
hasMasterPassword = userState.activeAccount.hasMasterPassword,
|
hasMasterPassword = userState.activeAccount.hasMasterPassword,
|
||||||
hideNotificationsDialog = isBuildVersionBelow(Build.VERSION_CODES.TIRAMISU) || isFdroid,
|
|
||||||
isRefreshing = false,
|
isRefreshing = false,
|
||||||
showImportActionCard = false,
|
showImportActionCard = false,
|
||||||
showSshKeys = showSshKeys,
|
showSshKeys = showSshKeys,
|
||||||
|
@ -713,7 +709,6 @@ data class VaultState(
|
||||||
private val isPullToRefreshSettingEnabled: Boolean,
|
private val isPullToRefreshSettingEnabled: Boolean,
|
||||||
val baseIconUrl: String,
|
val baseIconUrl: String,
|
||||||
val isIconLoadingDisabled: Boolean,
|
val isIconLoadingDisabled: Boolean,
|
||||||
val hideNotificationsDialog: Boolean,
|
|
||||||
val isRefreshing: Boolean,
|
val isRefreshing: Boolean,
|
||||||
val showImportActionCard: Boolean,
|
val showImportActionCard: Boolean,
|
||||||
val showSshKeys: Boolean,
|
val showSshKeys: Boolean,
|
||||||
|
|
73
app/src/main/res/drawable-night/img_2fa.xml
Normal file
73
app/src/main/res/drawable-night/img_2fa.xml
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="200dp"
|
||||||
|
android:height="201dp"
|
||||||
|
android:viewportWidth="200"
|
||||||
|
android:viewportHeight="201">
|
||||||
|
<path
|
||||||
|
android:fillColor="#AAC3EF"
|
||||||
|
android:pathData="M0,38.17C0,31.26 5.6,25.67 12.5,25.67H125C131.9,25.67 137.5,31.26 137.5,38.17V117.33C137.5,124.24 131.9,129.83 125,129.83H12.5C5.6,129.83 0,124.24 0,117.33V38.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M125,29.83H12.5C7.9,29.83 4.17,33.56 4.17,38.17V117.33C4.17,121.94 7.9,125.67 12.5,125.67H125C129.6,125.67 133.33,121.94 133.33,117.33V38.17C133.33,33.56 129.6,29.83 125,29.83ZM12.5,25.67C5.6,25.67 0,31.26 0,38.17V117.33C0,124.24 5.6,129.83 12.5,129.83H125C131.9,129.83 137.5,124.24 137.5,117.33V38.17C137.5,31.26 131.9,25.67 125,25.67H12.5Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#79A1E9"
|
||||||
|
android:pathData="M47.92,75.67C47.92,72.21 50.71,69.42 54.17,69.42H83.33C86.78,69.42 89.58,72.21 89.58,75.67V96.5C89.58,99.95 86.78,102.75 83.33,102.75H54.17C50.71,102.75 47.92,99.95 47.92,96.5V75.67Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M83.33,73.58H54.17C53.02,73.58 52.08,74.52 52.08,75.67V96.5C52.08,97.65 53.02,98.58 54.17,98.58H83.33C84.48,98.58 85.42,97.65 85.42,96.5V75.67C85.42,74.52 84.48,73.58 83.33,73.58ZM54.17,69.42C50.71,69.42 47.92,72.21 47.92,75.67V96.5C47.92,99.95 50.71,102.75 54.17,102.75H83.33C86.78,102.75 89.58,99.95 89.58,96.5V75.67C89.58,72.21 86.78,69.42 83.33,69.42H54.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:pathData="M66.67,81.92C66.67,80.77 67.6,79.83 68.75,79.83C69.9,79.83 70.83,80.77 70.83,81.92V90.25C70.83,91.4 69.9,92.33 68.75,92.33C67.6,92.33 66.67,91.4 66.67,90.25V81.92Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M58.33,67.33C58.33,61.58 63,56.92 68.75,56.92C74.5,56.92 79.17,61.58 79.17,67.33V69.42H75V67.33C75,63.88 72.2,61.08 68.75,61.08C65.3,61.08 62.5,63.88 62.5,67.33V69.42H58.33V67.33Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M135.42,50.67H2.08V46.5H135.42V50.67Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:pathData="M129.17,38.17C129.17,40.47 127.3,42.33 125,42.33C122.7,42.33 120.83,40.47 120.83,38.17C120.83,35.87 122.7,34 125,34C127.3,34 129.17,35.87 129.17,38.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:pathData="M116.67,38.17C116.67,40.47 114.8,42.33 112.5,42.33C110.2,42.33 108.33,40.47 108.33,38.17C108.33,35.87 110.2,34 112.5,34C114.8,34 116.67,35.87 116.67,38.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:pathData="M104.17,38.17C104.17,40.47 102.3,42.33 100,42.33C97.7,42.33 95.83,40.47 95.83,38.17C95.83,35.87 97.7,34 100,34C102.3,34 104.17,35.87 104.17,38.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#F3F6F9"
|
||||||
|
android:pathData="M170.83,88.17C170.83,104.28 157.77,117.33 141.67,117.33C125.56,117.33 112.5,104.28 112.5,88.17C112.5,72.06 125.56,59 141.67,59C157.77,59 170.83,72.06 170.83,88.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M141.67,113.17C155.47,113.17 166.67,101.97 166.67,88.17C166.67,74.36 155.47,63.17 141.67,63.17C127.86,63.17 116.67,74.36 116.67,88.17C116.67,101.97 127.86,113.17 141.67,113.17ZM141.67,117.33C157.77,117.33 170.83,104.28 170.83,88.17C170.83,72.06 157.77,59 141.67,59C125.56,59 112.5,72.06 112.5,88.17C112.5,104.28 125.56,117.33 141.67,117.33Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#F3F6F9"
|
||||||
|
android:pathData="M195.64,150.62C198.52,157.57 200,165.02 200,172.54C200,174.27 198.6,175.67 196.87,175.67H88.54C86.82,175.67 85.42,174.27 85.42,172.54C85.42,165.02 86.9,157.57 89.78,150.62C92.66,143.67 96.88,137.35 102.2,132.03C107.52,126.71 113.83,122.49 120.78,119.61C127.73,116.73 135.18,115.25 142.71,115.25C150.23,115.25 157.68,116.73 164.63,119.61C171.58,122.49 177.9,126.71 183.22,132.03C188.54,137.35 192.76,143.67 195.64,150.62Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M195.82,171.5C195.69,164.88 194.33,158.34 191.79,152.21C189.12,145.77 185.21,139.91 180.27,134.98C175.34,130.04 169.48,126.13 163.04,123.46C156.59,120.79 149.68,119.42 142.71,119.42C135.73,119.42 128.82,120.79 122.38,123.46C115.93,126.13 110.08,130.04 105.14,134.98C100.21,139.91 96.3,145.77 93.63,152.21C91.09,158.34 89.72,164.88 89.59,171.5H195.82ZM200,172.54C200,165.02 198.52,157.57 195.64,150.62C192.76,143.67 188.54,137.35 183.22,132.03C177.9,126.71 171.58,122.49 164.63,119.61C157.68,116.73 150.23,115.25 142.71,115.25C135.18,115.25 127.73,116.73 120.78,119.61C113.83,122.49 107.52,126.71 102.2,132.03C96.88,137.35 92.66,143.67 89.78,150.62C86.9,157.57 85.42,165.02 85.42,172.54C85.42,174.27 86.82,175.67 88.54,175.67H196.87C198.6,175.67 200,174.27 200,172.54Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFBF00"
|
||||||
|
android:pathData="M22.92,127.75C22.92,118.54 30.38,111.08 39.58,111.08H95.83C105.04,111.08 112.5,118.54 112.5,127.75C112.5,136.95 105.04,144.42 95.83,144.42H39.58C30.38,144.42 22.92,136.95 22.92,127.75Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M95.83,115.25H39.58C32.68,115.25 27.08,120.85 27.08,127.75C27.08,134.65 32.68,140.25 39.58,140.25H95.83C102.74,140.25 108.33,134.65 108.33,127.75C108.33,120.85 102.74,115.25 95.83,115.25ZM39.58,111.08C30.38,111.08 22.92,118.54 22.92,127.75C22.92,136.95 30.38,144.42 39.58,144.42H95.83C105.04,144.42 112.5,136.95 112.5,127.75C112.5,118.54 105.04,111.08 95.83,111.08H39.58Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M41.68,120.61C42.83,120.61 43.76,121.54 43.76,122.69V125.61L46.49,124.71C47.59,124.36 48.76,124.95 49.12,126.05C49.48,127.14 48.88,128.32 47.79,128.67L45.02,129.58L46.76,132.01C47.42,132.95 47.21,134.25 46.27,134.92C45.33,135.59 44.03,135.37 43.36,134.43L41.68,132.07L39.99,134.43C39.32,135.37 38.02,135.59 37.08,134.92C36.15,134.25 35.93,132.95 36.6,132.01L38.33,129.58L35.56,128.67C34.47,128.32 33.87,127.14 34.23,126.05C34.59,124.95 35.77,124.36 36.86,124.71L39.59,125.61V122.69C39.59,121.54 40.53,120.61 41.68,120.61ZM60.43,120.61C61.58,120.61 62.51,121.54 62.51,122.69V125.61L65.24,124.71C66.34,124.36 67.51,124.95 67.87,126.05C68.23,127.14 67.63,128.32 66.54,128.67L63.77,129.58L65.51,132.01C66.17,132.95 65.96,134.25 65.02,134.92C64.08,135.59 62.78,135.37 62.11,134.43L60.43,132.07L58.74,134.43C58.07,135.37 56.77,135.59 55.83,134.92C54.9,134.25 54.68,132.95 55.35,132.01L57.08,129.58L54.31,128.67C53.22,128.32 52.62,127.14 52.98,126.05C53.34,124.95 54.52,124.36 55.61,124.71L58.34,125.61V122.69C58.34,121.54 59.28,120.61 60.43,120.61Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M72.92,131.92C72.92,130.77 73.85,129.83 75,129.83L83.33,129.83C84.48,129.83 85.42,130.77 85.42,131.92C85.42,133.07 84.48,134 83.33,134L75,134C73.85,134 72.92,133.07 72.92,131.92Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#175DDC"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M89.58,131.92C89.58,130.77 90.52,129.83 91.67,129.83L100,129.83C101.15,129.83 102.08,130.77 102.08,131.92C102.08,133.07 101.15,134 100,134L91.67,134C90.52,134 89.58,133.07 89.58,131.92Z" />
|
||||||
|
</vector>
|
73
app/src/main/res/drawable/img_2fa.xml
Normal file
73
app/src/main/res/drawable/img_2fa.xml
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="200dp"
|
||||||
|
android:height="201dp"
|
||||||
|
android:viewportWidth="200"
|
||||||
|
android:viewportHeight="201">
|
||||||
|
<path
|
||||||
|
android:fillColor="#DBE5F6"
|
||||||
|
android:pathData="M0,38.17C0,31.26 5.6,25.67 12.5,25.67H125C131.9,25.67 137.5,31.26 137.5,38.17V117.33C137.5,124.24 131.9,129.83 125,129.83H12.5C5.6,129.83 0,124.24 0,117.33V38.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M125,29.83H12.5C7.9,29.83 4.17,33.56 4.17,38.17V117.33C4.17,121.94 7.9,125.67 12.5,125.67H125C129.6,125.67 133.33,121.94 133.33,117.33V38.17C133.33,33.56 129.6,29.83 125,29.83ZM12.5,25.67C5.6,25.67 0,31.26 0,38.17V117.33C0,124.24 5.6,129.83 12.5,129.83H125C131.9,129.83 137.5,124.24 137.5,117.33V38.17C137.5,31.26 131.9,25.67 125,25.67H12.5Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#AAC3EF"
|
||||||
|
android:pathData="M47.92,75.67C47.92,72.21 50.71,69.42 54.17,69.42H83.33C86.78,69.42 89.58,72.21 89.58,75.67V96.5C89.58,99.95 86.78,102.75 83.33,102.75H54.17C50.71,102.75 47.92,99.95 47.92,96.5V75.67Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M83.33,73.58H54.17C53.02,73.58 52.08,74.52 52.08,75.67V96.5C52.08,97.65 53.02,98.58 54.17,98.58H83.33C84.48,98.58 85.42,97.65 85.42,96.5V75.67C85.42,74.52 84.48,73.58 83.33,73.58ZM54.17,69.42C50.71,69.42 47.92,72.21 47.92,75.67V96.5C47.92,99.95 50.71,102.75 54.17,102.75H83.33C86.78,102.75 89.58,99.95 89.58,96.5V75.67C89.58,72.21 86.78,69.42 83.33,69.42H54.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:pathData="M66.67,81.92C66.67,80.77 67.6,79.83 68.75,79.83C69.9,79.83 70.83,80.77 70.83,81.92V90.25C70.83,91.4 69.9,92.33 68.75,92.33C67.6,92.33 66.67,91.4 66.67,90.25V81.92Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M58.33,67.33C58.33,61.58 63,56.92 68.75,56.92C74.5,56.92 79.17,61.58 79.17,67.33V69.42H75V67.33C75,63.88 72.2,61.08 68.75,61.08C65.3,61.08 62.5,63.88 62.5,67.33V69.42H58.33V67.33Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M135.42,50.67H2.08V46.5H135.42V50.67Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:pathData="M129.17,38.17C129.17,40.47 127.3,42.33 125,42.33C122.7,42.33 120.83,40.47 120.83,38.17C120.83,35.87 122.7,34 125,34C127.3,34 129.17,35.87 129.17,38.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:pathData="M116.67,38.17C116.67,40.47 114.8,42.33 112.5,42.33C110.2,42.33 108.33,40.47 108.33,38.17C108.33,35.87 110.2,34 112.5,34C114.8,34 116.67,35.87 116.67,38.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:pathData="M104.17,38.17C104.17,40.47 102.3,42.33 100,42.33C97.7,42.33 95.83,40.47 95.83,38.17C95.83,35.87 97.7,34 100,34C102.3,34 104.17,35.87 104.17,38.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M170.83,88.17C170.83,104.28 157.77,117.33 141.67,117.33C125.56,117.33 112.5,104.28 112.5,88.17C112.5,72.06 125.56,59 141.67,59C157.77,59 170.83,72.06 170.83,88.17Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M141.67,113.17C155.47,113.17 166.67,101.97 166.67,88.17C166.67,74.36 155.47,63.17 141.67,63.17C127.86,63.17 116.67,74.36 116.67,88.17C116.67,101.97 127.86,113.17 141.67,113.17ZM141.67,117.33C157.77,117.33 170.83,104.28 170.83,88.17C170.83,72.06 157.77,59 141.67,59C125.56,59 112.5,72.06 112.5,88.17C112.5,104.28 125.56,117.33 141.67,117.33Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M195.64,150.62C198.52,157.57 200,165.02 200,172.54C200,174.27 198.6,175.67 196.87,175.67H88.54C86.82,175.67 85.42,174.27 85.42,172.54C85.42,165.02 86.9,157.57 89.78,150.62C92.66,143.67 96.88,137.35 102.2,132.03C107.52,126.71 113.83,122.49 120.78,119.61C127.73,116.73 135.18,115.25 142.71,115.25C150.23,115.25 157.68,116.73 164.63,119.61C171.58,122.49 177.9,126.71 183.22,132.03C188.54,137.35 192.76,143.67 195.64,150.62Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M195.82,171.5C195.69,164.88 194.33,158.34 191.79,152.21C189.12,145.77 185.21,139.91 180.27,134.98C175.34,130.04 169.48,126.13 163.04,123.46C156.59,120.79 149.68,119.42 142.71,119.42C135.73,119.42 128.82,120.79 122.38,123.46C115.93,126.13 110.08,130.04 105.14,134.98C100.21,139.91 96.3,145.77 93.63,152.21C91.09,158.34 89.72,164.88 89.59,171.5H195.82ZM200,172.54C200,165.02 198.52,157.57 195.64,150.62C192.76,143.67 188.54,137.35 183.22,132.03C177.9,126.71 171.58,122.49 164.63,119.61C157.68,116.73 150.23,115.25 142.71,115.25C135.18,115.25 127.73,116.73 120.78,119.61C113.83,122.49 107.52,126.71 102.2,132.03C96.88,137.35 92.66,143.67 89.78,150.62C86.9,157.57 85.42,165.02 85.42,172.54C85.42,174.27 86.82,175.67 88.54,175.67H196.87C198.6,175.67 200,174.27 200,172.54Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFBF00"
|
||||||
|
android:pathData="M22.92,127.75C22.92,118.54 30.38,111.08 39.58,111.08H95.83C105.04,111.08 112.5,118.54 112.5,127.75C112.5,136.95 105.04,144.42 95.83,144.42H39.58C30.38,144.42 22.92,136.95 22.92,127.75Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M95.83,115.25H39.58C32.68,115.25 27.08,120.85 27.08,127.75C27.08,134.65 32.68,140.25 39.58,140.25H95.83C102.74,140.25 108.33,134.65 108.33,127.75C108.33,120.85 102.74,115.25 95.83,115.25ZM39.58,111.08C30.38,111.08 22.92,118.54 22.92,127.75C22.92,136.95 30.38,144.42 39.58,144.42H95.83C105.04,144.42 112.5,136.95 112.5,127.75C112.5,118.54 105.04,111.08 95.83,111.08H39.58Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M41.67,120.61C42.83,120.61 43.76,121.54 43.76,122.69V125.61L46.49,124.71C47.58,124.36 48.76,124.95 49.12,126.05C49.48,127.14 48.88,128.32 47.79,128.67L45.02,129.58L46.75,132.01C47.42,132.95 47.2,134.25 46.27,134.92C45.33,135.59 44.03,135.37 43.36,134.43L41.67,132.07L39.99,134.43C39.32,135.37 38.02,135.59 37.08,134.92C36.14,134.25 35.93,132.95 36.6,132.01L38.33,129.58L35.56,128.67C34.47,128.32 33.87,127.14 34.23,126.05C34.59,124.95 35.76,124.36 36.86,124.71L39.59,125.61V122.69C39.59,121.54 40.52,120.61 41.67,120.61ZM60.42,120.61C61.58,120.61 62.51,121.54 62.51,122.69V125.61L65.24,124.71C66.33,124.36 67.51,124.95 67.87,126.05C68.23,127.14 67.63,128.32 66.54,128.67L63.77,129.58L65.5,132.01C66.17,132.95 65.95,134.25 65.02,134.92C64.08,135.59 62.78,135.37 62.11,134.43L60.42,132.07L58.74,134.43C58.07,135.37 56.77,135.59 55.83,134.92C54.89,134.25 54.68,132.95 55.35,132.01L57.08,129.58L54.31,128.67C53.22,128.32 52.62,127.14 52.98,126.05C53.34,124.95 54.51,124.36 55.61,124.71L58.34,125.61V122.69C58.34,121.54 59.27,120.61 60.42,120.61Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M72.92,131.92C72.92,130.77 73.85,129.83 75,129.83L83.33,129.83C84.48,129.83 85.42,130.77 85.42,131.92C85.42,133.07 84.48,134 83.33,134L75,134C73.85,134 72.92,133.07 72.92,131.92Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#020F66"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M89.58,131.92C89.58,130.77 90.52,129.83 91.67,129.83L100,129.83C101.15,129.83 102.08,130.77 102.08,131.92C102.08,133.07 101.15,134 100,134L91.67,134C90.52,134 89.58,133.07 89.58,131.92Z" />
|
||||||
|
</vector>
|
|
@ -1082,4 +1082,8 @@ Do you want to switch to this account?</string>
|
||||||
<string name="ssh_keys">SSH keys</string>
|
<string name="ssh_keys">SSH keys</string>
|
||||||
<string name="copy_public_key">Copy public key</string>
|
<string name="copy_public_key">Copy public key</string>
|
||||||
<string name="copy_fingerprint">Copy fingerprint</string>
|
<string name="copy_fingerprint">Copy fingerprint</string>
|
||||||
|
<string name="enable_notifications">Enable notifications</string>
|
||||||
|
<string name="log_in_quickly_and_easily_across_devices">Log in quickly and easily across devices</string>
|
||||||
|
<string name="bitwarden_can_notify_you_each_time_you_receive_a_new_login_request_from_another_device">Bitwarden can notify you each time you receive a new login request from another device.</string>
|
||||||
|
<string name="skip_for_now">Skip for now</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,18 +1,33 @@
|
||||||
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.pendingrequests
|
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.pendingrequests
|
||||||
|
|
||||||
|
import androidx.compose.ui.semantics.SemanticsActions
|
||||||
import androidx.compose.ui.test.assert
|
import androidx.compose.ui.test.assert
|
||||||
|
import androidx.compose.ui.test.filterToOne
|
||||||
import androidx.compose.ui.test.hasAnyAncestor
|
import androidx.compose.ui.test.hasAnyAncestor
|
||||||
|
import androidx.compose.ui.test.hasClickAction
|
||||||
import androidx.compose.ui.test.isDialog
|
import androidx.compose.ui.test.isDialog
|
||||||
|
import androidx.compose.ui.test.onAllNodesWithText
|
||||||
import androidx.compose.ui.test.onNodeWithText
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
|
import androidx.compose.ui.test.performScrollTo
|
||||||
|
import androidx.compose.ui.test.performSemanticsAction
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||||
|
import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
|
||||||
|
import com.x8bit.bitwarden.data.platform.util.isFdroid
|
||||||
|
import com.x8bit.bitwarden.data.util.advanceTimeByAndRunCurrent
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||||
|
import com.x8bit.bitwarden.ui.platform.manager.permissions.FakePermissionManager
|
||||||
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.runs
|
||||||
|
import io.mockk.unmockkStatic
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
@ -24,22 +39,38 @@ class PendingRequestsScreenTest : BaseComposeTest() {
|
||||||
|
|
||||||
private val mutableEventFlow = bufferedMutableSharedFlow<PendingRequestsEvent>()
|
private val mutableEventFlow = bufferedMutableSharedFlow<PendingRequestsEvent>()
|
||||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||||
private val viewModel = mockk<PendingRequestsViewModel>(relaxed = true) {
|
private val viewModel = mockk<PendingRequestsViewModel> {
|
||||||
every { eventFlow } returns mutableEventFlow
|
every { eventFlow } returns mutableEventFlow
|
||||||
every { stateFlow } returns mutableStateFlow
|
every { stateFlow } returns mutableStateFlow
|
||||||
|
every { trySendAction(any()) } just runs
|
||||||
|
}
|
||||||
|
private val permissionsManager = FakePermissionManager().apply {
|
||||||
|
checkPermissionResult = false
|
||||||
|
shouldShowRequestRationale = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
|
mockkStatic(::isFdroid)
|
||||||
|
mockkStatic(::isBuildVersionBelow)
|
||||||
|
every { isFdroid } returns false
|
||||||
|
every { isBuildVersionBelow(any()) } returns false
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent {
|
||||||
PendingRequestsScreen(
|
PendingRequestsScreen(
|
||||||
onNavigateBack = { onNavigateBackCalled = true },
|
onNavigateBack = { onNavigateBackCalled = true },
|
||||||
onNavigateToLoginApproval = { _ -> onNavigateToLoginApprovalCalled = true },
|
onNavigateToLoginApproval = { _ -> onNavigateToLoginApprovalCalled = true },
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
|
permissionsManager = permissionsManager,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkStatic(::isFdroid)
|
||||||
|
unmockkStatic(::isBuildVersionBelow)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `on NavigateBack should call onNavigateBack`() {
|
fun `on NavigateBack should call onNavigateBack`() {
|
||||||
mutableEventFlow.tryEmit(PendingRequestsEvent.NavigateBack)
|
mutableEventFlow.tryEmit(PendingRequestsEvent.NavigateBack)
|
||||||
|
@ -70,6 +101,7 @@ class PendingRequestsScreenTest : BaseComposeTest() {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
hideBottomSheet = true,
|
||||||
)
|
)
|
||||||
composeTestRule.onNodeWithText("Decline all requests").performClick()
|
composeTestRule.onNodeWithText("Decline all requests").performClick()
|
||||||
composeTestRule
|
composeTestRule
|
||||||
|
@ -101,6 +133,7 @@ class PendingRequestsScreenTest : BaseComposeTest() {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
hideBottomSheet = true,
|
||||||
)
|
)
|
||||||
composeTestRule.onNodeWithText("Decline all requests").performClick()
|
composeTestRule.onNodeWithText("Decline all requests").performClick()
|
||||||
composeTestRule
|
composeTestRule
|
||||||
|
@ -114,12 +147,36 @@ class PendingRequestsScreenTest : BaseComposeTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
@Test
|
||||||
val DEFAULT_STATE: PendingRequestsState = PendingRequestsState(
|
fun `on skip for now click should emit HideBottomSheet`() {
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithText(text = "Skip for now")
|
||||||
|
.performScrollTo()
|
||||||
|
.performSemanticsAction(SemanticsActions.OnClick)
|
||||||
|
dispatcher.advanceTimeByAndRunCurrent(delayTimeMillis = 1000L)
|
||||||
|
verify(exactly = 1) {
|
||||||
|
viewModel.trySendAction(PendingRequestsAction.HideBottomSheet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on Enable notifications click should emit HideBottomSheet`() {
|
||||||
|
composeTestRule
|
||||||
|
.onAllNodesWithText(text = "Enable notifications")
|
||||||
|
.filterToOne(hasClickAction())
|
||||||
|
.performScrollTo()
|
||||||
|
.performSemanticsAction(SemanticsActions.OnClick)
|
||||||
|
dispatcher.advanceTimeByAndRunCurrent(delayTimeMillis = 1000L)
|
||||||
|
verify(exactly = 1) {
|
||||||
|
viewModel.trySendAction(PendingRequestsAction.HideBottomSheet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val DEFAULT_STATE: PendingRequestsState = PendingRequestsState(
|
||||||
authRequests = emptyList(),
|
authRequests = emptyList(),
|
||||||
viewState = PendingRequestsState.ViewState.Loading,
|
viewState = PendingRequestsState.ViewState.Loading,
|
||||||
isPullToRefreshSettingEnabled = false,
|
isPullToRefreshSettingEnabled = false,
|
||||||
isRefreshing = false,
|
isRefreshing = false,
|
||||||
)
|
hideBottomSheet = false,
|
||||||
}
|
)
|
||||||
}
|
|
||||||
|
|
|
@ -165,6 +165,13 @@ class PendingRequestsViewModelTest : BaseViewModelTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on HideBottomSheet should make hideBottomSheet true`() {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
viewModel.trySendAction(PendingRequestsAction.HideBottomSheet)
|
||||||
|
assertEquals(DEFAULT_STATE.copy(hideBottomSheet = true), viewModel.stateFlow.value)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `on RefreshPull should make auth request`() = runTest {
|
fun `on RefreshPull should make auth request`() = runTest {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
|
@ -370,13 +377,12 @@ class PendingRequestsViewModelTest : BaseViewModelTest() {
|
||||||
settingsRepository = settingsRepository,
|
settingsRepository = settingsRepository,
|
||||||
savedStateHandle = SavedStateHandle().apply { set("state", state) },
|
savedStateHandle = SavedStateHandle().apply { set("state", state) },
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
private val DEFAULT_STATE: PendingRequestsState = PendingRequestsState(
|
||||||
val DEFAULT_STATE: PendingRequestsState = PendingRequestsState(
|
|
||||||
authRequests = emptyList(),
|
authRequests = emptyList(),
|
||||||
viewState = PendingRequestsState.ViewState.Empty,
|
viewState = PendingRequestsState.ViewState.Empty,
|
||||||
isPullToRefreshSettingEnabled = false,
|
isPullToRefreshSettingEnabled = false,
|
||||||
isRefreshing = false,
|
isRefreshing = false,
|
||||||
)
|
hideBottomSheet = false,
|
||||||
}
|
)
|
||||||
}
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
|
||||||
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData
|
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
|
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.FakePermissionManager
|
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelay
|
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelay
|
||||||
import com.x8bit.bitwarden.ui.util.assertLockOrLogoutDialogIsDisplayed
|
import com.x8bit.bitwarden.ui.util.assertLockOrLogoutDialogIsDisplayed
|
||||||
import com.x8bit.bitwarden.ui.util.assertLogoutConfirmationDialogIsDisplayed
|
import com.x8bit.bitwarden.ui.util.assertLogoutConfirmationDialogIsDisplayed
|
||||||
|
@ -74,7 +73,6 @@ class VaultScreenTest : BaseComposeTest() {
|
||||||
private var onNavigateToSearchScreen = false
|
private var onNavigateToSearchScreen = false
|
||||||
private val exitManager = mockk<ExitManager>(relaxed = true)
|
private val exitManager = mockk<ExitManager>(relaxed = true)
|
||||||
private val intentManager = mockk<IntentManager>(relaxed = true)
|
private val intentManager = mockk<IntentManager>(relaxed = true)
|
||||||
private val permissionsManager = FakePermissionManager()
|
|
||||||
|
|
||||||
private val mutableEventFlow = bufferedMutableSharedFlow<VaultEvent>()
|
private val mutableEventFlow = bufferedMutableSharedFlow<VaultEvent>()
|
||||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||||
|
@ -101,7 +99,6 @@ class VaultScreenTest : BaseComposeTest() {
|
||||||
},
|
},
|
||||||
exitManager = exitManager,
|
exitManager = exitManager,
|
||||||
intentManager = intentManager,
|
intentManager = intentManager,
|
||||||
permissionsManager = permissionsManager,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1143,14 +1140,6 @@ class VaultScreenTest : BaseComposeTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `permissionManager is invoked for notifications based on state`() {
|
|
||||||
assertFalse(permissionsManager.hasGetLauncherBeenCalled)
|
|
||||||
mutableStateFlow.update { it.copy(hideNotificationsDialog = false) }
|
|
||||||
composeTestRule.waitForIdle()
|
|
||||||
assertTrue(permissionsManager.hasGetLauncherBeenCalled)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `action card for importing logins should show based on state`() {
|
fun `action card for importing logins should show based on state`() {
|
||||||
mutableStateFlow.update {
|
mutableStateFlow.update {
|
||||||
|
@ -1324,7 +1313,6 @@ private val DEFAULT_STATE: VaultState = VaultState(
|
||||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||||
isIconLoadingDisabled = false,
|
isIconLoadingDisabled = false,
|
||||||
hasMasterPassword = true,
|
hasMasterPassword = true,
|
||||||
hideNotificationsDialog = true,
|
|
||||||
isRefreshing = false,
|
isRefreshing = false,
|
||||||
showImportActionCard = false,
|
showImportActionCard = false,
|
||||||
showSshKeys = false,
|
showSshKeys = false,
|
||||||
|
|
|
@ -1930,7 +1930,6 @@ private fun createMockVaultState(
|
||||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||||
isIconLoadingDisabled = false,
|
isIconLoadingDisabled = false,
|
||||||
hasMasterPassword = true,
|
hasMasterPassword = true,
|
||||||
hideNotificationsDialog = true,
|
|
||||||
showImportActionCard = true,
|
showImportActionCard = true,
|
||||||
isRefreshing = false,
|
isRefreshing = false,
|
||||||
showSshKeys = showSshKeys,
|
showSshKeys = showSshKeys,
|
||||||
|
|
Loading…
Add table
Reference in a new issue