Revision control

Copy as Markdown

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.focus.searchwidget
import android.content.Context
import android.os.Bundle
import androidx.annotation.VisibleForTesting
import mozilla.components.browser.state.state.SessionState
import mozilla.components.feature.search.widget.BaseVoiceSearchActivity
import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.focus.GleanMetrics.SearchWidget
import org.mozilla.focus.activity.IntentReceiverActivity
import org.mozilla.focus.ext.components
import org.mozilla.focus.ext.settings
import org.mozilla.focus.perf.Performance
import org.mozilla.focus.state.AppAction
import org.mozilla.focus.utils.SearchUtils
/**
* Handles all actions from outside the app that starts it.
*/
object ExternalIntentNavigation {
/**
* Handle all the navigation from search widget, app icon, lockscreen and custom tab.
*
* @param bundle External Intent [Bundle] with data based on which destination will be computed.
* @param context Android [Context] used for system interactions or accessing dependencies.
*/
fun handleAppNavigation(
bundle: Bundle?,
context: Context,
) {
if (handleWidgetVoiceSearch(bundle, context)) return
if (handleWidgetTextSearch(bundle, context)) return
if (handleBrowserTabAlreadyOpen(context)) return
handleAppOpened(bundle, context)
}
/**
* Handle the app being opened with no specified destination.
* This can show the onboarding or the app's home screen.
*/
@VisibleForTesting
internal fun handleAppOpened(
bundle: Bundle?,
context: Context,
) {
if (context.settings.isFirstRun &&
!Performance.processIntentIfPerformanceTest(bundle, context)
) {
context.components.appStore.dispatch(AppAction.ShowFirstRun)
}
}
/**
* Try navigating to the currently selected tab.
*
* @return Whether or not the app navigated to the currently selected tab.
*/
@VisibleForTesting
internal fun handleBrowserTabAlreadyOpen(context: Context): Boolean {
val tabId = context.components.store.state.selectedTabId
return when (tabId != null) {
true -> {
context.components.appStore.dispatch(AppAction.OpenTab(tabId))
true
}
else -> false
}
}
/**
* Try navigating to search by text.
*
* @return Whether or not a widget interaction was detected and the app opened the search screen for it.
*/
@VisibleForTesting
internal fun handleWidgetTextSearch(bundle: Bundle?, context: Context): Boolean {
val searchWidgetIntent = bundle?.getBoolean(IntentReceiverActivity.SEARCH_WIDGET_EXTRA, false)
return when (searchWidgetIntent == true) {
true -> {
SearchWidget.newTabButton.record(NoExtras())
context.components.appStore.dispatch(AppAction.ShowHomeScreen)
true
}
else -> false
}
}
/**
* Try opening a new tab for the user voice search.
*
* @return Whether or not a voice search was identified and the app opened a new tab for it.
*/
@VisibleForTesting
internal fun handleWidgetVoiceSearch(bundle: Bundle?, context: Context): Boolean {
val voiceSearchText = bundle?.getString(BaseVoiceSearchActivity.SPEECH_PROCESSING)
return when (!voiceSearchText.isNullOrEmpty()) {
true -> {
val tabId = context.components.tabsUseCases.addTab(
url = SearchUtils.createSearchUrl(
context,
voiceSearchText,
),
source = SessionState.Source.External.ActionSend(null),
searchTerms = voiceSearchText,
selectTab = true,
private = true,
)
context.components.appStore.dispatch(AppAction.OpenTab(tabId))
true
}
else -> false
}
}
}