2019-07-13 01:32:00 +02:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
2020-07-27 18:37:09 +02:00
|
|
|
* 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/. */
|
2019-07-13 01:32:00 +02:00
|
|
|
|
|
|
|
package org.mozilla.fenix.search.toolbar
|
|
|
|
|
2020-06-21 23:56:49 +02:00
|
|
|
import android.content.Context
|
2019-10-15 17:54:04 +02:00
|
|
|
import android.graphics.Bitmap
|
|
|
|
import android.graphics.drawable.BitmapDrawable
|
2020-07-21 23:34:07 +02:00
|
|
|
import androidx.annotation.VisibleForTesting
|
2019-12-06 04:54:32 +01:00
|
|
|
import androidx.appcompat.content.res.AppCompatResources
|
2019-07-13 01:32:00 +02:00
|
|
|
import androidx.core.content.ContextCompat
|
|
|
|
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
|
|
|
|
import mozilla.components.browser.toolbar.BrowserToolbar
|
2020-03-02 22:21:56 +01:00
|
|
|
import mozilla.components.concept.engine.Engine
|
2019-07-13 01:32:00 +02:00
|
|
|
import mozilla.components.concept.storage.HistoryStorage
|
|
|
|
import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature
|
2020-02-06 17:14:32 +01:00
|
|
|
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
2020-07-16 03:04:36 +02:00
|
|
|
import mozilla.components.support.ktx.android.content.res.resolveAttribute
|
2020-04-17 20:20:12 +02:00
|
|
|
import mozilla.components.support.ktx.android.view.hideKeyboard
|
2019-07-13 01:32:00 +02:00
|
|
|
import org.mozilla.fenix.R
|
2019-08-30 15:21:34 +02:00
|
|
|
import org.mozilla.fenix.search.SearchFragmentState
|
2019-07-13 01:32:00 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Interface for the Toolbar Interactor. This interface is implemented by objects that want
|
2020-07-14 19:32:04 +02:00
|
|
|
* to respond to user interaction on the [ToolbarView]
|
2019-07-13 01:32:00 +02:00
|
|
|
*/
|
|
|
|
interface ToolbarInteractor {
|
|
|
|
|
|
|
|
/**
|
2020-07-14 19:32:04 +02:00
|
|
|
* Called when a user hits the return key while [ToolbarView] has focus.
|
|
|
|
* @param url the text inside the [ToolbarView] when committed
|
2019-07-13 01:32:00 +02:00
|
|
|
*/
|
|
|
|
fun onUrlCommitted(url: String)
|
|
|
|
|
|
|
|
/**
|
2020-07-14 19:32:04 +02:00
|
|
|
* Called when a user removes focus from the [ToolbarView]
|
2019-07-13 01:32:00 +02:00
|
|
|
*/
|
|
|
|
fun onEditingCanceled()
|
|
|
|
|
|
|
|
/**
|
2020-07-14 19:32:04 +02:00
|
|
|
* Called whenever the text inside the [ToolbarView] changes
|
|
|
|
* @param text the current text displayed by [ToolbarView]
|
2019-07-13 01:32:00 +02:00
|
|
|
*/
|
|
|
|
fun onTextChanged(text: String)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* View that contains and configures the BrowserToolbar to only be used in its editing mode.
|
|
|
|
*/
|
|
|
|
class ToolbarView(
|
2020-06-21 23:56:49 +02:00
|
|
|
private val context: Context,
|
2019-07-13 01:32:00 +02:00
|
|
|
private val interactor: ToolbarInteractor,
|
2019-07-30 00:00:48 +02:00
|
|
|
private val historyStorage: HistoryStorage?,
|
2020-03-02 22:21:56 +01:00
|
|
|
private val isPrivate: Boolean,
|
2020-06-21 23:56:49 +02:00
|
|
|
val view: BrowserToolbar,
|
2020-03-02 22:21:56 +01:00
|
|
|
engine: Engine
|
2020-06-21 23:56:49 +02:00
|
|
|
) {
|
2019-07-13 01:32:00 +02:00
|
|
|
|
2020-07-21 23:34:07 +02:00
|
|
|
@VisibleForTesting
|
|
|
|
internal var isInitialized = false
|
2019-07-13 01:32:00 +02:00
|
|
|
|
|
|
|
init {
|
|
|
|
view.apply {
|
|
|
|
editMode()
|
|
|
|
|
|
|
|
setOnUrlCommitListener {
|
2020-04-17 20:20:12 +02:00
|
|
|
// We're hiding the keyboard as early as possible to prevent the engine view
|
|
|
|
// from resizing in case the BrowserFragment is being displayed before the
|
|
|
|
// keyboard is gone: https://github.com/mozilla-mobile/fenix/issues/8399
|
|
|
|
hideKeyboard()
|
2019-07-13 01:32:00 +02:00
|
|
|
interactor.onUrlCommitted(it)
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2020-07-16 03:04:36 +02:00
|
|
|
background = AppCompatResources.getDrawable(
|
|
|
|
context, context.theme.resolveAttribute(R.attr.foundation)
|
|
|
|
)
|
2019-07-13 01:32:00 +02:00
|
|
|
|
2019-10-15 17:54:04 +02:00
|
|
|
edit.hint = context.getString(R.string.search_hint)
|
2019-07-13 01:32:00 +02:00
|
|
|
|
2019-10-15 17:54:04 +02:00
|
|
|
edit.colors = edit.colors.copy(
|
2020-06-21 23:56:49 +02:00
|
|
|
text = context.getColorFromAttr(R.attr.primaryText),
|
|
|
|
hint = context.getColorFromAttr(R.attr.secondaryText),
|
2019-10-15 17:54:04 +02:00
|
|
|
suggestionBackground = ContextCompat.getColor(
|
2020-06-21 23:56:49 +02:00
|
|
|
context,
|
2019-10-15 17:54:04 +02:00
|
|
|
R.color.suggestion_highlight_color
|
|
|
|
),
|
2020-06-21 23:56:49 +02:00
|
|
|
clear = context.getColorFromAttr(R.attr.primaryText)
|
2019-07-13 01:32:00 +02:00
|
|
|
)
|
|
|
|
|
2019-10-15 17:54:04 +02:00
|
|
|
edit.setUrlBackground(
|
2020-06-24 02:20:48 +02:00
|
|
|
AppCompatResources.getDrawable(context, R.drawable.search_url_background)
|
|
|
|
)
|
2019-10-15 17:54:04 +02:00
|
|
|
|
2019-07-30 00:00:48 +02:00
|
|
|
private = isPrivate
|
|
|
|
|
2019-07-13 01:32:00 +02:00
|
|
|
setOnEditListener(object : mozilla.components.concept.toolbar.Toolbar.OnEditListener {
|
|
|
|
override fun onCancelEditing(): Boolean {
|
2020-06-24 02:20:48 +02:00
|
|
|
interactor.onEditingCanceled()
|
2019-08-22 01:29:45 +02:00
|
|
|
// We need to return false to not show display mode
|
2019-07-13 01:32:00 +02:00
|
|
|
return false
|
|
|
|
}
|
2020-06-24 02:20:48 +02:00
|
|
|
|
2019-07-13 01:32:00 +02:00
|
|
|
override fun onTextChanged(text: String) {
|
|
|
|
url = text
|
2020-07-14 19:32:04 +02:00
|
|
|
interactor.onTextChanged(text)
|
2019-07-13 01:32:00 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-02 22:21:56 +01:00
|
|
|
val engineForSpeculativeConnects = if (!isPrivate) engine else null
|
|
|
|
ToolbarAutocompleteFeature(
|
|
|
|
view,
|
|
|
|
engineForSpeculativeConnects
|
|
|
|
).apply {
|
2019-07-13 01:32:00 +02:00
|
|
|
addDomainProvider(ShippedDomainsProvider().also { it.initialize(view.context) })
|
|
|
|
historyStorage?.also(::addHistoryStorageProvider)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-30 15:21:34 +02:00
|
|
|
fun update(searchState: SearchFragmentState) {
|
2019-07-13 01:32:00 +02:00
|
|
|
if (!isInitialized) {
|
2019-09-04 22:56:39 +02:00
|
|
|
view.url = searchState.pastedText ?: searchState.query
|
|
|
|
|
|
|
|
/* Only set the search terms if pasted text is null so that the search term doesn't
|
|
|
|
overwrite pastedText when view enters `editMode` */
|
|
|
|
if (searchState.pastedText.isNullOrEmpty()) {
|
2020-08-06 19:51:48 +02:00
|
|
|
// If we're in edit mode, setting the search term will update the toolbar,
|
|
|
|
// so we make sure we have the correct term/query to show.
|
|
|
|
val termOrQuery = if (searchState.searchTerms.isNotEmpty()) {
|
|
|
|
searchState.searchTerms
|
|
|
|
} else {
|
|
|
|
searchState.query
|
|
|
|
}
|
|
|
|
view.setSearchTerms(termOrQuery)
|
2019-09-04 22:56:39 +02:00
|
|
|
}
|
|
|
|
|
2020-03-03 18:22:52 +01:00
|
|
|
// We must trigger an onTextChanged so when search terms are set when transitioning to `editMode`
|
|
|
|
// we have the most up to date text
|
|
|
|
interactor.onTextChanged(view.url.toString())
|
|
|
|
|
2019-07-13 01:32:00 +02:00
|
|
|
view.editMode()
|
|
|
|
isInitialized = true
|
|
|
|
}
|
2019-10-15 17:54:04 +02:00
|
|
|
|
2020-06-24 02:20:48 +02:00
|
|
|
val iconSize =
|
|
|
|
context.resources.getDimensionPixelSize(R.dimen.preference_icon_drawable_size)
|
2019-10-15 17:54:04 +02:00
|
|
|
|
|
|
|
val scaledIcon = Bitmap.createScaledBitmap(
|
|
|
|
searchState.searchEngineSource.searchEngine.icon,
|
|
|
|
iconSize,
|
|
|
|
iconSize,
|
2020-06-24 02:20:48 +02:00
|
|
|
true
|
|
|
|
)
|
2019-10-15 17:54:04 +02:00
|
|
|
|
2020-06-21 23:56:49 +02:00
|
|
|
val icon = BitmapDrawable(context.resources, scaledIcon)
|
2019-10-15 17:54:04 +02:00
|
|
|
|
|
|
|
view.edit.setIcon(icon, searchState.searchEngineSource.searchEngine.name)
|
2019-07-13 01:32:00 +02:00
|
|
|
}
|
|
|
|
}
|