Skip to content

Improve autocomplete behaviour #8669

Merged
aibrahim- merged 5 commits into
developfrom
aibrahim/bugfix/improve-autocomplete-behaviour
May 25, 2026
Merged

Improve autocomplete behaviour #8669
aibrahim- merged 5 commits into
developfrom
aibrahim/bugfix/improve-autocomplete-behaviour

Conversation

@aibrahim-
Copy link
Copy Markdown
Contributor

@aibrahim- aibrahim- commented May 22, 2026

Task/Issue URL: https://app.asana.com/1/137249556945/project/1214157224317277/task/1214964760144656?focus=true

Description

Three related fixes around the native-input widget's autocomplete:

  1. Avoid spurious "loading from scratch" on widget re-open with same text. showNativeInput dropped the onClearAutocomplete call that emptied searchResults and re-triggered a fetch; restores the suggestions list visibility from the adapter when the cache matches the prefill.

  2. Reveal the browser behind the closing widget earlier. hideNtp now runs at the start of hideNativeInput so the webpage is visible during the exit animation, instead of after the 200ms exit + 150ms fade leaves the NTP background filling the screen.

  3. Always keep an autocomplete cache for the omnibar's text so in-widget typing never permanently displaces the omnibar's results. Adds an always-on parallel pipeline in BrowserTabViewModel (omnibarAutocompleteCache) fed by onOmnibarTextChanged; the widget restores from it on close (non-navigation) and on open if the cache matches the prefill.

Steps to test this PR

  • enable native input widget
  • start a search by typing on the keyboard
  • observe autocomplete
  • going back should immediately display the web page.
  • lauching the keyboard again should immediately display the autocomplete list.

UI changes

Before After
before after

Note

Medium Risk
Adds a second autocomplete pipeline and changes native-input open/close timing; behavior is gated on the native-input setting and covered by new ViewModel tests, but autocomplete and tab lifecycle edge cases warrant manual QA.

Overview
Improves native-input omnibar autocomplete so reopening the keyboard can show prior suggestions without refetching, and closing the widget feels more like returning to the page you were on.

Browser tab / ViewModel: Adds an omnibarAutocompleteCache pipeline (debounced, IO-backed) driven by onOmnibarTextRendered when omnibar text is rendered—not user typing. It runs only when the native-input setting is on, skips full URLs and Duck Player URIs, and mirrors autocomplete settings. restoreOmnibarAutocomplete reapplies cached results into view state; the fragment also refreshes the suggestions adapter when the cache matches.

Native input manager: Wires restoreOmnibarAutocomplete on widget close (non-navigation) and before prefill on open; removes onClearAutocomplete on open and manually shows the list when cache restore succeeds. Calls hideNtp() at the start of hide in browser mode so the exit animation plays over the live page. Guards onEnterComplete with isAttachedToWindow to avoid raising the IME after detach.

Widget enter animation: beginEnterAnimationPreview(isBottom) applies per-tab NativeInputState (or a position-only fallback) before measure so toggle/layout does not snap mid-animation.

Tests: Coverage for restore/no-match, cache gating (setting off, autocomplete off, blank query), and telemetry state after restore.

Reviewed by Cursor Bugbot for commit 3cffed1. Bugbot is set up for automated code reviews on this repo. Configure here.

aibrahim- and others added 2 commits May 22, 2026 13:58
Three related fixes around the native-input widget's autocomplete:

1. Avoid spurious "loading from scratch" on widget re-open with same text.
   showNativeInput dropped the onClearAutocomplete call that emptied
   searchResults and re-triggered a fetch; restores the suggestions list
   visibility from the adapter when the cache matches the prefill.

2. Reveal the browser behind the closing widget earlier. hideNtp now runs
   at the start of hideNativeInput so the webpage is visible during the
   exit animation, instead of after the 200ms exit + 150ms fade leaves
   the NTP background filling the screen.

3. Always keep an autocomplete cache for the omnibar's text so in-widget
   typing never permanently displaces the omnibar's results. Adds an
   always-on parallel pipeline in BrowserTabViewModel (omnibarAutocompleteCache)
   fed by onOmnibarTextChanged; the widget restores from it on close
   (non-navigation) and on open if the cache matches the prefill.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dismiss the IME and cancel any in-flight enter animation when the hosting
tab is backgrounded so the keyboard does not leak onto the next tab, and
seed beginEnterAnimationPreview's state locally instead of reading from
NativeInputStateProvider — ViewScope injection isn't guaranteed to be in
place by the time it runs on a tab-switch attach.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@aibrahim- aibrahim- changed the title Aibrahim/bugfix/improve autocomplete behaviour Improve autocomplete behaviour May 22, 2026
Comment thread app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt
Comment thread app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fbc586b. Configure here.

Comment thread app/src/main/java/com/duckduckgo/app/browser/BrowserWebViewClient.kt Outdated
@aibrahim- aibrahim- force-pushed the aibrahim/bugfix/improve-autocomplete-behaviour branch from fbc586b to 16e361d Compare May 22, 2026 12:42
@aibrahim- aibrahim- force-pushed the aibrahim/bugfix/improve-autocomplete-behaviour branch from 16e361d to 3cffed1 Compare May 25, 2026 09:53
@YoussefKeyrouz YoussefKeyrouz self-assigned this May 25, 2026
flowOf(AutoCompleteResult("", emptyList()))
} else {
omnibarTextStateFlow
.debounce(300)
Copy link
Copy Markdown
Collaborator

@YoussefKeyrouz YoussefKeyrouz May 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not Blocking: I think we could gain more speed on cold start by immediately firing the first emission before debouncing. We already have a utility function for that that we are using in duckchat but will probably need to be move out or replicated. Will leave it to you to decide if you want to do that, but I don’t see a reason to delay the first fetch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only the cache, not the actual user query execution, so the effect of it is very minimal. For the user to feel it, they have to try to hit the cache within 300ms of starting a new search.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right. We can do a follow up for the actual query execution to speed up the first tap. but not part of this PR.

*/
fun restoreOmnibarAutocomplete(forQuery: String): AutoCompleteResult? {
val cached = omnibarAutocompleteCache.value
if (cached.query != forQuery || cached.suggestions.isEmpty()) return null
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the suggestions are actually empty(e.g. searching for random thing like zxcsdcz)? Shouldn't we still update autoCompleteViewState to reflect the empty result? Right now it would still hold the previous query data because we return early and never set it. Unless I’m missing something.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestions are never empty, at the very least you get 2 options one for duck.ai and one for executing the search on whatever keywoard you entered anyway.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m just worried that some settings might turn off those (we have too many settings to keep up) so I usually implement it with that in mind. But for this, it’s fine I don’t think we’ll get to that scenario.

if (cacheRestored) {
rootView.findViewById<RecyclerView?>(R.id.autoCompleteSuggestionsList)?.let { list ->
if ((list.adapter?.itemCount ?: 0) > 0) {
list.show()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the renderAutocomplete function manipulates the visibility of the list every time autoCompleteViewState changes. Calling show here would add another code path that controls visibility. It’s nit because I didn’t find a scenario currently where the 2 code paths disagree and might cause one to override the decision of the other, but it’s good to keep in mind if we can have a quick change to have only one owner control the visibility instead of 2 (maybe add a flag?). Not blocker just something to think about

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, it's cleaner to have one owner.

Copy link
Copy Markdown
Collaborator

@YoussefKeyrouz YoussefKeyrouz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Thanks!. Tested few scenarios and they worked as expected.

Left few nits that we can address later as follow ups.

@aibrahim- aibrahim- merged commit 9bb47ab into develop May 25, 2026
31 checks passed
@aibrahim- aibrahim- deleted the aibrahim/bugfix/improve-autocomplete-behaviour branch May 25, 2026 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants