mirror of
https://github.com/bitwarden/android.git
synced 2026-05-22 14:11:47 -05:00
Merge branch 'main' into QA-1126b/adding-native-sanity-test
This commit is contained in:
@@ -199,31 +199,29 @@ private fun AutoFillScreenContent(
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
if (state.showInlineAutofillOption) {
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.inline_autofill),
|
||||
supportingText = stringResource(
|
||||
id = R.string.use_inline_autofill_explanation_long,
|
||||
),
|
||||
isChecked = state.isUseInlineAutoFillEnabled,
|
||||
onCheckedChange = autoFillHandlers.onUseInlineAutofillClick,
|
||||
enabled = state.canInteractWithInlineAutofillToggle,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("InlineAutofillSwitch")
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
|
||||
AnimatedVisibility(visible = state.showInlineAutofill) {
|
||||
Column {
|
||||
FillStyleSelector(
|
||||
selectedStyle = state.autofillStyle,
|
||||
onStyleChange = autoFillHandlers.onAutofillStyleChange,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
}
|
||||
|
||||
if (state.browserAutofillSettingsOptions.isNotEmpty()) {
|
||||
BrowserAutofillSettingsCard(
|
||||
options = state.browserAutofillSettingsOptions,
|
||||
onOptionClicked = autoFillHandlers.onBrowserAutofillSelected,
|
||||
enabled = state.isAutoFillServicesEnabled,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
AnimatedVisibility(visible = state.showBrowserSettingOptions) {
|
||||
Column {
|
||||
BrowserAutofillSettingsCard(
|
||||
options = state.browserAutofillSettingsOptions,
|
||||
onOptionClicked = autoFillHandlers.onBrowserAutofillSelected,
|
||||
enabled = state.isAutoFillServicesEnabled,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
}
|
||||
|
||||
if (state.showPasskeyManagementRow) {
|
||||
@@ -329,6 +327,26 @@ private fun AutoFillScreenContent(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FillStyleSelector(
|
||||
selectedStyle: AutofillStyle,
|
||||
onStyleChange: (AutofillStyle) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
resources: Resources = LocalContext.current.resources,
|
||||
) {
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.display_autofill_suggestions),
|
||||
supportingText = stringResource(id = R.string.use_inline_autofill_explanation_long),
|
||||
options = AutofillStyle.entries.map { it.label() }.toImmutableList(),
|
||||
selectedOption = selectedStyle.label(),
|
||||
onOptionSelected = {
|
||||
onStyleChange(AutofillStyle.entries.first { style -> style.label(resources) == it })
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier.testTag(tag = "InlineAutofillSelector"),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AccessibilityAutofillSwitch(
|
||||
isAccessibilityAutoFillEnabled: Boolean,
|
||||
|
||||
@@ -8,6 +8,8 @@ import com.bitwarden.core.util.isBuildVersionAtLeast
|
||||
import com.bitwarden.core.util.persistentListOfNotNull
|
||||
import com.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.bitwarden.ui.util.Text
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.autofill.manager.browser.BrowserThirdPartyAutofillEnabledManager
|
||||
import com.x8bit.bitwarden.data.autofill.model.browser.BrowserPackage
|
||||
@@ -52,7 +54,11 @@ class AutoFillViewModel @Inject constructor(
|
||||
.value,
|
||||
isAutoFillServicesEnabled = settingsRepository.isAutofillEnabledStateFlow.value,
|
||||
isCopyTotpAutomaticallyEnabled = !settingsRepository.isAutoCopyTotpDisabled,
|
||||
isUseInlineAutoFillEnabled = settingsRepository.isInlineAutofillEnabled,
|
||||
autofillStyle = if (settingsRepository.isInlineAutofillEnabled) {
|
||||
AutofillStyle.INLINE
|
||||
} else {
|
||||
AutofillStyle.POPUP
|
||||
},
|
||||
showInlineAutofillOption = isBuildVersionAtLeast(Build.VERSION_CODES.R),
|
||||
showPasskeyManagementRow = isBuildVersionAtLeast(
|
||||
Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
|
||||
@@ -117,7 +123,7 @@ class AutoFillViewModel @Inject constructor(
|
||||
is AutoFillAction.DefaultUriMatchTypeSelect -> handleDefaultUriMatchTypeSelect(action)
|
||||
AutoFillAction.BlockAutoFillClick -> handleBlockAutoFillClick()
|
||||
AutoFillAction.UseAccessibilityAutofillClick -> handleUseAccessibilityAutofillClick()
|
||||
is AutoFillAction.UseInlineAutofillClick -> handleUseInlineAutofillClick(action)
|
||||
is AutoFillAction.AutofillStyleSelected -> handleAutofillStyleSelected(action)
|
||||
AutoFillAction.PasskeyManagementClick -> handlePasskeyManagementClick()
|
||||
is AutoFillAction.Internal -> handleInternalAction(action)
|
||||
AutoFillAction.AutofillActionCardCtaClick -> handleAutofillActionCardCtaClick()
|
||||
@@ -228,9 +234,9 @@ class AutoFillViewModel @Inject constructor(
|
||||
sendEvent(AutoFillEvent.NavigateToAccessibilitySettings)
|
||||
}
|
||||
|
||||
private fun handleUseInlineAutofillClick(action: AutoFillAction.UseInlineAutofillClick) {
|
||||
settingsRepository.isInlineAutofillEnabled = action.isEnabled
|
||||
mutableStateFlow.update { it.copy(isUseInlineAutoFillEnabled = action.isEnabled) }
|
||||
private fun handleAutofillStyleSelected(action: AutoFillAction.AutofillStyleSelected) {
|
||||
settingsRepository.isInlineAutofillEnabled = action.style == AutofillStyle.INLINE
|
||||
mutableStateFlow.update { it.copy(autofillStyle = action.style) }
|
||||
}
|
||||
|
||||
private fun handlePasskeyManagementClick() {
|
||||
@@ -281,7 +287,7 @@ data class AutoFillState(
|
||||
val isAccessibilityAutofillEnabled: Boolean,
|
||||
val isAutoFillServicesEnabled: Boolean,
|
||||
val isCopyTotpAutomaticallyEnabled: Boolean,
|
||||
val isUseInlineAutoFillEnabled: Boolean,
|
||||
val autofillStyle: AutofillStyle,
|
||||
val showInlineAutofillOption: Boolean,
|
||||
val showPasskeyManagementRow: Boolean,
|
||||
val defaultUriMatchType: UriMatchType,
|
||||
@@ -290,13 +296,31 @@ data class AutoFillState(
|
||||
val browserAutofillSettingsOptions: ImmutableList<BrowserAutofillSettingsOption>,
|
||||
val isUserManagedPrivilegedAppsEnabled: Boolean,
|
||||
) : Parcelable {
|
||||
/**
|
||||
* Whether or not the dropdown controlling the [autofillStyle] value is displayed.
|
||||
*/
|
||||
val showInlineAutofill: Boolean get() = isAutoFillServicesEnabled && showInlineAutofillOption
|
||||
|
||||
/**
|
||||
* Whether or not the toggle controlling the [isUseInlineAutoFillEnabled] value can be
|
||||
* interacted with.
|
||||
* Whether or not the toggles for enabling 3rd-party autofill support should be displayed.
|
||||
*/
|
||||
val canInteractWithInlineAutofillToggle: Boolean
|
||||
get() = isAutoFillServicesEnabled
|
||||
val showBrowserSettingOptions: Boolean
|
||||
get() = isAutoFillServicesEnabled && browserAutofillSettingsOptions.isNotEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* The visual style of autofill that should be used.
|
||||
*/
|
||||
enum class AutofillStyle(val label: Text) {
|
||||
/**
|
||||
* Displays the autofill data in the keyboard.
|
||||
*/
|
||||
INLINE(label = R.string.autofill_suggestions_inline.asText()),
|
||||
|
||||
/**
|
||||
* Displays the autofill data as a popup attached to the field you are filling.
|
||||
*/
|
||||
POPUP(label = R.string.autofill_suggestions_popup.asText()),
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@@ -425,8 +449,8 @@ sealed class AutoFillAction {
|
||||
/**
|
||||
* User clicked use inline autofill button.
|
||||
*/
|
||||
data class UseInlineAutofillClick(
|
||||
val isEnabled: Boolean,
|
||||
data class AutofillStyleSelected(
|
||||
val style: AutofillStyle,
|
||||
) : AutoFillAction()
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.x8bit.bitwarden.data.autofill.model.browser.BrowserPackage
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.AutoFillAction
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.AutoFillViewModel
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.AutofillStyle
|
||||
|
||||
/**
|
||||
* Handlers for the AutoFill screen.
|
||||
@@ -14,7 +15,7 @@ class AutoFillHandlers(
|
||||
val onAutofillActionCardClick: () -> Unit,
|
||||
val onAutofillActionCardDismissClick: () -> Unit,
|
||||
val onAutofillServicesClick: (isEnabled: Boolean) -> Unit,
|
||||
val onUseInlineAutofillClick: (isEnabled: Boolean) -> Unit,
|
||||
val onAutofillStyleChange: (style: AutofillStyle) -> Unit,
|
||||
val onBrowserAutofillSelected: (browserPackage: BrowserPackage) -> Unit,
|
||||
val onPasskeyManagementClick: () -> Unit,
|
||||
val onPrivilegedAppsClick: () -> Unit,
|
||||
@@ -47,12 +48,8 @@ class AutoFillHandlers(
|
||||
),
|
||||
)
|
||||
},
|
||||
onUseInlineAutofillClick = {
|
||||
viewModel.trySendAction(
|
||||
AutoFillAction.UseInlineAutofillClick(
|
||||
it,
|
||||
),
|
||||
)
|
||||
onAutofillStyleChange = {
|
||||
viewModel.trySendAction(AutoFillAction.AutofillStyleSelected(it))
|
||||
},
|
||||
onBrowserAutofillSelected = {
|
||||
viewModel.trySendAction(AutoFillAction.BrowserAutofillSelected(it))
|
||||
|
||||
@@ -425,7 +425,9 @@ Scanning will happen automatically.</string>
|
||||
<string name="privacy_policy">Privacy Policy</string>
|
||||
<string name="passkey_management">Passkey management</string>
|
||||
<string name="autofill_services">Autofill services</string>
|
||||
<string name="inline_autofill">Use inline autofill</string>
|
||||
<string name="display_autofill_suggestions">Display autofill suggestions</string>
|
||||
<string name="autofill_suggestions_inline">Inline (shows in keyboard)</string>
|
||||
<string name="autofill_suggestions_popup">Popup (shows over input field)</string>
|
||||
<string name="accessibility">Use accessibility</string>
|
||||
<string name="accessibility_description5">Required to use the Autofill Quick-Action Tile.</string>
|
||||
<string name="personal_ownership_policy_in_effect">An organization policy is affecting your ownership options.</string>
|
||||
@@ -660,8 +662,8 @@ Do you want to switch to this account?</string>
|
||||
<string name="session_timeout_action">Session timeout action</string>
|
||||
<string name="account_fingerprint_phrase">Account fingerprint phrase</string>
|
||||
<string name="passkey_management_explanation_long">Use Bitwarden to save new passkeys and log in with passkeys stored in your vault.</string>
|
||||
<string name="autofill_services_explanation_long">The Android Autofill Framework is used to assist in filling login information into other apps on your device.</string>
|
||||
<string name="use_inline_autofill_explanation_long">Use inline autofill if your selected keyboard supports it. Otherwise, use the default overlay.</string>
|
||||
<string name="autofill_services_explanation_long">Allow Bitwarden to use your saved login information to sign in to other apps on your device.</string>
|
||||
<string name="use_inline_autofill_explanation_long">Choose how your autofill suggestions will appear when you sign in to other apps on your device.</string>
|
||||
<string name="additional_options">Additional options</string>
|
||||
<string name="continue_to_web_app">Continue to web app?</string>
|
||||
<string name="continue_to_x">Continue to %1$s?</string>
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
Authenticator App release variant: -->
|
||||
<item>B06B54566AF2FBCC762700C8844B84EC410C230EACC3878FCF0248C0D9772A95</item>
|
||||
|
||||
<!-- These are the SHA-256 digest for one-off builds created from branches by the Github
|
||||
Action of the Authenticator App release variant. Uncomment this to one off builds generated
|
||||
from the Authenticator App repo. -->
|
||||
<!-- <item>52f393fb529fbf2ab5bb018bf17bf0f829d2fbebce099915aba42deab5a2fbb8</item>-->
|
||||
<!-- These are the SHA-256 digest for APK signing key of the Authenticator App release
|
||||
variant. -->
|
||||
<item>52f393fb529fbf2ab5bb018bf17bf0f829d2fbebce099915aba42deab5a2fbb8</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -2,8 +2,6 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.autofill
|
||||
|
||||
import androidx.compose.ui.test.assert
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertIsEnabled
|
||||
import androidx.compose.ui.test.assertIsNotEnabled
|
||||
import androidx.compose.ui.test.assertIsOff
|
||||
import androidx.compose.ui.test.assertIsOn
|
||||
import androidx.compose.ui.test.filterToOne
|
||||
@@ -233,60 +231,85 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on use inline auto fill toggle should send UseInlineAutofillClick`() {
|
||||
fun `on inline autofill style selected should send AutofillStyleSelected`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
isAutoFillServicesEnabled = true,
|
||||
isUseInlineAutoFillEnabled = false,
|
||||
autofillStyle = AutofillStyle.POPUP,
|
||||
)
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText("Use inline autofill")
|
||||
.onNodeWithContentDescription(
|
||||
label = "Popup (shows over input field). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
)
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
verify { viewModel.trySendAction(AutoFillAction.UseInlineAutofillClick(true)) }
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Inline (shows in keyboard)")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(AutoFillAction.AutofillStyleSelected(AutofillStyle.INLINE))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `use inline autofill should be toggled on or off according to state`() {
|
||||
composeTestRule
|
||||
.onNodeWithText("Use inline autofill")
|
||||
.performScrollTo()
|
||||
.assertIsOff()
|
||||
mutableStateFlow.update { it.copy(isUseInlineAutoFillEnabled = true) }
|
||||
composeTestRule
|
||||
.onNodeWithText("Use inline autofill")
|
||||
.performScrollTo()
|
||||
.assertIsOn()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `use inline autofill should be disabled or enabled according to state`() {
|
||||
fun `autofill style should be display selection according to state`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
isAutoFillServicesEnabled = true,
|
||||
isUseInlineAutoFillEnabled = true,
|
||||
autofillStyle = AutofillStyle.INLINE,
|
||||
)
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
)
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
mutableStateFlow.update { it.copy(autofillStyle = AutofillStyle.POPUP) }
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Popup (shows over input field). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
)
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `use display autofill suggestions should be visible or enabled according to state`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(isAutoFillServicesEnabled = true)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Use inline autofill")
|
||||
.onNodeWithContentDescription(
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
)
|
||||
.performScrollTo()
|
||||
.assertIsOn()
|
||||
.assertIsEnabled()
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
isAutoFillServicesEnabled = false,
|
||||
isUseInlineAutoFillEnabled = true,
|
||||
)
|
||||
it.copy(isAutoFillServicesEnabled = false)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Use inline autofill")
|
||||
.performScrollTo()
|
||||
.assertIsOn()
|
||||
.assertIsNotEnabled()
|
||||
.onNodeWithContentDescription(
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
)
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@@ -319,11 +342,18 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
@Test
|
||||
fun `use inline autofill should be displayed according to state`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(showInlineAutofillOption = true)
|
||||
it.copy(
|
||||
isAutoFillServicesEnabled = true,
|
||||
showInlineAutofillOption = true,
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Use inline autofill")
|
||||
.onNodeWithContentDescription(
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
)
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
|
||||
@@ -331,7 +361,13 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
it.copy(showInlineAutofillOption = false)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(text = "Use inline autofill").assertDoesNotExist()
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
label = "Inline (shows in keyboard). Display autofill suggestions. " +
|
||||
"Choose how your autofill suggestions will appear when you sign in " +
|
||||
"to other apps on your device.",
|
||||
)
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -521,6 +557,7 @@ class AutoFillScreenTest : BitwardenComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `BrowserAutofillSettingsCard is only displayed when there are options in the list`() {
|
||||
mutableStateFlow.update { it.copy(isAutoFillServicesEnabled = true) }
|
||||
val browserAutofillSupportingText =
|
||||
"Improves login filling for supported websites on selected browsers. " +
|
||||
"Once enabled, you’ll be directed to browser settings to enable " +
|
||||
@@ -656,7 +693,7 @@ private val DEFAULT_STATE: AutoFillState = AutoFillState(
|
||||
isAccessibilityAutofillEnabled = false,
|
||||
isAutoFillServicesEnabled = false,
|
||||
isCopyTotpAutomaticallyEnabled = false,
|
||||
isUseInlineAutoFillEnabled = false,
|
||||
autofillStyle = AutofillStyle.INLINE,
|
||||
showInlineAutofillOption = true,
|
||||
showPasskeyManagementRow = true,
|
||||
defaultUriMatchType = UriMatchType.DOMAIN,
|
||||
|
||||
@@ -288,11 +288,12 @@ class AutoFillViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on UseInlineAutofillClick should update the state and save the new value to settings`() {
|
||||
fun `on AutofillStyleSelected should update the state and save the new value to settings`() {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(AutoFillAction.UseInlineAutofillClick(false))
|
||||
val autofillStyle = AutofillStyle.POPUP
|
||||
viewModel.trySendAction(AutoFillAction.AutofillStyleSelected(style = autofillStyle))
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(isUseInlineAutoFillEnabled = false),
|
||||
DEFAULT_STATE.copy(autofillStyle = autofillStyle),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
verify { settingsRepository.isInlineAutofillEnabled = false }
|
||||
@@ -466,7 +467,7 @@ private val DEFAULT_STATE: AutoFillState = AutoFillState(
|
||||
isAccessibilityAutofillEnabled = false,
|
||||
isAutoFillServicesEnabled = false,
|
||||
isCopyTotpAutomaticallyEnabled = false,
|
||||
isUseInlineAutoFillEnabled = true,
|
||||
autofillStyle = AutofillStyle.INLINE,
|
||||
showInlineAutofillOption = false,
|
||||
showPasskeyManagementRow = true,
|
||||
defaultUriMatchType = UriMatchType.DOMAIN,
|
||||
|
||||
@@ -15,16 +15,16 @@ androdixAutofill = "1.3.0"
|
||||
androidxBiometrics = "1.2.0-alpha05"
|
||||
androidxBrowser = "1.8.0"
|
||||
androidxCamera = "1.4.2"
|
||||
androidxComposeBom = "2025.05.01"
|
||||
androidxComposeBom = "2025.06.01"
|
||||
androidxCore = "1.16.0"
|
||||
androidxCredentials = "1.5.0"
|
||||
androidxHiltNavigationCompose = "1.2.0"
|
||||
androidxLifecycle = "2.9.0"
|
||||
androidxNavigation = "2.9.0"
|
||||
androidxRoom = "2.7.1"
|
||||
androidxRoom = "2.7.2"
|
||||
androidxSecurityCrypto = "1.1.0-alpha06"
|
||||
androidxSplash = "1.1.0-rc01"
|
||||
androidxWork = "2.10.1"
|
||||
androidxWork = "2.10.2"
|
||||
bitwardenSdk = "1.0.0-20250623.141835-223"
|
||||
crashlytics = "3.0.4"
|
||||
detekt = "1.23.8"
|
||||
@@ -48,7 +48,7 @@ ksp = "2.2.0-2.0.2"
|
||||
mockk = "1.14.2"
|
||||
okhttp = "4.12.0"
|
||||
retrofitBom = "3.0.0"
|
||||
robolectric = "4.14.1"
|
||||
robolectric = "4.15.1"
|
||||
sonarqube = "6.2.0.5505"
|
||||
testng = "7.11.0"
|
||||
timber = "5.0.1"
|
||||
|
||||
Reference in New Issue
Block a user