1
0
Fork 0

Pull awesome bar into a component

master
Jeff Boek 2019-01-30 15:51:49 -08:00
parent bacee18344
commit ed1e563087
8 changed files with 176 additions and 92 deletions

View File

@ -4,23 +4,23 @@
package org.mozilla.fenix.search
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.fragment_search.*
import kotlinx.android.synthetic.main.fragment_search.view.*
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
import org.mozilla.fenix.R
import org.mozilla.fenix.components.toolbar.ToolbarIntegration
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.search.awesomebar.AwesomeBarAction
import org.mozilla.fenix.search.awesomebar.AwesomeBarChange
import org.mozilla.fenix.search.awesomebar.AwesomeBarComponent
import org.mozilla.fenix.search.toolbar.*
class SearchFragment : Fragment() {
private lateinit var searchComponent: SearchComponent
private lateinit var toolbarComponent: ToolbarComponent
private lateinit var awesomeBarComponent: AwesomeBarComponent
override fun onCreateView(
inflater: LayoutInflater,
@ -28,40 +28,54 @@ class SearchFragment : Fragment() {
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_search, container, false)
searchComponent = SearchComponent(view.toolbar_wrapper, ActionBusFactory.get(this),
{ v -> transitionToBrowser(v) })
toolbarComponent = ToolbarComponent(view.toolbar_wrapper, ActionBusFactory.get(this))
awesomeBarComponent = AwesomeBarComponent(view.search_layout, ActionBusFactory.get(this))
return view
}
override fun onResume() {
super.onResume()
searchComponent.editMode()
toolbarComponent.editMode()
}
@SuppressLint("CheckResult")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
layoutComponents(view.search_layout)
lifecycle.addObserver(
ToolbarIntegration(
requireContext(),
searchComponent.getView(),
ShippedDomainsProvider().also { it.initialize(requireContext()) },
requireComponents.core.historyStorage
)
)
lifecycle.addObserver((toolbarComponent.uiView as ToolbarUIView).toolbarIntegration)
toolbar_wrapper.clipToOutline = false
view.toolbar_wrapper.clipToOutline = false
toolbarComponent
.getModelChangeEvents<SearchChange>()
.subscribe {
when (it) {
is SearchChange.QueryChanged -> {
ActionBusFactory.get(this).emit(AwesomeBarChange::class.java, AwesomeBarChange.UpdateQuery(it.query))
}
}
}
toolbarComponent
.getUserInteractionEvents<SearchAction>()
.subscribe {
when (it) {
is SearchAction.UrlCommitted -> transitionToBrowser()
}
}
awesomeBarComponent
.getUserInteractionEvents<AwesomeBarAction>()
.subscribe {
when (it) {
is AwesomeBarAction.ItemSelected -> transitionToBrowser()
}
}
}
private fun transitionToBrowser(toolbar: View) {
Navigation.findNavController(toolbar)
private fun transitionToBrowser() {
Navigation.findNavController(view!!.search_layout)
.navigate(R.id.action_searchFragment_to_browserFragment, null, null)
}
companion object {
const val toolbarTextSizeSp = 14f
}
}

View File

@ -8,6 +8,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.UNSET
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.component_awesomebar.*
import kotlinx.android.synthetic.main.fragment_search.*
import mozilla.components.support.base.log.logger.Logger
import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder.Side.BOTTOM

View File

@ -0,0 +1,37 @@
package org.mozilla.fenix.search.awesomebar
/* This Source Code Form is subject to the terms of the Mozilla Public
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/. */
import android.view.ViewGroup
import org.mozilla.fenix.mvi.*
data class AwesomeBarState(val query: String) : ViewState {
fun updateQuery(query: String) = AwesomeBarState(query)
}
sealed class AwesomeBarAction: Action {
object ItemSelected: AwesomeBarAction()
}
sealed class AwesomeBarChange : Change {
data class UpdateQuery(val query: String): AwesomeBarChange()
}
class AwesomeBarComponent(
private val container: ViewGroup,
override val bus: ActionBusFactory,
override var initialState: AwesomeBarState = AwesomeBarState("")
) : UIComponent<AwesomeBarState, AwesomeBarAction, AwesomeBarChange>(bus) {
override val reducer: Reducer<AwesomeBarState, AwesomeBarChange> = { state, change ->
when (change) {
is AwesomeBarChange.UpdateQuery -> state.updateQuery(change.query)
}
}
override fun initView() = AwesomeBarUIView(container, bus)
init {
render(reducer)
}
}

