1
0
Fork 0

For #1019 Add onboarding search suggestion hint panel

master
mcarare 2019-11-15 18:01:34 +02:00 committed by Emily Kager
parent 5543f3272d
commit 1d36098878
11 changed files with 171 additions and 8 deletions

View File

@ -14,11 +14,11 @@ import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.searchEngineManager
import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.searchEngineManager
/** /**
* An interface that handles the view manipulation of the Search, triggered by the Interactor * An interface that handles the view manipulation of the Search, triggered by the Interactor
@ -69,6 +69,12 @@ class DefaultSearchController(
store.dispatch(SearchFragmentAction.ShowSearchShortcutEnginePicker( store.dispatch(SearchFragmentAction.ShowSearchShortcutEnginePicker(
text.isEmpty() && context.settings().shouldShowSearchShortcuts text.isEmpty() && context.settings().shouldShowSearchShortcuts
)) ))
store.dispatch(SearchFragmentAction.ShowSearchSuggestionsHint(
text.isNotEmpty() &&
(context as HomeActivity).browsingModeManager.mode.isPrivate &&
!context.settings().shouldShowSearchSuggestionsInPrivate &&
!context.settings().showSearchSuggestionsInPrivateOnboardingFinished
))
} }
override fun handleUrlTapped(url: String) { override fun handleUrlTapped(url: String) {

View File

@ -14,13 +14,16 @@ 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 androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.transition.TransitionInflater import androidx.transition.TransitionInflater
import kotlinx.android.synthetic.main.fragment_search.* 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.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.concept.storage.HistoryStorage import mozilla.components.concept.storage.HistoryStorage
import mozilla.components.feature.qr.QrFeature import mozilla.components.feature.qr.QrFeature
@ -34,12 +37,13 @@ import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getSpannable import org.mozilla.fenix.ext.getSpannable
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
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.ext.components
import org.mozilla.fenix.ext.settings
@Suppress("TooManyFunctions", "LargeClass") @Suppress("TooManyFunctions", "LargeClass")
class SearchFragment : Fragment(), BackHandler { class SearchFragment : Fragment(), BackHandler {
@ -95,6 +99,7 @@ class SearchFragment : Fragment(), BackHandler {
searchEngineSource = currentSearchEngine, searchEngineSource = currentSearchEngine,
defaultEngineSource = currentSearchEngine, defaultEngineSource = currentSearchEngine,
showSearchSuggestions = showSearchSuggestions, showSearchSuggestions = showSearchSuggestions,
showSearchSuggestionsHint = false,
showSearchShortcuts = requireContext().settings().shouldShowSearchShortcuts && url.isEmpty(), showSearchShortcuts = requireContext().settings().shouldShowSearchShortcuts && url.isEmpty(),
showClipboardSuggestions = requireContext().settings().shouldShowClipboardSuggestions, showClipboardSuggestions = requireContext().settings().shouldShowClipboardSuggestions,
showHistorySuggestions = requireContext().settings().shouldShowHistorySuggestions, showHistorySuggestions = requireContext().settings().shouldShowHistorySuggestions,
@ -184,6 +189,34 @@ class SearchFragment : Fragment(), BackHandler {
qrFeature.get()?.scan(R.id.container) qrFeature.get()?.scan(R.id.container)
} }
val stubListener = ViewStub.OnInflateListener { _, inflated ->
inflated.learn_more.setOnClickListener {
(activity as HomeActivity)
.openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getGenericSumoURLForTopic(
SupportUtils.SumoTopic.SEARCH_SUGGESTION),
newTab = searchStore.state.session == null,
from = BrowserDirection.FromSearch
)
}
inflated.allow.setOnClickListener {
inflated.visibility = View.GONE
context?.settings()?.shouldShowSearchSuggestionsInPrivate = true
context?.settings()?.showSearchSuggestionsInPrivateOnboardingFinished = true
}
inflated.dismiss.setOnClickListener {
inflated.visibility = View.GONE
context?.settings()?.shouldShowSearchSuggestionsInPrivate = false
context?.settings()?.showSearchSuggestionsInPrivateOnboardingFinished = true
}
}
view.search_suggestions_onboarding.setOnInflateListener((stubListener)
)
view.toolbar_wrapper.clipToOutline = false view.toolbar_wrapper.clipToOutline = false
fill_link_from_clipboard.setOnClickListener { fill_link_from_clipboard.setOnClickListener {
@ -200,6 +233,7 @@ class SearchFragment : Fragment(), BackHandler {
toolbarView.update(it) toolbarView.update(it)
updateSearchWithLabel(it) updateSearchWithLabel(it)
updateClipboardSuggestion(it, requireContext().components.clipboardHandler.url) updateClipboardSuggestion(it, requireContext().components.clipboardHandler.url)
updateSearchSuggestionsHintVisibility(it)
} }
startPostponedEnterTransition() startPostponedEnterTransition()
@ -287,6 +321,11 @@ class SearchFragment : Fragment(), BackHandler {
} else null } else null
} }
private fun updateSearchSuggestionsHintVisibility(state: SearchFragmentState) {
view?.findViewById<View>(R.id.search_suggestions_onboarding)
?.isVisible = state.showSearchSuggestionsHint
}
companion object { companion object {
private const val SHARED_TRANSITION_MS = 200L private const val SHARED_TRANSITION_MS = 200L
private const val REQUEST_CODE_CAMERA_PERMISSIONS = 1 private const val REQUEST_CODE_CAMERA_PERMISSIONS = 1

View File

@ -36,6 +36,7 @@ sealed class SearchEngineSource {
* @property searchEngineSource The current selected search engine with the context of how it was selected * @property searchEngineSource The current selected search engine with the context of how it was selected
* @property defaultEngineSource The current default search engine source * @property defaultEngineSource The current default search engine source
* @property showSearchSuggestions Whether or not to show search suggestions from the search engine in the AwesomeBar * @property showSearchSuggestions Whether or not to show search suggestions from the search engine in the AwesomeBar
* @property showSearchSuggestionsHint Whether or not to show search suggestions in private hint panel
* @property showSearchShortcuts Whether or not to show search shortcuts in the AwesomeBar * @property showSearchShortcuts Whether or not to show search shortcuts in the AwesomeBar
* @property showClipboardSuggestions Whether or not to show clipboard suggestion in the AwesomeBar * @property showClipboardSuggestions Whether or not to show clipboard suggestion in the AwesomeBar
* @property showHistorySuggestions Whether or not to show history suggestions in the AwesomeBar * @property showHistorySuggestions Whether or not to show history suggestions in the AwesomeBar
@ -48,6 +49,7 @@ data class SearchFragmentState(
val searchEngineSource: SearchEngineSource, val searchEngineSource: SearchEngineSource,
val defaultEngineSource: SearchEngineSource.Default, val defaultEngineSource: SearchEngineSource.Default,
val showSearchSuggestions: Boolean, val showSearchSuggestions: Boolean,
val showSearchSuggestionsHint: Boolean,
val showSearchShortcuts: Boolean, val showSearchShortcuts: Boolean,
val showClipboardSuggestions: Boolean, val showClipboardSuggestions: Boolean,
val showHistorySuggestions: Boolean, val showHistorySuggestions: Boolean,
@ -63,6 +65,7 @@ sealed class SearchFragmentAction : Action {
data class SearchShortcutEngineSelected(val engine: SearchEngine) : SearchFragmentAction() data class SearchShortcutEngineSelected(val engine: SearchEngine) : SearchFragmentAction()
data class SelectNewDefaultSearchEngine(val engine: SearchEngine) : SearchFragmentAction() data class SelectNewDefaultSearchEngine(val engine: SearchEngine) : SearchFragmentAction()
data class ShowSearchShortcutEnginePicker(val show: Boolean) : SearchFragmentAction() data class ShowSearchShortcutEnginePicker(val show: Boolean) : SearchFragmentAction()
data class ShowSearchSuggestionsHint(val show: Boolean) : SearchFragmentAction()
data class UpdateQuery(val query: String) : SearchFragmentAction() data class UpdateQuery(val query: String) : SearchFragmentAction()
} }
@ -84,5 +87,7 @@ private fun searchStateReducer(state: SearchFragmentState, action: SearchFragmen
state.copy( state.copy(
searchEngineSource = SearchEngineSource.Default(action.engine) searchEngineSource = SearchEngineSource.Default(action.engine)
) )
is SearchFragmentAction.ShowSearchSuggestionsHint ->
state.copy(showSearchSuggestionsHint = action.show)
} }
} }

View File

@ -71,7 +71,7 @@ internal fun SearchFragment.setOutOfExperimentConstraints(layout: ConstraintLayo
awesomeBar { awesomeBar {
connect( connect(
TOP to TOP of UNSET, TOP to TOP of UNSET,
TOP to BOTTOM of search_with_shortcuts, TOP to BOTTOM of awesomeBar_barrier,
BOTTOM to TOP of pillWrapper BOTTOM to TOP of pillWrapper
) )
} }

View File

@ -31,6 +31,7 @@ object SupportUtils {
WHATS_NEW("whats-new-firefox-preview"), WHATS_NEW("whats-new-firefox-preview"),
SEND_TABS("send-tab-preview"), SEND_TABS("send-tab-preview"),
SET_AS_DEFAULT_BROWSER("set-firefox-preview-default"), SET_AS_DEFAULT_BROWSER("set-firefox-preview-default"),
SEARCH_SUGGESTION("how-search-firefox-preview"),
CUSTOM_SEARCH_ENGINES("custom-search-engines") CUSTOM_SEARCH_ENGINES("custom-search-engines")
} }

View File

@ -263,11 +263,16 @@ class Settings private constructor(
default = true default = true
) )
val shouldShowSearchSuggestionsInPrivate by booleanPreference( var shouldShowSearchSuggestionsInPrivate by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_show_search_suggestions_in_private), appContext.getPreferenceKey(R.string.pref_key_show_search_suggestions_in_private),
default = false default = false
) )
var showSearchSuggestionsInPrivateOnboardingFinished by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_show_search_suggestions_in_private_onboarding),
default = false
)
@VisibleForTesting(otherwise = PRIVATE) @VisibleForTesting(otherwise = PRIVATE)
internal val trackingProtectionOnboardingCount by intPreference( internal val trackingProtectionOnboardingCount by intPreference(
appContext.getPreferenceKey(R.string.pref_key_tracking_protection_onboarding), appContext.getPreferenceKey(R.string.pref_key_tracking_protection_onboarding),

View File

@ -14,6 +14,6 @@
app:layout_constraintBottom_toTopOf="@id/search_divider" app:layout_constraintBottom_toTopOf="@id/search_divider"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_wrapper" app:layout_constraintTop_toBottomOf="@id/awesomeBar_barrier"
mozac:awesomeBarDescriptionTextColor="?secondaryText" mozac:awesomeBarDescriptionTextColor="?secondaryText"
mozac:awesomeBarTitleTextColor="?primaryText" /> mozac:awesomeBarTitleTextColor="?primaryText" />

View File

@ -11,6 +11,18 @@
android:background="?foundation" android:background="?foundation"
tools:context=".search.SearchFragment"> tools:context=".search.SearchFragment">
<ViewStub
android:id="@+id/search_suggestions_onboarding"
android:inflatedId="@+id/search_suggestions_onboarding"
android:layout="@layout/search_suggestions_onboarding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/awesomeBar_barrier"
app:layout_constraintTop_toBottomOf="@id/toolbar_wrapper"/>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/toolbar_wrapper" android:id="@+id/toolbar_wrapper"
android:layout_width="0dp" android:layout_width="0dp"
@ -103,11 +115,18 @@
android:layout_marginTop="@dimen/search_fragment_shortcuts_label_margin_vertical" android:layout_marginTop="@dimen/search_fragment_shortcuts_label_margin_vertical"
android:layout_marginEnd="@dimen/search_fragment_shortcuts_label_margin_horizontal" android:layout_marginEnd="@dimen/search_fragment_shortcuts_label_margin_horizontal"
android:text="@string/search_shortcuts_search_with" android:text="@string/search_shortcuts_search_with"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@id/toolbar_wrapper" app:layout_constraintStart_toStartOf="@id/toolbar_wrapper"
app:layout_constraintTop_toBottomOf="@id/fill_link_from_clipboard" app:layout_constraintTop_toBottomOf="@id/fill_link_from_clipboard"
app:layout_constraintBottom_toTopOf="@id/awesomeBar_barrier"
tools:text="Search with" /> tools:text="Search with" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/awesomeBar_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="search_with_shortcuts,search_suggestions_onboarding"/>
<View <View
android:id="@+id/search_divider" android:id="@+id/search_divider"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:paddingBottom="10dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:id="@+id/info_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_info"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/title"
android:textAppearance="?android:attr/textAppearanceListItem"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:text="@string/search_suggestions_onboarding_title"
android:paddingBottom="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/text"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/search_suggestions_onboarding_text"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintEnd_toEndOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintBottom_toTopOf="@id/learn_more"/>
<TextView
android:id="@+id/learn_more"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?attr/accentHighContrast"
android:textStyle="bold"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/exceptions_empty_message_learn_more_link"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintEnd_toEndOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/text"
app:layout_constraintBottom_toTopOf="@id/allow"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/allow"
style="@style/ThemeIndependentMaterialGreyButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="12dp"
android:text="@string/search_suggestions_onboarding_allow_button"
app:layout_constraintEnd_toEndOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/text"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="@+id/dismiss"
android:textColor="#ffffff"
android:textStyle="bold"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="20dp"
android:text="@string/search_suggestions_onboarding_do_not_allow_button"
app:layout_constraintEnd_toStartOf="@id/allow"
app:layout_constraintBottom_toBottomOf="@id/allow"
app:layout_constraintTop_toTopOf="@id/allow"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -75,6 +75,7 @@
<string name="pref_key_search_browsing_history" translatable="false">pref_key_search_browsing_history</string> <string name="pref_key_search_browsing_history" translatable="false">pref_key_search_browsing_history</string>
<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>
<!-- Site Permissions Settings --> <!-- Site Permissions Settings -->

View File

@ -116,6 +116,16 @@
<string name="search_shortcuts_search_with">Search with</string> <string name="search_shortcuts_search_with">Search with</string>
<!-- Button in the search view that lets a user navigate to the site in their clipboard --> <!-- Button in the search view that lets a user navigate to the site in their clipboard -->
<string name="awesomebar_clipboard_title">Fill link from clipboard</string> <string name="awesomebar_clipboard_title">Fill link from clipboard</string>
<!-- Button in the search suggestions onboarding that allows search suggestions in private sessions -->
<string name="search_suggestions_onboarding_allow_button">Allow</string>
<!-- Button in the search suggestions onboarding that does not allow search suggestions in private sessions -->
<string name="search_suggestions_onboarding_do_not_allow_button">Don\'t allow</string>
<!-- Search suggestion onboarding hint title text -->
<string name="search_suggestions_onboarding_title">Allow search suggestions in private sessions?</string>
<!-- Search suggestion onboarding hint description text -->
<string name="search_suggestions_onboarding_text">Firefox Preview will share everything you type in the address bar with your default search engine.</string>
<!-- Search suggestion onboarding hint Learn more link text -->
<string name="search_suggestions_onboarding_learn_more_link">Learn more</string>
<!-- Search Widget --> <!-- Search Widget -->
<!-- Text preview for smaller sized widgets --> <!-- Text preview for smaller sized widgets -->