[PM-24411] Generalize IntentManager activity handling (#5689)

This commit is contained in:
Patrick Honkonen 2025-08-13 18:03:09 -04:00 committed by GitHub
parent a688693f43
commit 26252ebcdb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 173 additions and 108 deletions

View File

@ -22,6 +22,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@ -50,6 +51,7 @@ import com.x8bit.bitwarden.ui.platform.components.image.BitwardenGifImage
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
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.utils.startSystemAutofillSettingsActivity
/** /**
* Top level composable for the Auto-fill setup screen. * Top level composable for the Auto-fill setup screen.
@ -62,12 +64,15 @@ fun SetupAutoFillScreen(
intentManager: IntentManager = LocalIntentManager.current, intentManager: IntentManager = LocalIntentManager.current,
viewModel: SetupAutoFillViewModel = hiltViewModel(), viewModel: SetupAutoFillViewModel = hiltViewModel(),
) { ) {
val context = LocalContext.current
val state by viewModel.stateFlow.collectAsStateWithLifecycle() val state by viewModel.stateFlow.collectAsStateWithLifecycle()
val handler = rememberSetupAutoFillHandler(viewModel = viewModel) val handler = rememberSetupAutoFillHandler(viewModel = viewModel)
EventsEffect(viewModel = viewModel) { event -> EventsEffect(viewModel = viewModel) { event ->
when (event) { when (event) {
SetupAutoFillEvent.NavigateToAutofillSettings -> { SetupAutoFillEvent.NavigateToAutofillSettings -> {
val showFallback = !intentManager.startSystemAutofillSettingsActivity() val showFallback = !intentManager.startSystemAutofillSettingsActivity(
context = context,
)
if (showFallback) { if (showFallback) {
handler.sendAutoFillServiceFallback.invoke() handler.sendAutoFillServiceFallback.invoke()
} }

View File

@ -44,6 +44,7 @@ import com.x8bit.bitwarden.ui.auth.feature.checkemail.handlers.rememberCheckEmai
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
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.utils.startDefaultEmailApplication
/** /**
* Top level composable for the check email screen. * Top level composable for the check email screen.

View File

@ -1,5 +1,6 @@
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -68,6 +69,7 @@ import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricSupportStatus import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricSupportStatus
import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager
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.utils.startApplicationDetailsSettingsActivity
import com.x8bit.bitwarden.ui.platform.util.displayLabel import com.x8bit.bitwarden.ui.platform.util.displayLabel
import com.x8bit.bitwarden.ui.platform.util.minutes import com.x8bit.bitwarden.ui.platform.util.minutes
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@ -91,6 +93,7 @@ fun AccountSecurityScreen(
biometricsManager: BiometricsManager = LocalBiometricsManager.current, biometricsManager: BiometricsManager = LocalBiometricsManager.current,
intentManager: IntentManager = LocalIntentManager.current, intentManager: IntentManager = LocalIntentManager.current,
) { ) {
val context: Context = LocalContext.current
val state by viewModel.stateFlow.collectAsStateWithLifecycle() val state by viewModel.stateFlow.collectAsStateWithLifecycle()
var showBiometricsPrompt by rememberSaveable { mutableStateOf(false) } var showBiometricsPrompt by rememberSaveable { mutableStateOf(false) }
val unlockWithBiometricToggle: (cipher: Cipher) -> Unit = remember(viewModel) { val unlockWithBiometricToggle: (cipher: Cipher) -> Unit = remember(viewModel) {
@ -105,7 +108,7 @@ fun AccountSecurityScreen(
AccountSecurityEvent.NavigateBack -> onNavigateBack() AccountSecurityEvent.NavigateBack -> onNavigateBack()
AccountSecurityEvent.NavigateToApplicationDataSettings -> { AccountSecurityEvent.NavigateToApplicationDataSettings -> {
intentManager.startApplicationDetailsSettingsActivity() intentManager.startApplicationDetailsSettingsActivity(context = context)
} }
AccountSecurityEvent.NavigateToDeleteAccount -> onNavigateToDeleteAccount() AccountSecurityEvent.NavigateToDeleteAccount -> onNavigateToDeleteAccount()

View File

@ -61,6 +61,9 @@ import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.handlers.AutoFi
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.util.displayLabel import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.util.displayLabel
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.util.isAdvancedMatching import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.util.isAdvancedMatching
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.utils.startBrowserAutofillSettingsActivity
import com.x8bit.bitwarden.ui.platform.manager.utils.startSystemAccessibilitySettingsActivity
import com.x8bit.bitwarden.ui.platform.manager.utils.startSystemAutofillSettingsActivity
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
/** /**
@ -90,7 +93,9 @@ fun AutoFillScreen(
} }
AutoFillEvent.NavigateToAutofillSettings -> { AutoFillEvent.NavigateToAutofillSettings -> {
val isSuccess = intentManager.startSystemAutofillSettingsActivity() val isSuccess = intentManager.startSystemAutofillSettingsActivity(
context = context,
)
shouldShowAutofillFallbackDialog = !isSuccess shouldShowAutofillFallbackDialog = !isSuccess
} }

View File

@ -8,9 +8,11 @@ import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResult
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.bitwarden.annotation.OmitFromCoverage
import com.bitwarden.core.data.manager.BuildInfoManager
import com.bitwarden.ui.platform.model.FileData import com.bitwarden.ui.platform.model.FileData
import com.x8bit.bitwarden.data.autofill.model.browser.BrowserPackage
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import java.time.Clock
/** /**
* A manager class for simplifying the handling of Android Intents within a given context. * A manager class for simplifying the handling of Android Intents within a given context.
@ -20,40 +22,21 @@ import kotlinx.parcelize.Parcelize
interface IntentManager { interface IntentManager {
/** /**
* Start an activity using the provided [Intent]. * Start an activity using the provided [Intent].
*
* @return `true` if the activity was started successfully, `false` otherwise.
*/ */
fun startActivity(intent: Intent) fun startActivity(intent: Intent): Boolean
/** /**
* Start a Custom Tabs Activity using the provided [Uri]. * Start a Custom Tabs Activity using the provided [Uri].
*/ */
fun startCustomTabsActivity(uri: Uri) fun startCustomTabsActivity(uri: Uri)
/**
* Attempts to start the system accessibility settings activity.
*/
fun startSystemAccessibilitySettingsActivity()
/**
* Attempts to start the system autofill settings activity. The return value indicates whether
* or not this was successful.
*/
fun startSystemAutofillSettingsActivity(): Boolean
/**
* Starts the application's settings activity.
*/
fun startApplicationDetailsSettingsActivity()
/** /**
* Starts the credential manager settings. * Starts the credential manager settings.
*/ */
fun startCredentialManagerSettings(context: Context) fun startCredentialManagerSettings(context: Context)
/**
* Starts the browser autofill settings activity for the provided [BrowserPackage].
*/
fun startBrowserAutofillSettingsActivity(browserPackage: BrowserPackage): Boolean
/** /**
* Start an activity to view the given [uri] in an external browser. * Start an activity to view the given [uri] in an external browser.
*/ */
@ -107,11 +90,6 @@ interface IntentManager {
*/ */
fun createDocumentIntent(fileName: String): Intent fun createDocumentIntent(fileName: String): Intent
/**
* Open the default email app on device.
*/
fun startDefaultEmailApplication()
/** /**
* Represents data for a share request coming from outside the app. * Represents data for a share request coming from outside the app.
*/ */
@ -133,4 +111,21 @@ interface IntentManager {
val fileData: FileData, val fileData: FileData,
) : ShareData() ) : ShareData()
} }
@Suppress("UndocumentedPublicClass")
@OmitFromCoverage
companion object {
/**
* Creates a new [IntentManager] instance.
*/
fun create(
context: Context,
clock: Clock,
buildInfoManager: BuildInfoManager,
): IntentManager = IntentManagerImpl(
context = context,
clock = clock,
buildInfoManager = buildInfoManager,
)
}
} }

View File

@ -10,7 +10,6 @@ import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.provider.MediaStore import android.provider.MediaStore
import android.provider.Settings
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
@ -30,7 +29,6 @@ import com.bitwarden.ui.platform.manager.util.deviceData
import com.bitwarden.ui.platform.manager.util.fileProviderAuthority import com.bitwarden.ui.platform.manager.util.fileProviderAuthority
import com.bitwarden.ui.platform.model.FileData import com.bitwarden.ui.platform.model.FileData
import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.platform.resource.BitwardenString
import com.x8bit.bitwarden.data.autofill.model.browser.BrowserPackage
import java.io.File import java.io.File
import java.time.Clock import java.time.Clock
@ -55,12 +53,11 @@ class IntentManagerImpl(
private val clock: Clock, private val clock: Clock,
private val buildInfoManager: BuildInfoManager, private val buildInfoManager: BuildInfoManager,
) : IntentManager { ) : IntentManager {
override fun startActivity(intent: Intent) { override fun startActivity(intent: Intent): Boolean = try {
try { context.startActivity(intent)
context.startActivity(intent) true
} catch (_: ActivityNotFoundException) { } catch (_: ActivityNotFoundException) {
// no-op false
}
} }
@Composable @Composable
@ -79,50 +76,12 @@ class IntentManagerImpl(
.launchUrl(context, uri) .launchUrl(context, uri)
} }
override fun startSystemAccessibilitySettingsActivity() {
context.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
}
override fun startSystemAutofillSettingsActivity(): Boolean =
try {
val intent = Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE)
.apply {
data = "package:${context.packageName}".toUri()
}
context.startActivity(intent)
true
} catch (_: ActivityNotFoundException) {
false
}
override fun startApplicationDetailsSettingsActivity() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = "package:${context.packageName}".toUri()
startActivity(intent = intent)
}
override fun startCredentialManagerSettings(context: Context) { override fun startCredentialManagerSettings(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
CredentialManager.create(context).createSettingsPendingIntent().send() CredentialManager.create(context).createSettingsPendingIntent().send()
} }
} }
override fun startBrowserAutofillSettingsActivity(
browserPackage: BrowserPackage,
): Boolean = try {
val intent = Intent(Intent.ACTION_APPLICATION_PREFERENCES)
.apply {
addCategory(Intent.CATEGORY_DEFAULT)
addCategory(Intent.CATEGORY_APP_BROWSER)
addCategory(Intent.CATEGORY_PREFERENCE)
setPackage(browserPackage.packageName)
}
context.startActivity(intent)
true
} catch (_: ActivityNotFoundException) {
false
}
override fun launchUri(uri: Uri) { override fun launchUri(uri: Uri) {
if (uri.scheme.equals(other = "androidapp", ignoreCase = true)) { if (uri.scheme.equals(other = "androidapp", ignoreCase = true)) {
val packageName = uri.toString().removePrefix(prefix = "androidapp://") val packageName = uri.toString().removePrefix(prefix = "androidapp://")
@ -271,13 +230,6 @@ class IntentManagerImpl(
putExtra(Intent.EXTRA_TITLE, fileName) putExtra(Intent.EXTRA_TITLE, fileName)
} }
override fun startDefaultEmailApplication() {
val intent = Intent(Intent.ACTION_MAIN)
intent.addCategory(Intent.CATEGORY_APP_EMAIL)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
private fun createPlayStoreIntent(packageName: String): Intent { private fun createPlayStoreIntent(packageName: String): Intent {
val playStoreUri = "https://play.google.com/store/apps/details" val playStoreUri = "https://play.google.com/store/apps/details"
.toUri() .toUri()

View File

@ -0,0 +1,69 @@
@file:OmitFromCoverage
package com.x8bit.bitwarden.ui.platform.manager.utils
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.provider.Settings
import androidx.core.net.toUri
import com.bitwarden.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.autofill.model.browser.BrowserPackage
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
/**
* Starts the system autofill settings activity.
*
* @param context The context from which to start the activity.
* @return `true` if the activity was started successfully, `false` otherwise.
*/
fun IntentManager.startSystemAutofillSettingsActivity(
context: Context,
): Boolean = !startActivity(
intent = Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE)
.setData("package:${context.packageName}".toUri()),
)
/**
* Attempts to start the system accessibility settings activity.
*/
fun IntentManager.startSystemAccessibilitySettingsActivity() {
startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
}
/**
* Starts the browser autofill settings activity for the provided [browserPackage].
*/
fun IntentManager.startBrowserAutofillSettingsActivity(
browserPackage: BrowserPackage,
): Boolean = try {
val intent = Intent(Intent.ACTION_APPLICATION_PREFERENCES)
.apply {
addCategory(Intent.CATEGORY_DEFAULT)
addCategory(Intent.CATEGORY_APP_BROWSER)
addCategory(Intent.CATEGORY_PREFERENCE)
setPackage(browserPackage.packageName)
}
startActivity(intent)
} catch (_: ActivityNotFoundException) {
false
}
/**
* Starts the application's settings activity.
*/
fun IntentManager.startApplicationDetailsSettingsActivity(context: Context) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = "package:${context.packageName}".toUri()
startActivity(intent = intent)
}
/**
* Open the default email app on device.
*/
fun IntentManager.startDefaultEmailApplication() {
val intent = Intent(Intent.ACTION_MAIN)
intent.addCategory(Intent.CATEGORY_APP_EMAIL)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}

View File

@ -13,8 +13,10 @@ import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import com.bitwarden.ui.util.assertNoDialogExists import com.bitwarden.ui.util.assertNoDialogExists
import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest
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.utils.startSystemAutofillSettingsActivity
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.verify import io.mockk.verify
import junit.framework.TestCase.assertTrue import junit.framework.TestCase.assertTrue
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -106,19 +108,23 @@ class SetupAutofillScreenTest : BitwardenComposeTest() {
@Test @Test
fun `NavigateToAutoFillSettings should start system autofill settings activity`() { fun `NavigateToAutoFillSettings should start system autofill settings activity`() {
every { intentManager.startSystemAutofillSettingsActivity() } returns true mockkStatic(IntentManager::startSystemAutofillSettingsActivity) {
mutableEventFlow.tryEmit(SetupAutoFillEvent.NavigateToAutofillSettings) every { intentManager.startSystemAutofillSettingsActivity(any()) } returns true
verify { mutableEventFlow.tryEmit(SetupAutoFillEvent.NavigateToAutofillSettings)
intentManager.startSystemAutofillSettingsActivity() verify {
intentManager.startSystemAutofillSettingsActivity(any())
}
} }
} }
@Suppress("MaxLineLength") @Suppress("MaxLineLength")
@Test @Test
fun `NavigateToAutoFillSettings should send AutoFillServiceFallback action when intent fails`() { fun `NavigateToAutoFillSettings should send AutoFillServiceFallback action when intent fails`() {
every { intentManager.startSystemAutofillSettingsActivity() } returns false mockkStatic(IntentManager::startSystemAutofillSettingsActivity) {
mutableEventFlow.tryEmit(SetupAutoFillEvent.NavigateToAutofillSettings) every { intentManager.startSystemAutofillSettingsActivity(any()) } returns false
verify { viewModel.trySendAction(SetupAutoFillAction.AutoFillServiceFallback) } mutableEventFlow.tryEmit(SetupAutoFillEvent.NavigateToAutofillSettings)
verify { viewModel.trySendAction(SetupAutoFillAction.AutoFillServiceFallback) }
}
} }
@Test @Test

View File

@ -7,9 +7,11 @@ import androidx.compose.ui.test.performScrollTo
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest
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.utils.startDefaultEmailApplication
import io.mockk.every import io.mockk.every
import io.mockk.just import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.runs import io.mockk.runs
import io.mockk.verify import io.mockk.verify
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -18,9 +20,7 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
class CheckEmailScreenTest : BitwardenComposeTest() { class CheckEmailScreenTest : BitwardenComposeTest() {
private val intentManager = mockk<IntentManager>(relaxed = true) { private val intentManager = mockk<IntentManager>(relaxed = true)
every { startDefaultEmailApplication() } just runs
}
private var onNavigateBackCalled = false private var onNavigateBackCalled = false
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE) private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
@ -72,9 +72,12 @@ class CheckEmailScreenTest : BitwardenComposeTest() {
@Test @Test
fun `NavigateToEmailApp should call openEmailApp`() { fun `NavigateToEmailApp should call openEmailApp`() {
mutableEventFlow.tryEmit(CheckEmailEvent.NavigateToEmailApp) mockkStatic(IntentManager::startDefaultEmailApplication) {
verify { every { intentManager.startDefaultEmailApplication() } just runs
intentManager.startDefaultEmailApplication() mutableEventFlow.tryEmit(CheckEmailEvent.NavigateToEmailApp)
verify {
intentManager.startDefaultEmailApplication()
}
} }
} }

View File

@ -39,7 +39,7 @@ class StartRegistrationScreenTest : BitwardenComposeTest() {
private val intentManager = mockk<IntentManager>(relaxed = true) { private val intentManager = mockk<IntentManager>(relaxed = true) {
every { startCustomTabsActivity(any()) } just runs every { startCustomTabsActivity(any()) } just runs
every { startActivity(any()) } just runs every { startActivity(any()) } returns true
every { launchUri(any()) } just runs every { launchUri(any()) } just runs
} }

View File

@ -29,10 +29,12 @@ import com.x8bit.bitwarden.ui.platform.components.toggle.UnlockWithPinState
import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricSupportStatus import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricSupportStatus
import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager
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.utils.startApplicationDetailsSettingsActivity
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.every import io.mockk.every
import io.mockk.just import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.runs import io.mockk.runs
import io.mockk.slot import io.mockk.slot
import io.mockk.verify import io.mockk.verify
@ -53,8 +55,7 @@ class AccountSecurityScreenTest : BitwardenComposeTest() {
private val intentManager = mockk<IntentManager> { private val intentManager = mockk<IntentManager> {
every { launchUri(any()) } just runs every { launchUri(any()) } just runs
every { startActivity(any()) } just runs every { startActivity(any()) } returns true
every { startApplicationDetailsSettingsActivity() } just runs
} }
private val captureBiometricsSuccess = slot<(cipher: Cipher?) -> Unit>() private val captureBiometricsSuccess = slot<(cipher: Cipher?) -> Unit>()
private val captureBiometricsCancel = slot<() -> Unit>() private val captureBiometricsCancel = slot<() -> Unit>()
@ -104,9 +105,11 @@ class AccountSecurityScreenTest : BitwardenComposeTest() {
@Test @Test
fun `on NavigateToApplicationDataSettings should launch the correct intent`() { fun `on NavigateToApplicationDataSettings should launch the correct intent`() {
mutableEventFlow.tryEmit(AccountSecurityEvent.NavigateToApplicationDataSettings) mockkStatic(IntentManager::startApplicationDetailsSettingsActivity) {
every { intentManager.startApplicationDetailsSettingsActivity(any()) } just runs
verify { intentManager.startApplicationDetailsSettingsActivity() } mutableEventFlow.tryEmit(AccountSecurityEvent.NavigateToApplicationDataSettings)
verify { intentManager.startApplicationDetailsSettingsActivity(any()) }
}
} }
@Test @Test

View File

@ -20,14 +20,20 @@ import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.browser.model.BrowserAutofillSettingsOption import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.browser.model.BrowserAutofillSettingsOption
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.utils.startBrowserAutofillSettingsActivity
import com.x8bit.bitwarden.ui.platform.manager.utils.startSystemAccessibilitySettingsActivity
import com.x8bit.bitwarden.ui.platform.manager.utils.startSystemAutofillSettingsActivity
import io.mockk.every import io.mockk.every
import io.mockk.just import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.runs import io.mockk.runs
import io.mockk.unmockkStatic
import io.mockk.verify import io.mockk.verify
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import org.junit.After
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -49,15 +55,23 @@ class AutoFillScreenTest : BitwardenComposeTest() {
every { stateFlow } returns mutableStateFlow every { stateFlow } returns mutableStateFlow
} }
private val intentManager: IntentManager = mockk { private val intentManager: IntentManager = mockk {
every { startSystemAutofillSettingsActivity() } answers { isSystemSettingsRequestSuccess }
every { startCredentialManagerSettings(any()) } just runs every { startCredentialManagerSettings(any()) } just runs
every { startSystemAccessibilitySettingsActivity() } just runs
every { launchUri(any()) } just runs every { launchUri(any()) } just runs
every { startBrowserAutofillSettingsActivity(any()) } returns true
} }
@Before @Before
fun setUp() { fun setUp() {
mockkStatic(
IntentManager::startSystemAutofillSettingsActivity,
IntentManager::startSystemAccessibilitySettingsActivity,
IntentManager::startBrowserAutofillSettingsActivity,
)
every { intentManager.startBrowserAutofillSettingsActivity(any()) } returns true
every {
intentManager.startSystemAutofillSettingsActivity(any())
} answers { isSystemSettingsRequestSuccess }
every { intentManager.startSystemAccessibilitySettingsActivity() } just runs
setContent( setContent(
intentManager = intentManager, intentManager = intentManager,
) { ) {
@ -76,6 +90,15 @@ class AutoFillScreenTest : BitwardenComposeTest() {
} }
} }
@After
fun tearDown() {
unmockkStatic(
IntentManager::startSystemAutofillSettingsActivity,
IntentManager::startSystemAccessibilitySettingsActivity,
IntentManager::startBrowserAutofillSettingsActivity,
)
}
@Test @Test
fun `on NavigateToAccessibilitySettings should attempt to navigate to system settings`() { fun `on NavigateToAccessibilitySettings should attempt to navigate to system settings`() {
mutableEventFlow.tryEmit(AutoFillEvent.NavigateToAccessibilitySettings) mutableEventFlow.tryEmit(AutoFillEvent.NavigateToAccessibilitySettings)
@ -93,7 +116,7 @@ class AutoFillScreenTest : BitwardenComposeTest() {
mutableEventFlow.tryEmit(AutoFillEvent.NavigateToAutofillSettings) mutableEventFlow.tryEmit(AutoFillEvent.NavigateToAutofillSettings)
verify { verify {
intentManager.startSystemAutofillSettingsActivity() intentManager.startSystemAutofillSettingsActivity(any())
} }
composeTestRule.assertNoDialogExists() composeTestRule.assertNoDialogExists()
} }
@ -106,7 +129,7 @@ class AutoFillScreenTest : BitwardenComposeTest() {
mutableEventFlow.tryEmit(AutoFillEvent.NavigateToAutofillSettings) mutableEventFlow.tryEmit(AutoFillEvent.NavigateToAutofillSettings)
verify { verify {
intentManager.startSystemAutofillSettingsActivity() intentManager.startSystemAutofillSettingsActivity(any())
} }
composeTestRule composeTestRule