View File

@ -0,0 +1,41 @@
package org.mozilla.fenix.search.awesomebar
/* This Source Code Form is subject to the terms of the Mozilla Public
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/. */
import android.view.LayoutInflater
import android.view.ViewGroup
import io.reactivex.functions.Consumer
import mozilla.components.browser.awesomebar.BrowserAwesomeBar
import mozilla.components.feature.awesomebar.provider.ClipboardSuggestionProvider
import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider
import mozilla.components.feature.awesomebar.provider.SessionSuggestionProvider
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.UIView
class AwesomeBarUIView(container: ViewGroup, bus: ActionBusFactory) :
UIView<AwesomeBarState>(container, bus) {
override val view: BrowserAwesomeBar = LayoutInflater.from(container.context)
.inflate(R.layout.component_awesomebar, container, true)
.findViewById(R.id.awesomeBar)
init {
with(container.context) {
view.addProviders(ClipboardSuggestionProvider(this, components.useCases.sessionUseCases.loadUrl))
view.addProviders(SessionSuggestionProvider(components.core.sessionManager, components.useCases.tabsUseCases.selectTab))
view.addProviders(SearchSuggestionProvider(
components.search.searchEngineManager.getDefaultSearchEngine(this),
components.useCases.searchUseCases.defaultSearch,
SearchSuggestionProvider.Mode.MULTIPLE_SUGGESTIONS)
)
view.setOnStopListener { bus.emit(AwesomeBarAction::class.java, AwesomeBarAction.ItemSelected) }
}
}
override fun updateView() = Consumer<AwesomeBarState> {
view.onInputChanged(it.query)
}
}

View File

