1
0
Fork 0

Fixes #290: Integrate new Fenix architecture

master
Colin Lee 2019-01-29 13:20:29 -06:00
parent 8af55652be
commit 273f33b244
20 changed files with 361 additions and 82 deletions

View File

@ -104,6 +104,7 @@ dependencies {
implementation Deps.mozilla_concept_toolbar implementation Deps.mozilla_concept_toolbar
implementation Deps.mozilla_browser_awesomebar implementation Deps.mozilla_browser_awesomebar
implementation Deps.mozilla_feature_downloads
implementation Deps.mozilla_browser_domains implementation Deps.mozilla_browser_domains
implementation Deps.mozilla_browser_engine_gecko_nightly implementation Deps.mozilla_browser_engine_gecko_nightly
implementation Deps.mozilla_browser_session implementation Deps.mozilla_browser_session
@ -116,6 +117,7 @@ dependencies {
implementation Deps.mozilla_feature_session implementation Deps.mozilla_feature_session
implementation Deps.mozilla_feature_toolbar implementation Deps.mozilla_feature_toolbar
implementation Deps.mozilla_feature_tabs implementation Deps.mozilla_feature_tabs
implementation Deps.mozilla_service_fretboard
implementation Deps.mozilla_support_ktx implementation Deps.mozilla_support_ktx

View File

@ -0,0 +1,24 @@
/* 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/. */
package org.mozilla.fenix
import android.content.Context
import mozilla.components.service.fretboard.ExperimentDescriptor
const val EXPERIMENTS_JSON_FILENAME = "experiments.json"
const val EXPERIMENTS_BASE_URL = "https://settings.prod.mozaws.net/v1"
const val EXPERIMENTS_BUCKET_NAME = "main"
// TODO Change this after fenix-experiments is created
const val EXPERIMENTS_COLLECTION_NAME = "focus-experiments"
object Experiments {
val AATestDescriptor = ExperimentDescriptor("AAtest")
}
val Context.app: FenixApplication
get() = applicationContext as FenixApplication
fun Context.isInExperiment(descriptor: ExperimentDescriptor): Boolean =
app.fretboard.isInExperiment(this, descriptor)

View File

@ -5,15 +5,50 @@
package org.mozilla.fenix package org.mozilla.fenix
import android.app.Application import android.app.Application
import android.content.Context
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import mozilla.components.service.fretboard.Fretboard
import mozilla.components.service.fretboard.ValuesProvider
import mozilla.components.service.fretboard.source.kinto.KintoExperimentSource
import mozilla.components.service.fretboard.storage.flatfile.FlatFileExperimentStorage
import mozilla.components.support.base.log.Log
import mozilla.components.support.base.log.logger.Logger
import mozilla.components.support.base.log.sink.AndroidLogSink
import org.mozilla.fenix.components.Components import org.mozilla.fenix.components.Components
import java.io.File
class FenixApplication : Application() { class FenixApplication : Application() {
lateinit var fretboard: Fretboard
val components by lazy { Components(this) } val components by lazy { Components(this) }
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
Log.addSink(AndroidLogSink())
setupCrashReporting() setupCrashReporting()
loadExperiments()
}
private fun loadExperiments() {
val experimentsFile = File(filesDir, EXPERIMENTS_JSON_FILENAME)
val experimentSource = KintoExperimentSource(
EXPERIMENTS_BASE_URL, EXPERIMENTS_BUCKET_NAME, EXPERIMENTS_COLLECTION_NAME
)
fretboard = Fretboard(experimentSource, FlatFileExperimentStorage(experimentsFile),
object : ValuesProvider() {
override fun getClientId(context: Context): String {
return "10" // hardcode clientId to determine in or out of experiment
}
})
fretboard.loadExperiments()
Logger.debug("Bucket is ${fretboard.getUserBucket(this@FenixApplication)}")
Logger.debug("Experiments active: ${fretboard.getExperimentsMap(this@FenixApplication)}")
GlobalScope.launch(Dispatchers.IO) {
fretboard.updateExperiments()
}
} }
private fun setupCrashReporting() { private fun setupCrashReporting() {

View File

@ -1,3 +1,7 @@
/* 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/. */
package org.mozilla.fenix package org.mozilla.fenix
import android.app.Activity import android.app.Activity

View File

@ -1,8 +1,10 @@
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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 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.browser package org.mozilla.fenix.browser
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.transition.TransitionInflater import android.transition.TransitionInflater
@ -10,6 +12,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_browser.* import kotlinx.android.synthetic.main.fragment_browser.*
import mozilla.components.feature.downloads.DownloadsFeature import mozilla.components.feature.downloads.DownloadsFeature
@ -19,7 +22,6 @@ import mozilla.components.support.ktx.android.arch.lifecycle.addObservers
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.toolbar.ToolbarIntegration import org.mozilla.fenix.components.toolbar.ToolbarIntegration
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
import androidx.coordinatorlayout.widget.CoordinatorLayout
class BrowserFragment : Fragment() { class BrowserFragment : Fragment() {
@ -40,6 +42,7 @@ class BrowserFragment : Fragment() {
sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move) sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
} }
@SuppressLint("CheckResult")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -60,17 +63,6 @@ class BrowserFragment : Fragment() {
engineView engineView
) )
lifecycle.addObserver(
ToolbarIntegration(
requireContext(),
toolbar,
requireComponents.toolbar.shippedDomainsProvider,
requireComponents.core.historyStorage
)
)
lifecycle.addObservers(sessionFeature)
// Stop toolbar from collapsing if TalkBack is enabled // Stop toolbar from collapsing if TalkBack is enabled
val accessibilityManager = context?.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager val accessibilityManager = context?.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
if (accessibilityManager.isEnabled) { if (accessibilityManager.isEnabled) {
@ -80,7 +72,11 @@ class BrowserFragment : Fragment() {
lifecycle.addObservers( lifecycle.addObservers(
downloadsFeature, downloadsFeature,
sessionFeature sessionFeature,
ToolbarIntegration(requireContext(),
toolbar,
requireComponents.toolbar.shippedDomainsProvider,
requireComponents.core.historyStorage)
) )
} }

View File

@ -1,3 +1,7 @@
/* 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/. */
package org.mozilla.fenix.components package org.mozilla.fenix.components
import android.content.Context import android.content.Context

View File

@ -5,20 +5,15 @@
package org.mozilla.fenix.components.toolbar package org.mozilla.fenix.components.toolbar
import android.content.Context import android.content.Context
import android.view.LayoutInflater
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.OnLifecycleEvent
import androidx.navigation.Navigation import androidx.navigation.Navigation
import androidx.navigation.fragment.FragmentNavigator
import mozilla.components.browser.domains.autocomplete.DomainAutocompleteProvider import mozilla.components.browser.domains.autocomplete.DomainAutocompleteProvider
import mozilla.components.browser.toolbar.BrowserToolbar import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.concept.storage.HistoryStorage import mozilla.components.concept.storage.HistoryStorage
import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature
import mozilla.components.feature.toolbar.ToolbarFeature import mozilla.components.feature.toolbar.ToolbarFeature
import mozilla.components.support.ktx.android.content.res.pxToDp
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.ext.application import org.mozilla.fenix.ext.application
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
@ -33,11 +28,6 @@ class ToolbarIntegration(
init { init {
toolbar.setMenuBuilder(context.components.toolbar.menuBuilder) toolbar.setMenuBuilder(context.components.toolbar.menuBuilder)
toolbar.browserActionMargin = toolbar.resources.pxToDp(browserActionMarginDp)
toolbar.textColor = ContextCompat.getColor(context, R.color.searchText)
toolbar.urlBoxView = LayoutInflater.from(context).inflate(R.layout.layout_url_backround, null)
toolbar.urlBoxMargin = toolbar.resources.pxToDp(urlBoxMargin)
val home = BrowserToolbar.Button( val home = BrowserToolbar.Button(
context.resources.getDrawable( context.resources.getDrawable(
R.drawable.ic_home, R.drawable.ic_home,
@ -49,15 +39,6 @@ class ToolbarIntegration(
toolbar.addBrowserAction(home) toolbar.addBrowserAction(home)
toolbar.onUrlClicked = {
val extras = FragmentNavigator.Extras.Builder().addSharedElement(
toolbar, ViewCompat.getTransitionName(toolbar)!!
).build()
Navigation.findNavController(toolbar)
.navigate(R.id.action_browserFragment_to_searchFragment, null, null, extras)
false
}
ToolbarAutocompleteFeature(toolbar).apply { ToolbarAutocompleteFeature(toolbar).apply {
addDomainProvider(domainAutocompleteProvider) addDomainProvider(domainAutocompleteProvider)
addHistoryStorageProvider(historyStorage) addHistoryStorageProvider(historyStorage)

View File

@ -28,13 +28,16 @@ class HomeFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
val view = inflater.inflate(R.layout.fragment_home, container, false) val view = inflater.inflate(R.layout.fragment_home, container, false)
SessionsComponent(view.homeLayout, ActionBusFactory.get(this)).setup() SessionsComponent(view.homeLayout, ActionBusFactory.get(this))
ActionBusFactory.get(this).logMergedObservables()
return view return view
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
layoutComponents(view.homeLayout)
val searchIcon = requireComponents.search.searchEngineManager.getDefaultSearchEngine(requireContext()).let { val searchIcon = requireComponents.search.searchEngineManager.getDefaultSearchEngine(requireContext()).let {
BitmapDrawable(resources, it.icon) BitmapDrawable(resources, it.icon)
} }
@ -45,7 +48,6 @@ class HomeFragment : Fragment() {
toolbar.setOnClickListener { it -> toolbar.setOnClickListener { it ->
Navigation.findNavController(it).navigate(R.id.action_homeFragment_to_searchFragment, null, null) Navigation.findNavController(it).navigate(R.id.action_homeFragment_to_searchFragment, null, null)
} }
layoutComponents(homeLayout)
} }
companion object { companion object {

View File

@ -7,16 +7,19 @@ package org.mozilla.fenix.home.sessions
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.view.ViewGroup import android.view.ViewGroup
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
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
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 SessionsComponent(private val container: ViewGroup, override val bus: ActionBusFactory) : class SessionsComponent(
UIComponent<SessionsState, SessionsAction, SessionsChange>(bus) { private val container: ViewGroup,
override val bus: ActionBusFactory,
override var initialState: SessionsState = SessionsState(emptyList()) override var initialState: SessionsState = SessionsState(emptyList())
) :
UIComponent<SessionsState, SessionsAction, SessionsChange>(bus) {
override val reducer: (SessionsState, SessionsChange) -> SessionsState = { state, change -> override val reducer: (SessionsState, SessionsChange) -> SessionsState = { state, change ->
when (change) { when (change) {
@ -26,9 +29,17 @@ class SessionsComponent(private val container: ViewGroup, override val bus: Acti
override fun initView() = SessionsUIView(container, bus) override fun initView() = SessionsUIView(container, bus)
init {
setup()
}
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
fun setup(): SessionsComponent { fun setup(): SessionsComponent {
render(reducer) render(reducer)
getUserInteractionEvents<SessionsAction>()
.subscribe {
Logger("SessionsComponent").debug(it.toString())
}
return this return this
} }
} }

View File

@ -1,13 +1,17 @@
/* 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/. */
package org.mozilla.fenix.home.sessions package org.mozilla.fenix.home.sessions
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID
import kotlinx.android.synthetic.main.component_sessions.* import kotlinx.android.synthetic.main.component_sessions.*
import kotlinx.android.synthetic.main.fragment_home.* import kotlinx.android.synthetic.main.fragment_home.*
import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder.Side.TOP
import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder.Side.BOTTOM import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder.Side.BOTTOM
import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder.Side.START
import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder.Side.END import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder.Side.END
import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder.Side.START
import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder.Side.TOP
import org.jetbrains.anko.constraint.layout.applyConstraintSet import org.jetbrains.anko.constraint.layout.applyConstraintSet
import org.mozilla.fenix.home.HomeFragment import org.mozilla.fenix.home.HomeFragment

View File

@ -21,6 +21,7 @@ class SessionsUIView(container: ViewGroup, bus: ActionBusFactory) :
.findViewById(R.id.session_list) .findViewById(R.id.session_list)
private val sessionsAdapter = SessionsAdapter() private val sessionsAdapter = SessionsAdapter()
init { init {
view.apply { view.apply {
layoutManager = LinearLayoutManager(container.context) layoutManager = LinearLayoutManager(container.context)

View File

@ -0,0 +1,67 @@
/* 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/. */
package org.mozilla.fenix.search
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
import org.mozilla.fenix.mvi.Reducer
import org.mozilla.fenix.mvi.UIComponent
import org.mozilla.fenix.mvi.ViewState
class SearchComponent(
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
}
}
override fun initView() = SearchUIView(container, bus)
init {
setup()
}
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
sealed class SearchAction : Action {
object UrlClicked : SearchAction()
object EditComplete : SearchAction()
}
sealed class SearchChange : Change {
object Changed : SearchChange()
}

View File

@ -1,72 +1,64 @@
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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 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
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.core.content.ContextCompat
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.*
import kotlinx.android.synthetic.main.fragment_search.view.*
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
import mozilla.components.feature.awesomebar.AwesomeBarFeature
import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.toolbar.ToolbarIntegration import org.mozilla.fenix.components.toolbar.ToolbarIntegration
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.mvi.ActionBusFactory
class SearchFragment : Fragment() { class SearchFragment : Fragment() {
private lateinit var awesomeBarFeature: AwesomeBarFeature private lateinit var searchComponent: SearchComponent
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
return 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),
{ v -> transitionToBrowser(v) })
return view
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
toolbar.editMode() searchComponent.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)
lifecycle.addObserver( lifecycle.addObserver(
ToolbarIntegration( ToolbarIntegration(
requireContext(), requireContext(),
toolbar, searchComponent.getView(),
ShippedDomainsProvider().also { it.initialize(requireContext()) }, ShippedDomainsProvider().also { it.initialize(requireContext()) },
requireComponents.core.historyStorage requireComponents.core.historyStorage
) )
) )
awesomeBarFeature = AwesomeBarFeature(awesomeBar, toolbar, null, onEditComplete = ::userDidSearch)
.addClipboardProvider(requireContext(), requireComponents.useCases.sessionUseCases.loadUrl)
.addSearchProvider(
requireComponents.search.searchEngineManager.getDefaultSearchEngine(requireContext()),
requireComponents.useCases.searchUseCases.defaultSearch,
SearchSuggestionProvider.Mode.MULTIPLE_SUGGESTIONS)
.addSessionProvider(
requireComponents.core.sessionManager,
requireComponents.useCases.tabsUseCases.selectTab)
toolbar_wrapper.clipToOutline = false toolbar_wrapper.clipToOutline = false
toolbar.apply {
textColor = ContextCompat.getColor(context, R.color.searchText)
textSize = toolbarTextSizeSp
hint = context.getString(R.string.search_hint)
hintColor = ContextCompat.getColor(context, R.color.searchText)
}
} }
private fun userDidSearch() { private fun transitionToBrowser(toolbar: View) {
Navigation.findNavController(toolbar).navigate(R.id.action_searchFragment_to_browserFragment, null, null) Navigation.findNavController(toolbar)
.navigate(R.id.action_searchFragment_to_browserFragment, null, null)
} }
companion object { companion object {

View File

@ -0,0 +1,79 @@
/* 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/. */
package org.mozilla.fenix.search
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.fragment_search.*
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.TOP
import org.jetbrains.anko.constraint.layout.applyConstraintSet
import org.mozilla.fenix.Experiments.AATestDescriptor
import org.mozilla.fenix.isInExperiment
internal fun SearchFragment.layoutComponents(layout: ConstraintLayout) {
context?.let {
when {
it.isInExperiment(AATestDescriptor) -> {
setInExperimentConstraints(layout)
}
else -> {
setOutOfExperimentConstraints(layout)
}
}
} // we're unattached if context is null
}
internal fun SearchFragment.setInExperimentConstraints(layout: ConstraintLayout) {
Logger.debug("Loading in experiment constraints")
layout.applyConstraintSet {
toolbar_wrapper {
connect(
TOP to TOP of UNSET,
BOTTOM to TOP of pill_wrapper
)
}
awesomeBar {
connect(
TOP to TOP of PARENT_ID,
BOTTOM to TOP of toolbar_wrapper
)
}
(awesomeBar.layoutManager as? LinearLayoutManager)?.reverseLayout = true
pill_wrapper {
connect(
BOTTOM to BOTTOM of PARENT_ID
)
}
}
}
internal fun SearchFragment.setOutOfExperimentConstraints(layout: ConstraintLayout) {
Logger.debug("Loading out of experiment constraints")
layout.applyConstraintSet {
toolbar_wrapper {
connect(
TOP to TOP of PARENT_ID,
BOTTOM to TOP of UNSET
)
}
awesomeBar {
connect(
TOP to TOP of UNSET,
TOP to BOTTOM of toolbar_wrapper,
BOTTOM to TOP of pill_wrapper
)
}
(awesomeBar.layoutManager as? LinearLayoutManager)?.reverseLayout = false
pill_wrapper {
connect(
BOTTOM to BOTTOM of PARENT_ID
)
}
}
}

View File

@ -0,0 +1,72 @@
/* 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/. */
package org.mozilla.fenix.search
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.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.ext.components
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.UIView
class SearchUIView(container: ViewGroup, bus: ActionBusFactory) :
UIView<SearchState>(container, bus) {
override val view: BrowserToolbar = LayoutInflater.from(container.context)
.inflate(R.layout.component_search, container, true)
.findViewById(R.id.toolbar)
private val urlBackground = LayoutInflater.from(container.context)
.inflate(R.layout.layout_url_backround, container, false)
init {
view.apply {
onUrlClicked = {
bus.emit(SearchAction::class.java, SearchAction.UrlClicked)
false
}
browserActionMargin = resources.pxToDp(browserActionMarginDp)
urlBoxView = urlBackground
urlBoxMargin = this.resources.pxToDp(urlBoxMarginDp)
textColor = ContextCompat.getColor(context, R.color.searchText)
textSize = toolbarTextSizeSp
hint = context.getString(R.string.search_hint)
hintColor = ContextCompat.getColor(context, R.color.searchText)
}
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
)
}
}
override fun updateView() = Consumer<SearchState> {
}
companion object {
const val toolbarTextSizeSp = 14f
const val browserActionMarginDp = 8
const val urlBoxMarginDp = 8
}
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<mozilla.components.browser.toolbar.BrowserToolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="8dp"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"/>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/session_list" android:id="@+id/session_list"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"

View File

@ -8,9 +8,10 @@
xmlns:mozac="http://schemas.android.com/apk/res-auto" xmlns:mozac="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".search.SearchFragment"> tools:context=".search.SearchFragment"
android:id="@+id/search_layout">
<FrameLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/toolbar_wrapper" android:id="@+id/toolbar_wrapper"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -21,17 +22,7 @@
android:outlineProvider="paddedBounds" android:outlineProvider="paddedBounds"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent"/>
<mozilla.components.browser.toolbar.BrowserToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="8dp"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true" />
</FrameLayout>
<mozilla.components.browser.awesomebar.BrowserAwesomeBar <mozilla.components.browser.awesomebar.BrowserAwesomeBar
android:id="@+id/awesomeBar" android:id="@+id/awesomeBar"

View File

@ -13,7 +13,8 @@
<fragment android:id="@+id/searchFragment" android:name="org.mozilla.fenix.search.SearchFragment" <fragment android:id="@+id/searchFragment" android:name="org.mozilla.fenix.search.SearchFragment"
android:label="fragment_search" tools:layout="@layout/fragment_search"> android:label="fragment_search" tools:layout="@layout/fragment_search">
<action android:id="@+id/action_searchFragment_to_browserFragment" app:destination="@id/browserFragment"/> <action android:id="@+id/action_searchFragment_to_browserFragment" app:destination="@id/browserFragment"
app:popUpTo="@id/homeFragment"/>
</fragment> </fragment>
<fragment android:id="@+id/browserFragment" android:name="org.mozilla.fenix.browser.BrowserFragment" <fragment android:id="@+id/browserFragment" android:name="org.mozilla.fenix.browser.BrowserFragment"

View File

@ -73,6 +73,8 @@ object Deps {
const val mozilla_feature_prompts = "org.mozilla.components:feature-prompts:${Versions.mozilla_android_components}" const val mozilla_feature_prompts = "org.mozilla.components:feature-prompts:${Versions.mozilla_android_components}"
const val mozilla_feature_toolbar = "org.mozilla.components:feature-toolbar:${Versions.mozilla_android_components}" const val mozilla_feature_toolbar = "org.mozilla.components:feature-toolbar:${Versions.mozilla_android_components}"
const val mozilla_service_fretboard = "org.mozilla.components:service-fretboard:${Versions.mozilla_android_components}"
const val mozilla_lib_crash = "org.mozilla.components:lib-crash:${Versions.mozilla_android_components}" const val mozilla_lib_crash = "org.mozilla.components:lib-crash:${Versions.mozilla_android_components}"
const val mozilla_support_ktx = "org.mozilla.components:support-ktx:${Versions.mozilla_android_components}" const val mozilla_support_ktx = "org.mozilla.components:support-ktx:${Versions.mozilla_android_components}"