For #1216: Add voice search
parent
c0e4056c16
commit
011a9e7d96
|
@ -33,7 +33,6 @@ import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.session.SessionManager
|
import mozilla.components.browser.session.SessionManager
|
||||||
import mozilla.components.browser.session.runWithSessionIdOrSelected
|
import mozilla.components.browser.session.runWithSessionIdOrSelected
|
||||||
import mozilla.components.concept.engine.prompt.ShareData
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import mozilla.components.feature.accounts.FxaCapability
|
|
||||||
import mozilla.components.feature.accounts.FxaWebChannelFeature
|
import mozilla.components.feature.accounts.FxaWebChannelFeature
|
||||||
import mozilla.components.feature.app.links.AppLinksFeature
|
import mozilla.components.feature.app.links.AppLinksFeature
|
||||||
import mozilla.components.feature.contextmenu.ContextMenuCandidate
|
import mozilla.components.feature.contextmenu.ContextMenuCandidate
|
||||||
|
@ -482,7 +481,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||||
view.swipeRefresh.setOnChildScrollUpCallback { _, _ -> true }
|
view.swipeRefresh.setOnChildScrollUpCallback { _, _ -> true }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ConstantConditionIf")
|
// @Suppress("ConstantConditionIf")
|
||||||
|
/*
|
||||||
if (!FeatureFlags.asFeatureWebChannelsDisabled) {
|
if (!FeatureFlags.asFeatureWebChannelsDisabled) {
|
||||||
webchannelIntegration.set(
|
webchannelIntegration.set(
|
||||||
feature = FxaWebChannelFeature(
|
feature = FxaWebChannelFeature(
|
||||||
|
@ -499,6 +499,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
initializeEngineView(toolbarHeight)
|
initializeEngineView(toolbarHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,22 @@
|
||||||
package org.mozilla.fenix.search
|
package org.mozilla.fenix.search
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
import android.app.Activity.RESULT_OK
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
import android.graphics.Typeface.BOLD
|
import android.graphics.Typeface.BOLD
|
||||||
import android.graphics.Typeface.ITALIC
|
import android.graphics.Typeface.ITALIC
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.speech.RecognizerIntent
|
||||||
|
import android.speech.RecognizerIntent.EXTRA_RESULTS
|
||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewStub
|
import android.view.ViewStub
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -25,6 +30,7 @@ import kotlinx.android.synthetic.main.fragment_search.*
|
||||||
import kotlinx.android.synthetic.main.fragment_search.view.*
|
import kotlinx.android.synthetic.main.fragment_search.view.*
|
||||||
import kotlinx.android.synthetic.main.search_suggestions_onboarding.view.*
|
import kotlinx.android.synthetic.main.search_suggestions_onboarding.view.*
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import mozilla.components.browser.toolbar.BrowserToolbar
|
||||||
import mozilla.components.concept.storage.HistoryStorage
|
import mozilla.components.concept.storage.HistoryStorage
|
||||||
import mozilla.components.feature.qr.QrFeature
|
import mozilla.components.feature.qr.QrFeature
|
||||||
import mozilla.components.lib.state.ext.consumeFrom
|
import mozilla.components.lib.state.ext.consumeFrom
|
||||||
|
@ -47,6 +53,7 @@ import org.mozilla.fenix.ext.settings
|
||||||
import org.mozilla.fenix.search.awesomebar.AwesomeBarView
|
import org.mozilla.fenix.search.awesomebar.AwesomeBarView
|
||||||
import org.mozilla.fenix.search.toolbar.ToolbarView
|
import org.mozilla.fenix.search.toolbar.ToolbarView
|
||||||
import org.mozilla.fenix.settings.SupportUtils
|
import org.mozilla.fenix.settings.SupportUtils
|
||||||
|
import org.mozilla.fenix.widget.VoiceSearchActivity.Companion.SPEECH_REQUEST_CODE
|
||||||
|
|
||||||
@Suppress("TooManyFunctions", "LargeClass")
|
@Suppress("TooManyFunctions", "LargeClass")
|
||||||
class SearchFragment : Fragment(), UserInteractionHandler {
|
class SearchFragment : Fragment(), UserInteractionHandler {
|
||||||
|
@ -127,6 +134,14 @@ class SearchFragment : Fragment(), UserInteractionHandler {
|
||||||
requireComponents.core.engine
|
requireComponents.core.engine
|
||||||
)
|
)
|
||||||
|
|
||||||
|
toolbarView.view.addEditAction(
|
||||||
|
BrowserToolbar.Button(
|
||||||
|
ContextCompat.getDrawable(requireContext(), R.drawable.ic_microphone)!!,
|
||||||
|
requireContext().getString(R.string.voice_search_content_description),
|
||||||
|
visible = { requireContext().settings().shouldShowVoiceSearch },
|
||||||
|
listener = ::launchVoiceSearch
|
||||||
|
)
|
||||||
|
)
|
||||||
val urlView = toolbarView.view
|
val urlView = toolbarView.view
|
||||||
.findViewById<InlineAutocompleteEditText>(R.id.mozac_browser_toolbar_edit_url_view)
|
.findViewById<InlineAutocompleteEditText>(R.id.mozac_browser_toolbar_edit_url_view)
|
||||||
urlView?.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
urlView?.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
||||||
|
@ -136,6 +151,14 @@ class SearchFragment : Fragment(), UserInteractionHandler {
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun launchVoiceSearch() {
|
||||||
|
val speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
|
||||||
|
putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
|
||||||
|
putExtra(RecognizerIntent.EXTRA_PROMPT, requireContext().getString(R.string.voice_search_explainer))
|
||||||
|
}
|
||||||
|
startActivityForResult(speechIntent, SPEECH_REQUEST_CODE)
|
||||||
|
}
|
||||||
|
|
||||||
private fun clearToolbarFocus() {
|
private fun clearToolbarFocus() {
|
||||||
toolbarView.view.clearFocus()
|
toolbarView.view.clearFocus()
|
||||||
}
|
}
|
||||||
|
@ -274,7 +297,7 @@ class SearchFragment : Fragment(), UserInteractionHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!permissionDidUpdate) {
|
if (!permissionDidUpdate) {
|
||||||
toolbarView.view.requestFocus()
|
toolbarView.view.edit.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateClipboardSuggestion(
|
updateClipboardSuggestion(
|
||||||
|
@ -286,6 +309,16 @@ class SearchFragment : Fragment(), UserInteractionHandler {
|
||||||
hideToolbar()
|
hideToolbar()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
||||||
|
if (requestCode == 0 && resultCode == RESULT_OK) {
|
||||||
|
intent?.getStringArrayListExtra(EXTRA_RESULTS)?.first()?.also {
|
||||||
|
toolbarView.view.edit.updateUrl(url = it, shouldHighlight = true)
|
||||||
|
searchInteractor.onTextChanged(it)
|
||||||
|
toolbarView.view.edit.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
toolbarView.view.clearFocus()
|
toolbarView.view.clearFocus()
|
||||||
|
@ -295,6 +328,7 @@ class SearchFragment : Fragment(), UserInteractionHandler {
|
||||||
// Note: Actual navigation happens in `handleEditingCancelled` in SearchController
|
// Note: Actual navigation happens in `handleEditingCancelled` in SearchController
|
||||||
return when {
|
return when {
|
||||||
qrFeature.onBackPressed() -> {
|
qrFeature.onBackPressed() -> {
|
||||||
|
toolbarView.view.edit.focus()
|
||||||
view?.search_scan_button?.isChecked = false
|
view?.search_scan_button?.isChecked = false
|
||||||
toolbarView.view.requestFocus()
|
toolbarView.view.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,11 @@ class SearchEngineFragment : PreferenceFragmentCompat() {
|
||||||
val searchEngineListPreference =
|
val searchEngineListPreference =
|
||||||
findPreference<SearchEngineListPreference>(getPreferenceKey(R.string.pref_key_search_engine_list))
|
findPreference<SearchEngineListPreference>(getPreferenceKey(R.string.pref_key_search_engine_list))
|
||||||
|
|
||||||
|
val showVoiceSearchPreference =
|
||||||
|
findPreference<SwitchPreference>(getPreferenceKey(R.string.pref_key_show_voice_search))?.apply {
|
||||||
|
isChecked = context.settings().shouldShowVoiceSearch
|
||||||
|
}
|
||||||
|
|
||||||
searchEngineListPreference?.reload(requireContext())
|
searchEngineListPreference?.reload(requireContext())
|
||||||
searchSuggestionsPreference?.onPreferenceChangeListener = SharedPreferenceUpdater()
|
searchSuggestionsPreference?.onPreferenceChangeListener = SharedPreferenceUpdater()
|
||||||
showSearchShortcuts?.onPreferenceChangeListener = SharedPreferenceUpdater()
|
showSearchShortcuts?.onPreferenceChangeListener = SharedPreferenceUpdater()
|
||||||
|
@ -67,6 +72,7 @@ class SearchEngineFragment : PreferenceFragmentCompat() {
|
||||||
showBookmarkSuggestions?.onPreferenceChangeListener = SharedPreferenceUpdater()
|
showBookmarkSuggestions?.onPreferenceChangeListener = SharedPreferenceUpdater()
|
||||||
showClipboardSuggestions?.onPreferenceChangeListener = SharedPreferenceUpdater()
|
showClipboardSuggestions?.onPreferenceChangeListener = SharedPreferenceUpdater()
|
||||||
searchSuggestionsInPrivatePreference?.onPreferenceChangeListener = SharedPreferenceUpdater()
|
searchSuggestionsInPrivatePreference?.onPreferenceChangeListener = SharedPreferenceUpdater()
|
||||||
|
showVoiceSearchPreference?.onPreferenceChangeListener = SharedPreferenceUpdater()
|
||||||
|
|
||||||
searchSuggestionsPreference?.setOnPreferenceClickListener {
|
searchSuggestionsPreference?.setOnPreferenceClickListener {
|
||||||
if (!searchSuggestionsPreference.isChecked) {
|
if (!searchSuggestionsPreference.isChecked) {
|
||||||
|
|
|
@ -545,6 +545,11 @@ class Settings private constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var shouldShowVoiceSearch by booleanPreference(
|
||||||
|
appContext.getPreferenceKey(R.string.pref_key_show_voice_search),
|
||||||
|
default = true
|
||||||
|
)
|
||||||
|
|
||||||
var shouldPromptToSaveLogins by booleanPreference(
|
var shouldPromptToSaveLogins by booleanPreference(
|
||||||
appContext.getPreferenceKey(R.string.pref_key_save_logins),
|
appContext.getPreferenceKey(R.string.pref_key_save_logins),
|
||||||
default = true
|
default = true
|
||||||
|
|
|
@ -87,6 +87,7 @@
|
||||||
<string name="pref_key_search_bookmarks" translatable="false">pref_key_search_bookmarks</string>
|
<string name="pref_key_search_bookmarks" translatable="false">pref_key_search_bookmarks</string>
|
||||||
<string name="pref_key_show_search_suggestions_in_private" translatable="false">pref_key_show_search_suggestions_in_private</string>
|
<string name="pref_key_show_search_suggestions_in_private" translatable="false">pref_key_show_search_suggestions_in_private</string>
|
||||||
<string name="pref_key_show_search_suggestions_in_private_onboarding" translatable="false">pref_key_show_search_suggestions_in_privateonboarding</string>
|
<string name="pref_key_show_search_suggestions_in_private_onboarding" translatable="false">pref_key_show_search_suggestions_in_privateonboarding</string>
|
||||||
|
<string name="pref_key_show_voice_search" translatable="false">pref_key_show_voice_search</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- Site Permissions Settings -->
|
<!-- Site Permissions Settings -->
|
||||||
|
|
|
@ -248,6 +248,8 @@
|
||||||
<string name="preferences_show_search_shortcuts">Show search shortcuts</string>
|
<string name="preferences_show_search_shortcuts">Show search shortcuts</string>
|
||||||
<!-- Preference title for switch preference to show search suggestions -->
|
<!-- Preference title for switch preference to show search suggestions -->
|
||||||
<string name="preferences_show_search_suggestions">Show search suggestions</string>
|
<string name="preferences_show_search_suggestions">Show search suggestions</string>
|
||||||
|
<!-- Preference title for switch preference to show voice search button -->
|
||||||
|
<string name="preferences_show_voice_search">Show voice search</string>
|
||||||
<!-- Preference title for switch preference to show search suggestions also in private mode -->
|
<!-- Preference title for switch preference to show search suggestions also in private mode -->
|
||||||
<string name="preferences_show_search_suggestions_in_private">Show in private sessions</string>
|
<string name="preferences_show_search_suggestions_in_private">Show in private sessions</string>
|
||||||
<!-- Preference title for switch preference to show a clipboard suggestion when searching -->
|
<!-- Preference title for switch preference to show a clipboard suggestion when searching -->
|
||||||
|
@ -1360,4 +1362,8 @@
|
||||||
<string name="edit">Edit</string>
|
<string name="edit">Edit</string>
|
||||||
<!-- The error message in edit login view when password field is blank. -->
|
<!-- The error message in edit login view when password field is blank. -->
|
||||||
<string name="saved_login_password_required">Password required</string>
|
<string name="saved_login_password_required">Password required</string>
|
||||||
|
<!-- Voice search button content description -->
|
||||||
|
<string name="voice_search_content_description">Voice search</string>
|
||||||
|
<!-- Voice search prompt description displayed after the user presses the voice search button -->
|
||||||
|
<string name="voice_search_explainer">Speak now</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -51,6 +51,12 @@
|
||||||
android:title="@string/preferences_show_search_suggestions"
|
android:title="@string/preferences_show_search_suggestions"
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:allowDividerAbove="false"/>
|
app:allowDividerAbove="false"/>
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:key="@string/pref_key_show_voice_search"
|
||||||
|
android:title="@string/preferences_show_voice_search"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:allowDividerAbove="false"/>
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:dependency="@string/pref_key_show_search_suggestions"
|
android:dependency="@string/pref_key_show_search_suggestions"
|
||||||
|
|
Loading…
Reference in New Issue