1
0
Fork 0

Fixes #351 Create home screen component for multitasking

master
Colin Lee 2019-02-08 12:35:48 -06:00
parent 77883c0f30
commit f09dc2453f
28 changed files with 527 additions and 45 deletions

View File

@ -6,6 +6,7 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply from: "$project.rootDir/automation/gradle/versionCode.gradle"
apply plugin: 'androidx.navigation.safeargs'
android {
compileSdkVersion 28

View File

@ -17,7 +17,6 @@ import mozilla.components.concept.engine.EngineView
import mozilla.components.feature.intent.IntentProcessor
import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.utils.SafeIntent
import org.mozilla.fenix.browser.BrowserFragment
import org.mozilla.fenix.ext.components
open class HomeActivity : AppCompatActivity() {
@ -71,10 +70,8 @@ open class HomeActivity : AppCompatActivity() {
private fun openToBrowser() {
val sessionId = SafeIntent(intent).getStringExtra(IntentProcessor.ACTIVE_SESSION_ID)
val host = supportFragmentManager.findFragmentById(R.id.container) as NavHostFragment
host.navController.navigate(R.id.action_global_browser, Bundle().apply {
putString(BrowserFragment.SESSION_ID, sessionId)
})
val directions = NavGraphDirections.actionGlobalBrowser(sessionId)
host.navController.navigate(directions)
}
companion object {

View File

@ -55,17 +55,19 @@ class BrowserFragment : Fragment(), BackHandler {
private val findInPageIntegration = ViewBoundFeatureWrapper<FindInPageIntegration>()
private val customTabsToolbarFeature = ViewBoundFeatureWrapper<CustomTabsToolbarFeature>()
private val toolbarIntegration = ViewBoundFeatureWrapper<ToolbarIntegration>()
var sessionId: String? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
sessionId = BrowserFragmentArgs.fromBundle(arguments!!).sessionId
val view = inflater.inflate(R.layout.fragment_browser, container, false)
toolbarComponent = ToolbarComponent(
view.browserLayout,
ActionBusFactory.get(this),
ActionBusFactory.get(this), sessionId,
SearchState("", isEditing = false)
)
@ -98,7 +100,7 @@ class BrowserFragment : Fragment(), BackHandler {
.subscribe {
when (it) {
is SearchAction.ToolbarTapped -> Navigation.findNavController(toolbar)
.navigate(R.id.action_browserFragment_to_searchFragment, null, null)
.navigate(BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(null))
is SearchAction.ToolbarMenuItemTapped -> handleToolbarItemInteraction(it)
}
}
@ -109,8 +111,6 @@ class BrowserFragment : Fragment(), BackHandler {
(activity as AppCompatActivity).supportActionBar?.hide()
val sessionId = arguments?.getString(SESSION_ID)
val sessionManager = requireComponents.core.sessionManager
contextMenuFeature.set(
@ -182,8 +182,6 @@ class BrowserFragment : Fragment(), BackHandler {
if (sessionFeature.onBackPressed()) return true
if (customTabsToolbarFeature.onBackPressed()) return true
// We'll want to improve this when we add multitasking
requireComponents.core.sessionManager.remove()
return false
}
@ -211,9 +209,9 @@ class BrowserFragment : Fragment(), BackHandler {
is ToolbarMenu.Item.Forward -> sessionUseCases.goForward.invoke()
is ToolbarMenu.Item.Reload -> sessionUseCases.reload.invoke()
is ToolbarMenu.Item.Settings -> Navigation.findNavController(toolbar)
.navigate(R.id.action_browserFragment_to_settingsFragment, null, null)
.navigate(BrowserFragmentDirections.actionBrowserFragmentToSettingsFragment())
is ToolbarMenu.Item.Library -> Navigation.findNavController(toolbar)
.navigate(R.id.action_browserFragment_to_libraryFragment, null, null)
.navigate(BrowserFragmentDirections.actionBrowserFragmentToLibraryFragment())
is ToolbarMenu.Item.RequestDesktop -> sessionUseCases.requestDesktopSite.invoke(action.item.isChecked)
is ToolbarMenu.Item.Share -> requireComponents.core.sessionManager
.selectedSession?.url?.apply { requireContext().share(this) }
@ -231,7 +229,6 @@ class BrowserFragment : Fragment(), BackHandler {
}
companion object {
const val SESSION_ID = "session_id"
private const val REQUEST_CODE_DOWNLOAD_PERMISSIONS = 1
private const val REQUEST_CODE_PROMPT_PERMISSIONS = 2
private const val TOOLBAR_HEIGHT = 56f

View File

@ -0,0 +1,21 @@
/* 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.ext
import android.graphics.Rect
import android.view.TouchDelegate
import android.view.View
fun View?.increaseTapArea(extraDps: Int) {
this!!.post {
val touchRect = Rect()
getHitRect(touchRect)
touchRect.top -= extraDps
touchRect.left -= extraDps
touchRect.right += extraDps
touchRect.bottom += extraDps
(parent as View).touchDelegate = TouchDelegate(touchRect, this)
}
}

View File

@ -4,6 +4,7 @@
package org.mozilla.fenix.home
import android.annotation.SuppressLint
import android.content.res.Resources
import android.graphics.drawable.BitmapDrawable
import android.os.Bundle
@ -16,28 +17,42 @@ import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.fragment_home.view.*
import kotlinx.android.synthetic.main.tab_list_header.view.*
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.ThemeManager
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.home.sessions.SessionsComponent
import org.mozilla.fenix.home.sessions.layoutComponents
import org.mozilla.fenix.home.tabs.TabsAction
import org.mozilla.fenix.home.tabs.TabsChange
import org.mozilla.fenix.home.tabs.TabsComponent
import org.mozilla.fenix.home.tabs.TabsState
import org.mozilla.fenix.isPrivate
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.mvi.getSafeManagedObservable
import kotlin.math.roundToInt
class HomeFragment : Fragment() {
private val bus = ActionBusFactory.get(this)
private var sessionObserver: SessionManager.Observer? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_home, container, false)
SessionsComponent(view.homeLayout, ActionBusFactory.get(this))
TabsComponent(view.homeLayout, bus, TabsState(requireComponents.core.sessionManager.sessions))
SessionsComponent(view.homeLayout, bus)
ActionBusFactory.get(this).logMergedObservables()
return view
}
@SuppressLint("CheckResult")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -45,20 +60,41 @@ class HomeFragment : Fragment() {
layoutComponents(view.homeLayout)
getSafeManagedObservable<TabsAction>()
.subscribe {
when (it) {
is TabsAction.Select -> {
requireComponents.core.sessionManager.select(it.session)
val directions = HomeFragmentDirections.actionHomeFragmentToBrowserFragment(it.session.id)
Navigation.findNavController(view).navigate(directions)
}
is TabsAction.Close -> {
requireComponents.core.sessionManager.remove(it.session)
}
}
}
val searchIcon = requireComponents.search.searchEngineManager.getDefaultSearchEngine(requireContext()).let {
BitmapDrawable(resources, it.icon)
}
// Temporary so we can easily test settings
view.menuButton.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_homeFragment_to_settingsFragment, null, null)
val directions = HomeFragmentDirections.actionHomeFragmentToSettingsFragment()
Navigation.findNavController(it).navigate(directions)
}
view.toolbar.setCompoundDrawablesWithIntrinsicBounds(searchIcon, null, null, null)
val roundToInt = (toolbarPaddingDp * Resources.getSystem().displayMetrics.density).roundToInt()
view.toolbar.compoundDrawablePadding = roundToInt
view.toolbar.setOnClickListener { it ->
Navigation.findNavController(it).navigate(R.id.action_homeFragment_to_searchFragment, null, null)
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null)
Navigation.findNavController(it).navigate(directions)
}
view.add_tab_button.increaseTapArea(addTabButtonIncreaseDps)
view.add_tab_button.setOnClickListener {
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null)
Navigation.findNavController(it).navigate(directions)
}
// There is currently an issue with visibility changes in ConstraintLayout 2.0.0-alpha3
@ -85,6 +121,7 @@ class HomeFragment : Fragment() {
firstKeyTrigger.conditionallyFire(progress)
secondKeyTrigger.conditionallyFire(progress)
}
override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) { }
})
@ -148,7 +185,56 @@ class HomeFragment : Fragment() {
}
}
override fun onResume() {
super.onResume()
sessionObserver = subscribeToSessions()
}
override fun onPause() {
super.onPause()
sessionObserver?.let {
requireComponents.core.sessionManager.unregister(it)
}
}
private fun subscribeToSessions(): SessionManager.Observer {
val observer = object : SessionManager.Observer {
override fun onSessionAdded(session: Session) {
super.onSessionAdded(session)
getManagedEmitter<TabsChange>().onNext(
TabsChange.Changed(requireComponents.core.sessionManager.sessions))
}
override fun onSessionRemoved(session: Session) {
super.onSessionRemoved(session)
getManagedEmitter<TabsChange>().onNext(
TabsChange.Changed(requireComponents.core.sessionManager.sessions))
}
override fun onSessionSelected(session: Session) {
super.onSessionSelected(session)
getManagedEmitter<TabsChange>().onNext(
TabsChange.Changed(requireComponents.core.sessionManager.sessions))
}
override fun onSessionsRestored() {
super.onSessionsRestored()
getManagedEmitter<TabsChange>().onNext(
TabsChange.Changed(requireComponents.core.sessionManager.sessions))
}
override fun onAllSessionsRemoved() {
super.onAllSessionsRemoved()
getManagedEmitter<TabsChange>().onNext(
TabsChange.Changed(requireComponents.core.sessionManager.sessions))
}
}
requireComponents.core.sessionManager.register(observer)
return observer
}
companion object {
const val addTabButtonIncreaseDps = 8
const val toolbarPaddingDp = 12f
const val firstKeyTriggerFrame = 55
const val secondKeyTriggerFrame = 90

View File

@ -2,27 +2,41 @@
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
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.component_tabs.*
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.tab_list_header.*
import org.jetbrains.anko.constraint.layout.ConstraintSetBuilder.Side.BOTTOM
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
fun HomeFragment.layoutComponents(layout: ConstraintLayout) {
layout.applyConstraintSet {
tabs_header {
connect(
TOP to BOTTOM of homeDivider,
START to START of tabs_list,
END to END of PARENT_ID
)
}
tabs_list {
connect(
TOP to BOTTOM of tabs_header,
START to START of PARENT_ID,
END to END of PARENT_ID
)
}
session_list {
connect(
BOTTOM to BOTTOM of PARENT_ID,
TOP to BOTTOM of tabs_list,
START to START of PARENT_ID,
END to END of PARENT_ID,
TOP to BOTTOM of toolbar_wrapper
END to END of PARENT_ID
)
}
}

View File

@ -24,7 +24,7 @@ class SessionsComponent(
override val reducer: (SessionsState, SessionsChange) -> SessionsState = { state, change ->
when (change) {
is SessionsChange.SessionsChanged -> state // copy state with changes here
is SessionsChange.Changed -> state // copy state with changes here
}
}
@ -42,5 +42,5 @@ sealed class SessionsAction : Action {
}
sealed class SessionsChange : Change {
object SessionsChanged : SessionsChange()
object Changed : SessionsChange()
}

View File

@ -0,0 +1,106 @@
/* 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.tabs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observer
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.tab_list_row.*
import mozilla.components.browser.session.Session
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.increaseTapArea
class TabsAdapter(private val actionEmitter: Observer<TabsAction>) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var sessions = listOf<Session>()
set(value) {
val diffResult = DiffUtil.calculateDiff(TabsDiffCallback(field, value), true)
field = value
diffResult.dispatchUpdatesTo(this@TabsAdapter)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return TabViewHolder(view, actionEmitter)
}
override fun getItemViewType(position: Int) = TabViewHolder.LAYOUT_ID
override fun getItemCount(): Int = sessions.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is TabViewHolder -> {
holder.bindSession(sessions[position])
}
}
}
private class TabViewHolder(
view: View,
actionEmitter: Observer<TabsAction>,
override val containerView: View? = view
) :
RecyclerView.ViewHolder(view), LayoutContainer {
var session: Session? = null
init {
item_tab.setOnClickListener {
actionEmitter.onNext(TabsAction.Select(session!!))
}
close_tab_button?.run {
increaseTapArea(closeButtonIncreaseDps)
setOnClickListener {
actionEmitter.onNext(TabsAction.Close(session!!))
}
}
}
fun bindSession(session: Session) {
this.session = session
text_url.text = session.url
}
companion object {
const val closeButtonIncreaseDps = 12
const val LAYOUT_ID = R.layout.tab_list_row
}
}
}
class TabsDiffCallback(
private val oldList: List<Session>,
private val newList: List<Session>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldSession = oldList[oldItemPosition]
val newSession = newList[newItemPosition]
val diffBundle = Bundle()
if (oldSession.url != newSession.url) {
diffBundle.putString("url", newSession.url)
}
return if (diffBundle.size() == 0) null else diffBundle
}
}

View File

@ -0,0 +1,47 @@
/* 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.tabs
import android.view.ViewGroup
import mozilla.components.browser.session.Session
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 TabsComponent(
private val container: ViewGroup,
bus: ActionBusFactory,
override var initialState: TabsState = TabsState(listOf())
) :
UIComponent<TabsState, TabsAction, TabsChange>(
bus.getManagedEmitter(TabsAction::class.java),
bus.getSafeManagedObservable(TabsChange::class.java)
) {
override val reducer: (TabsState, TabsChange) -> TabsState = { state, change ->
when (change) {
is TabsChange.Changed -> state.copy(sessions = change.sessions)
}
}
override fun initView() = TabsUIView(container, actionEmitter, changesObservable)
init {
render(reducer)
}
}
data class TabsState(val sessions: List<Session>) : ViewState
sealed class TabsAction : Action {
data class Select(val session: Session) : TabsAction()
data class Close(val session: Session) : TabsAction()
}
sealed class TabsChange : Change {
data class Changed(val sessions: List<Session>) : TabsChange()
}

View File

@ -0,0 +1,49 @@
/* 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.tabs
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.functions.Consumer
import org.mozilla.fenix.R
import org.mozilla.fenix.mvi.UIView
class TabsUIView(
container: ViewGroup,
actionEmitter: Observer<TabsAction>,
changesObservable: Observable<TabsChange>
) :
UIView<TabsState, TabsAction, TabsChange>(container, actionEmitter, changesObservable) {
private val header: ConstraintLayout = LayoutInflater.from(container.context)
.inflate(R.layout.tab_list_header, container, true)
.findViewById(R.id.tabs_header)
override val view: RecyclerView = LayoutInflater.from(container.context)
.inflate(R.layout.component_tabs, container, true)
.findViewById(R.id.tabs_list)
private val tabsAdapter = TabsAdapter(actionEmitter)
init {
view.apply {
layoutManager = LinearLayoutManager(container.context)
adapter = tabsAdapter
itemAnimator = DefaultItemAnimator()
}
}
override fun updateView() = Consumer<TabsState> {
tabsAdapter.sessions = it.sessions
header.visibility = if (it.sessions.isEmpty()) View.GONE else View.VISIBLE
}
}

View File

@ -15,10 +15,12 @@ import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.fragment_search.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.mvi.getSafeManagedObservable
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.awesomebar.AwesomeBarState
import org.mozilla.fenix.search.toolbar.SearchAction
import org.mozilla.fenix.search.toolbar.SearchState
import org.mozilla.fenix.search.toolbar.ToolbarComponent
@ -33,13 +35,18 @@ class SearchFragment : Fragment() {
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val sessionId = SearchFragmentArgs.fromBundle(arguments!!).sessionId
val view = inflater.inflate(R.layout.fragment_search, container, false)
toolbarComponent = ToolbarComponent(
view.toolbar_wrapper,
ActionBusFactory.get(this),
sessionId,
SearchState("", isEditing = true)
)
awesomeBarComponent = AwesomeBarComponent(view.search_layout, ActionBusFactory.get(this))
awesomeBarComponent = AwesomeBarComponent(
view.search_layout, ActionBusFactory.get(this),
AwesomeBarState("", sessionId == null)
)
ActionBusFactory.get(this).logMergedObservables()
return view
}
@ -61,8 +68,7 @@ class SearchFragment : Fragment() {
when (it) {
is SearchAction.UrlCommitted -> transitionToBrowser()
is SearchAction.TextChanged -> {
ActionBusFactory.get(this)
.emit(AwesomeBarChange::class.java, AwesomeBarChange.UpdateQuery(it.query))
getManagedEmitter<AwesomeBarChange>().onNext(AwesomeBarChange.UpdateQuery(it.query))
}
}
}
@ -76,7 +82,8 @@ class SearchFragment : Fragment() {
}
private fun transitionToBrowser() {
Navigation.findNavController(view!!.search_layout)
.navigate(R.id.action_searchFragment_to_browserFragment, null, null)
val sessionId = SearchFragmentArgs.fromBundle(arguments!!).sessionId
val directions = SearchFragmentDirections.actionSearchFragmentToBrowserFragment(sessionId)
Navigation.findNavController(view!!.search_layout).navigate(directions)
}
}

View File

@ -11,7 +11,7 @@ import org.mozilla.fenix.mvi.Reducer
import org.mozilla.fenix.mvi.UIComponent
import org.mozilla.fenix.mvi.ViewState
data class AwesomeBarState(val query: String) : ViewState
data class AwesomeBarState(val query: String, val useNewTab: Boolean = false) : ViewState
sealed class AwesomeBarAction : Action {
object ItemSelected : AwesomeBarAction()
@ -35,7 +35,7 @@ class AwesomeBarComponent(
}
}
override fun initView() = AwesomeBarUIView(container, actionEmitter, changesObservable)
override fun initView() = AwesomeBarUIView(initialState.useNewTab, container, actionEmitter, changesObservable)
init {
render(reducer)

View File

@ -19,6 +19,7 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.mvi.UIView
class AwesomeBarUIView(
useNewTab: Boolean,
container: ViewGroup,
actionEmitter: Observer<AwesomeBarAction>,
changesObservable: Observable<AwesomeBarChange>
@ -32,7 +33,11 @@ class AwesomeBarUIView(
with(container.context) {
view.addProviders(ClipboardSuggestionProvider(
this,
components.useCases.sessionUseCases.loadUrl,
if (useNewTab) {
components.useCases.tabsUseCases.addTab
} else {
components.useCases.sessionUseCases.loadUrl
},
getDrawable(R.drawable.ic_link).toBitmap(),
getString(R.string.awesomebar_clipboard_title)
)

View File

@ -20,6 +20,7 @@ import org.mozilla.fenix.mvi.ViewState
class ToolbarComponent(
private val container: ViewGroup,
bus: ActionBusFactory,
private val sessionId: String?,
override var initialState: SearchState = SearchState("", false)
) :
UIComponent<SearchState, SearchAction, SearchChange>(
@ -33,7 +34,7 @@ class ToolbarComponent(
}
}
override fun initView() = ToolbarUIView(container, actionEmitter, changesObservable)
override fun initView() = ToolbarUIView(sessionId, container, actionEmitter, changesObservable)
init {
render(reducer)
applyTheme()

View File

@ -16,6 +16,7 @@ import mozilla.components.feature.toolbar.ToolbarFeature
import org.mozilla.fenix.DefaultThemeManager
import mozilla.components.support.base.feature.LifecycleAwareFeature
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BrowserFragmentDirections
import org.mozilla.fenix.ext.application
import org.mozilla.fenix.ext.components
@ -38,7 +39,8 @@ class ToolbarIntegration(
context.getString(R.string.browser_home_button),
visible = { sessionManager.runWithSession(sessionId) { it.isCustomTabSession().not() } }
) {
Navigation.findNavController(toolbar).navigate(R.id.action_browserFragment_to_homeFragment)
Navigation.findNavController(toolbar)
.navigate(BrowserFragmentDirections.actionBrowserFragmentToHomeFragment())
}
toolbar.addBrowserAction(home)

View File

@ -18,6 +18,7 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.mvi.UIView
class ToolbarUIView(
sessionId: String?,
container: ViewGroup,
actionEmitter: Observer<SearchAction>,
changesObservable: Observable<SearchChange>
@ -51,11 +52,12 @@ class ToolbarUIView(
setOnEditListener(object : mozilla.components.concept.toolbar.Toolbar.OnEditListener {
override fun onTextChanged(text: String) {
url = text
actionEmitter.onNext(SearchAction.TextChanged(text))
}
override fun onStopEditing() {
actionEmitter.onNext(SearchAction.UrlCommitted("foo"))
actionEmitter.onNext(SearchAction.UrlCommitted(url))
}
})
}
@ -71,7 +73,7 @@ class ToolbarUIView(
ShippedDomainsProvider().also { it.initialize(this) },
components.core.historyStorage,
components.core.sessionManager,
components.core.sessionManager.selectedSession?.id
sessionId ?: components.core.sessionManager.selectedSession?.id
)
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@ -1,12 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/session_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_wrapper"/>
android:layout_height="wrap_content"
android:layout_margin="16dp"/>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tabs_list"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_wrapper"/>

View File

@ -74,4 +74,16 @@
android:textSize="14sp" />
</org.mozilla.fenix.home.SearchView>
<View
android:id="@+id/homeDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/photonGrey30"
app:layout_constraintTop_toBottomOf="@id/toolbar_wrapper"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="30dp"
android:layout_marginStart="40dp"
android:layout_marginEnd="40dp"/>
</androidx.constraintlayout.motion.widget.MotionLayout>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tabs_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="@+id/header_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/tabs_header_title"
android:textSize="24sp"
android:textColor="@android:color/black"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
android:id="@+id/add_tab_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:src="@drawable/ic_add_black_24dp"
android:baselineAlignBottom="true"
app:layout_constraintBaseline_toBaselineOf="@id/header_text"
app:layout_constraintEnd_toStartOf="@id/tabs_overflow_button"/>
<ImageView
android:id="@+id/tabs_overflow_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:src="@drawable/ic_menu"
android:baselineAlignBottom="true"
app:layout_constraintBaseline_toBaselineOf="@id/header_text"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:padding="10dp"
android:elevation="5dp"
app:cardCornerRadius="10dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/item_tab"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="10dp">
<ImageView android:id="@+id/favicon_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_link"
android:tint="@android:color/black"
android:paddingStart="10dp"
android:paddingEnd="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/favicon_content_description"/>
<TextView android:id="@+id/text_url"
android:textSize="18sp"
android:maxLines="1"
android:ellipsize="end"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/favicon_image"
app:layout_constraintEnd_toStartOf="@id/close_tab_button"/>
<ImageView android:id="@+id/close_tab_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_close_black_24dp"
android:paddingEnd="10dp"
android:contentDescription="@string/close_tab"
app:layout_constraintBottom_toBottomOf="@id/text_url"
app:layout_constraintEnd_toEndOf="parent"/>
<ImageView android:layout_width="0dp"
android:layout_height="100dp"
android:src="@color/photonBlue40"
app:layout_constraintTop_toBottomOf="@id/text_url"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:contentDescription="TODO"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

View File

@ -37,6 +37,7 @@
android:id="@+id/action_searchFragment_to_browserFragment"
app:destination="@id/browserFragment"
app:popUpTo="@id/homeFragment" />
<argument android:name="session_id" app:argType="string" app:nullable="true"/>
</fragment>
<fragment

View File

@ -125,4 +125,7 @@
<string name="library_title">Library</string>
<!-- Settings Page Title -->
<string name="settings_title">Settings</string>
<string name="tabs_header_title">Current Session</string>
<string name="close_tab">Close tab</string>
<string name="favicon_content_description">Favorite icon</string>
</resources>

View File

@ -8,6 +8,7 @@ buildscript {
dependencies {
classpath Deps.tools_androidgradle
classpath Deps.tools_kotlingradle
classpath Deps.androidx_safeargs
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -18,6 +18,7 @@ private object Versions {
const val androidx_annotation = "1.0.1"
const val androidx_lifecycle = "2.0.0"
const val androidx_fragment = "1.1.0-alpha04"
const val androidx_safeargs = "1.0.0-beta01"
const val mozilla_android_components = "0.42.0-SNAPSHOT"
@ -99,6 +100,7 @@ object Deps {
const val androidx_legacy = "androidx.legacy:legacy-support-v4:${Versions.androidx_legacy}"
const val androidx_lifecycle_runtime = "androidx.lifecycle:lifecycle-runtime:${Versions.androidx_lifecycle}"
const val androidx_preference = "androidx.preference:preference-ktx:${Versions.androidx_preference}"
const val androidx_safeargs = "android.arch.navigation:navigation-safe-args-gradle-plugin:${Versions.androidx_safeargs}"
const val android_arch_navigation = "android.arch.navigation:navigation-fragment:${Versions.android_arch_navigation}"
const val android_arch_navigation_ui = "android.arch.navigation:navigation-ui:${Versions.android_arch_navigation}"

View File

@ -1,5 +1,6 @@
#Wed Feb 06 16:31:29 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip