diff --git a/app/build.gradle b/app/build.gradle index 202e1e8e6..3769b8d2e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -104,6 +104,7 @@ dependencies { implementation Deps.mozilla_concept_toolbar implementation Deps.mozilla_browser_awesomebar + implementation Deps.mozilla_feature_downloads implementation Deps.mozilla_browser_domains implementation Deps.mozilla_browser_engine_gecko_nightly implementation Deps.mozilla_browser_session @@ -116,6 +117,7 @@ dependencies { implementation Deps.mozilla_feature_session implementation Deps.mozilla_feature_toolbar implementation Deps.mozilla_feature_tabs + implementation Deps.mozilla_service_fretboard implementation Deps.mozilla_support_ktx diff --git a/app/src/main/java/org/mozilla/fenix/Experiments.kt b/app/src/main/java/org/mozilla/fenix/Experiments.kt new file mode 100644 index 000000000..dea011638 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/Experiments.kt @@ -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) diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index e66528104..6385a1f41 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -5,15 +5,50 @@ package org.mozilla.fenix 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 java.io.File class FenixApplication : Application() { + lateinit var fretboard: Fretboard + val components by lazy { Components(this) } override fun onCreate() { super.onCreate() + Log.addSink(AndroidLogSink()) 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() { diff --git a/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt b/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt index 6c1e2e4d0..5a4ab787e 100644 --- a/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt @@ -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 import android.app.Activity diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index f6a4c3379..f9175308b 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -1,8 +1,10 @@ /* 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.browser +import android.annotation.SuppressLint import android.content.Context import android.os.Bundle import android.transition.TransitionInflater @@ -10,6 +12,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityManager +import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.fragment.app.Fragment import kotlinx.android.synthetic.main.fragment_browser.* 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.components.toolbar.ToolbarIntegration import org.mozilla.fenix.ext.requireComponents -import androidx.coordinatorlayout.widget.CoordinatorLayout class BrowserFragment : Fragment() { @@ -40,6 +42,7 @@ class BrowserFragment : Fragment() { sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move) } + @SuppressLint("CheckResult") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -60,17 +63,6 @@ class BrowserFragment : Fragment() { engineView ) - lifecycle.addObserver( - ToolbarIntegration( - requireContext(), - toolbar, - requireComponents.toolbar.shippedDomainsProvider, - requireComponents.core.historyStorage - ) - ) - - lifecycle.addObservers(sessionFeature) - // Stop toolbar from collapsing if TalkBack is enabled val accessibilityManager = context?.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager if (accessibilityManager.isEnabled) { @@ -80,7 +72,11 @@ class BrowserFragment : Fragment() { lifecycle.addObservers( downloadsFeature, - sessionFeature + sessionFeature, + ToolbarIntegration(requireContext(), + toolbar, + requireComponents.toolbar.shippedDomainsProvider, + requireComponents.core.historyStorage) ) } diff --git a/app/src/main/java/org/mozilla/fenix/components/Utilities.kt b/app/src/main/java/org/mozilla/fenix/components/Utilities.kt index f742cbb29..40438e9f3 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Utilities.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Utilities.kt @@ -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 import android.content.Context diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt index 3b626cd15..14c25cfa0 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt @@ -5,20 +5,15 @@ package org.mozilla.fenix.components.toolbar 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.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import androidx.navigation.Navigation -import androidx.navigation.fragment.FragmentNavigator import mozilla.components.browser.domains.autocomplete.DomainAutocompleteProvider import mozilla.components.browser.toolbar.BrowserToolbar import mozilla.components.concept.storage.HistoryStorage import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature import mozilla.components.feature.toolbar.ToolbarFeature -import mozilla.components.support.ktx.android.content.res.pxToDp import org.mozilla.fenix.R import org.mozilla.fenix.ext.application import org.mozilla.fenix.ext.components @@ -33,11 +28,6 @@ class ToolbarIntegration( init { 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( context.resources.getDrawable( R.drawable.ic_home, @@ -49,15 +39,6 @@ class ToolbarIntegration( 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 { addDomainProvider(domainAutocompleteProvider) addHistoryStorageProvider(historyStorage) diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index f7c812164..54e582c5e 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -28,13 +28,16 @@ class HomeFragment : Fragment() { savedInstanceState: Bundle? ): View? { 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 } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + layoutComponents(view.homeLayout) + val searchIcon = requireComponents.search.searchEngineManager.getDefaultSearchEngine(requireContext()).let { BitmapDrawable(resources, it.icon) } @@ -45,7 +48,6 @@ class HomeFragment : Fragment() { toolbar.setOnClickListener { it -> Navigation.findNavController(it).navigate(R.id.action_homeFragment_to_searchFragment, null, null) } - layoutComponents(homeLayout) } companion object { diff --git a/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsComponent.kt b/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsComponent.kt index b9afd97de..6e1adbceb 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsComponent.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsComponent.kt @@ -7,16 +7,19 @@ package org.mozilla.fenix.home.sessions import android.annotation.SuppressLint import android.view.ViewGroup 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.ActionBusFactory import org.mozilla.fenix.mvi.Change import org.mozilla.fenix.mvi.UIComponent import org.mozilla.fenix.mvi.ViewState -class SessionsComponent(private val container: ViewGroup, override val bus: ActionBusFactory) : - UIComponent(bus) { - +class SessionsComponent( + private val container: ViewGroup, + override val bus: ActionBusFactory, override var initialState: SessionsState = SessionsState(emptyList()) +) : + UIComponent(bus) { override val reducer: (SessionsState, SessionsChange) -> SessionsState = { state, change -> when (change) { @@ -26,9 +29,17 @@ class SessionsComponent(private val container: ViewGroup, override val bus: Acti override fun initView() = SessionsUIView(container, bus) + init { + setup() + } + @SuppressLint("CheckResult") fun setup(): SessionsComponent { render(reducer) + getUserInteractionEvents() + .subscribe { + Logger("SessionsComponent").debug(it.toString()) + } return this } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsLayouts.kt b/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsLayouts.kt index 46f5ea604..6752dead4 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsLayouts.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsLayouts.kt @@ -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 import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID import kotlinx.android.synthetic.main.component_sessions.* 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.START 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.mozilla.fenix.home.HomeFragment diff --git a/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsUIView.kt b/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsUIView.kt index 2975e6579..95d201431 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsUIView.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessions/SessionsUIView.kt @@ -21,6 +21,7 @@ class SessionsUIView(container: ViewGroup, bus: ActionBusFactory) : .findViewById(R.id.session_list) private val sessionsAdapter = SessionsAdapter() + init { view.apply { layoutManager = LinearLayoutManager(container.context) diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchComponent.kt b/app/src/main/java/org/mozilla/fenix/search/SearchComponent.kt new file mode 100644 index 000000000..ac310d299 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/search/SearchComponent.kt @@ -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(bus) { + + override val reducer: Reducer = { 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() + .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() +} diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt index 37aff5027..f0d1838ae 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt @@ -1,72 +1,64 @@ /* 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.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.content.ContextCompat 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 mozilla.components.feature.awesomebar.AwesomeBarFeature -import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider import org.mozilla.fenix.R import org.mozilla.fenix.components.toolbar.ToolbarIntegration import org.mozilla.fenix.ext.requireComponents +import org.mozilla.fenix.mvi.ActionBusFactory class SearchFragment : Fragment() { - private lateinit var awesomeBarFeature: AwesomeBarFeature + private lateinit var searchComponent: SearchComponent override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): 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() { super.onResume() - toolbar.editMode() + searchComponent.editMode() } + @SuppressLint("CheckResult") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + layoutComponents(view.search_layout) + lifecycle.addObserver( ToolbarIntegration( - requireContext(), - toolbar, - ShippedDomainsProvider().also { it.initialize(requireContext()) }, - requireComponents.core.historyStorage + requireContext(), + searchComponent.getView(), + ShippedDomainsProvider().also { it.initialize(requireContext()) }, + 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.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() { - Navigation.findNavController(toolbar).navigate(R.id.action_searchFragment_to_browserFragment, null, null) + private fun transitionToBrowser(toolbar: View) { + Navigation.findNavController(toolbar) + .navigate(R.id.action_searchFragment_to_browserFragment, null, null) } companion object { diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchLayouts.kt b/app/src/main/java/org/mozilla/fenix/search/SearchLayouts.kt new file mode 100644 index 000000000..cdadb77bd --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/search/SearchLayouts.kt @@ -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 + ) + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchUIView.kt b/app/src/main/java/org/mozilla/fenix/search/SearchUIView.kt new file mode 100644 index 000000000..28c860586 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/search/SearchUIView.kt @@ -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(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 { + } + + companion object { + const val toolbarTextSizeSp = 14f + const val browserActionMarginDp = 8 + const val urlBoxMarginDp = 8 + } +} diff --git a/app/src/main/res/layout/component_search.xml b/app/src/main/res/layout/component_search.xml new file mode 100644 index 000000000..a490703e0 --- /dev/null +++ b/app/src/main/res/layout/component_search.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/component_sessions.xml b/app/src/main/res/layout/component_sessions.xml index 9c3c30702..ddab1eff5 100644 --- a/app/src/main/res/layout/component_sessions.xml +++ b/app/src/main/res/layout/component_sessions.xml @@ -1,6 +1,7 @@ + tools:context=".search.SearchFragment" + android:id="@+id/search_layout"> - - - - + app:layout_constraintTop_toTopOf="parent"/> - +