mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
Handle navigation for auth requests from notification (#934)
This commit is contained in:
parent
89dd552908
commit
b15dc065be
11 changed files with 148 additions and 17 deletions
|
@ -44,10 +44,6 @@
|
||||||
<data android:mimeType="video/*" />
|
<data android:mimeType="video/*" />
|
||||||
<data android:mimeType="text/*" />
|
<data android:mimeType="text/*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.x8bit.bitwarden.data.auth.manager.AUTH_REQUEST" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.os.Parcelable
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.bitwarden.core.CipherView
|
import com.bitwarden.core.CipherView
|
||||||
|
import com.x8bit.bitwarden.data.auth.util.getPasswordlessRequestDataIntentOrNull
|
||||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager
|
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager
|
||||||
import com.x8bit.bitwarden.data.autofill.util.getAutofillSaveItemOrNull
|
import com.x8bit.bitwarden.data.autofill.util.getAutofillSaveItemOrNull
|
||||||
import com.x8bit.bitwarden.data.autofill.util.getAutofillSelectionDataOrNull
|
import com.x8bit.bitwarden.data.autofill.util.getAutofillSelectionDataOrNull
|
||||||
|
@ -111,10 +112,21 @@ class MainViewModel @Inject constructor(
|
||||||
intent: Intent,
|
intent: Intent,
|
||||||
isFirstIntent: Boolean,
|
isFirstIntent: Boolean,
|
||||||
) {
|
) {
|
||||||
|
val passwordlessRequestData = intent.getPasswordlessRequestDataIntentOrNull()
|
||||||
val autofillSaveItem = intent.getAutofillSaveItemOrNull()
|
val autofillSaveItem = intent.getAutofillSaveItemOrNull()
|
||||||
val autofillSelectionData = intent.getAutofillSelectionDataOrNull()
|
val autofillSelectionData = intent.getAutofillSelectionDataOrNull()
|
||||||
val shareData = intentManager.getShareDataFromIntent(intent)
|
val shareData = intentManager.getShareDataFromIntent(intent)
|
||||||
when {
|
when {
|
||||||
|
passwordlessRequestData != null -> {
|
||||||
|
specialCircumstanceManager.specialCircumstance =
|
||||||
|
SpecialCircumstance.PasswordlessRequest(
|
||||||
|
passwordlessRequestData = passwordlessRequestData,
|
||||||
|
// Allow users back into the already-running app when completing the
|
||||||
|
// autofill task when this is not the first intent.
|
||||||
|
shouldFinishWhenComplete = isFirstIntent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
autofillSaveItem != null -> {
|
autofillSaveItem != null -> {
|
||||||
specialCircumstanceManager.specialCircumstance =
|
specialCircumstanceManager.specialCircumstance =
|
||||||
SpecialCircumstance.AutofillSave(
|
SpecialCircumstance.AutofillSave(
|
||||||
|
|
|
@ -3,14 +3,13 @@ package com.x8bit.bitwarden.data.auth.manager
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.core.app.NotificationChannelCompat
|
import androidx.core.app.NotificationChannelCompat
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import com.x8bit.bitwarden.MainActivity
|
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||||
|
import com.x8bit.bitwarden.data.auth.util.createPasswordlessRequestDataIntent
|
||||||
import com.x8bit.bitwarden.data.autofill.util.toPendingIntentMutabilityFlag
|
import com.x8bit.bitwarden.data.autofill.util.toPendingIntentMutabilityFlag
|
||||||
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
||||||
import com.x8bit.bitwarden.data.platform.manager.PushManager
|
import com.x8bit.bitwarden.data.platform.manager.PushManager
|
||||||
|
@ -79,9 +78,7 @@ class AuthRequestNotificationManagerImpl(
|
||||||
PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
NOTIFICATION_REQUEST_CODE,
|
NOTIFICATION_REQUEST_CODE,
|
||||||
Intent(context, MainActivity::class.java)
|
createPasswordlessRequestDataIntent(context, data),
|
||||||
.setAction(NOTIFICATION_ACTION)
|
|
||||||
.putExtra(NOTIFICATION_DATA, data),
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT.toPendingIntentMutabilityFlag(),
|
PendingIntent.FLAG_UPDATE_CURRENT.toPendingIntentMutabilityFlag(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -101,9 +98,7 @@ class AuthRequestNotificationManagerImpl(
|
||||||
?: NotificationManagerCompat.IMPORTANCE_DEFAULT
|
?: NotificationManagerCompat.IMPORTANCE_DEFAULT
|
||||||
}
|
}
|
||||||
|
|
||||||
const val NOTIFICATION_ACTION: String = "com.x8bit.bitwarden.data.auth.manager.AUTH_REQUEST"
|
|
||||||
private const val NOTIFICATION_CHANNEL_ID: String = "general_notification_channel"
|
private const val NOTIFICATION_CHANNEL_ID: String = "general_notification_channel"
|
||||||
private const val NOTIFICATION_ID: Int = 2_6072_022
|
private const val NOTIFICATION_ID: Int = 2_6072_022
|
||||||
private const val NOTIFICATION_DATA: String = "notificationData"
|
|
||||||
private const val NOTIFICATION_REQUEST_CODE: Int = 20220801
|
private const val NOTIFICATION_REQUEST_CODE: Int = 20220801
|
||||||
private const val NOTIFICATION_DEFAULT_TIMEOUT_MILLIS: Long = 15L * 60L * 1_000L
|
private const val NOTIFICATION_DEFAULT_TIMEOUT_MILLIS: Long = 15L * 60L * 1_000L
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.x8bit.bitwarden.data.auth.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import com.x8bit.bitwarden.MainActivity
|
||||||
|
import com.x8bit.bitwarden.data.platform.manager.model.PasswordlessRequestData
|
||||||
|
import com.x8bit.bitwarden.data.platform.util.getSafeParcelableExtra
|
||||||
|
|
||||||
|
private const val NOTIFICATION_DATA: String = "notificationData"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an [Intent] that can be used to navigate the pending auth approval screen.
|
||||||
|
*/
|
||||||
|
fun createPasswordlessRequestDataIntent(
|
||||||
|
context: Context,
|
||||||
|
data: PasswordlessRequestData,
|
||||||
|
): Intent =
|
||||||
|
Intent(context, MainActivity::class.java)
|
||||||
|
.putExtra(NOTIFICATION_DATA, data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given [Intent] contains data for passwordless authorization.
|
||||||
|
* The [PasswordlessRequestData] will be returned when present.
|
||||||
|
*/
|
||||||
|
fun Intent.getPasswordlessRequestDataIntentOrNull(): PasswordlessRequestData? =
|
||||||
|
this.getSafeParcelableExtra(NOTIFICATION_DATA)
|
|
@ -37,4 +37,13 @@ sealed class SpecialCircumstance : Parcelable {
|
||||||
val autofillSelectionData: AutofillSelectionData,
|
val autofillSelectionData: AutofillSelectionData,
|
||||||
val shouldFinishWhenComplete: Boolean,
|
val shouldFinishWhenComplete: Boolean,
|
||||||
) : SpecialCircumstance()
|
) : SpecialCircumstance()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The app was launched in order to allow the user to authorize a passwordless login.
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data class PasswordlessRequest(
|
||||||
|
val passwordlessRequestData: PasswordlessRequestData,
|
||||||
|
val shouldFinishWhenComplete: Boolean,
|
||||||
|
) : SpecialCircumstance()
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ fun SpecialCircumstance.toAutofillSaveItemOrNull(): AutofillSaveItem? =
|
||||||
when (this) {
|
when (this) {
|
||||||
is SpecialCircumstance.AutofillSave -> this.autofillSaveItem
|
is SpecialCircumstance.AutofillSave -> this.autofillSaveItem
|
||||||
is SpecialCircumstance.AutofillSelection -> null
|
is SpecialCircumstance.AutofillSelection -> null
|
||||||
|
is SpecialCircumstance.PasswordlessRequest -> null
|
||||||
is SpecialCircumstance.ShareNewSend -> null
|
is SpecialCircumstance.ShareNewSend -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,5 +22,6 @@ fun SpecialCircumstance.toAutofillSelectionDataOrNull(): AutofillSelectionData?
|
||||||
when (this) {
|
when (this) {
|
||||||
is SpecialCircumstance.AutofillSave -> null
|
is SpecialCircumstance.AutofillSave -> null
|
||||||
is SpecialCircumstance.AutofillSelection -> this.autofillSelectionData
|
is SpecialCircumstance.AutofillSelection -> this.autofillSelectionData
|
||||||
|
is SpecialCircumstance.PasswordlessRequest -> null
|
||||||
is SpecialCircumstance.ShareNewSend -> null
|
is SpecialCircumstance.ShareNewSend -> null
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.VAULT_UNLOCK_ROUTE
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.navigateToVaultUnlock
|
import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.navigateToVaultUnlock
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.vaultUnlockDestination
|
import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.vaultUnlockDestination
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.rootnav.util.toVaultItemListingType
|
import com.x8bit.bitwarden.ui.platform.feature.rootnav.util.toVaultItemListingType
|
||||||
|
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.loginapproval.navigateToLoginApproval
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.splash.SPLASH_ROUTE
|
import com.x8bit.bitwarden.ui.platform.feature.splash.SPLASH_ROUTE
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.splash.navigateToSplash
|
import com.x8bit.bitwarden.ui.platform.feature.splash.navigateToSplash
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.splash.splashDestination
|
import com.x8bit.bitwarden.ui.platform.feature.splash.splashDestination
|
||||||
|
@ -90,6 +91,7 @@ fun RootNavScreen(
|
||||||
is RootNavState.VaultUnlockedForAutofillSave,
|
is RootNavState.VaultUnlockedForAutofillSave,
|
||||||
is RootNavState.VaultUnlockedForAutofillSelection,
|
is RootNavState.VaultUnlockedForAutofillSelection,
|
||||||
is RootNavState.VaultUnlockedForNewSend,
|
is RootNavState.VaultUnlockedForNewSend,
|
||||||
|
is RootNavState.VaultUnlockedForAuthRequest,
|
||||||
-> VAULT_UNLOCKED_GRAPH_ROUTE
|
-> VAULT_UNLOCKED_GRAPH_ROUTE
|
||||||
}
|
}
|
||||||
val currentRoute = navController.currentDestination?.rootLevelRoute()
|
val currentRoute = navController.currentDestination?.rootLevelRoute()
|
||||||
|
@ -144,6 +146,14 @@ fun RootNavScreen(
|
||||||
navOptions = rootNavOptions,
|
navOptions = rootNavOptions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RootNavState.VaultUnlockedForAuthRequest -> {
|
||||||
|
navController.navigateToVaultUnlockedGraph(rootNavOptions)
|
||||||
|
navController.navigateToLoginApproval(
|
||||||
|
fingerprint = null,
|
||||||
|
navOptions = rootNavOptions,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,10 @@ class RootNavViewModel @Inject constructor(
|
||||||
|
|
||||||
is SpecialCircumstance.ShareNewSend -> RootNavState.VaultUnlockedForNewSend
|
is SpecialCircumstance.ShareNewSend -> RootNavState.VaultUnlockedForNewSend
|
||||||
|
|
||||||
|
is SpecialCircumstance.PasswordlessRequest -> {
|
||||||
|
RootNavState.VaultUnlockedForAuthRequest
|
||||||
|
}
|
||||||
|
|
||||||
null -> {
|
null -> {
|
||||||
RootNavState.VaultUnlocked(
|
RootNavState.VaultUnlocked(
|
||||||
activeUserId = userState.activeAccount.userId,
|
activeUserId = userState.activeAccount.userId,
|
||||||
|
@ -156,6 +160,12 @@ sealed class RootNavState : Parcelable {
|
||||||
*/
|
*/
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data object VaultUnlockedForNewSend : RootNavState()
|
data object VaultUnlockedForNewSend : RootNavState()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App should show the auth confirmation screen for an unlocked user.
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data object VaultUnlockedForAuthRequest : RootNavState()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,20 +4,22 @@ import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavOptions
|
import androidx.navigation.NavOptions
|
||||||
|
import androidx.navigation.NavType
|
||||||
|
import androidx.navigation.navArgument
|
||||||
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||||
|
|
||||||
private const val FINGERPRINT: String = "fingerprint"
|
private const val FINGERPRINT: String = "fingerprint"
|
||||||
private const val LOGIN_APPROVAL_PREFIX = "login_approval"
|
private const val LOGIN_APPROVAL_PREFIX = "login_approval"
|
||||||
private const val LOGIN_APPROVAL_ROUTE = "$LOGIN_APPROVAL_PREFIX/{$FINGERPRINT}"
|
private const val LOGIN_APPROVAL_ROUTE = "$LOGIN_APPROVAL_PREFIX?$FINGERPRINT={$FINGERPRINT}"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to retrieve login approval arguments from the [SavedStateHandle].
|
* Class to retrieve login approval arguments from the [SavedStateHandle].
|
||||||
*/
|
*/
|
||||||
@OmitFromCoverage
|
@OmitFromCoverage
|
||||||
data class LoginApprovalArgs(val fingerprint: String) {
|
data class LoginApprovalArgs(val fingerprint: String?) {
|
||||||
constructor(savedStateHandle: SavedStateHandle) : this(
|
constructor(savedStateHandle: SavedStateHandle) : this(
|
||||||
checkNotNull(savedStateHandle[FINGERPRINT]) as String,
|
fingerprint = savedStateHandle.get<String>(FINGERPRINT),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +31,13 @@ fun NavGraphBuilder.loginApprovalDestination(
|
||||||
) {
|
) {
|
||||||
composableWithSlideTransitions(
|
composableWithSlideTransitions(
|
||||||
route = LOGIN_APPROVAL_ROUTE,
|
route = LOGIN_APPROVAL_ROUTE,
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument(FINGERPRINT) {
|
||||||
|
type = NavType.StringType
|
||||||
|
nullable = true
|
||||||
|
defaultValue = null
|
||||||
|
},
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
LoginApprovalScreen(
|
LoginApprovalScreen(
|
||||||
onNavigateBack = onNavigateBack,
|
onNavigateBack = onNavigateBack,
|
||||||
|
@ -40,8 +49,8 @@ fun NavGraphBuilder.loginApprovalDestination(
|
||||||
* Navigate to the Login Approval screen.
|
* Navigate to the Login Approval screen.
|
||||||
*/
|
*/
|
||||||
fun NavController.navigateToLoginApproval(
|
fun NavController.navigateToLoginApproval(
|
||||||
fingerprint: String,
|
fingerprint: String?,
|
||||||
navOptions: NavOptions? = null,
|
navOptions: NavOptions? = null,
|
||||||
) {
|
) {
|
||||||
navigate("$LOGIN_APPROVAL_PREFIX/$fingerprint", navOptions)
|
navigate("$LOGIN_APPROVAL_PREFIX?$FINGERPRINT=$fingerprint", navOptions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ class LoginApprovalViewModel @Inject constructor(
|
||||||
) : BaseViewModel<LoginApprovalState, LoginApprovalEvent, LoginApprovalAction>(
|
) : BaseViewModel<LoginApprovalState, LoginApprovalEvent, LoginApprovalAction>(
|
||||||
initialState = savedStateHandle[KEY_STATE]
|
initialState = savedStateHandle[KEY_STATE]
|
||||||
?: LoginApprovalState(
|
?: LoginApprovalState(
|
||||||
fingerprint = LoginApprovalArgs(savedStateHandle).fingerprint,
|
fingerprint = requireNotNull(LoginApprovalArgs(savedStateHandle).fingerprint),
|
||||||
masterPasswordHash = null,
|
masterPasswordHash = null,
|
||||||
publicKey = "",
|
publicKey = "",
|
||||||
requestId = "",
|
requestId = "",
|
||||||
|
|
|
@ -6,6 +6,7 @@ import app.cash.turbine.test
|
||||||
import com.bitwarden.core.CipherView
|
import com.bitwarden.core.CipherView
|
||||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||||
|
import com.x8bit.bitwarden.data.auth.util.getPasswordlessRequestDataIntentOrNull
|
||||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager
|
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager
|
||||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManagerImpl
|
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManagerImpl
|
||||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
||||||
|
@ -13,6 +14,7 @@ import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||||
import com.x8bit.bitwarden.data.autofill.util.getAutofillSaveItemOrNull
|
import com.x8bit.bitwarden.data.autofill.util.getAutofillSaveItemOrNull
|
||||||
import com.x8bit.bitwarden.data.autofill.util.getAutofillSelectionDataOrNull
|
import com.x8bit.bitwarden.data.autofill.util.getAutofillSelectionDataOrNull
|
||||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl
|
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl
|
||||||
|
import com.x8bit.bitwarden.data.platform.manager.model.PasswordlessRequestData
|
||||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
||||||
|
@ -131,6 +133,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
val mockIntent = mockk<Intent>()
|
val mockIntent = mockk<Intent>()
|
||||||
val shareData = mockk<IntentManager.ShareData>()
|
val shareData = mockk<IntentManager.ShareData>()
|
||||||
|
every { mockIntent.getPasswordlessRequestDataIntentOrNull() } returns null
|
||||||
every { mockIntent.getAutofillSaveItemOrNull() } returns null
|
every { mockIntent.getAutofillSaveItemOrNull() } returns null
|
||||||
every { mockIntent.getAutofillSelectionDataOrNull() } returns null
|
every { mockIntent.getAutofillSelectionDataOrNull() } returns null
|
||||||
every { intentManager.getShareDataFromIntent(mockIntent) } returns shareData
|
every { intentManager.getShareDataFromIntent(mockIntent) } returns shareData
|
||||||
|
@ -155,6 +158,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
val mockIntent = mockk<Intent>()
|
val mockIntent = mockk<Intent>()
|
||||||
val autofillSelectionData = mockk<AutofillSelectionData>()
|
val autofillSelectionData = mockk<AutofillSelectionData>()
|
||||||
|
every { mockIntent.getPasswordlessRequestDataIntentOrNull() } returns null
|
||||||
every { mockIntent.getAutofillSaveItemOrNull() } returns null
|
every { mockIntent.getAutofillSaveItemOrNull() } returns null
|
||||||
every { mockIntent.getAutofillSelectionDataOrNull() } returns autofillSelectionData
|
every { mockIntent.getAutofillSelectionDataOrNull() } returns autofillSelectionData
|
||||||
every { intentManager.getShareDataFromIntent(mockIntent) } returns null
|
every { intentManager.getShareDataFromIntent(mockIntent) } returns null
|
||||||
|
@ -179,6 +183,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
val mockIntent = mockk<Intent>()
|
val mockIntent = mockk<Intent>()
|
||||||
val autofillSaveItem = mockk<AutofillSaveItem>()
|
val autofillSaveItem = mockk<AutofillSaveItem>()
|
||||||
|
every { mockIntent.getPasswordlessRequestDataIntentOrNull() } returns null
|
||||||
every { mockIntent.getAutofillSaveItemOrNull() } returns autofillSaveItem
|
every { mockIntent.getAutofillSaveItemOrNull() } returns autofillSaveItem
|
||||||
every { mockIntent.getAutofillSelectionDataOrNull() } returns null
|
every { mockIntent.getAutofillSelectionDataOrNull() } returns null
|
||||||
every { intentManager.getShareDataFromIntent(mockIntent) } returns null
|
every { intentManager.getShareDataFromIntent(mockIntent) } returns null
|
||||||
|
@ -196,12 +201,40 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `on ReceiveFirstIntent with a passwordless request data should set the special circumstance to PasswordlessRequest`() {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
val mockIntent = mockk<Intent>()
|
||||||
|
val passwordlessRequestData = mockk<PasswordlessRequestData>()
|
||||||
|
every {
|
||||||
|
mockIntent.getPasswordlessRequestDataIntentOrNull()
|
||||||
|
} returns passwordlessRequestData
|
||||||
|
every { mockIntent.getAutofillSaveItemOrNull() } returns null
|
||||||
|
every { mockIntent.getAutofillSelectionDataOrNull() } returns null
|
||||||
|
every { intentManager.getShareDataFromIntent(mockIntent) } returns null
|
||||||
|
|
||||||
|
viewModel.trySendAction(
|
||||||
|
MainAction.ReceiveFirstIntent(
|
||||||
|
intent = mockIntent,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
SpecialCircumstance.PasswordlessRequest(
|
||||||
|
passwordlessRequestData = passwordlessRequestData,
|
||||||
|
shouldFinishWhenComplete = true,
|
||||||
|
),
|
||||||
|
specialCircumstanceManager.specialCircumstance,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `on ReceiveNewIntent with share data should set the special circumstance to ShareNewSend`() {
|
fun `on ReceiveNewIntent with share data should set the special circumstance to ShareNewSend`() {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
val mockIntent = mockk<Intent>()
|
val mockIntent = mockk<Intent>()
|
||||||
val shareData = mockk<IntentManager.ShareData>()
|
val shareData = mockk<IntentManager.ShareData>()
|
||||||
|
every { mockIntent.getPasswordlessRequestDataIntentOrNull() } returns null
|
||||||
every { mockIntent.getAutofillSaveItemOrNull() } returns null
|
every { mockIntent.getAutofillSaveItemOrNull() } returns null
|
||||||
every { mockIntent.getAutofillSelectionDataOrNull() } returns null
|
every { mockIntent.getAutofillSelectionDataOrNull() } returns null
|
||||||
every { intentManager.getShareDataFromIntent(mockIntent) } returns shareData
|
every { intentManager.getShareDataFromIntent(mockIntent) } returns shareData
|
||||||
|
@ -226,6 +259,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
val mockIntent = mockk<Intent>()
|
val mockIntent = mockk<Intent>()
|
||||||
val autofillSelectionData = mockk<AutofillSelectionData>()
|
val autofillSelectionData = mockk<AutofillSelectionData>()
|
||||||
|
every { mockIntent.getPasswordlessRequestDataIntentOrNull() } returns null
|
||||||
every { mockIntent.getAutofillSaveItemOrNull() } returns null
|
every { mockIntent.getAutofillSaveItemOrNull() } returns null
|
||||||
every { mockIntent.getAutofillSelectionDataOrNull() } returns autofillSelectionData
|
every { mockIntent.getAutofillSelectionDataOrNull() } returns autofillSelectionData
|
||||||
every { intentManager.getShareDataFromIntent(mockIntent) } returns null
|
every { intentManager.getShareDataFromIntent(mockIntent) } returns null
|
||||||
|
@ -250,6 +284,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
val mockIntent = mockk<Intent>()
|
val mockIntent = mockk<Intent>()
|
||||||
val autofillSaveItem = mockk<AutofillSaveItem>()
|
val autofillSaveItem = mockk<AutofillSaveItem>()
|
||||||
|
every { mockIntent.getPasswordlessRequestDataIntentOrNull() } returns null
|
||||||
every { mockIntent.getAutofillSaveItemOrNull() } returns autofillSaveItem
|
every { mockIntent.getAutofillSaveItemOrNull() } returns autofillSaveItem
|
||||||
every { mockIntent.getAutofillSelectionDataOrNull() } returns null
|
every { mockIntent.getAutofillSelectionDataOrNull() } returns null
|
||||||
every { intentManager.getShareDataFromIntent(mockIntent) } returns null
|
every { intentManager.getShareDataFromIntent(mockIntent) } returns null
|
||||||
|
@ -267,6 +302,33 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `on ReceiveNewIntent with a passwordless auth request data should set the special circumstance to PasswordlessRequest`() {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
val mockIntent = mockk<Intent>()
|
||||||
|
val passwordlessRequestData = mockk<PasswordlessRequestData>()
|
||||||
|
every {
|
||||||
|
mockIntent.getPasswordlessRequestDataIntentOrNull()
|
||||||
|
} returns passwordlessRequestData
|
||||||
|
every { mockIntent.getAutofillSaveItemOrNull() } returns null
|
||||||
|
every { mockIntent.getAutofillSelectionDataOrNull() } returns null
|
||||||
|
every { intentManager.getShareDataFromIntent(mockIntent) } returns null
|
||||||
|
|
||||||
|
viewModel.trySendAction(
|
||||||
|
MainAction.ReceiveNewIntent(
|
||||||
|
intent = mockIntent,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
SpecialCircumstance.PasswordlessRequest(
|
||||||
|
passwordlessRequestData = passwordlessRequestData,
|
||||||
|
shouldFinishWhenComplete = false,
|
||||||
|
),
|
||||||
|
specialCircumstanceManager.specialCircumstance,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `changes in the allowed screen capture value should result in emissions of ScreenCaptureSettingChange `() =
|
fun `changes in the allowed screen capture value should result in emissions of ScreenCaptureSettingChange `() =
|
||||||
|
|
Loading…
Reference in a new issue