From 4510695f769dd29feb97efe933d72db5215c79dd Mon Sep 17 00:00:00 2001 From: Brian Yencho Date: Sun, 21 Jan 2024 13:31:26 -0600 Subject: [PATCH] BIT-1486: Use inline autofill based on user's setting (#702) --- .../data/autofill/di/AutofillModule.kt | 8 +- .../autofill/parser/AutofillParserImpl.kt | 10 ++- .../autofill/util/FillRequestExtensions.kt | 6 +- .../autofill/parser/AutofillParserTests.kt | 83 ++++++++++++++++++- .../util/FillRequestExtensionsTest.kt | 44 +++++++++- 5 files changed, 141 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/di/AutofillModule.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/di/AutofillModule.kt index e50c6e98d0..89469c547f 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/di/AutofillModule.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/di/AutofillModule.kt @@ -13,6 +13,7 @@ import com.x8bit.bitwarden.data.autofill.processor.AutofillProcessorImpl import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProvider import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProviderImpl import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager +import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -34,7 +35,12 @@ object AutofillModule { ): AutofillManager = context.getSystemService(AutofillManager::class.java) @Provides - fun providesAutofillParser(): AutofillParser = AutofillParserImpl() + fun providesAutofillParser( + settingsRepository: SettingsRepository, + ): AutofillParser = + AutofillParserImpl( + settingsRepository = settingsRepository, + ) @Provides fun providesAutofillCipherProvider(): AutofillCipherProvider = AutofillCipherProviderImpl() diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt index 82f9cc9cc1..5bfff6c152 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt @@ -12,12 +12,15 @@ import com.x8bit.bitwarden.data.autofill.util.buildUriOrNull import com.x8bit.bitwarden.data.autofill.util.getInlinePresentationSpecs import com.x8bit.bitwarden.data.autofill.util.getMaxInlineSuggestionsCount import com.x8bit.bitwarden.data.autofill.util.toAutofillView +import com.x8bit.bitwarden.data.platform.repository.SettingsRepository /** * The default [AutofillParser] implementation for the app. This is a tool for parsing autofill data * from the OS into domain models. */ -class AutofillParserImpl : AutofillParser { +class AutofillParserImpl( + private val settingsRepository: SettingsRepository, +) : AutofillParser { override fun parse( autofillAppInfo: AutofillAppInfo, fillRequest: FillRequest, @@ -79,12 +82,15 @@ class AutofillParserImpl : AutofillParser { .map { it.ignoreAutofillIds } .flatten() + // Get inline information if available + val isInlineAutofillEnabled = settingsRepository.isInlineAutofillEnabled val maxInlineSuggestionsCount = fillRequest.getMaxInlineSuggestionsCount( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = isInlineAutofillEnabled, ) - val inlinePresentationSpecs = fillRequest.getInlinePresentationSpecs( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = isInlineAutofillEnabled, ) return AutofillRequest.Fillable( diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/FillRequestExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/FillRequestExtensions.kt index 27812ffe6b..cba743b6de 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/FillRequestExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/FillRequestExtensions.kt @@ -12,8 +12,9 @@ import com.x8bit.bitwarden.data.autofill.model.AutofillAppInfo @SuppressLint("NewApi") fun FillRequest.getInlinePresentationSpecs( autofillAppInfo: AutofillAppInfo, + isInlineAutofillEnabled: Boolean, ): List = - if (autofillAppInfo.sdkInt >= Build.VERSION_CODES.R) { + if (isInlineAutofillEnabled && autofillAppInfo.sdkInt >= Build.VERSION_CODES.R) { inlineSuggestionsRequest?.inlinePresentationSpecs ?: emptyList() } else { emptyList() @@ -26,8 +27,9 @@ fun FillRequest.getInlinePresentationSpecs( @SuppressLint("NewApi") fun FillRequest.getMaxInlineSuggestionsCount( autofillAppInfo: AutofillAppInfo, + isInlineAutofillEnabled: Boolean, ): Int = - if (autofillAppInfo.sdkInt >= Build.VERSION_CODES.R) { + if (isInlineAutofillEnabled && autofillAppInfo.sdkInt >= Build.VERSION_CODES.R) { inlineSuggestionsRequest?.maxSuggestionCount ?: 0 } else { 0 diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt index b00be6c2dd..f40ad2b36e 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt @@ -15,6 +15,7 @@ import com.x8bit.bitwarden.data.autofill.util.buildUriOrNull import com.x8bit.bitwarden.data.autofill.util.getInlinePresentationSpecs import com.x8bit.bitwarden.data.autofill.util.getMaxInlineSuggestionsCount import com.x8bit.bitwarden.data.autofill.util.toAutofillView +import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic @@ -64,6 +65,11 @@ class AutofillParserTests { every { this@mockk.fillContexts } returns listOf(fillContext) } private val inlinePresentationSpecs: List = mockk() + private val settingsRepository: SettingsRepository = mockk { + every { isInlineAutofillEnabled } answers { mockIsInlineAutofillEnabled } + } + + private var mockIsInlineAutofillEnabled = true @BeforeEach fun setup() { @@ -76,15 +82,31 @@ class AutofillParserTests { every { fillRequest.getInlinePresentationSpecs( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) } returns inlinePresentationSpecs + every { + fillRequest.getInlinePresentationSpecs( + autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = false, + ) + } returns emptyList() every { fillRequest.getMaxInlineSuggestionsCount( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) } returns MAX_INLINE_SUGGESTION_COUNT + every { + fillRequest.getMaxInlineSuggestionsCount( + autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = false, + ) + } returns 0 every { any>().buildUriOrNull(assistStructure) } returns URI - parser = AutofillParserImpl() + parser = AutofillParserImpl( + settingsRepository = settingsRepository, + ) } @AfterEach @@ -183,9 +205,11 @@ class AutofillParserTests { verify(exactly = 1) { fillRequest.getInlinePresentationSpecs( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) fillRequest.getMaxInlineSuggestionsCount( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) any>().buildUriOrNull(assistStructure) } @@ -231,9 +255,11 @@ class AutofillParserTests { verify(exactly = 1) { fillRequest.getInlinePresentationSpecs( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) fillRequest.getMaxInlineSuggestionsCount( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) any>().buildUriOrNull(assistStructure) } @@ -279,9 +305,11 @@ class AutofillParserTests { verify(exactly = 1) { fillRequest.getInlinePresentationSpecs( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) fillRequest.getMaxInlineSuggestionsCount( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) any>().buildUriOrNull(assistStructure) } @@ -327,9 +355,62 @@ class AutofillParserTests { verify(exactly = 1) { fillRequest.getInlinePresentationSpecs( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) fillRequest.getMaxInlineSuggestionsCount( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, + ) + any>().buildUriOrNull(assistStructure) + } + } + + @Test + fun `parse should return empty inline suggestions when inline autofill is disabled`() { + // Setup + mockIsInlineAutofillEnabled = false + setupAssistStructureWithAllAutofillViewTypes() + val cardAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth( + data = autofillViewData.copy( + autofillId = cardAutofillId, + isFocused = true, + ), + ) + val loginAutofillView: AutofillView.Login = AutofillView.Login.Username( + data = autofillViewData.copy( + autofillId = loginAutofillId, + isFocused = true, + ), + ) + val autofillPartition = AutofillPartition.Card( + views = listOf(cardAutofillView), + ) + val expected = AutofillRequest.Fillable( + ignoreAutofillIds = emptyList(), + inlinePresentationSpecs = emptyList(), + maxInlineSuggestionsCount = 0, + partition = autofillPartition, + uri = URI, + ) + every { cardViewNode.toAutofillView() } returns cardAutofillView + every { loginViewNode.toAutofillView() } returns loginAutofillView + + // Test + val actual = parser.parse( + autofillAppInfo = autofillAppInfo, + fillRequest = fillRequest, + ) + + // Verify + assertEquals(expected, actual) + verify(exactly = 1) { + fillRequest.getInlinePresentationSpecs( + autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = false, + ) + fillRequest.getMaxInlineSuggestionsCount( + autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = false, ) any>().buildUriOrNull(assistStructure) } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/FillRequestExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/FillRequestExtensionsTest.kt index f878e10bdd..817c242c86 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/FillRequestExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/FillRequestExtensionsTest.kt @@ -20,7 +20,23 @@ class FillRequestExtensionsTest { } @Test - fun `getInlinePresentationSpecs should return empty list when pre-R`() { + fun `getInlinePresentationSpecs should return empty list when disabled`() { + // Setup + val autofillAppInfo: AutofillAppInfo = mockk() + val expected: List = emptyList() + + // Test + val actual = fillRequest.getInlinePresentationSpecs( + autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = false, + ) + + // Verify + assertEquals(expected, actual) + } + + @Test + fun `getInlinePresentationSpecs should return empty list when enabled pre-R`() { // Setup val autofillAppInfo = AutofillAppInfo( context = mockk(), @@ -32,6 +48,7 @@ class FillRequestExtensionsTest { // Test val actual = fillRequest.getInlinePresentationSpecs( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) // Verify @@ -39,7 +56,7 @@ class FillRequestExtensionsTest { } @Test - fun `getInlinePresentationSpecs should return populated list when post-R`() { + fun `getInlinePresentationSpecs should return populated list when enabled and post-R`() { // Setup val autofillAppInfo = AutofillAppInfo( context = mockk(), @@ -51,6 +68,7 @@ class FillRequestExtensionsTest { // Test val actual = fillRequest.getInlinePresentationSpecs( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) // Verify @@ -58,7 +76,23 @@ class FillRequestExtensionsTest { } @Test - fun `getMaxInlineSuggestionsCount should return 0 when pre-R`() { + fun `getMaxInlineSuggestionsCount should return 0 when disabled`() { + // Setup + val autofillAppInfo: AutofillAppInfo = mockk() + val expected = 0 + + // Test + val actual = fillRequest.getMaxInlineSuggestionsCount( + autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = false, + ) + + // Verify + assertEquals(expected, actual) + } + + @Test + fun `getMaxInlineSuggestionsCount should return 0 when enabled and pre-R`() { // Setup val autofillAppInfo = AutofillAppInfo( context = mockk(), @@ -70,6 +104,7 @@ class FillRequestExtensionsTest { // Test val actual = fillRequest.getMaxInlineSuggestionsCount( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) // Verify @@ -77,7 +112,7 @@ class FillRequestExtensionsTest { } @Test - fun `getMaxInlineSuggestionsCount should return the max count when post-R`() { + fun `getMaxInlineSuggestionsCount should return the max count when enabled and post-R`() { // Setup val autofillAppInfo = AutofillAppInfo( context = mockk(), @@ -89,6 +124,7 @@ class FillRequestExtensionsTest { // Test val actual = fillRequest.getMaxInlineSuggestionsCount( autofillAppInfo = autofillAppInfo, + isInlineAutofillEnabled = true, ) // Verify