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 package org.mozilla.fenix.search
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
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 androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.Navigation import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.fragment_search.*
import kotlinx.android.synthetic.main.fragment_search.view.* import kotlinx.android.synthetic.main.fragment_search.view.*
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
import org.mozilla.fenix.R 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.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() { class SearchFragment : Fragment() {
private lateinit var searchComponent: SearchComponent private lateinit var toolbarComponent: ToolbarComponent
private lateinit var awesomeBarComponent: AwesomeBarComponent
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -28,40 +28,54 @@ class SearchFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
val view = inflater.inflate(R.layout.fragment_search, container, false) val view = inflater.inflate(R.layout.fragment_search, container, false)
searchComponent = SearchComponent(view.toolbar_wrapper, ActionBusFactory.get(this), toolbarComponent = ToolbarComponent(view.toolbar_wrapper, ActionBusFactory.get(this))
{ v -> transitionToBrowser(v) }) awesomeBarComponent = AwesomeBarComponent(view.search_layout, ActionBusFactory.get(this))
return view return view
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
searchComponent.editMode() toolbarComponent.editMode()
} }
@SuppressLint("CheckResult")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
layoutComponents(view.search_layout) layoutComponents(view.search_layout)
lifecycle.addObserver( lifecycle.addObserver((toolbarComponent.uiView as ToolbarUIView).toolbarIntegration)
ToolbarIntegration(
requireContext(),
searchComponent.getView(),
ShippedDomainsProvider().also { it.initialize(requireContext()) },
requireComponents.core.historyStorage
)
)
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) { private fun transitionToBrowser() {
Navigation.findNavController(toolbar) Navigation.findNavController(view!!.search_layout)
.navigate(R.id.action_searchFragment_to_browserFragment, null, null) .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.PARENT_ID
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.UNSET import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.UNSET
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.component_awesomebar.*
import kotlinx.android.synthetic.main.fragment_search.* import kotlinx.android.synthetic.main.fragment_search.*
import mozilla.components.support.base.log.logger.Logger import mozilla.components.support.base.log.logger.Logger
import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder.Side.BOTTOM 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 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/. */ 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 android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_browser.* import kotlinx.android.synthetic.main.fragment_browser.*
import mozilla.components.browser.toolbar.BrowserToolbar 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.Action
import org.mozilla.fenix.mvi.ActionBusFactory import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.Change 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.UIComponent
import org.mozilla.fenix.mvi.ViewState import org.mozilla.fenix.mvi.ViewState
class SearchComponent( class ToolbarComponent(
private val container: ViewGroup, private val container: ViewGroup,
override val bus: ActionBusFactory, override val bus: ActionBusFactory,
private val onEditComplete: (View) -> Unit,
override var initialState: SearchState = SearchState("") override var initialState: SearchState = SearchState("")
) : ) :
UIComponent<SearchState, SearchAction, SearchChange>(bus) { UIComponent<SearchState, SearchAction, SearchChange>(bus) {
override val reducer: Reducer<SearchState, SearchChange> = { state, change -> override val reducer: Reducer<SearchState, SearchChange> = { state, change ->
when (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 { init {
setup() render(reducer)
} }
fun getView(): BrowserToolbar = uiView.toolbar fun getView(): BrowserToolbar = uiView.toolbar
fun editMode() = getView().editMode() 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 { sealed class SearchAction : Action {
object UrlClicked : SearchAction() data class UrlCommitted(val url: String): SearchAction()
object EditComplete : SearchAction()
} }
sealed class SearchChange : Change { 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 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/. */ 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.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import io.reactivex.functions.Consumer 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.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 mozilla.components.support.ktx.android.content.res.pxToDp
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.toolbar.ToolbarIntegration
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.mvi.ActionBusFactory import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.UIView import org.mozilla.fenix.mvi.UIView
class SearchUIView(container: ViewGroup, bus: ActionBusFactory) : class ToolbarUIView(container: ViewGroup, bus: ActionBusFactory) :
UIView<SearchState>(container, bus) { UIView<SearchState>(container, bus) {
val toolbarIntegration: ToolbarIntegration
override val view: BrowserToolbar = LayoutInflater.from(container.context) override val view: BrowserToolbar = LayoutInflater.from(container.context)
.inflate(R.layout.component_search, container, true) .inflate(R.layout.component_search, container, true)
.findViewById(R.id.toolbar) .findViewById(R.id.toolbar)
@ -30,10 +32,8 @@ class SearchUIView(container: ViewGroup, bus: ActionBusFactory) :
init { init {
view.apply { view.apply {
onUrlClicked = { onUrlClicked = { false }
bus.emit(SearchAction::class.java, SearchAction.UrlClicked) setOnUrlCommitListener { bus.emit(SearchAction::class.java, SearchAction.UrlCommitted(it)) }
false
}
browserActionMargin = resources.pxToDp(browserActionMarginDp) browserActionMargin = resources.pxToDp(browserActionMarginDp)
urlBoxView = urlBackground urlBoxView = urlBackground
@ -43,21 +43,25 @@ class SearchUIView(container: ViewGroup, bus: ActionBusFactory) :
textSize = toolbarTextSizeSp textSize = toolbarTextSizeSp
hint = context.getString(R.string.search_hint) hint = context.getString(R.string.search_hint)
hintColor = ContextCompat.getColor(context, R.color.searchText) 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) { with(view.context) {
AwesomeBarFeature(container.rootView.awesomeBar, view, null, toolbarIntegration = ToolbarIntegration(
onEditComplete = { bus.emit(SearchAction::class.java, SearchAction.EditComplete) }) this,
.addClipboardProvider(this, components.useCases.sessionUseCases.loadUrl) view,
.addSearchProvider( ShippedDomainsProvider().also { it.initialize(this) },
components.search.searchEngineManager.getDefaultSearchEngine(this), components.core.historyStorage
components.useCases.searchUseCases.defaultSearch, )
SearchSuggestionProvider.Mode.MULTIPLE_SUGGESTIONS
)
.addSessionProvider(
components.core.sessionManager,
components.useCases.tabsUseCases.selectTab
)
} }
} }

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_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="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 <LinearLayout
android:id="@+id/pill_wrapper" android:id="@+id/pill_wrapper"
android:background="@color/offwhite" android:background="@color/offwhite"