@@ -37,6 +37,7 @@ import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggesti
3737import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySuggestion
3838import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteInAppMessageSuggestion
3939import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteSwitchToTabSuggestion
40+ import com.duckduckgo.browser.api.autocomplete.AutoCompleteFactory
4041import com.duckduckgo.browser.api.autocomplete.AutoCompleteSettings
4142import com.duckduckgo.browser.ui.omnibar.OmnibarPosition
4243import com.duckduckgo.common.utils.DispatcherProvider
@@ -55,7 +56,6 @@ import com.duckduckgo.voice.api.VoiceSearchAvailability
5556import kotlinx.coroutines.CoroutineScope
5657import kotlinx.coroutines.ExperimentalCoroutinesApi
5758import kotlinx.coroutines.FlowPreview
58- import kotlinx.coroutines.flow.Flow
5959import kotlinx.coroutines.flow.MutableSharedFlow
6060import kotlinx.coroutines.flow.MutableStateFlow
6161import kotlinx.coroutines.flow.SharingStarted
@@ -67,6 +67,7 @@ import kotlinx.coroutines.flow.flatMapLatest
6767import kotlinx.coroutines.flow.flow
6868import kotlinx.coroutines.flow.flowOn
6969import kotlinx.coroutines.flow.map
70+ import kotlinx.coroutines.flow.onEach
7071import kotlinx.coroutines.flow.stateIn
7172import kotlinx.coroutines.flow.update
7273import kotlinx.coroutines.launch
@@ -76,19 +77,13 @@ import logcat.asLog
7677import logcat.logcat
7778import javax.inject.Inject
7879
79- data class SystemSearchResult (
80- val autocomplete : AutoCompleteResult ,
81- val deviceApps : List <DeviceApp >,
82- )
83-
8480@ContributesViewModel(ActivityScope ::class )
8581class SystemSearchViewModel @Inject constructor(
8682 private val duckAiFeatureState : DuckAiFeatureState ,
8783 private val voiceSearchAvailability : VoiceSearchAvailability ,
8884 private val duckChat : DuckChat ,
8985 private var userStageStore : UserStageStore ,
90- private val autoComplete : AutoComplete ,
91- private val deviceAppLookup : DeviceAppLookup ,
86+ autoCompleteFactory : AutoCompleteFactory ,
9287 private val pixel : Pixel ,
9388 private val savedSitesRepository : SavedSitesRepository ,
9489 private val appSettingsPreferencesStore : SettingsDataStore ,
@@ -116,7 +111,6 @@ class SystemSearchViewModel @Inject constructor(
116111 sealed class Suggestions {
117112 data class SystemSearchResultsViewState (
118113 val autocompleteResults : AutoCompleteResult = AutoCompleteResult ("", emptyList()),
119- val appResults : List <DeviceApp > = emptyList(),
120114 ) : Suggestions()
121115
122116 data class QuickAccessItems (
@@ -155,7 +149,7 @@ class SystemSearchViewModel @Inject constructor(
155149 ) : Command()
156150
157151 data class LaunchDeviceApplication (
158- val deviceApp : DeviceApp ,
152+ val deviceAppSuggestion : AutoCompleteSuggestion . AutoCompleteDeviceAppSuggestion ,
159153 ) : Command()
160154
161155 data class ShowAppNotFoundMessage (
@@ -191,17 +185,33 @@ class SystemSearchViewModel @Inject constructor(
191185 private var hasUserSeenHistory = false
192186 private var omnibarPosition: OmnibarPosition = appSettingsPreferencesStore.omnibarPosition
193187
188+ private val autoComplete: AutoComplete = autoCompleteFactory.create(
189+ AutoComplete .Config (showInstalledApps = true ),
190+ )
191+
194192 @OptIn(FlowPreview ::class , ExperimentalCoroutinesApi ::class )
195193 val suggestionsViewState =
196194 combine(queryFlow, refreshTrigger) { query, _ -> query.trim() }
197195 .debounce(DEBOUNCE_TIME_MS )
198196 .distinctUntilChanged()
199197 .flatMapLatest { query ->
200- buildResultsFlow (query)
198+ autoComplete.autoComplete (query)
201199 }.flowOn(dispatchers.io())
202200 .catch { t: Throwable ? -> logcat(WARN ) { " Failed to get search results: ${t?.asLog()} " } }
203- .map { results ->
204- updateResults(results)
201+ .onEach { results ->
202+ if (results.suggestions.contains(AutoCompleteInAppMessageSuggestion )) {
203+ hasUserSeenHistory = true
204+ }
205+ }
206+ .map {
207+ val result = it.copy(
208+ suggestions = if (isSearchOnly.value) {
209+ it.suggestions.filterNot { suggestion -> suggestion is AutoCompleteSuggestion .AutoCompleteDuckAIPrompt }
210+ } else {
211+ it.suggestions
212+ },
213+ )
214+ Suggestions .SystemSearchResultsViewState (autocompleteResults = result)
205215 }.stateIn(viewModelScope, SharingStarted .Lazily , Suggestions .SystemSearchResultsViewState ())
206216
207217 val favoritesViewState =
@@ -235,7 +245,6 @@ class SystemSearchViewModel @Inject constructor(
235245
236246 init {
237247 resetViewState()
238- refreshAppList()
239248 }
240249
241250 fun setLaunchedFromSearchOnlyWidget (launchedFromSearchOnlyWidget : Boolean ) {
@@ -264,17 +273,6 @@ class SystemSearchViewModel @Inject constructor(
264273 }
265274 }
266275
267- private fun buildResultsFlow (query : String ): Flow <SystemSearchResult > =
268- combine(
269- autoComplete.autoComplete(query),
270- flow { emit(deviceAppLookup.query(query)) },
271- ) { autocompleteResult, appsResult ->
272- if (autocompleteResult.suggestions.contains(AutoCompleteInAppMessageSuggestion )) {
273- hasUserSeenHistory = true
274- }
275- SystemSearchResult (autocompleteResult, appsResult)
276- }
277-
278276 fun onOmnibarConfigured (position : OmnibarPosition ) {
279277 omnibarPosition = position
280278 }
@@ -323,19 +321,6 @@ class SystemSearchViewModel @Inject constructor(
323321 }
324322 }
325323
326- private fun updateResults (results : SystemSearchResult ): Suggestions .SystemSearchResultsViewState {
327- val suggestions = results.autocomplete.suggestions
328- val appResults = results.deviceApps
329- val hasMultiResults = suggestions.isNotEmpty() && appResults.isNotEmpty()
330-
331- val updatedSuggestions = if (hasMultiResults) suggestions.take(RESULTS_MAX_RESULTS_PER_GROUP ) else suggestions
332- val updatedApps = if (hasMultiResults) appResults.take(RESULTS_MAX_RESULTS_PER_GROUP ) else appResults
333- return Suggestions .SystemSearchResultsViewState (
334- autocompleteResults = AutoCompleteResult (results.autocomplete.query, updatedSuggestions),
335- appResults = updatedApps,
336- )
337- }
338-
339324 private fun inputCleared () {
340325 queryFlow.update { " " }
341326 }
@@ -369,15 +354,20 @@ class SystemSearchViewModel @Inject constructor(
369354 when (suggestion) {
370355 is AutoCompleteSwitchToTabSuggestion -> {
371356 command.value = Command .LaunchBrowserAndSwitchToTab (suggestion.phrase, suggestion.tabId)
357+ pixel.fire(INTERSTITIAL_LAUNCH_BROWSER_QUERY )
372358 }
373359 is AutoCompleteSuggestion .AutoCompleteDuckAIPrompt -> {
374360 onDuckAiRequested(suggestion.phrase)
375361 }
362+ is AutoCompleteSuggestion .AutoCompleteDeviceAppSuggestion -> {
363+ command.value = Command .LaunchDeviceApplication (deviceAppSuggestion = suggestion)
364+ pixel.fire(INTERSTITIAL_LAUNCH_DEVICE_APP )
365+ }
376366 else -> {
377367 command.value = Command .LaunchBrowser (suggestion.phrase)
368+ pixel.fire(INTERSTITIAL_LAUNCH_BROWSER_QUERY )
378369 }
379370 }
380- pixel.fire(INTERSTITIAL_LAUNCH_BROWSER_QUERY )
381371 }
382372
383373 fun userLongPressedAutocomplete (suggestion : AutoCompleteSuggestion ) {
@@ -418,21 +408,8 @@ class SystemSearchViewModel @Inject constructor(
418408 }
419409 }
420410
421- fun userSelectedApp (app : DeviceApp ) {
422- command.value = Command .LaunchDeviceApplication (app)
423- pixel.fire(INTERSTITIAL_LAUNCH_DEVICE_APP )
424- }
425-
426- fun appNotFound (app : DeviceApp ) {
427- command.value = Command .ShowAppNotFoundMessage (app.shortName)
428-
429- refreshAppList()
430- }
431-
432- private fun refreshAppList () {
433- viewModelScope.launch(dispatchers.io()) {
434- deviceAppLookup.refreshAppList()
435- }
411+ fun appNotFound (deviceAppSuggestion : AutoCompleteSuggestion .AutoCompleteDeviceAppSuggestion ) {
412+ command.value = Command .ShowAppNotFoundMessage (deviceAppSuggestion.shortName)
436413 }
437414
438415 fun onQuickAccessListChanged (newList : List <FavoritesQuickAccessAdapter .QuickAccessFavorite >) {
@@ -462,7 +439,6 @@ class SystemSearchViewModel @Inject constructor(
462439
463440 companion object {
464441 private const val DEBOUNCE_TIME_MS = 200L
465- private const val RESULTS_MAX_RESULTS_PER_GROUP = 4
466442 }
467443
468444 override fun onFavouriteEdited (favorite : Favorite ) {
0 commit comments