Create shared custom view for library items
parent
ed60bdf470
commit
2467588c4a
|
@ -0,0 +1,69 @@
|
|||
/* 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 android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
import kotlinx.android.synthetic.main.library_site_item.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.increaseTapArea
|
||||
import org.mozilla.fenix.ext.loadIntoView
|
||||
|
||||
class LibrarySiteItemView(
|
||||
context: Context
|
||||
) : ConstraintLayout(context) {
|
||||
|
||||
val titleView: TextView get() = title
|
||||
|
||||
val urlView: TextView get() = url
|
||||
|
||||
val iconView: ImageView get() = favicon
|
||||
|
||||
val overflowView: ImageButton get() = overflow_menu
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.library_site_item, this, true)
|
||||
|
||||
overflow_menu.increaseTapArea(OVERFLOW_EXTRA_DIPS)
|
||||
}
|
||||
|
||||
/**
|
||||
* Change visibility of parts of this view based on what type of item is being represented.
|
||||
*/
|
||||
fun displayAs(mode: ItemType) {
|
||||
favicon.isVisible = mode != ItemType.SEPARATOR
|
||||
title.isVisible = mode != ItemType.SEPARATOR
|
||||
url.isVisible = mode == ItemType.SITE
|
||||
overflow_menu.isVisible = mode != ItemType.SEPARATOR
|
||||
separator.isVisible = mode == ItemType.SEPARATOR
|
||||
isClickable = mode != ItemType.SEPARATOR
|
||||
isFocusable = mode != ItemType.SEPARATOR
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the icon to show a check mark if [isSelected]
|
||||
*/
|
||||
fun changeSelected(isSelected: Boolean) {
|
||||
icon.displayedChild = if (isSelected) 1 else 0
|
||||
}
|
||||
|
||||
fun loadFavicon(url: String) {
|
||||
context.components.core.icons.loadIntoView(favicon, url)
|
||||
}
|
||||
|
||||
enum class ItemType {
|
||||
SITE, FOLDER, SEPARATOR;
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val OVERFLOW_EXTRA_DIPS = 16
|
||||
}
|
||||
}
|
|
@ -4,24 +4,21 @@
|
|||
|
||||
package org.mozilla.fenix.library.bookmarks
|
||||
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.bookmark_row.*
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
import mozilla.components.browser.menu.BrowserMenu
|
||||
import mozilla.components.concept.storage.BookmarkNode
|
||||
import mozilla.components.concept.storage.BookmarkNodeType
|
||||
import org.jetbrains.anko.image
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ThemeManager
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.increaseTapArea
|
||||
import org.mozilla.fenix.ext.loadIntoView
|
||||
import org.mozilla.fenix.library.LibrarySiteItemView
|
||||
|
||||
class BookmarkAdapter(val emptyView: View, val interactor: BookmarkViewInteractor) :
|
||||
RecyclerView.Adapter<BookmarkAdapter.BookmarkNodeViewHolder>() {
|
||||
|
@ -76,29 +73,28 @@ class BookmarkAdapter(val emptyView: View, val interactor: BookmarkViewInteracto
|
|||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookmarkNodeViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.bookmark_row, parent, false)
|
||||
val view = LibrarySiteItemView(parent.context).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
|
||||
}
|
||||
|
||||
return when (viewType) {
|
||||
BookmarkItemViewHolder.viewType.ordinal -> BookmarkItemViewHolder(
|
||||
view, interactor
|
||||
)
|
||||
BookmarkFolderViewHolder.viewType.ordinal -> BookmarkFolderViewHolder(
|
||||
view, interactor
|
||||
)
|
||||
BookmarkSeparatorViewHolder.viewType.ordinal -> BookmarkSeparatorViewHolder(
|
||||
view, interactor
|
||||
)
|
||||
LibrarySiteItemView.ItemType.SITE.ordinal ->
|
||||
BookmarkItemViewHolder(view, interactor)
|
||||
LibrarySiteItemView.ItemType.FOLDER.ordinal ->
|
||||
BookmarkFolderViewHolder(view, interactor)
|
||||
LibrarySiteItemView.ItemType.SEPARATOR.ordinal ->
|
||||
BookmarkSeparatorViewHolder(view, interactor)
|
||||
else -> throw IllegalStateException("ViewType $viewType does not match to a ViewHolder")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (tree[position].type) {
|
||||
BookmarkNodeType.ITEM -> ViewType.ITEM.ordinal
|
||||
BookmarkNodeType.FOLDER -> ViewType.FOLDER.ordinal
|
||||
BookmarkNodeType.SEPARATOR -> ViewType.SEPARATOR.ordinal
|
||||
BookmarkNodeType.ITEM -> LibrarySiteItemView.ItemType.SITE
|
||||
BookmarkNodeType.FOLDER -> LibrarySiteItemView.ItemType.FOLDER
|
||||
BookmarkNodeType.SEPARATOR -> LibrarySiteItemView.ItemType.SEPARATOR
|
||||
else -> throw IllegalStateException("Item $tree[position] does not match to a ViewType")
|
||||
}
|
||||
}.ordinal
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = tree.size
|
||||
|
@ -111,270 +107,145 @@ class BookmarkAdapter(val emptyView: View, val interactor: BookmarkViewInteracto
|
|||
)
|
||||
}
|
||||
|
||||
open class BookmarkNodeViewHolder(
|
||||
view: View,
|
||||
val interactor: BookmarkViewInteractor,
|
||||
override val containerView: View? = view
|
||||
) : RecyclerView.ViewHolder(view), LayoutContainer {
|
||||
|
||||
open fun bind(item: BookmarkNode, mode: BookmarkState.Mode, selected: Boolean) {}
|
||||
}
|
||||
|
||||
class BookmarkItemViewHolder(
|
||||
view: View,
|
||||
interactor: BookmarkViewInteractor,
|
||||
override val containerView: View? = view
|
||||
abstract class BookmarkNodeViewHolder(
|
||||
val view: LibrarySiteItemView,
|
||||
val interactor: BookmarkViewInteractor
|
||||
) :
|
||||
BookmarkNodeViewHolder(view, interactor, containerView) {
|
||||
RecyclerView.ViewHolder(view), LayoutContainer {
|
||||
|
||||
@Suppress("ComplexMethod")
|
||||
override fun bind(item: BookmarkNode, mode: BookmarkState.Mode, selected: Boolean) {
|
||||
override val containerView get() = view
|
||||
|
||||
val shiftTwoDp = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP, TWO_DIGIT_MARGIN, containerView!!.context.resources.displayMetrics
|
||||
).toInt()
|
||||
val params = bookmark_title.layoutParams as ViewGroup.MarginLayoutParams
|
||||
params.topMargin = shiftTwoDp
|
||||
bookmark_title.layoutParams = params
|
||||
abstract fun bind(item: BookmarkNode, mode: BookmarkState.Mode, selected: Boolean)
|
||||
|
||||
bookmark_favicon.visibility = View.VISIBLE
|
||||
bookmark_title.visibility = View.VISIBLE
|
||||
bookmark_url.visibility = View.VISIBLE
|
||||
bookmark_overflow.visibility = View.VISIBLE
|
||||
bookmark_separator.visibility = View.GONE
|
||||
bookmark_layout.isClickable = true
|
||||
|
||||
val bookmarkItemMenu = BookmarkItemMenu(containerView.context, item) {
|
||||
protected fun setupMenu(item: BookmarkNode) {
|
||||
val bookmarkItemMenu = BookmarkItemMenu(view.context, item) {
|
||||
when (it) {
|
||||
is BookmarkItemMenu.Item.Edit -> {
|
||||
interactor.edit(item)
|
||||
}
|
||||
is BookmarkItemMenu.Item.Select -> {
|
||||
interactor.select(item)
|
||||
}
|
||||
is BookmarkItemMenu.Item.Copy -> {
|
||||
interactor.copy(item)
|
||||
}
|
||||
is BookmarkItemMenu.Item.Share -> {
|
||||
interactor.share(item)
|
||||
}
|
||||
is BookmarkItemMenu.Item.OpenInNewTab -> {
|
||||
interactor.openInNewTab(item)
|
||||
}
|
||||
is BookmarkItemMenu.Item.OpenInPrivateTab -> {
|
||||
interactor.openInPrivateTab(item)
|
||||
}
|
||||
is BookmarkItemMenu.Item.Delete -> {
|
||||
interactor.delete(item)
|
||||
}
|
||||
is BookmarkItemMenu.Item.Edit -> interactor.edit(item)
|
||||
is BookmarkItemMenu.Item.Select -> interactor.select(item)
|
||||
is BookmarkItemMenu.Item.Copy -> interactor.copy(item)
|
||||
is BookmarkItemMenu.Item.Share -> interactor.share(item)
|
||||
is BookmarkItemMenu.Item.OpenInNewTab -> interactor.openInNewTab(item)
|
||||
is BookmarkItemMenu.Item.OpenInPrivateTab -> interactor.openInPrivateTab(item)
|
||||
is BookmarkItemMenu.Item.Delete -> interactor.delete(item)
|
||||
}
|
||||
}
|
||||
|
||||
bookmark_overflow.increaseTapArea(bookmarkOverflowExtraDips)
|
||||
bookmark_overflow.setOnClickListener {
|
||||
bookmarkItemMenu.menuBuilder.build(containerView.context).show(anchor = it)
|
||||
}
|
||||
bookmark_title.text = if (item.title.isNullOrBlank()) item.url else item.title
|
||||
bookmark_url.text = item.url
|
||||
updateUrl(item, mode, selected)
|
||||
}
|
||||
|
||||
private fun updateUrl(item: BookmarkNode, mode: BookmarkState.Mode, selected: Boolean) {
|
||||
setClickListeners(mode, item, selected)
|
||||
|
||||
setColorsAndIcons(selected, item)
|
||||
}
|
||||
|
||||
private fun setColorsAndIcons(selected: Boolean, item: BookmarkNode) {
|
||||
val backgroundTint =
|
||||
if (selected) {
|
||||
ThemeManager.resolveAttribute(R.attr.accentHighContrast, containerView!!.context)
|
||||
} else {
|
||||
ThemeManager.resolveAttribute(R.attr.neutral, containerView!!.context)
|
||||
}
|
||||
|
||||
val backgroundTintList = ContextCompat.getColorStateList(containerView.context, backgroundTint)
|
||||
bookmark_favicon.backgroundTintList = backgroundTintList
|
||||
if (selected) bookmark_favicon.setImageResource(R.drawable.mozac_ic_check)
|
||||
|
||||
val url = item.url ?: return
|
||||
if (!selected && url.startsWith("http")) {
|
||||
bookmark_layout.context.components.core.icons.loadIntoView(bookmark_favicon, url)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setClickListeners(
|
||||
mode: BookmarkState.Mode,
|
||||
item: BookmarkNode,
|
||||
selected: Boolean
|
||||
) {
|
||||
bookmark_layout.setOnClickListener {
|
||||
if (mode == BookmarkState.Mode.Normal) {
|
||||
interactor.open(item)
|
||||
} else {
|
||||
if (selected) interactor.deselect(item) else interactor.select(item)
|
||||
}
|
||||
}
|
||||
|
||||
bookmark_layout.setOnLongClickListener {
|
||||
if (mode == BookmarkState.Mode.Normal) {
|
||||
if (selected) interactor.deselect(item) else interactor.select(item)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal const val TWO_DIGIT_MARGIN = 2F
|
||||
|
||||
val viewType = ViewType.ITEM
|
||||
}
|
||||
}
|
||||
|
||||
class BookmarkFolderViewHolder(
|
||||
view: View,
|
||||
interactor: BookmarkViewInteractor,
|
||||
override val containerView: View? = view
|
||||
) :
|
||||
BookmarkNodeViewHolder(view, interactor, containerView) {
|
||||
|
||||
override fun bind(item: BookmarkNode, mode: BookmarkState.Mode, selected: Boolean) {
|
||||
containerView?.context?.let {
|
||||
val drawable = it.getDrawable(R.drawable.ic_folder_icon)
|
||||
drawable?.setTint(
|
||||
ContextCompat.getColor(
|
||||
it,
|
||||
R.color.primary_text_light_theme
|
||||
)
|
||||
)
|
||||
bookmark_favicon.setImageDrawable(drawable)
|
||||
}
|
||||
bookmark_favicon.visibility = View.VISIBLE
|
||||
bookmark_title.visibility = View.VISIBLE
|
||||
bookmark_url.visibility = View.GONE
|
||||
bookmark_overflow.visibility = View.VISIBLE
|
||||
bookmark_separator.visibility = View.GONE
|
||||
bookmark_layout.isClickable = true
|
||||
|
||||
setClickListeners(mode, item, selected)
|
||||
|
||||
setMenu(item, containerView!!)
|
||||
|
||||
val backgroundTint = if (selected) {
|
||||
ThemeManager.resolveAttribute(R.attr.accentHighContrast, containerView.context)
|
||||
} else {
|
||||
ThemeManager.resolveAttribute(R.attr.neutral, containerView.context)
|
||||
}
|
||||
|
||||
val backgroundTintList = ContextCompat.getColorStateList(containerView.context, backgroundTint)
|
||||
bookmark_favicon.backgroundTintList = backgroundTintList
|
||||
val res = if (selected) R.drawable.mozac_ic_check else R.drawable.ic_folder_icon
|
||||
bookmark_favicon.setImageResource(res)
|
||||
|
||||
bookmark_title?.text = item.title
|
||||
}
|
||||
|
||||
private fun setMenu(
|
||||
item: BookmarkNode,
|
||||
containerView: View
|
||||
) {
|
||||
val bookmarkItemMenu = BookmarkItemMenu(containerView.context, item) {
|
||||
when (it) {
|
||||
is BookmarkItemMenu.Item.Edit -> {
|
||||
interactor.edit(item)
|
||||
}
|
||||
is BookmarkItemMenu.Item.Select -> {
|
||||
interactor.select(item)
|
||||
}
|
||||
is BookmarkItemMenu.Item.Delete -> {
|
||||
interactor.delete(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!item.inRoots()) {
|
||||
bookmark_overflow.increaseTapArea(bookmarkOverflowExtraDips)
|
||||
bookmark_overflow.setOnClickListener {
|
||||
bookmarkItemMenu.menuBuilder.build(containerView.context).show(
|
||||
anchor = it,
|
||||
orientation = BrowserMenu.Orientation.DOWN
|
||||
)
|
||||
}
|
||||
bookmark_layout.setOnLongClickListener(null)
|
||||
} else {
|
||||
bookmark_overflow.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun setClickListeners(
|
||||
mode: BookmarkState.Mode,
|
||||
item: BookmarkNode,
|
||||
selected: Boolean
|
||||
) {
|
||||
bookmark_layout.setOnClickListener {
|
||||
if (mode == BookmarkState.Mode.Normal) {
|
||||
interactor.expand(item)
|
||||
} else {
|
||||
if (selected) interactor.deselect(item) else interactor.select(item)
|
||||
}
|
||||
}
|
||||
|
||||
bookmark_layout.setOnLongClickListener {
|
||||
if (mode == BookmarkState.Mode.Normal && !item.inRoots()) {
|
||||
if (selected) interactor.deselect(item) else interactor.select(item)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val viewType = ViewType.FOLDER
|
||||
}
|
||||
}
|
||||
|
||||
class BookmarkSeparatorViewHolder(
|
||||
view: View,
|
||||
interactor: BookmarkViewInteractor,
|
||||
override val containerView: View? = view
|
||||
) : BookmarkNodeViewHolder(view, interactor, containerView) {
|
||||
|
||||
override fun bind(item: BookmarkNode, mode: BookmarkState.Mode, selected: Boolean) {
|
||||
|
||||
bookmark_favicon.visibility = View.GONE
|
||||
bookmark_title.visibility = View.GONE
|
||||
bookmark_url.visibility = View.GONE
|
||||
bookmark_overflow.increaseTapArea(bookmarkOverflowExtraDips)
|
||||
bookmark_overflow.visibility = View.GONE
|
||||
bookmark_separator.visibility = View.VISIBLE
|
||||
bookmark_layout.isClickable = false
|
||||
|
||||
val bookmarkItemMenu = BookmarkItemMenu(containerView!!.context, item) {
|
||||
when (it) {
|
||||
is BookmarkItemMenu.Item.Delete -> {
|
||||
interactor.delete(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bookmark_overflow.setOnClickListener {
|
||||
bookmarkItemMenu.menuBuilder.build(containerView.context).show(
|
||||
view.overflowView.setOnClickListener {
|
||||
bookmarkItemMenu.menuBuilder.build(view.context).show(
|
||||
anchor = it,
|
||||
orientation = BrowserMenu.Orientation.DOWN
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val viewType = ViewType.SEPARATOR
|
||||
class BookmarkItemViewHolder(
|
||||
view: LibrarySiteItemView,
|
||||
interactor: BookmarkViewInteractor
|
||||
) :
|
||||
BookmarkNodeViewHolder(view, interactor) {
|
||||
|
||||
@Suppress("ComplexMethod")
|
||||
override fun bind(item: BookmarkNode, mode: BookmarkState.Mode, selected: Boolean) {
|
||||
|
||||
view.displayAs(LibrarySiteItemView.ItemType.SITE)
|
||||
|
||||
setupMenu(item)
|
||||
view.titleView.text = if (item.title.isNullOrBlank()) item.url else item.title
|
||||
view.urlView.text = item.url
|
||||
|
||||
setClickListeners(mode, item, selected)
|
||||
view.changeSelected(selected)
|
||||
setColorsAndIcons(item.url)
|
||||
}
|
||||
|
||||
private fun setColorsAndIcons(url: String?) {
|
||||
if (url != null && url.startsWith("http")) {
|
||||
view.loadFavicon(url)
|
||||
} else {
|
||||
view.iconView.setImageDrawable(null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setClickListeners(
|
||||
mode: BookmarkState.Mode,
|
||||
item: BookmarkNode,
|
||||
selected: Boolean
|
||||
) {
|
||||
view.setOnClickListener {
|
||||
when {
|
||||
mode == BookmarkState.Mode.Normal -> interactor.open(item)
|
||||
selected -> interactor.deselect(item)
|
||||
else -> interactor.select(item)
|
||||
}
|
||||
}
|
||||
|
||||
view.setOnLongClickListener {
|
||||
if (mode == BookmarkState.Mode.Normal) {
|
||||
interactor.select(item)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val bookmarkOverflowExtraDips = 16
|
||||
class BookmarkFolderViewHolder(
|
||||
view: LibrarySiteItemView,
|
||||
interactor: BookmarkViewInteractor
|
||||
) :
|
||||
BookmarkNodeViewHolder(view, interactor) {
|
||||
|
||||
override fun bind(item: BookmarkNode, mode: BookmarkState.Mode, selected: Boolean) {
|
||||
|
||||
view.displayAs(LibrarySiteItemView.ItemType.FOLDER)
|
||||
|
||||
setClickListeners(mode, item, selected)
|
||||
|
||||
if (!item.inRoots()) {
|
||||
setupMenu(item)
|
||||
view.setOnLongClickListener(null)
|
||||
} else {
|
||||
view.overflowView.visibility = View.GONE
|
||||
}
|
||||
|
||||
view.changeSelected(selected)
|
||||
view.iconView.image = view.context.getDrawable(R.drawable.ic_folder_icon)?.apply {
|
||||
setTint(ContextCompat.getColor(view.context, R.color.primary_text_light_theme))
|
||||
}
|
||||
view.titleView.text = item.title
|
||||
}
|
||||
|
||||
private fun setClickListeners(
|
||||
mode: BookmarkState.Mode,
|
||||
item: BookmarkNode,
|
||||
selected: Boolean
|
||||
) {
|
||||
view.setOnClickListener {
|
||||
when {
|
||||
mode == BookmarkState.Mode.Normal -> interactor.expand(item)
|
||||
selected -> interactor.deselect(item)
|
||||
else -> interactor.select(item)
|
||||
}
|
||||
}
|
||||
|
||||
view.setOnLongClickListener {
|
||||
if (mode == BookmarkState.Mode.Normal && !item.inRoots()) {
|
||||
interactor.select(item)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ViewType {
|
||||
ITEM, FOLDER, SEPARATOR
|
||||
class BookmarkSeparatorViewHolder(
|
||||
view: LibrarySiteItemView,
|
||||
interactor: BookmarkViewInteractor
|
||||
) : BookmarkNodeViewHolder(view, interactor) {
|
||||
|
||||
override fun bind(item: BookmarkNode, mode: BookmarkState.Mode, selected: Boolean) {
|
||||
view.displayAs(LibrarySiteItemView.ItemType.SEPARATOR)
|
||||
setupMenu(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ class BookmarkItemMenu(
|
|||
val menuBuilder by lazy { BrowserMenuBuilder(menuItems) }
|
||||
|
||||
private val menuItems by lazy {
|
||||
listOf(
|
||||
listOfNotNull(
|
||||
if (item.type in listOf(BookmarkNodeType.ITEM, BookmarkNodeType.FOLDER)) {
|
||||
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_edit_button)) {
|
||||
onItemTapped.invoke(BookmarkItemMenu.Item.Edit)
|
||||
|
@ -63,6 +63,6 @@ class BookmarkItemMenu(
|
|||
) {
|
||||
onItemTapped.invoke(BookmarkItemMenu.Item.Delete)
|
||||
}
|
||||
).filterNotNull()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,19 +4,15 @@
|
|||
|
||||
package org.mozilla.fenix.library.bookmarks.selectfolder
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.bookmark_row.*
|
||||
import mozilla.components.concept.storage.BookmarkNode
|
||||
import mozilla.components.concept.storage.BookmarkNodeType
|
||||
import mozilla.components.support.ktx.android.util.dpToPx
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.getColorResFromAttr
|
||||
import org.mozilla.fenix.library.LibrarySiteItemView
|
||||
import org.mozilla.fenix.library.bookmarks.BookmarksSharedViewModel
|
||||
|
||||
class SelectBookmarkFolderAdapter(private val sharedViewModel: BookmarksSharedViewModel) :
|
||||
|
@ -30,21 +26,9 @@ class SelectBookmarkFolderAdapter(private val sharedViewModel: BookmarksSharedVi
|
|||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookmarkFolderViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.bookmark_row, parent, false)
|
||||
val view = LibrarySiteItemView(parent.context)
|
||||
|
||||
return when (viewType) {
|
||||
BookmarkFolderViewHolder.viewType -> SelectBookmarkFolderAdapter.BookmarkFolderViewHolder(
|
||||
view
|
||||
)
|
||||
else -> throw IllegalStateException("ViewType $viewType does not match to a ViewHolder")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (tree[position].node.type) {
|
||||
BookmarkNodeType.FOLDER -> BookmarkFolderViewHolder.viewType
|
||||
else -> throw IllegalStateException("Item $tree[position] does not match to a ViewType")
|
||||
}
|
||||
return BookmarkFolderViewHolder(view)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = tree.size
|
||||
|
@ -52,63 +36,40 @@ class SelectBookmarkFolderAdapter(private val sharedViewModel: BookmarksSharedVi
|
|||
override fun onBindViewHolder(holder: BookmarkFolderViewHolder, position: Int) {
|
||||
holder.bind(
|
||||
tree[position],
|
||||
tree[position].node == sharedViewModel.selectedFolder,
|
||||
object : SelectionInterface {
|
||||
override fun itemSelected(node: BookmarkNode) {
|
||||
sharedViewModel.apply {
|
||||
when (selectedFolder) {
|
||||
node -> selectedFolder = null
|
||||
else -> selectedFolder = node
|
||||
}
|
||||
}
|
||||
notifyDataSetChanged()
|
||||
tree[position].node == sharedViewModel.selectedFolder
|
||||
) { node ->
|
||||
sharedViewModel.apply {
|
||||
when (selectedFolder) {
|
||||
node -> selectedFolder = null
|
||||
else -> selectedFolder = node
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
interface SelectionInterface {
|
||||
fun itemSelected(node: BookmarkNode)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
class BookmarkFolderViewHolder(
|
||||
view: View,
|
||||
override val containerView: View? = view
|
||||
val view: LibrarySiteItemView
|
||||
) :
|
||||
RecyclerView.ViewHolder(view), LayoutContainer {
|
||||
|
||||
override val containerView get() = view
|
||||
|
||||
init {
|
||||
bookmark_favicon.visibility = View.VISIBLE
|
||||
bookmark_title.visibility = View.VISIBLE
|
||||
bookmark_url.visibility = View.GONE
|
||||
bookmark_separator.visibility = View.GONE
|
||||
bookmark_layout.isClickable = true
|
||||
view.displayAs(LibrarySiteItemView.ItemType.FOLDER)
|
||||
view.overflowView.visibility = View.GONE
|
||||
}
|
||||
|
||||
fun bind(folder: BookmarkNodeWithDepth, selected: Boolean, selectionInterface: SelectionInterface) {
|
||||
val backgroundTintAttr = if (selected) R.attr.accentBright else R.attr.neutral
|
||||
|
||||
// Center the bookmark title since we don't have a url
|
||||
val constraintSet = ConstraintSet()
|
||||
constraintSet.clone(bookmark_layout)
|
||||
constraintSet.connect(
|
||||
bookmark_title.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM
|
||||
)
|
||||
constraintSet.applyTo(bookmark_layout)
|
||||
|
||||
val backgroundTint = containerView!!.context.getColorResFromAttr(backgroundTintAttr)
|
||||
val backgroundTintList = ContextCompat.getColorStateList(containerView.context, backgroundTint)
|
||||
bookmark_favicon.backgroundTintList = backgroundTintList
|
||||
val res = if (selected) R.drawable.mozac_ic_check else R.drawable.ic_folder_icon
|
||||
bookmark_favicon.setImageResource(res)
|
||||
bookmark_overflow.visibility = View.GONE
|
||||
bookmark_title?.text = folder.node.title
|
||||
bookmark_layout.setOnClickListener {
|
||||
selectionInterface.itemSelected(folder.node)
|
||||
fun bind(folder: BookmarkNodeWithDepth, selected: Boolean, onSelect: (BookmarkNode) -> Unit) {
|
||||
view.changeSelected(selected)
|
||||
view.iconView.setImageResource(R.drawable.ic_folder_icon)
|
||||
view.titleView.text = folder.node.title
|
||||
view.setOnClickListener {
|
||||
onSelect(folder.node)
|
||||
}
|
||||
val pxToIndent = dpsToIndent.dpToPx(containerView.resources.displayMetrics)
|
||||
val pxToIndent = dpsToIndent.dpToPx(view.context.resources.displayMetrics)
|
||||
val padding = pxToIndent * if (folder.depth > maxDepth) maxDepth else folder.depth
|
||||
bookmark_layout.setPadding(padding, 0, 0, 0)
|
||||
view.setPadding(padding, 0, 0, 0)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -118,17 +79,12 @@ class SelectBookmarkFolderAdapter(private val sharedViewModel: BookmarksSharedVi
|
|||
|
||||
data class BookmarkNodeWithDepth(val depth: Int, val node: BookmarkNode, val parent: String?)
|
||||
|
||||
private fun BookmarkNode?.convertToFolderDepthTree(
|
||||
depth: Int = 0,
|
||||
list: List<BookmarkNodeWithDepth> = listOf()
|
||||
): List<BookmarkNodeWithDepth> {
|
||||
return if (this != null) {
|
||||
val newList = list.plus(listOf(BookmarkNodeWithDepth(depth, this, this.parentGuid)))
|
||||
newList.plus(
|
||||
children?.filter { it.type == BookmarkNodeType.FOLDER }
|
||||
?.flatMap { it.convertToFolderDepthTree(depth + 1) }
|
||||
?: listOf())
|
||||
} else listOf()
|
||||
private fun BookmarkNode.convertToFolderDepthTree(depth: Int = 0): List<BookmarkNodeWithDepth> {
|
||||
val newList = listOf(BookmarkNodeWithDepth(depth, this, this.parentGuid))
|
||||
return newList + children
|
||||
?.filter { it.type == BookmarkNodeType.FOLDER }
|
||||
?.flatMap { it.convertToFolderDepthTree(depth = depth + 1) }
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -24,12 +24,12 @@ import kotlinx.android.synthetic.main.fragment_select_bookmark_folder.view.*
|
|||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
import mozilla.components.concept.storage.BookmarkNode
|
||||
import mozilla.components.concept.sync.AccountObserver
|
||||
import mozilla.components.concept.sync.OAuthAccount
|
||||
import mozilla.components.concept.sync.Profile
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.getColorFromAttr
|
||||
import org.mozilla.fenix.ext.nav
|
||||
|
@ -75,26 +75,22 @@ class SelectBookmarkFolderFragment : Fragment(), AccountObserver {
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
context?.let {
|
||||
setRootTitles(it, showMobileRoot = true)
|
||||
}
|
||||
(activity as AppCompatActivity).title =
|
||||
getString(R.string.bookmark_select_folder_fragment_label)
|
||||
(activity as AppCompatActivity).supportActionBar?.show()
|
||||
context?.let { setRootTitles(it, showMobileRoot = true) }
|
||||
activity?.title = getString(R.string.bookmark_select_folder_fragment_label)
|
||||
(activity as? AppCompatActivity)?.supportActionBar?.show()
|
||||
|
||||
folderGuid = SelectBookmarkFolderFragmentArgs.fromBundle(arguments!!).folderGuid ?: BookmarkRoot.Root.id
|
||||
checkIfSignedIn()
|
||||
|
||||
lifecycleScope.launch(IO) {
|
||||
bookmarkNode =
|
||||
lifecycleScope.launch(Main) {
|
||||
bookmarkNode = withContext(IO) {
|
||||
requireComponents.core.bookmarksStorage.getTree(BookmarkRoot.Root.id, true)
|
||||
.withOptionalDesktopFolders(context, showMobileRoot = true)
|
||||
launch(Main) {
|
||||
(activity as HomeActivity).title = bookmarkNode?.title ?: getString(R.string.library_bookmarks)
|
||||
val adapter = SelectBookmarkFolderAdapter(sharedViewModel)
|
||||
recylerView_bookmark_folders.adapter = adapter
|
||||
adapter.updateData(bookmarkNode)
|
||||
}
|
||||
activity?.title = bookmarkNode?.title ?: getString(R.string.library_bookmarks)
|
||||
val adapter = SelectBookmarkFolderAdapter(sharedViewModel)
|
||||
recylerView_bookmark_folders.adapter = adapter
|
||||
adapter.updateData(bookmarkNode)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,12 @@ import android.content.Context
|
|||
import android.text.format.DateUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.library.LibrarySiteItemView
|
||||
import org.mozilla.fenix.library.history.viewholders.HistoryDeleteButtonViewHolder
|
||||
import org.mozilla.fenix.library.history.viewholders.HistoryHeaderViewHolder
|
||||
import org.mozilla.fenix.library.history.viewholders.HistoryListItemViewHolder
|
||||
|
@ -149,18 +152,23 @@ class HistoryAdapter(private val historyInteractor: HistoryInteractor) :
|
|||
return when (historyList.items[position]) {
|
||||
is AdapterItem.DeleteButton -> HistoryDeleteButtonViewHolder.LAYOUT_ID
|
||||
is AdapterItem.SectionHeader -> HistoryHeaderViewHolder.LAYOUT_ID
|
||||
is AdapterItem.Item -> HistoryListItemViewHolder.LAYOUT_ID
|
||||
is AdapterItem.Item -> HistoryListItemViewHolder.ID
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||
|
||||
return when (viewType) {
|
||||
HistoryDeleteButtonViewHolder.LAYOUT_ID -> HistoryDeleteButtonViewHolder(view, historyInteractor)
|
||||
HistoryHeaderViewHolder.LAYOUT_ID -> HistoryHeaderViewHolder(view)
|
||||
HistoryListItemViewHolder.LAYOUT_ID -> HistoryListItemViewHolder(view, historyInteractor)
|
||||
else -> throw IllegalStateException()
|
||||
return if (viewType == HistoryListItemViewHolder.ID) {
|
||||
val view = LibrarySiteItemView(parent.context).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
|
||||
}
|
||||
HistoryListItemViewHolder(view, historyInteractor)
|
||||
} else {
|
||||
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||
when (viewType) {
|
||||
HistoryDeleteButtonViewHolder.LAYOUT_ID -> HistoryDeleteButtonViewHolder(view, historyInteractor)
|
||||
HistoryHeaderViewHolder.LAYOUT_ID -> HistoryHeaderViewHolder(view)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,47 +4,22 @@
|
|||
|
||||
package org.mozilla.fenix.library.history.viewholders
|
||||
|
||||
import android.view.View
|
||||
import android.widget.CompoundButton
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.history_list_item.view.*
|
||||
import mozilla.components.browser.menu.BrowserMenu
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ThemeManager
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.loadIntoView
|
||||
import org.mozilla.fenix.library.LibrarySiteItemView
|
||||
import org.mozilla.fenix.library.history.HistoryInteractor
|
||||
import org.mozilla.fenix.library.history.HistoryItem
|
||||
import org.mozilla.fenix.library.history.HistoryItemMenu
|
||||
import org.mozilla.fenix.library.history.HistoryState
|
||||
|
||||
class HistoryListItemViewHolder(
|
||||
view: View,
|
||||
val view: LibrarySiteItemView,
|
||||
private val historyInteractor: HistoryInteractor
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private val favicon = view.history_favicon
|
||||
private val title = view.history_title
|
||||
private val url = view.history_url
|
||||
private val menuButton = view.history_item_overflow
|
||||
|
||||
private var item: HistoryItem? = null
|
||||
private lateinit var historyMenu: HistoryItemMenu
|
||||
private var mode: HistoryState.Mode = HistoryState.Mode.Normal
|
||||
private val checkListener = CompoundButton.OnCheckedChangeListener { _, isChecked ->
|
||||
if (mode is HistoryState.Mode.Normal) {
|
||||
return@OnCheckedChangeListener
|
||||
}
|
||||
|
||||
item?.apply {
|
||||
if (isChecked) {
|
||||
historyInteractor.onItemAddedForRemoval(this)
|
||||
} else {
|
||||
historyInteractor.onItemRemovedForRemoval(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
setupMenu()
|
||||
|
@ -57,55 +32,33 @@ class HistoryListItemViewHolder(
|
|||
true
|
||||
}
|
||||
|
||||
menuButton.setOnClickListener {
|
||||
view.overflowView.setOnClickListener {
|
||||
historyMenu.menuBuilder.build(view.context).show(
|
||||
anchor = it,
|
||||
orientation = BrowserMenu.Orientation.DOWN
|
||||
)
|
||||
}
|
||||
|
||||
view.displayAs(LibrarySiteItemView.ItemType.SITE)
|
||||
}
|
||||
|
||||
fun bind(item: HistoryItem, mode: HistoryState.Mode) {
|
||||
this.item = item
|
||||
this.mode = mode
|
||||
|
||||
title.text = item.title
|
||||
url.text = item.url
|
||||
view.titleView.text = item.title
|
||||
view.urlView.text = item.url
|
||||
|
||||
val selected = when (mode) {
|
||||
is HistoryState.Mode.Editing -> mode.selectedItems.contains(item)
|
||||
else -> false
|
||||
}
|
||||
val selected = mode is HistoryState.Mode.Editing && mode.selectedItems.contains(item)
|
||||
|
||||
setClickListeners(item, selected)
|
||||
|
||||
if (mode is HistoryState.Mode.Editing) {
|
||||
val backgroundTint =
|
||||
if (selected) {
|
||||
ThemeManager.resolveAttribute(R.attr.accentHighContrast, itemView.context)
|
||||
} else {
|
||||
ThemeManager.resolveAttribute(R.attr.neutral, itemView.context)
|
||||
}
|
||||
val backgroundTintList =
|
||||
ContextCompat.getColorStateList(itemView.context, backgroundTint)
|
||||
favicon.backgroundTintList = backgroundTintList
|
||||
|
||||
if (selected) {
|
||||
favicon.setImageResource(R.drawable.mozac_ic_check)
|
||||
} else {
|
||||
updateFavIcon(item.url)
|
||||
}
|
||||
} else {
|
||||
val backgroundTint = ThemeManager.resolveAttribute(R.attr.neutral, itemView.context)
|
||||
val backgroundTintList =
|
||||
ContextCompat.getColorStateList(itemView.context, backgroundTint)
|
||||
favicon.backgroundTintList = backgroundTintList
|
||||
updateFavIcon(item.url)
|
||||
}
|
||||
view.changeSelected(selected)
|
||||
view.loadFavicon(item.url)
|
||||
}
|
||||
|
||||
private fun setupMenu() {
|
||||
this.historyMenu = HistoryItemMenu(itemView.context) {
|
||||
historyMenu = HistoryItemMenu(view.context) {
|
||||
when (it) {
|
||||
is HistoryItemMenu.Item.Delete -> {
|
||||
item?.apply { historyInteractor.onDeleteOne(this) }
|
||||
|
@ -114,28 +67,20 @@ class HistoryListItemViewHolder(
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateFavIcon(url: String) {
|
||||
favicon.context.components.core.icons.loadIntoView(favicon, url)
|
||||
}
|
||||
|
||||
private fun setClickListeners(
|
||||
item: HistoryItem,
|
||||
selected: Boolean
|
||||
) {
|
||||
itemView.history_layout.setOnClickListener {
|
||||
if (mode == HistoryState.Mode.Normal) {
|
||||
historyInteractor.onHistoryItemOpened(item)
|
||||
} else {
|
||||
if (selected) {
|
||||
historyInteractor.onItemRemovedForRemoval(item)
|
||||
} else {
|
||||
historyInteractor.onItemAddedForRemoval(item)
|
||||
}
|
||||
view.setOnClickListener {
|
||||
when {
|
||||
mode == HistoryState.Mode.Normal -> historyInteractor.onHistoryItemOpened(item)
|
||||
selected -> historyInteractor.onItemRemovedForRemoval(item)
|
||||
else -> historyInteractor.onItemAddedForRemoval(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.history_list_item
|
||||
val ID = LibrarySiteItemView.ItemType.SITE.ordinal
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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/bookmark_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/bookmark_favicon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:background="@drawable/favicon_background"
|
||||
android:importantForAccessibility="no"
|
||||
android:padding="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_folder_icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bookmark_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/bookmark_url"
|
||||
app:layout_constraintEnd_toStartOf="@id/bookmark_overflow"
|
||||
app:layout_constraintStart_toEndOf="@id/bookmark_favicon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="Internet" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bookmark_url"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?secondaryText"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/bookmark_overflow"
|
||||
app:layout_constraintStart_toEndOf="@id/bookmark_favicon"
|
||||
app:layout_constraintTop_toBottomOf="@id/bookmark_title"
|
||||
tools:text="https://github.com/mozilla-mobile/fenix" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/bookmark_overflow"
|
||||
android:layout_width="@dimen/glyph_button_width"
|
||||
android:layout_height="@dimen/glyph_button_height"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/bookmark_menu_content_description"
|
||||
android:src="@drawable/ic_menu"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/bookmark_separator"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="2dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="?neutralFaded"
|
||||
android:importantForAccessibility="no"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,67 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<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/history_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="4dp"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="0dp">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/history_item_overflow"
|
||||
android:layout_width="@dimen/glyph_button_width"
|
||||
android:layout_height="@dimen/glyph_button_height"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/content_description_history_menu"
|
||||
android:src="@drawable/ic_menu"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/history_favicon"
|
||||
android:layout_width="@dimen/history_favicon_width_height"
|
||||
android:layout_height="@dimen/history_favicon_width_height"
|
||||
android:background="@drawable/favicon_background"
|
||||
android:padding="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/history_url"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?secondaryText"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toStartOf="@id/history_item_overflow"
|
||||
app:layout_constraintStart_toEndOf="@id/history_favicon"
|
||||
app:layout_constraintTop_toBottomOf="@id/history_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/history_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="18sp"
|
||||
android:layout_marginTop="2dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/history_item_overflow"
|
||||
app:layout_constraintStart_toEndOf="@id/history_favicon"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,98 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
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:layout_width="match_parent"
|
||||
android:layout_height="@dimen/library_item_height"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageSwitcher
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/history_favicon_width_height"
|
||||
android:layout_height="@dimen/history_favicon_width_height"
|
||||
android:layout_marginStart="20dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
<ImageView
|
||||
android:id="@+id/favicon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="10dp"
|
||||
android:background="@drawable/favicon_background"
|
||||
android:backgroundTint="?neutral"
|
||||
android:importantForAccessibility="no"
|
||||
tools:src="@drawable/ic_folder_icon" />
|
||||
<ImageView
|
||||
android:id="@+id/checkmark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="10dp"
|
||||
android:background="@drawable/favicon_background"
|
||||
android:backgroundTint="?accentHighContrast"
|
||||
android:src="@drawable/mozac_ic_check" />
|
||||
</ImageSwitcher>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textSize="18sp"
|
||||
android:textColor="?primaryText"
|
||||
tools:text="Example site"
|
||||
app:layout_constraintEnd_toStartOf="@id/overflow_menu"
|
||||
app:layout_constraintStart_toEndOf="@id/icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/url"
|
||||
app:layout_constraintVertical_chainStyle="packed"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/url"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?secondaryText"
|
||||
android:textSize="12sp"
|
||||
tools:text="https://example.com/"
|
||||
app:layout_constraintEnd_toStartOf="@id/overflow_menu"
|
||||
app:layout_constraintStart_toEndOf="@id/icon"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/overflow_menu"
|
||||
android:layout_width="@dimen/glyph_button_width"
|
||||
android:layout_height="@dimen/glyph_button_height"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/content_description_menu"
|
||||
android:src="@drawable/ic_menu"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/separator"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="2dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:background="?neutralFaded"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue