1
0
Fork 0

Issue #2379 - Use LibraryPageView in history

master
Tiger Oakes 2019-08-01 11:58:41 -04:00 committed by Emily Kager
parent 2813a3cff7
commit ccae66c08a
11 changed files with 172 additions and 265 deletions

View File

@ -17,7 +17,6 @@ import android.view.View
import android.view.ViewGroup
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
@ -100,14 +99,9 @@ fun Context.share(text: String, subject: String = ""): Boolean {
fun Context.getRootView(): View? =
asActivity()?.window?.decorView?.findViewById<View>(android.R.id.content) as? ViewGroup
/**
* Returns the color resource corresponding to the attribute.
*/
@ColorRes
fun Context.getColorResFromAttr(@AttrRes attr: Int) = ThemeManager.resolveAttribute(attr, this)
/**
* Returns the color int corresponding to the attribute.
*/
@ColorInt
fun Context.getColorFromAttr(@AttrRes attr: Int) = ContextCompat.getColor(this, getColorResFromAttr(attr))
fun Context.getColorFromAttr(@AttrRes attr: Int) =
ContextCompat.getColor(this, ThemeManager.resolveAttribute(attr, this))

View File

@ -0,0 +1,39 @@
/* 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.library
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import org.mozilla.fenix.BrowsingModeManager
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
abstract class LibraryPageFragment<T> : Fragment() {
abstract val selectedItems: Set<T>
protected fun close() {
findNavController().popBackStack(R.id.libraryFragment, true)
}
protected fun openItemsInNewTab(private: Boolean = false, toUrl: (T) -> String?) {
context?.components?.useCases?.tabsUseCases?.let { tabsUseCases ->
val addTab = if (private) tabsUseCases.addPrivateTab else tabsUseCases.addTab
selectedItems.asSequence()
.mapNotNull(toUrl)
.forEach { url ->
addTab.invoke(url)
}
}
(activity as HomeActivity).browsingModeManager.mode = if (private) {
BrowsingModeManager.Mode.Private
} else {
BrowsingModeManager.Mode.Normal
}
(activity as HomeActivity).supportActionBar?.hide()
}
}

View File

@ -11,29 +11,44 @@ import android.graphics.PorterDuffColorFilter
import android.view.ViewGroup
import android.widget.ActionMenuView
import android.widget.ImageButton
import androidx.annotation.ColorRes
import androidx.annotation.ColorInt
import androidx.appcompat.view.menu.ActionMenuItemView
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.core.view.forEach
import kotlinx.android.extensions.LayoutContainer
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.asActivity
import org.mozilla.fenix.ext.getColorFromAttr
open class LibraryPageView(
container: ViewGroup
) {
protected val context: Context = container.context
override val containerView: ViewGroup
) : LayoutContainer {
protected val context: Context inline get() = containerView.context
protected val activity = context.asActivity()
protected fun setUiForNormalMode(title: String?) {
activity?.title = title
setToolbarColors(
ContextCompat.getColor(context, R.color.white_color),
context.getColorFromAttr(R.attr.accentHighContrast)
)
}
protected fun setUiForSelectingMode(title: String?) {
activity?.title = title
setToolbarColors(
context.getColorFromAttr(R.attr.primaryText),
context.getColorFromAttr(R.attr.foundation)
)
}
/**
* Adjust the colors of the [Toolbar] on the top of the screen.
*/
protected fun setToolbarColors(@ColorRes foregroundRes: Int, @ColorRes backgroundRes: Int) {
private fun setToolbarColors(@ColorInt foreground: Int, @ColorInt background: Int) {
val toolbar = activity?.findViewById<Toolbar>(R.id.navigationToolbar)
val foreground = ContextCompat.getColor(context, foregroundRes)
val background = ContextCompat.getColor(context, backgroundRes)
toolbar?.apply {
setBackgroundColor(background)
setTitleTextColor(foreground)

View File

@ -15,7 +15,6 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
@ -34,8 +33,6 @@ import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.concept.sync.Profile
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.BackHandler
import org.mozilla.fenix.BrowsingModeManager
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbarPresenter
import org.mozilla.fenix.components.StoreProvider
@ -47,10 +44,11 @@ import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.setRootTitles
import org.mozilla.fenix.ext.urlToTrimmedHost
import org.mozilla.fenix.ext.withOptionalDesktopFolders
import org.mozilla.fenix.library.LibraryPageFragment
import org.mozilla.fenix.utils.allowUndo
@SuppressWarnings("TooManyFunctions", "LargeClass")
class BookmarkFragment : Fragment(), BackHandler, AccountObserver {
class BookmarkFragment : LibraryPageFragment<BookmarkNode>(), BackHandler, AccountObserver {
private lateinit var bookmarkStore: BookmarkStore
private lateinit var bookmarkView: BookmarkView
@ -75,6 +73,8 @@ class BookmarkFragment : Fragment(), BackHandler, AccountObserver {
private val metrics
get() = context?.components?.analytics?.metrics
override val selectedItems get() = bookmarkStore.state.mode.selectedItems
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_bookmark, container, false)
@ -173,8 +173,7 @@ class BookmarkFragment : Fragment(), BackHandler, AccountObserver {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.libraryClose -> {
navigation
.popBackStack(R.id.libraryFragment, true)
close()
true
}
R.id.add_bookmark_folder -> {
@ -186,20 +185,21 @@ class BookmarkFragment : Fragment(), BackHandler, AccountObserver {
true
}
R.id.open_bookmarks_in_new_tabs_multi_select -> {
getSelectedBookmarks().forEach { node ->
node.url?.let {
context?.components?.useCases?.tabsUseCases?.addTab?.invoke(it)
}
}
openItemsInNewTab { node -> node.url }
(activity as HomeActivity).browsingModeManager.mode = BrowsingModeManager.Mode.Normal
(activity as HomeActivity).supportActionBar?.hide()
nav(R.id.bookmarkFragment, BookmarkFragmentDirections.actionBookmarkFragmentToHomeFragment())
metrics?.track(Event.OpenedBookmarksInNewTabs)
true
}
R.id.open_bookmarks_in_private_tabs_multi_select -> {
openItemsInNewTab(private = true) { node -> node.url }
nav(R.id.bookmarkFragment, BookmarkFragmentDirections.actionBookmarkFragmentToHomeFragment())
metrics?.track(Event.OpenedBookmarksInPrivateTabs)
true
}
R.id.edit_bookmark_multi_select -> {
val bookmark = getSelectedBookmarks().first()
val bookmark = bookmarkStore.state.mode.selectedItems.first()
nav(
R.id.bookmarkFragment,
BookmarkFragmentDirections
@ -207,21 +207,8 @@ class BookmarkFragment : Fragment(), BackHandler, AccountObserver {
)
true
}
R.id.open_bookmarks_in_private_tabs_multi_select -> {
getSelectedBookmarks().forEach { node ->
node.url?.let {
context?.components?.useCases?.tabsUseCases?.addPrivateTab?.invoke(it)
}
}
(activity as HomeActivity).browsingModeManager.mode = BrowsingModeManager.Mode.Private
(activity as HomeActivity).supportActionBar?.hide()
nav(R.id.bookmarkFragment, BookmarkFragmentDirections.actionBookmarkFragmentToHomeFragment())
metrics?.track(Event.OpenedBookmarksInPrivateTabs)
true
}
R.id.delete_bookmarks_multi_select -> {
deleteMulti(getSelectedBookmarks())
deleteMulti(bookmarkStore.state.mode.selectedItems)
true
}
else -> super.onOptionsItemSelected(item)
@ -247,8 +234,6 @@ class BookmarkFragment : Fragment(), BackHandler, AccountObserver {
override fun onProfileUpdated(profile: Profile) {
}
private fun getSelectedBookmarks() = bookmarkView.getSelected()
private suspend fun refreshBookmarks() {
context?.bookmarkStorage()?.getTree(bookmarkStore.state.tree!!.guid, false).withOptionalDesktopFolders(context)
?.let { node ->
@ -265,7 +250,7 @@ class BookmarkFragment : Fragment(), BackHandler, AccountObserver {
super.onPause()
}
private suspend fun deleteSelectedBookmarks(selected: Set<BookmarkNode> = getSelectedBookmarks()) {
private suspend fun deleteSelectedBookmarks(selected: Set<BookmarkNode>) {
selected.forEach {
context?.bookmarkStorage()?.deleteNode(it.guid)
}

View File

@ -22,8 +22,10 @@ class BookmarkStore(
*/
data class BookmarkState(val tree: BookmarkNode?, val mode: Mode = Mode.Normal) : State {
sealed class Mode {
open val selectedItems = emptySet<BookmarkNode>()
object Normal : Mode()
data class Selecting(val selectedItems: Set<BookmarkNode>) : Mode()
data class Selecting(override val selectedItems: Set<BookmarkNode>) : Mode()
}
}
@ -46,32 +48,22 @@ sealed class BookmarkAction : Action {
fun bookmarkStateReducer(state: BookmarkState, action: BookmarkAction): BookmarkState {
return when (action) {
is BookmarkAction.Change -> {
val mode =
if (state.mode is BookmarkState.Mode.Selecting) {
val items = state.mode.selectedItems.filter {
it in action.tree
}.toSet()
if (items.isEmpty()) BookmarkState.Mode.Normal else BookmarkState.Mode.Selecting(items)
} else state.mode
state.copy(tree = action.tree, mode = mode)
}
is BookmarkAction.Select -> {
val selectedItems = if (state.mode is BookmarkState.Mode.Selecting) {
state.mode.selectedItems + action.item
} else setOf(action.item)
state.copy(mode = BookmarkState.Mode.Selecting(selectedItems))
val items = state.mode.selectedItems.filter { it in action.tree }
state.copy(
tree = action.tree,
mode = if (items.isEmpty()) BookmarkState.Mode.Normal else BookmarkState.Mode.Selecting(items.toSet())
)
}
is BookmarkAction.Select ->
state.copy(mode = BookmarkState.Mode.Selecting(state.mode.selectedItems + action.item))
is BookmarkAction.Deselect -> {
val selectedItems = if (state.mode is BookmarkState.Mode.Selecting) {
state.mode.selectedItems - action.item
} else setOf()
val mode =
if (selectedItems.isEmpty()) BookmarkState.Mode.Normal else BookmarkState.Mode.Selecting(selectedItems)
state.copy(mode = mode)
val items = state.mode.selectedItems - action.item
state.copy(
mode = if (items.isEmpty()) BookmarkState.Mode.Normal else BookmarkState.Mode.Selecting(items)
)
}
BookmarkAction.DeselectAll -> {
BookmarkAction.DeselectAll ->
state.copy(mode = BookmarkState.Mode.Normal)
}
}
}

View File

@ -5,16 +5,13 @@
package org.mozilla.fenix.library.bookmarks
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.component_bookmark.view.*
import mozilla.appservices.places.BookmarkRoot
import mozilla.components.concept.storage.BookmarkNode
import mozilla.components.support.base.feature.BackHandler
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.getColorResFromAttr
import org.mozilla.fenix.library.LibraryPageView
/**
@ -129,12 +126,13 @@ interface BookmarkViewInteractor {
}
class BookmarkView(
private val container: ViewGroup,
container: ViewGroup,
val interactor: BookmarkViewInteractor
) : LibraryPageView(container), LayoutContainer, BackHandler {
) : LibraryPageView(container), BackHandler {
override val containerView: View?
get() = container
val view: LinearLayout = LayoutInflater.from(container.context)
.inflate(R.layout.component_bookmark, container, true)
.findViewById(R.id.bookmarks_wrapper)
var mode: BookmarkState.Mode = BookmarkState.Mode.Normal
private set
@ -142,9 +140,6 @@ class BookmarkView(
private set
private var canGoBack = false
val view: LinearLayout = LayoutInflater.from(container.context)
.inflate(R.layout.component_bookmark, container, true) as LinearLayout
private val bookmarkAdapter: BookmarkAdapter
init {
@ -155,17 +150,19 @@ class BookmarkView(
}
fun update(state: BookmarkState) {
canGoBack = !(listOf(null, BookmarkRoot.Root.id).contains(state.tree?.guid))
if (state.tree != tree) {
tree = state.tree
}
canGoBack = BookmarkRoot.Root.matches(state.tree)
tree = state.tree
if (state.mode != mode) {
mode = state.mode
interactor.switchMode(mode)
}
when (val modeCopy = state.mode) {
is BookmarkState.Mode.Normal -> setUIForNormalMode(state.tree)
is BookmarkState.Mode.Selecting -> setUIForSelectingMode(state.tree, modeCopy)
bookmarkAdapter.updateData(state.tree, mode)
when (state.mode) {
is BookmarkState.Mode.Normal ->
setUiForNormalMode(state.tree)
is BookmarkState.Mode.Selecting ->
setUiForSelectingMode(context.getString(R.string.bookmarks_multi_select_title, mode.selectedItems.size))
}
}
@ -183,34 +180,16 @@ class BookmarkView(
}
}
fun getSelected(): Set<BookmarkNode> = bookmarkAdapter.selected
private fun setUIForSelectingMode(
root: BookmarkNode?,
mode: BookmarkState.Mode.Selecting
) {
bookmarkAdapter.updateData(root, mode)
activity?.title =
context.getString(R.string.bookmarks_multi_select_title, mode.selectedItems.size)
setToolbarColors(
R.color.white_color,
context.getColorResFromAttr(R.attr.accentHighContrast)
private fun setUiForNormalMode(root: BookmarkNode?) {
super.setUiForNormalMode(
if (BookmarkRoot.Mobile.matches(root)) context.getString(R.string.library_bookmarks) else root?.title
)
}
private fun setUIForNormalMode(root: BookmarkNode?) {
bookmarkAdapter.updateData(root, BookmarkState.Mode.Normal)
setTitle(root)
setToolbarColors(
context.getColorResFromAttr(R.attr.primaryText),
context.getColorResFromAttr(R.attr.foundation)
)
}
private fun setTitle(root: BookmarkNode?) {
activity?.title = when (root?.guid) {
BookmarkRoot.Mobile.id, null -> context.getString(R.string.library_bookmarks)
else -> root.title
}
/**
* Returns true if [root] matches the bookmark root ID.
*/
private fun BookmarkRoot.matches(root: BookmarkNode?): Boolean {
return root == null || id == root.guid
}
}

View File

@ -17,10 +17,8 @@ import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.fragment_history.view.*
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
@ -37,10 +35,11 @@ import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.library.LibraryPageFragment
import org.mozilla.fenix.share.ShareTab
@SuppressWarnings("TooManyFunctions")
class HistoryFragment : Fragment(), BackHandler {
class HistoryFragment : LibraryPageFragment<HistoryItem>(), BackHandler {
private lateinit var historyStore: HistoryStore
private lateinit var historyView: HistoryView
private lateinit var historyInteractor: HistoryInteractor
@ -71,6 +70,8 @@ class HistoryFragment : Fragment(), BackHandler {
return view
}
override val selectedItems get() = historyStore.state.mode.selectedItems
private fun invalidateOptionsMenu() {
activity?.invalidateOptionsMenu()
}
@ -91,7 +92,7 @@ class HistoryFragment : Fragment(), BackHandler {
setHasOptionsMenu(true)
}
fun deleteHistoryItems(items: Set<HistoryItem>) {
private fun deleteHistoryItems(items: Set<HistoryItem>) {
lifecycleScope.launch {
val storage = context?.components?.core?.historyStorage
for (item in items) {
@ -126,10 +127,8 @@ class HistoryFragment : Fragment(), BackHandler {
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
val mode = historyStore.state.mode
when (mode) {
HistoryState.Mode.Normal ->
R.menu.library_menu
is HistoryState.Mode.Editing ->
R.menu.history_select_multi
HistoryState.Mode.Normal -> R.menu.library_menu
is HistoryState.Mode.Editing -> R.menu.history_select_multi
else -> null
}?.let { inflater.inflate(it, menu) }
@ -146,11 +145,10 @@ class HistoryFragment : Fragment(), BackHandler {
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
R.id.share_history_multi_select -> {
val selectedHistory =
(historyStore.state.mode as? HistoryState.Mode.Editing)?.selectedItems ?: setOf()
val selectedHistory = historyStore.state.mode.selectedItems
when {
selectedHistory.size == 1 ->
share(selectedHistory.first().url)
share(url = selectedHistory.first().url)
selectedHistory.size > 1 -> {
val shareTabs = selectedHistory.map { ShareTab(it.url, it.title) }
share(tabs = shareTabs)
@ -159,36 +157,25 @@ class HistoryFragment : Fragment(), BackHandler {
true
}
R.id.libraryClose -> {
Navigation.findNavController(requireActivity(), R.id.container)
.popBackStack(R.id.libraryFragment, true)
close()
true
}
R.id.delete_history_multi_select -> {
val components = context?.applicationContext?.components!!
val selectedHistory =
(historyStore.state.mode as? HistoryState.Mode.Editing)?.selectedItems ?: setOf()
val components = context?.components!!
lifecycleScope.launch(Main) {
deleteSelectedHistory(selectedHistory, components)
deleteSelectedHistory(historyStore.state.mode.selectedItems, components)
viewModel.invalidate()
historyStore.dispatch(HistoryAction.ExitDeletionMode)
}
true
}
R.id.open_history_in_new_tabs_multi_select -> {
val selectedHistory =
(historyStore.state.mode as? HistoryState.Mode.Editing)?.selectedItems ?: setOf()
requireComponents.useCases.tabsUseCases.addTab.let { useCase ->
for (selectedItem in selectedHistory) {
requireComponents.analytics.metrics.track(Event.HistoryItemOpened)
useCase.invoke(selectedItem.url)
}
openItemsInNewTab { selectedItem ->
requireComponents.analytics.metrics.track(Event.HistoryItemOpened)
selectedItem.url
}
(activity as HomeActivity).apply {
browsingModeManager.mode = BrowsingModeManager.Mode.Normal
supportActionBar?.hide()
}
nav(
R.id.historyFragment,
HistoryFragmentDirections.actionHistoryFragmentToHomeFragment()
@ -196,13 +183,9 @@ class HistoryFragment : Fragment(), BackHandler {
true
}
R.id.open_history_in_private_tabs_multi_select -> {
val selectedHistory =
(historyStore.state.mode as? HistoryState.Mode.Editing)?.selectedItems ?: setOf()
requireComponents.useCases.tabsUseCases.addPrivateTab.let { useCase ->
for (selectedItem in selectedHistory) {
requireComponents.analytics.metrics.track(Event.HistoryItemOpened)
useCase.invoke(selectedItem.url)
}
openItemsInNewTab(private = true) { selectedItem ->
requireComponents.analytics.metrics.track(Event.HistoryItemOpened)
selectedItem.url
}
(activity as HomeActivity).apply {
@ -220,7 +203,7 @@ class HistoryFragment : Fragment(), BackHandler {
override fun onBackPressed(): Boolean = historyView.onBackPressed()
fun openItem(item: HistoryItem) {
private fun openItem(item: HistoryItem) {
requireComponents.analytics.metrics.track(Event.HistoryItemOpened)
(activity as HomeActivity).openToBrowserAndLoad(
searchTermOrURL = item.url,

View File

@ -16,8 +16,7 @@ class HistoryInteractor(
private val deleteHistoryItems: (Set<HistoryItem>) -> Unit
) : HistoryViewInteractor {
override fun onItemPress(item: HistoryItem) {
val mode = store.state.mode
when (mode) {
when (val mode = store.state.mode) {
is HistoryState.Mode.Normal -> openToBrowser(item)
is HistoryState.Mode.Editing -> {
val isSelected = mode.selectedItems.contains(item)
@ -32,9 +31,7 @@ class HistoryInteractor(
}
override fun onItemLongPress(item: HistoryItem) {
val isSelected = (store.state.mode as? HistoryState.Mode.Editing)?.let {
it.selectedItems.contains(item)
} ?: false
val isSelected = store.state.mode.selectedItems.contains(item)
if (isSelected) {
store.dispatch(HistoryAction.RemoveItemForRemoval(item))

View File

@ -41,9 +41,11 @@ sealed class HistoryAction : Action {
*/
data class HistoryState(val items: List<HistoryItem>, val mode: Mode) : State {
sealed class Mode {
open val selectedItems = emptySet<HistoryItem>()
object Normal : Mode()
data class Editing(val selectedItems: Set<HistoryItem>) : Mode()
object Deleting : Mode()
data class Editing(override val selectedItems: Set<HistoryItem>) : Mode()
}
}
@ -52,24 +54,13 @@ data class HistoryState(val items: List<HistoryItem>, val mode: Mode) : State {
*/
fun historyStateReducer(state: HistoryState, action: HistoryAction): HistoryState {
return when (action) {
is HistoryAction.AddItemForRemoval -> {
val mode = state.mode
if (mode is HistoryState.Mode.Editing) {
val items = mode.selectedItems + setOf(action.item)
state.copy(mode = HistoryState.Mode.Editing(items))
} else {
state.copy(mode = HistoryState.Mode.Editing(setOf(action.item)))
}
}
is HistoryAction.AddItemForRemoval ->
state.copy(mode = HistoryState.Mode.Editing(state.mode.selectedItems + action.item))
is HistoryAction.RemoveItemForRemoval -> {
var mode = state.mode
if (mode is HistoryState.Mode.Editing) {
val items = mode.selectedItems.minus(action.item)
state.copy(mode = HistoryState.Mode.Editing(items))
} else {
state
}
val selected = state.mode.selectedItems - action.item
state.copy(
mode = if (selected.isEmpty()) HistoryState.Mode.Normal else HistoryState.Mode.Editing(selected)
)
}
is HistoryAction.ExitEditMode -> state.copy(mode = HistoryState.Mode.Normal)
is HistoryAction.EnterDeletionMode -> state.copy(mode = HistoryState.Mode.Deleting)

View File

@ -4,27 +4,17 @@
package org.mozilla.fenix.library.history
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.component_history.*
import kotlinx.android.synthetic.main.component_history.view.*
import kotlinx.android.synthetic.main.component_history.view.history_list
import mozilla.components.support.base.feature.BackHandler
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.asActivity
import org.mozilla.fenix.ext.getColorResFromAttr
import org.mozilla.fenix.library.LibraryPageView
/**
* Interface for the HistoryViewInteractor. This interface is implemented by objects that want
@ -73,27 +63,22 @@ interface HistoryViewInteractor {
* View that contains and configures the History List
*/
class HistoryView(
private val container: ViewGroup,
container: ViewGroup,
val interactor: HistoryInteractor
) : LayoutContainer, BackHandler {
) : LibraryPageView(container), BackHandler {
val view: ConstraintLayout = LayoutInflater.from(container.context)
.inflate(R.layout.component_history, container, true)
.findViewById(R.id.history_wrapper)
override val containerView: View?
get() = container
val historyAdapter: HistoryAdapter
private var items: List<HistoryItem> = listOf()
private val context = container.context
var mode: HistoryState.Mode = HistoryState.Mode.Normal
private set
private val activity = context?.asActivity()
private val layoutManager = LinearLayoutManager(container.context)
init {
historyAdapter = HistoryAdapter(interactor)
val historyAdapter = HistoryAdapter(interactor)
private val layoutManager = LinearLayoutManager(container.context)
init {
view.history_list.apply {
layoutManager = this@HistoryView.layoutManager
adapter = historyAdapter
@ -102,37 +87,36 @@ class HistoryView(
}
fun update(state: HistoryState) {
view.progress_bar.visibility =
if (state.mode is HistoryState.Mode.Deleting) View.VISIBLE else View.GONE
val oldMode = mode
if (state.mode != mode) {
view.progress_bar.isVisible = state.mode === HistoryState.Mode.Deleting
items = state.items
mode = state.mode
if (state.mode != oldMode) {
interactor.onModeSwitched()
historyAdapter.updateMode(state.mode)
val oldMode = mode
if (oldMode is HistoryState.Mode.Editing) {
oldMode.selectedItems.forEach {
historyAdapter.notifyItemChanged(it.id)
}
// Deselect all the previously selected items
oldMode.selectedItems.forEach {
historyAdapter.notifyItemChanged(it.id)
}
}
(state.mode as? HistoryState.Mode.Editing)?.also {
val oldMode = (mode as? HistoryState.Mode.Editing)
val unselectedItems = oldMode?.selectedItems?.minus(it.selectedItems) ?: setOf()
if (state.mode is HistoryState.Mode.Editing) {
val unselectedItems = oldMode.selectedItems - state.mode.selectedItems
it.selectedItems.union(unselectedItems).forEach { item ->
state.mode.selectedItems.union(unselectedItems).forEach { item ->
historyAdapter.notifyItemChanged(item.id)
}
}
items = state.items
when (val mode = state.mode) {
is HistoryState.Mode.Normal -> setUIForNormalMode()
is HistoryState.Mode.Editing -> setUIForSelectingMode(mode.selectedItems.size)
is HistoryState.Mode.Normal ->
setUiForNormalMode(context.getString(R.string.library_history))
is HistoryState.Mode.Editing ->
setUiForSelectingMode(context.getString(R.string.history_multi_select_title, mode.selectedItems.size))
}
mode = state.mode
}
fun updateEmptyState(userHasHistory: Boolean) {
@ -140,59 +124,6 @@ class HistoryView(
history_empty_view.isVisible = !userHasHistory
}
private fun setUIForSelectingMode(selectedItemSize: Int) {
activity?.title =
context.getString(R.string.history_multi_select_title, selectedItemSize)
setToolbarColors(
R.color.white_color,
context!!.getColorResFromAttr(R.attr.accentHighContrast)
)
}
private fun setUIForNormalMode() {
activity?.title = context.getString(R.string.library_history)
setToolbarColors(
context!!.getColorResFromAttr(R.attr.primaryText),
context.getColorResFromAttr(R.attr.foundation)
)
}
private fun setToolbarColors(foreground: Int, background: Int) {
val toolbar = (activity as AppCompatActivity).findViewById<Toolbar>(R.id.navigationToolbar)
val colorFilter = PorterDuffColorFilter(
ContextCompat.getColor(context, foreground),
PorterDuff.Mode.SRC_IN
)
toolbar.setBackgroundColor(ContextCompat.getColor(context, background))
toolbar.setTitleTextColor(ContextCompat.getColor(context, foreground))
themeToolbar(
toolbar, foreground,
background, colorFilter
)
}
private fun themeToolbar(
toolbar: Toolbar,
textColor: Int,
backgroundColor: Int,
colorFilter: PorterDuffColorFilter? = null
) {
toolbar.setTitleTextColor(ContextCompat.getColor(context!!, textColor))
toolbar.setBackgroundColor(ContextCompat.getColor(context, backgroundColor))
if (colorFilter == null) {
return
}
toolbar.overflowIcon?.colorFilter = colorFilter
(0 until toolbar.childCount).forEach {
when (val item = toolbar.getChildAt(it)) {
is ImageButton -> item.drawable.colorFilter = colorFilter
}
}
}
override fun onBackPressed(): Boolean {
return interactor.onBackPressed()
}

View File

@ -6,6 +6,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/bookmarks_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -17,7 +18,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="@layout/bookmark_row" />
tools:listitem="@layout/library_site_item" />
<TextView
android:id="@+id/bookmarks_empty_view"