From f760deff65d4028660415e50f6b7bccb7736dca4 Mon Sep 17 00:00:00 2001 From: Craig Russell <1336281+CDRussell@users.noreply.github.com> Date: Wed, 9 Jul 2025 12:47:12 +0100 Subject: [PATCH] Create dialog inside autofill-impl instead of BTF being responsible for it --- .../app/browser/BrowserTabFragment.kt | 23 +++++++++++-------- .../autofill/api/AutofillCredentialDialogs.kt | 5 ---- .../autofill/api/BrowserAutofill.kt | 19 ++++++++++++--- .../impl/AutofillJavascriptInterface.kt | 13 +++++++++-- .../impl/importing/InBrowserImportPromo.kt | 16 +++++++++++++ .../ImportGooglePasswordsWebFlowFragment.kt | 2 +- .../CredentialAutofillDialogAndroidFactory.kt | 6 ----- ...tofillStoredBackJavascriptInterfaceTest.kt | 2 +- .../impl/InlineBrowserAutofillTest.kt | 2 +- 9 files changed, 60 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index d635416d479d..f64102de12c5 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -210,9 +210,10 @@ import com.duckduckgo.autoconsent.api.AutoconsentCallback import com.duckduckgo.autofill.api.AutofillCapabilityChecker import com.duckduckgo.autofill.api.AutofillEventListener import com.duckduckgo.autofill.api.AutofillFragmentResultsPlugin -import com.duckduckgo.autofill.api.AutofillImportLaunchSource.InBrowserPromo import com.duckduckgo.autofill.api.AutofillPrompt import com.duckduckgo.autofill.api.AutofillPrompt.ImportPasswords +import com.duckduckgo.autofill.api.AutofillPrompt.UrlMatchType.AnyUrl +import com.duckduckgo.autofill.api.AutofillPrompt.UrlMatchType.ExactUrl import com.duckduckgo.autofill.api.AutofillScreenLaunchSource import com.duckduckgo.autofill.api.AutofillScreens.AutofillPasswordsManagementScreenWithSuggestions import com.duckduckgo.autofill.api.AutofillScreens.AutofillPasswordsManagementViewCredential @@ -764,18 +765,22 @@ class BrowserTabFragment : viewModel.onShowUserCredentialsSaved(savedCredentials) } - override suspend fun promptUserTo(event: AutofillPrompt) { + override suspend fun showAutofillPrompt(event: AutofillPrompt) { + logcat { "showAutofillPrompt called: ${event.javaClass.simpleName}" } + + // some prompts requiring the guard that we're still on the same URL before showing the dialog + val requiredUrl = when (val matchType = event.urlMatchType) { + is AnyUrl -> null + is ExactUrl -> matchType.requiredUrl + } + withContext(dispatchers.main()) { when (event) { is ImportPasswords -> { showDialogHidingPrevious( - credentialAutofillDialogFactory.autofillImportPasswordsPromoDialog( - importSource = InBrowserPromo, - tabId = tabId, - url = event.currentUrl, - ), - AUTOFILL_DIALOG_TAB, - event.currentUrl, + dialog = event.prompt, + tag = AUTOFILL_DIALOG_TAB, + requiredUrl = requiredUrl, ) } } diff --git a/autofill/autofill-api/src/main/java/com/duckduckgo/autofill/api/AutofillCredentialDialogs.kt b/autofill/autofill-api/src/main/java/com/duckduckgo/autofill/api/AutofillCredentialDialogs.kt index 4bf2e3b3fe5a..5c48a402777a 100644 --- a/autofill/autofill-api/src/main/java/com/duckduckgo/autofill/api/AutofillCredentialDialogs.kt +++ b/autofill/autofill-api/src/main/java/com/duckduckgo/autofill/api/AutofillCredentialDialogs.kt @@ -249,11 +249,6 @@ interface CredentialAutofillDialogFactory { * Creates a dialog which prompts the user to sign up for Email Protection */ fun emailProtectionInContextSignUpDialog(tabId: String): DialogFragment - - /** - * Creates a dialog which prompts the user to import passwords from Google Passwords - */ - fun autofillImportPasswordsPromoDialog(importSource: AutofillImportLaunchSource, tabId: String, url: String): DialogFragment } private fun prefix( diff --git a/autofill/autofill-api/src/main/java/com/duckduckgo/autofill/api/BrowserAutofill.kt b/autofill/autofill-api/src/main/java/com/duckduckgo/autofill/api/BrowserAutofill.kt index 425c93753f72..3ccaacd6520b 100644 --- a/autofill/autofill-api/src/main/java/com/duckduckgo/autofill/api/BrowserAutofill.kt +++ b/autofill/autofill-api/src/main/java/com/duckduckgo/autofill/api/BrowserAutofill.kt @@ -17,6 +17,7 @@ package com.duckduckgo.autofill.api import android.webkit.WebView +import androidx.fragment.app.DialogFragment import com.duckduckgo.autofill.api.domain.app.LoginCredentials import com.duckduckgo.autofill.api.domain.app.LoginTriggerType @@ -159,9 +160,21 @@ interface Callback { */ fun onCredentialsSaved(savedCredentials: LoginCredentials) - suspend fun promptUserTo(event: AutofillPrompt) + /** + * Called when the user should be shown an autofill prompt + */ + suspend fun showAutofillPrompt(event: AutofillPrompt) } -sealed interface AutofillPrompt { - data class ImportPasswords(val currentUrl: String) : AutofillPrompt +sealed class AutofillPrompt(open val urlMatchType: UrlMatchType) { + + data class ImportPasswords( + val prompt: DialogFragment, + override val urlMatchType: UrlMatchType, + ) : AutofillPrompt(urlMatchType) + + sealed interface UrlMatchType { + data object AnyUrl : UrlMatchType + data class ExactUrl(val requiredUrl: String) : UrlMatchType + } } diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/AutofillJavascriptInterface.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/AutofillJavascriptInterface.kt index 23f8141b27bb..09950f2df533 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/AutofillJavascriptInterface.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/AutofillJavascriptInterface.kt @@ -20,7 +20,9 @@ import android.webkit.JavascriptInterface import android.webkit.WebView import com.duckduckgo.app.di.AppCoroutineScope import com.duckduckgo.autofill.api.AutofillCapabilityChecker +import com.duckduckgo.autofill.api.AutofillImportLaunchSource.InBrowserPromo import com.duckduckgo.autofill.api.AutofillPrompt.ImportPasswords +import com.duckduckgo.autofill.api.AutofillPrompt.UrlMatchType.ExactUrl import com.duckduckgo.autofill.api.Callback import com.duckduckgo.autofill.api.CredentialUpdateExistingCredentialsDialog.CredentialUpdateType import com.duckduckgo.autofill.api.EmailProtectionInContextSignupFlowListener @@ -196,8 +198,15 @@ class AutofillStoredBackJavascriptInterface @Inject constructor( } private fun handlePromoteImport(url: String) { - coroutineScope.launch { - callback?.promptUserTo(ImportPasswords(url)) + tabId?.let { + coroutineScope.launch(dispatcherProvider.io()) { + val dialog = inBrowserImportPromo.autofillImportPasswordsPromoDialog( + importSource = InBrowserPromo, + tabId = it, + url = url, + ) + callback?.showAutofillPrompt(ImportPasswords(dialog, ExactUrl(url))) + } } } diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/InBrowserImportPromo.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/InBrowserImportPromo.kt index edad31949c51..e26854b8b503 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/InBrowserImportPromo.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/InBrowserImportPromo.kt @@ -16,9 +16,12 @@ package com.duckduckgo.autofill.impl.importing +import androidx.fragment.app.DialogFragment import com.duckduckgo.autofill.api.AutofillFeature +import com.duckduckgo.autofill.api.AutofillImportLaunchSource import com.duckduckgo.autofill.impl.store.InternalAutofillStore import com.duckduckgo.autofill.impl.store.NeverSavedSiteRepository +import com.duckduckgo.autofill.impl.ui.credential.management.importpassword.google.ImportFromGooglePasswordsDialog import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.AppScope import com.squareup.anvil.annotations.ContributesBinding @@ -31,6 +34,11 @@ interface InBrowserImportPromo { credentialsAvailableForCurrentPage: Boolean, url: String?, ): Boolean + + /** + * Creates a dialog which prompts the user to import passwords from Google Passwords + */ + fun autofillImportPasswordsPromoDialog(importSource: AutofillImportLaunchSource, tabId: String, url: String): DialogFragment } @ContributesBinding(AppScope::class) @@ -78,6 +86,14 @@ class RealInBrowserImportPromo @Inject constructor( } } + override fun autofillImportPasswordsPromoDialog( + importSource: AutofillImportLaunchSource, + tabId: String, + url: String, + ): DialogFragment { + return ImportFromGooglePasswordsDialog.instance(importSource = importSource, tabId = tabId, originalUrl = url) + } + companion object { const val MAX_PROMO_SHOWN_COUNT = 5 const val MAX_CREDENTIALS_FOR_PROMO = 25 diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/gpm/webflow/ImportGooglePasswordsWebFlowFragment.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/gpm/webflow/ImportGooglePasswordsWebFlowFragment.kt index 5fdf45505297..f6e95375bbb7 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/gpm/webflow/ImportGooglePasswordsWebFlowFragment.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/importing/gpm/webflow/ImportGooglePasswordsWebFlowFragment.kt @@ -325,7 +325,7 @@ class ImportGooglePasswordsWebFlowFragment : } } - override suspend fun promptUserTo(event: AutofillPrompt) { + override suspend fun showAutofillPrompt(event: AutofillPrompt) { // no-op, we don't prompt the user for anything in this flow } diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/ui/CredentialAutofillDialogAndroidFactory.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/ui/CredentialAutofillDialogAndroidFactory.kt index 099f144be784..badc6a00573c 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/ui/CredentialAutofillDialogAndroidFactory.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/ui/CredentialAutofillDialogAndroidFactory.kt @@ -17,14 +17,12 @@ package com.duckduckgo.autofill.impl.ui import androidx.fragment.app.DialogFragment -import com.duckduckgo.autofill.api.AutofillImportLaunchSource import com.duckduckgo.autofill.api.CredentialAutofillDialogFactory import com.duckduckgo.autofill.api.CredentialUpdateExistingCredentialsDialog.CredentialUpdateType import com.duckduckgo.autofill.api.domain.app.LoginCredentials import com.duckduckgo.autofill.api.domain.app.LoginTriggerType import com.duckduckgo.autofill.impl.email.EmailProtectionChooseEmailFragment import com.duckduckgo.autofill.impl.email.incontext.prompt.EmailProtectionInContextSignUpPromptFragment -import com.duckduckgo.autofill.impl.ui.credential.management.importpassword.google.ImportFromGooglePasswordsDialog import com.duckduckgo.autofill.impl.ui.credential.passwordgeneration.AutofillUseGeneratedPasswordDialogFragment import com.duckduckgo.autofill.impl.ui.credential.saving.AutofillSavingCredentialsDialogFragment import com.duckduckgo.autofill.impl.ui.credential.selecting.AutofillSelectCredentialsDialogFragment @@ -103,8 +101,4 @@ class CredentialAutofillDialogAndroidFactory @Inject constructor() : CredentialA override fun emailProtectionInContextSignUpDialog(tabId: String): DialogFragment { return EmailProtectionInContextSignUpPromptFragment.instance(tabId) } - - override fun autofillImportPasswordsPromoDialog(importSource: AutofillImportLaunchSource, tabId: String, url: String): DialogFragment { - return ImportFromGooglePasswordsDialog.instance(importSource = importSource, tabId = tabId, originalUrl = url) - } } diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/AutofillStoredBackJavascriptInterfaceTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/AutofillStoredBackJavascriptInterfaceTest.kt index 00056fa9a9d2..2a9e72b6bcc0 100644 --- a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/AutofillStoredBackJavascriptInterfaceTest.kt +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/AutofillStoredBackJavascriptInterfaceTest.kt @@ -614,7 +614,7 @@ class AutofillStoredBackJavascriptInterfaceTest { onCredentialsSavedCalled = true } - override suspend fun promptUserTo(event: AutofillPrompt) { + override suspend fun showAutofillDialgo(event: AutofillPrompt) { } } diff --git a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/InlineBrowserAutofillTest.kt b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/InlineBrowserAutofillTest.kt index d78764873bfb..9b78da2a0526 100644 --- a/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/InlineBrowserAutofillTest.kt +++ b/autofill/autofill-impl/src/test/java/com/duckduckgo/autofill/impl/InlineBrowserAutofillTest.kt @@ -73,7 +73,7 @@ class InlineBrowserAutofillTest { override fun onCredentialsSaved(savedCredentials: LoginCredentials) { } - override suspend fun promptUserTo(event: AutofillPrompt) { + override suspend fun showAutofillDialgo(event: AutofillPrompt) { } }