@ -2,14 +2,11 @@
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/. */
package org.mozilla.fenix.search
package org.mozilla.fenix.search.toolbar
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_browser.*
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.support.base.log.logger.Logger
import org.mozilla.fenix.mvi.Action
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.Change
@ -17,51 +14,36 @@ import org.mozilla.fenix.mvi.Reducer
import org.mozilla.fenix.mvi.UIComponent
import org.mozilla.fenix.mvi.ViewState
class SearchComponent(
class ToolbarComponent(
private val container: ViewGroup,
override val bus: ActionBusFactory,
private val onEditComplete: (View) -> Unit,
override var initialState: SearchState = SearchState("")
) :
UIComponent<SearchState, SearchAction, SearchChange>(bus) {
override val reducer: Reducer<SearchState, SearchChange> = { state, change ->
when (change) {
is SearchChange.Changed -> state // TODO handle state changes here
is SearchChange.QueryChanged -> state.updateQuery(change.query)
}
}
override fun initView() = SearchUIView(container, bus)
override fun initView() = ToolbarUIView(container, bus)
init {
setup()
render(reducer)
}
fun getView(): BrowserToolbar = uiView.toolbar
fun editMode() = getView().editMode()
@SuppressLint("CheckResult")
fun setup(): SearchComponent {
render(reducer)
getUserInteractionEvents<SearchAction>()
.subscribe {
Logger("SearchComponent").debug(it.toString())
when (it) {
is SearchAction.EditComplete -> {
onEditComplete.invoke(getView())
}
else -> {}
}
}
return this
}
}
data class SearchState(val term: String) : ViewState
data class SearchState(val query: String) : ViewState {
fun updateQuery(query: String) = SearchState(query)
}
sealed class SearchAction : Action {
object UrlClicked : SearchAction()
object EditComplete : SearchAction()
data class UrlCommitted(val url: String): SearchAction()
}
sealed class SearchChange : Change {
object Changed : SearchChange()
data class QueryChanged(val query: String) : SearchChange()
}

View File

@ -2,25 +2,27 @@
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/. */
package org.mozilla.fenix.search
package org.mozilla.fenix.search.toolbar
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import io.reactivex.functions.Consumer
import kotlinx.android.synthetic.main.fragment_search.view.*
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.feature.awesomebar.AwesomeBarFeature
import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider
import mozilla.components.support.ktx.android.content.res.pxToDp
import org.mozilla.fenix.R
import org.mozilla.fenix.components.toolbar.ToolbarIntegration
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.UIView
class SearchUIView(container: ViewGroup, bus: ActionBusFactory) :
class ToolbarUIView(container: ViewGroup, bus: ActionBusFactory) :
UIView<SearchState>(container, bus) {
val toolbarIntegration: ToolbarIntegration
override val view: BrowserToolbar = LayoutInflater.from(container.context)
.inflate(R.layout.component_search, container, true)
.findViewById(R.id.toolbar)
@ -30,10 +32,8 @@ class SearchUIView(container: ViewGroup, bus: ActionBusFactory) :
init {
view.apply {
onUrlClicked = {
bus.emit(SearchAction::class.java, SearchAction.UrlClicked)
false
}
onUrlClicked = { false }
setOnUrlCommitListener { bus.emit(SearchAction::class.java, SearchAction.UrlCommitted(it)) }
browserActionMargin = resources.pxToDp(browserActionMarginDp)
urlBoxView = urlBackground
@ -43,21 +43,25 @@ class SearchUIView(container: ViewGroup, bus: ActionBusFactory) :
textSize = toolbarTextSizeSp
hint = context.getString(R.string.search_hint)
hintColor = ContextCompat.getColor(context, R.color.searchText)
setOnEditListener(object : mozilla.components.concept.toolbar.Toolbar.OnEditListener {
override fun onTextChanged(text: String) {
bus.emit(SearchChange::class.java, SearchChange.QueryChanged(text))
}
override fun onStopEditing() {
bus.emit(SearchAction::class.java, SearchAction.UrlCommitted("foo"))
}
})
}
with(container.context) {
AwesomeBarFeature(container.rootView.awesomeBar, view, null,
onEditComplete = { bus.emit(SearchAction::class.java, SearchAction.EditComplete) })
.addClipboardProvider(this, components.useCases.sessionUseCases.loadUrl)
.addSearchProvider(
components.search.searchEngineManager.getDefaultSearchEngine(this),
components.useCases.searchUseCases.defaultSearch,
SearchSuggestionProvider.Mode.MULTIPLE_SUGGESTIONS
)
.addSessionProvider(
components.core.sessionManager,
components.useCases.tabsUseCases.selectTab
)
with(view.context) {
toolbarIntegration = ToolbarIntegration(
this,
view,
ShippedDomainsProvider().also { it.initialize(this) },
components.core.historyStorage
)
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- 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/. -->
<mozilla.components.browser.awesomebar.BrowserAwesomeBar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:mozac="http://schemas.android.com/apk/res-auto"
android:id="@+id/awesomeBar"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="4dp"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@id/toolbar_wrapper"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/pill_wrapper"
mozac:awesomeBarTitleTextColor="#212121"
mozac:awesomeBarDescriptionTextColor="#6b6b6b"
mozac:awesomeBarChipTextColor="#ffffff"
mozac:awesomeBarChipBackgroundColor="#444444" />

View File

@ -24,22 +24,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<mozilla.components.browser.awesomebar.BrowserAwesomeBar
android:id="@+id/awesomeBar"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="4dp"
android:visibility="gone"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@id/toolbar_wrapper"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/pill_wrapper"
mozac:awesomeBarTitleTextColor="#212121"
mozac:awesomeBarDescriptionTextColor="#6b6b6b"
mozac:awesomeBarChipTextColor="#ffffff"
mozac:awesomeBarChipBackgroundColor="#444444" />
<LinearLayout
android:id="@+id/pill_wrapper"
android:background="@color/offwhite"