diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt index 022b2d2f4..ed46fa96d 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt @@ -4,16 +4,16 @@ package org.mozilla.fenix.library.bookmarks +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.view.isVisible import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import mozilla.appservices.places.BookmarkRoot import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNodeType +import org.mozilla.fenix.R import org.mozilla.fenix.library.LibrarySiteItemView import org.mozilla.fenix.library.SelectionHolder import org.mozilla.fenix.library.bookmarks.viewholders.BookmarkFolderViewHolder @@ -68,9 +68,8 @@ class BookmarkAdapter(val emptyView: View, val interactor: BookmarkViewInteracto } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookmarkNodeViewHolder { - val view = LibrarySiteItemView(parent.context).apply { - layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT) - } + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.bookmark_list_item, parent, false) as LibrarySiteItemView return when (viewType) { LibrarySiteItemView.ItemType.SITE.ordinal -> BookmarkItemViewHolder(view, interactor, this) diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkTouchHelper.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkTouchHelper.kt new file mode 100644 index 000000000..69c0186ed --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkTouchHelper.kt @@ -0,0 +1,124 @@ +/* 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.bookmarks + +import android.graphics.Canvas +import android.graphics.Rect +import android.graphics.drawable.Drawable +import androidx.appcompat.content.res.AppCompatResources +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import mozilla.components.support.ktx.android.content.getColorFromAttr +import mozilla.components.support.ktx.android.content.getDrawableWithTint +import mozilla.components.support.ktx.android.util.dpToPx +import org.mozilla.fenix.R +import org.mozilla.fenix.home.sessioncontrol.SwipeToDeleteCallback +import org.mozilla.fenix.library.bookmarks.viewholders.BookmarkNodeViewHolder +import org.mozilla.fenix.library.bookmarks.viewholders.BookmarkSeparatorViewHolder + +class BookmarkTouchHelper(interactor: BookmarkViewInteractor) : + ItemTouchHelper(BookmarkTouchCallback(interactor)) + +class BookmarkTouchCallback(private val interactor: BookmarkViewInteractor) : + ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) { + + override fun getSwipeDirs( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + val item = (viewHolder as BookmarkNodeViewHolder).item + return if (viewHolder is BookmarkSeparatorViewHolder || item?.inRoots() == true) { + 0 + } else { + super.getSwipeDirs(recyclerView, viewHolder) + } + } + + /** + * Delete the bookmark when swiped. + */ + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + val item = (viewHolder as BookmarkNodeViewHolder).item + item?.let { interactor.onDelete(setOf(it)) } + } + + override fun onChildDraw( + c: Canvas, + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + dX: Float, + dY: Float, + actionState: Int, + isCurrentlyActive: Boolean + ) { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) + val icon = recyclerView.context.getDrawableWithTint( + R.drawable.ic_delete, + recyclerView.context.getColorFromAttr(R.attr.destructive) + )!! + val background = AppCompatResources.getDrawable( + recyclerView.context, + R.drawable.swipe_delete_background + )!! + val margin = + SwipeToDeleteCallback.MARGIN.dpToPx(recyclerView.context.resources.displayMetrics) + val cellHeight = viewHolder.itemView.bottom - viewHolder.itemView.top + val iconTop = viewHolder.itemView.top + (cellHeight - icon.intrinsicHeight) / 2 + val iconBottom = iconTop + icon.intrinsicHeight + + when { + dX > 0 -> { // Swiping to the right + val backgroundBounds = Rect( + viewHolder.itemView.left, viewHolder.itemView.top, + (viewHolder.itemView.left + dX).toInt() + SwipeToDeleteCallback.BACKGROUND_CORNER_OFFSET, + viewHolder.itemView.bottom + ) + val iconLeft = viewHolder.itemView.left + margin + val iconRight = viewHolder.itemView.left + margin + icon.intrinsicWidth + val iconBounds = Rect(iconLeft, iconTop, iconRight, iconBottom) + + setBounds(background, backgroundBounds, icon, iconBounds) + draw(background, icon, c) + } + dX < 0 -> { // Swiping to the left + val backgroundBounds = Rect( + (viewHolder.itemView.right + dX).toInt() - SwipeToDeleteCallback.BACKGROUND_CORNER_OFFSET, + viewHolder.itemView.top, viewHolder.itemView.right, viewHolder.itemView.bottom + ) + val iconLeft = viewHolder.itemView.right - margin - icon.intrinsicWidth + val iconRight = viewHolder.itemView.right - margin + val iconBounds = Rect(iconLeft, iconTop, iconRight, iconBottom) + + setBounds(background, backgroundBounds, icon, iconBounds) + draw(background, icon, c) + } + else -> { // View not swiped + val bounds = Rect(0, 0, 0, 0) + setBounds(background, bounds, icon, bounds) + } + } + } + + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean = false + + private fun setBounds( + background: Drawable, + backgroundBounds: Rect, + icon: Drawable, + iconBounds: Rect + ) { + background.bounds = backgroundBounds + icon.bounds = iconBounds + } + + private fun draw(background: Drawable, icon: Drawable, c: Canvas) { + background.draw(c) + icon.draw(c) + } +} diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt index 12216e8a7..10d91822b 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt @@ -13,8 +13,8 @@ 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.UserInteractionHandler -import org.mozilla.fenix.R import org.mozilla.fenix.NavGraphDirections +import org.mozilla.fenix.R import org.mozilla.fenix.library.LibraryPageView import org.mozilla.fenix.library.SelectionInteractor @@ -125,6 +125,8 @@ class BookmarkView( view.swipe_refresh.setOnRefreshListener { interactor.onRequestSync() } + + BookmarkTouchHelper(interactor).attachToRecyclerView(view.bookmark_list) } fun update(state: BookmarkFragmentState) { diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkFolderViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkFolderViewHolder.kt index 2973c243a..ff81425ff 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkFolderViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkFolderViewHolder.kt @@ -25,7 +25,10 @@ class BookmarkFolderViewHolder( private val selectionHolder: SelectionHolder ) : BookmarkNodeViewHolder(view, interactor) { + override var item: BookmarkNode? = null + override fun bind(item: BookmarkNode) { + this.item = item containerView.displayAs(LibrarySiteItemView.ItemType.FOLDER) diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkItemViewHolder.kt index 6d51750bc..23aab9ca0 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkItemViewHolder.kt @@ -20,7 +20,10 @@ class BookmarkItemViewHolder( private val selectionHolder: SelectionHolder ) : BookmarkNodeViewHolder(view, interactor) { + override var item: BookmarkNode? = null + override fun bind(item: BookmarkNode) { + this.item = item containerView.displayAs(LibrarySiteItemView.ItemType.SITE) diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkNodeViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkNodeViewHolder.kt index a22fc264b..a8bf8c4c3 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkNodeViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkNodeViewHolder.kt @@ -18,8 +18,9 @@ import org.mozilla.fenix.library.bookmarks.BookmarkViewInteractor abstract class BookmarkNodeViewHolder( override val containerView: LibrarySiteItemView, private val interactor: BookmarkViewInteractor -) : - RecyclerView.ViewHolder(containerView), LayoutContainer { +) : RecyclerView.ViewHolder(containerView), LayoutContainer { + + abstract var item: BookmarkNode? abstract fun bind(item: BookmarkNode) diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkSeparatorViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkSeparatorViewHolder.kt index 83ee21fcc..49e5faff8 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkSeparatorViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkSeparatorViewHolder.kt @@ -16,7 +16,10 @@ class BookmarkSeparatorViewHolder( interactor: BookmarkViewInteractor ) : BookmarkNodeViewHolder(view, interactor) { + override var item: BookmarkNode? = null + override fun bind(item: BookmarkNode) { + this.item = item containerView.displayAs(LibrarySiteItemView.ItemType.SEPARATOR) setupMenu(item) } diff --git a/app/src/main/res/layout/bookmark_list_item.xml b/app/src/main/res/layout/bookmark_list_item.xml new file mode 100644 index 000000000..ee42f2c06 --- /dev/null +++ b/app/src/main/res/layout/bookmark_list_item.xml @@ -0,0 +1,8 @@ + + +