Skip to content

Commit

Permalink
For mozilla-mobile#26329 Refactor the search widget to use the A-C co…
Browse files Browse the repository at this point in the history
…mmon behaviour
  • Loading branch information
iorgamgabriel committed Aug 8, 2022
1 parent 1a2b357 commit 8a016cd
Show file tree
Hide file tree
Showing 18 changed files with 49 additions and 805 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import mozilla.components.browser.state.search.SearchEngine
import mozilla.components.browser.state.state.selectedOrDefaultSearchEngine
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.feature.search.ext.waitForSelectedOrDefaultSearchEngine
import mozilla.components.feature.search.widget.BaseVoiceSearchActivity.Companion.SPEECH_PROCESSING
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.components.metrics.MetricsUtils
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.widget.VoiceSearchActivity.Companion.SPEECH_PROCESSING

/**
* The search widget has a microphone button to let users search with their voice.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
Expand Down Expand Up @@ -88,7 +87,6 @@ import org.mozilla.fenix.search.toolbar.SearchSelectorMenu
import org.mozilla.fenix.search.toolbar.SearchSelectorToolbarAction
import org.mozilla.fenix.search.toolbar.ToolbarView
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.widget.VoiceSearchActivity

typealias SearchDialogFragmentStore = SearchFragmentStore

Expand Down Expand Up @@ -549,7 +547,7 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
}

override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
if (requestCode == VoiceSearchActivity.SPEECH_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
if (requestCode == SPEECH_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
intent?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)?.first()?.also {
toolbarView.view.edit.updateUrl(url = it, shouldHighlight = true)
interactor.onTextChanged(it)
Expand Down Expand Up @@ -790,7 +788,7 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
putExtra(RecognizerIntent.EXTRA_PROMPT, requireContext().getString(R.string.voice_search_explainer))
}
startActivityForResult(speechIntent, VoiceSearchActivity.SPEECH_REQUEST_CODE)
startActivityForResult(speechIntent, SPEECH_REQUEST_CODE)
}

private fun updateQrButton(searchFragmentState: SearchFragmentState) {
Expand Down Expand Up @@ -904,6 +902,7 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {

companion object {
private const val TAP_INCREASE_DPS = 8
const val SPEECH_REQUEST_CODE = 0
private const val QR_FRAGMENT_TAG = "MOZAC_QR_FRAGMENT"
private const val REQUEST_CODE_CAMERA_PERMISSIONS = 1
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.preference.CheckBoxPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import mozilla.components.feature.search.widget.AppSearchWidgetProvider.Companion.updateAllWidgets
import mozilla.components.support.ktx.android.view.hideKeyboard
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.getPreferenceKey
Expand Down Expand Up @@ -90,7 +91,7 @@ class SearchEngineFragment : PreferenceFragmentCompat() {
requireContext().settings().preferences.edit {
putBoolean(preference.key, newBooleanValue)
}
SearchWidgetProvider.updateAllWidgets(requireContext())
updateAllWidgets(requireContext(), SearchWidgetProvider::class.java)
return true
}
}
Expand Down
110 changes: 15 additions & 95 deletions app/src/main/java/org/mozilla/fenix/widget/VoiceSearchActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,118 +4,38 @@

package org.mozilla.fenix.widget

import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
import android.os.StrictMode
import android.speech.RecognizerIntent
import androidx.appcompat.app.AppCompatActivity
import mozilla.components.feature.search.widget.BaseVoiceSearchActivity
import mozilla.components.support.locale.LocaleManager
import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.fenix.GleanMetrics.SearchWidget
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.IntentReceiverActivity
import org.mozilla.fenix.ext.components
import java.util.Locale

/**
* Launches voice recognition then uses it to start a new web search.
* Implementation of voice search that is needed in search widget
*/
class VoiceSearchActivity : AppCompatActivity() {
class VoiceSearchActivity : BaseVoiceSearchActivity() {

/**
* Holds the intent that initially started this activity
* so that it can persist through the speech activity.
*/
private var previousIntent: Intent? = null

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(PREVIOUS_INTENT, previousIntent)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

if (Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).resolveActivity(packageManager) == null) {
finish()
return
}

// Retrieve the previous intent from the saved state
previousIntent = savedInstanceState?.get(PREVIOUS_INTENT) as Intent?
if (previousIntent.isForSpeechProcessing()) {
// Don't reopen the speech recognizer
return
}

// The intent property is nullable, but the rest of the code below assumes it is not.
val intent = intent?.let { Intent(intent) } ?: Intent()

if (intent.isForSpeechProcessing()) {
previousIntent = intent
displaySpeechRecognizer()
} else {
finish()
override fun getCurrentLocale(): Locale {
val locale = components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
LocaleManager.getCurrentLocale(this@VoiceSearchActivity)
?: LocaleManager.getSystemDefault()
}
return locale
}

/**
* Displays a speech recognizer popup that listens for input from the user.
*/
@Suppress("DEPRECATION")
// https://github.com/mozilla-mobile/fenix/issues/19919
private fun displaySpeechRecognizer() {
val intentSpeech = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
putExtra(
RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
)
putExtra(
RecognizerIntent.EXTRA_LANGUAGE,
components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
LocaleManager.getCurrentLocale(this@VoiceSearchActivity)
}
)
}
override fun recordVoiceButtonTelemetry() {
SearchWidget.voiceButton.record(NoExtras())

startActivityForResult(intentSpeech, SPEECH_REQUEST_CODE)
}

@Suppress("DEPRECATION")
// https://github.com/mozilla-mobile/fenix/issues/19919
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

if (requestCode == SPEECH_REQUEST_CODE && resultCode == RESULT_OK) {
val spokenText = data?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)?.first()
val context = this

previousIntent?.apply {
component = ComponentName(context, IntentReceiverActivity::class.java)
putExtra(SPEECH_PROCESSING, spokenText)
putExtra(HomeActivity.OPEN_TO_BROWSER_AND_LOAD, true)
startActivity(this)
}
}

finish()
}

/**
* Returns true if the [SPEECH_PROCESSING] extra is present and set to true.
* Returns false if the intent is null.
*/
private fun Intent?.isForSpeechProcessing(): Boolean =
this?.getBooleanExtra(SPEECH_PROCESSING, false) == true

companion object {
internal const val SPEECH_REQUEST_CODE = 0
internal const val PREVIOUS_INTENT = "org.mozilla.fenix.previous_intent"
/**
* In [VoiceSearchActivity] activity, used to store if the speech processing should start.
* In [IntentReceiverActivity] activity, used to store the search terms.
*/
const val SPEECH_PROCESSING = "speech_processing"
override fun startIntentAfterVoiceSearch(spokenText: String?) {
val intent = Intent(this, IntentReceiverActivity::class.java)
intent.putExtra(SPEECH_PROCESSING, spokenText)
intent.putExtra(HomeActivity.OPEN_TO_BROWSER_AND_LOAD, true)
startActivity(intent)
}
}
Loading

0 comments on commit 8a016cd

Please sign in to comment.