1
0
Fork 0

For #1216: Add voice search

master
Sawyer Blatz 2020-05-14 10:47:13 -07:00
parent c0e4056c16
commit 011a9e7d96
7 changed files with 63 additions and 3 deletions

View File

@ -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)
} }
} }

View File

@ -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()
} }

View File

@ -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) {

View File

@ -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

View File

@ -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 -->

View File

@ -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>

View File

@ -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"