[fenix] Revert "Closes https://github.com/mozilla-mobile/fenix/issues/25967: add classes to support multiple viewHolders"
This reverts commit 88f0b1962495247959a687f6c403e1e54b4946db.pull/600/head
parent
97b5c4cfc0
commit
b8ee63d226
@ -1,113 +0,0 @@
|
||||
/* 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.history
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* The class represents the items types used by [HistoryAdapter] to populate the list.
|
||||
* It contains the data for viewHolders. Subclasses match the variety of viewHolders.
|
||||
*/
|
||||
sealed class HistoryViewItem : Parcelable {
|
||||
|
||||
/**
|
||||
* A class representing a regular history record in the history and synced history lists.
|
||||
*
|
||||
* @param data history item that will be displayed.
|
||||
* @param collapsed state flag to support collapsed header feature; collapsed items will be
|
||||
* filtered out from the list of displayed items.
|
||||
*/
|
||||
@Parcelize
|
||||
data class HistoryItem(
|
||||
val data: History.Regular,
|
||||
val collapsed: Boolean = false
|
||||
) : HistoryViewItem()
|
||||
|
||||
/**
|
||||
* A class representing a search group (a group of history items) in the history list.
|
||||
*
|
||||
* @param data History group item that will be displayed.
|
||||
* @param collapsed State flag to support collapsed header feature; collapsed items will be
|
||||
* filtered out from the list of displayed items.
|
||||
*/
|
||||
@Parcelize
|
||||
data class HistoryGroupItem(
|
||||
val data: History.Group,
|
||||
val collapsed: Boolean = false
|
||||
) : HistoryViewItem()
|
||||
|
||||
/**
|
||||
* A class representing a header in the history and synced history lists.
|
||||
*
|
||||
* @param title inside a time group header.
|
||||
* @param timeGroup A time group associated with a Header.
|
||||
* @param collapsed state flag to support collapsed header feature; collapsed items will be
|
||||
* filtered out from the list of displayed items.
|
||||
*/
|
||||
@Parcelize
|
||||
data class TimeGroupHeader(
|
||||
val title: String,
|
||||
val timeGroup: HistoryItemTimeGroup,
|
||||
val collapsed: Boolean = false
|
||||
) : HistoryViewItem()
|
||||
|
||||
/**
|
||||
* A class representing a recently closed button in the history list.
|
||||
*
|
||||
* @param title of a recently closed button inside History screen.
|
||||
* @param body of a recently closed button inside History screen.
|
||||
*/
|
||||
@Parcelize
|
||||
data class RecentlyClosedItem(
|
||||
val title: String,
|
||||
val body: String
|
||||
) : HistoryViewItem()
|
||||
|
||||
/**
|
||||
* A class representing a synced history button in the history list.
|
||||
*
|
||||
* @param title of a recently closed button inside History screen.
|
||||
*/
|
||||
@Parcelize
|
||||
data class SyncedHistoryItem(
|
||||
val title: String
|
||||
) : HistoryViewItem()
|
||||
|
||||
/**
|
||||
* A class representing empty state in history and synced history screens.
|
||||
*
|
||||
* @param emptyMessage of an emptyView inside History screen.
|
||||
*/
|
||||
@Parcelize
|
||||
data class EmptyHistoryItem(
|
||||
val emptyMessage: String
|
||||
) : HistoryViewItem()
|
||||
|
||||
/**
|
||||
* A class representing a sign-in window inside the synced history screen.
|
||||
*/
|
||||
@Parcelize
|
||||
object SignInHistoryItem : HistoryViewItem()
|
||||
|
||||
/**
|
||||
* A class representing an extra space that header items have above them when they are
|
||||
* not in a collapsed state.
|
||||
*
|
||||
* @param timeGroup A time group associated with a separator; separator relates to the time group
|
||||
* of items above, not below. In case the time group is collapsed, it should be hidden with its
|
||||
* time group as well, so collapsed groups wouldn't have extra spacing in between.
|
||||
*/
|
||||
@Parcelize
|
||||
data class TimeGroupSeparatorHistoryItem(
|
||||
val timeGroup: HistoryItemTimeGroup?
|
||||
) : HistoryViewItem()
|
||||
|
||||
/**
|
||||
* A class representing a space at the top of history and synced history lists.
|
||||
*/
|
||||
@Parcelize
|
||||
object TopSeparatorHistoryItem : HistoryViewItem()
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/* 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.history.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.HistoryListEmptyBinding
|
||||
import org.mozilla.fenix.library.history.HistoryAdapter
|
||||
import org.mozilla.fenix.library.history.HistoryViewItem
|
||||
|
||||
/**
|
||||
* A view representing the empty state in history and synced history screens.
|
||||
* [HistoryAdapter] is responsible for creating and populating the view.
|
||||
*
|
||||
* @param view that is passed down to the parent's constructor.
|
||||
*/
|
||||
class EmptyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private val binding = HistoryListEmptyBinding.bind(view)
|
||||
|
||||
/**
|
||||
* Binds data to the view.
|
||||
*
|
||||
* @param item Data associated with the view.
|
||||
*/
|
||||
fun bind(item: HistoryViewItem.EmptyHistoryItem) {
|
||||
binding.emptyMessage.text = item.emptyMessage
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.history_list_empty
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
/* 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.history.viewholders
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.HistoryListGroupBinding
|
||||
import org.mozilla.fenix.ext.hideAndDisable
|
||||
import org.mozilla.fenix.ext.showAndEnable
|
||||
import org.mozilla.fenix.library.history.History
|
||||
import org.mozilla.fenix.library.history.HistoryAdapter
|
||||
import org.mozilla.fenix.library.history.HistoryFragmentState
|
||||
import org.mozilla.fenix.library.history.HistoryInteractor
|
||||
import org.mozilla.fenix.library.history.HistoryViewItem
|
||||
import org.mozilla.fenix.selection.SelectionHolder
|
||||
import org.mozilla.fenix.library.LibrarySiteItemView
|
||||
|
||||
/**
|
||||
* A view representing a search group (a group of history items) in the history list.
|
||||
* [HistoryAdapter] is responsible for creating and populating the view.
|
||||
*
|
||||
* @param view that is passed down to the parent's constructor.
|
||||
* @param historyInteractor Passed down to [LibrarySiteItemView], to handle selection of multiple items.
|
||||
* @param selectionHolder Contains selected elements.
|
||||
* @param onDeleteClicked Invokes when a delete button is pressed.
|
||||
*/
|
||||
class HistoryGroupViewHolder(
|
||||
view: View,
|
||||
private val historyInteractor: HistoryInteractor,
|
||||
private val selectionHolder: SelectionHolder<History>,
|
||||
private val onDeleteClicked: (Int) -> Unit
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private val binding = HistoryListGroupBinding.bind(view)
|
||||
|
||||
init {
|
||||
binding.historyGroupLayout.overflowView.apply {
|
||||
setImageResource(R.drawable.ic_close)
|
||||
contentDescription = view.context.getString(R.string.history_delete_item)
|
||||
setOnClickListener {
|
||||
onDeleteClicked.invoke(bindingAdapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds data to the view.
|
||||
*
|
||||
* @param item Data associated with the view.
|
||||
* @param mode is used to determine if the list is in the multiple-selection state or not.
|
||||
* @param groupPendingDeletionCount is used to adjust the number of items inside a group,
|
||||
* based on the number of items the user has removed from it.
|
||||
*/
|
||||
fun bind(
|
||||
item: HistoryViewItem.HistoryGroupItem,
|
||||
mode: HistoryFragmentState.Mode,
|
||||
groupPendingDeletionCount: Int
|
||||
) {
|
||||
with(binding.historyGroupLayout) {
|
||||
iconView.setImageResource(R.drawable.ic_multiple_tabs)
|
||||
|
||||
titleView.text = item.data.title
|
||||
urlView.text = getGroupCountText(
|
||||
itemSize = item.data.items.size,
|
||||
pendingDeletionSize = groupPendingDeletionCount,
|
||||
resources = resources
|
||||
)
|
||||
|
||||
setSelectionInteractor(item.data, selectionHolder, historyInteractor)
|
||||
changeSelected(item.data in selectionHolder.selectedItems)
|
||||
|
||||
if (mode is HistoryFragmentState.Mode.Editing) {
|
||||
overflowView.hideAndDisable()
|
||||
} else {
|
||||
overflowView.showAndEnable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getGroupCountText(
|
||||
itemSize: Int,
|
||||
pendingDeletionSize: Int,
|
||||
resources: Resources
|
||||
): String {
|
||||
val numChildren = itemSize - pendingDeletionSize
|
||||
val stringId = if (numChildren == 1) {
|
||||
R.string.history_search_group_site
|
||||
} else {
|
||||
R.string.history_search_group_sites
|
||||
}
|
||||
return String.format(resources.getString(stringId), numChildren)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.history_list_group
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
/* 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.history.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.HistoryListHistoryBinding
|
||||
import org.mozilla.fenix.ext.hideAndDisable
|
||||
import org.mozilla.fenix.ext.showAndEnable
|
||||
import org.mozilla.fenix.library.LibrarySiteItemView
|
||||
import org.mozilla.fenix.library.history.History
|
||||
import org.mozilla.fenix.library.history.HistoryAdapter
|
||||
import org.mozilla.fenix.library.history.HistoryFragmentState
|
||||
import org.mozilla.fenix.library.history.HistoryInteractor
|
||||
import org.mozilla.fenix.library.history.HistoryViewItem
|
||||
import org.mozilla.fenix.selection.SelectionHolder
|
||||
|
||||
/**
|
||||
* A view representing a regular history record in the history and synced history lists.
|
||||
* [HistoryAdapter] is responsible for creating and populating the view.
|
||||
*
|
||||
* @param view that is passed down to the parent's constructor.
|
||||
* @param historyInteractor Passed down to [LibrarySiteItemView], to handle selection of multiple items.
|
||||
* @param selectionHolder Contains selected elements.
|
||||
* @param onDeleteClicked Invokes when a delete button is pressed.
|
||||
*/
|
||||
class HistoryViewHolder(
|
||||
view: View,
|
||||
val historyInteractor: HistoryInteractor,
|
||||
val selectionHolder: SelectionHolder<History>,
|
||||
private val onDeleteClicked: (Int) -> Unit
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private lateinit var historyItem: HistoryViewItem.HistoryItem
|
||||
val binding = HistoryListHistoryBinding.bind(view)
|
||||
|
||||
init {
|
||||
binding.historyLayout.overflowView.apply {
|
||||
setImageResource(R.drawable.ic_close)
|
||||
contentDescription = view.context.getString(R.string.history_delete_item)
|
||||
setOnClickListener {
|
||||
onDeleteClicked.invoke(bindingAdapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds data to the view.
|
||||
*
|
||||
* @param item Data associated with the view.
|
||||
* @param mode is used to determine if the list is in the multiple-selection state or not.
|
||||
*/
|
||||
fun bind(item: HistoryViewItem.HistoryItem, mode: HistoryFragmentState.Mode) {
|
||||
with(binding.historyLayout) {
|
||||
titleView.text = item.data.title
|
||||
urlView.text = item.data.url
|
||||
|
||||
setSelectionInteractor(item.data, selectionHolder, historyInteractor)
|
||||
changeSelected(item.data in selectionHolder.selectedItems)
|
||||
|
||||
if (!::historyItem.isInitialized ||
|
||||
historyItem.data.url != item.data.url
|
||||
) {
|
||||
loadFavicon(item.data.url)
|
||||
}
|
||||
|
||||
if (mode is HistoryFragmentState.Mode.Editing) {
|
||||
overflowView.hideAndDisable()
|
||||
} else {
|
||||
overflowView.showAndEnable()
|
||||
}
|
||||
}
|
||||
|
||||
historyItem = item
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.history_list_history
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/* 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.history.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.RecentlyClosedNavItemBinding
|
||||
import org.mozilla.fenix.library.history.HistoryAdapter
|
||||
import org.mozilla.fenix.library.history.HistoryInteractor
|
||||
import org.mozilla.fenix.library.history.HistoryViewItem
|
||||
|
||||
/**
|
||||
* A view containing a recently closed button in the history list.
|
||||
* [HistoryAdapter] is responsible for creating and populating the view.
|
||||
*
|
||||
* @param view that is passed down to the parent's constructor.
|
||||
* @param historyInteractor Handles a click even on the item.
|
||||
*/
|
||||
class RecentlyClosedViewHolder(
|
||||
view: View,
|
||||
private val historyInteractor: HistoryInteractor
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private val binding = RecentlyClosedNavItemBinding.bind(view)
|
||||
|
||||
init {
|
||||
binding.root.setOnClickListener {
|
||||
historyInteractor.onRecentlyClosedClicked()
|
||||
}
|
||||
binding.recentlyClosedNav.isVisible = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds data to the view.
|
||||
*
|
||||
* @param item Data associated with the view.
|
||||
*/
|
||||
fun bind(item: HistoryViewItem.RecentlyClosedItem) {
|
||||
binding.recentlyClosedTabsHeader.text = item.title
|
||||
binding.recentlyClosedTabsDescription.text = item.body
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.recently_closed_nav_item
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/* 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.history.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.HistoryListSignInBinding
|
||||
import org.mozilla.fenix.library.history.HistoryAdapter
|
||||
|
||||
/**
|
||||
* A view representing a sign in window inside the synced history screen.
|
||||
* [HistoryAdapter] is responsible for creating and populating the view.
|
||||
*
|
||||
* @param view that is passed down to the parent's constructor.
|
||||
* @param onSignInClicked Invokes when a signIn button is pressed.
|
||||
* @param onCreateAccountClicked Invokes when a createAccount button is pressed.
|
||||
*/
|
||||
class SignInViewHolder(
|
||||
view: View,
|
||||
private val onSignInClicked: () -> Unit,
|
||||
private val onCreateAccountClicked: () -> Unit
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private val binding = HistoryListSignInBinding.bind(view)
|
||||
|
||||
init {
|
||||
binding.signInButton.setOnClickListener {
|
||||
onSignInClicked.invoke()
|
||||
}
|
||||
binding.createAccount.setOnClickListener {
|
||||
onCreateAccountClicked.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.history_list_sign_in
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/* 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.history.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.library.history.HistoryAdapter
|
||||
|
||||
/**
|
||||
* A view used as an extra space for time group items, when they are not in a collapsed state.
|
||||
* [HistoryAdapter] is responsible for creating this view.
|
||||
*
|
||||
* @param view that is passed down to the parent's constructor.
|
||||
*/
|
||||
class TimeGroupSeparatorViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.history_list_time_group_separator
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/* 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.history.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.HistoryListHeaderBinding
|
||||
import org.mozilla.fenix.library.history.HistoryAdapter
|
||||
import org.mozilla.fenix.library.history.HistoryItemTimeGroup
|
||||
import org.mozilla.fenix.library.history.HistoryViewItem
|
||||
|
||||
/**
|
||||
* A view representing a Header in the history and synced history lists.
|
||||
* [HistoryAdapter] is responsible for creating and populating the view with data.
|
||||
*
|
||||
* @param view that is passed down to the parent's constructor.
|
||||
* @param onClickListener Invokes on a click event on the viewHolder.
|
||||
*/
|
||||
class TimeGroupViewHolder(
|
||||
view: View,
|
||||
private val onClickListener: (HistoryItemTimeGroup, Boolean) -> Unit
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private val binding = HistoryListHeaderBinding.bind(view)
|
||||
private lateinit var item: HistoryViewItem.TimeGroupHeader
|
||||
|
||||
init {
|
||||
binding.root.setOnClickListener {
|
||||
onClickListener.invoke(item.timeGroup, item.collapsed)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds data to the view.
|
||||
*
|
||||
* @param item Data associated with the view.
|
||||
*/
|
||||
fun bind(item: HistoryViewItem.TimeGroupHeader) {
|
||||
binding.headerTitle.text = item.title
|
||||
binding.chevron.isActivated = !item.collapsed
|
||||
this.item = item
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.history_list_header
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/* 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.history.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.library.history.HistoryAdapter
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
/**
|
||||
* A view used as an extra space at the top of history and synced history lists.
|
||||
* [HistoryAdapter] is responsible for creating this view.
|
||||
*
|
||||
* @param view that is passed down to the parent's constructor.
|
||||
*/
|
||||
class TopSeparatorViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.history_list_top_separator
|
||||
}
|
||||
}
|
@ -1,22 +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:layout_width="match_parent"
|
||||
android:layout_height="0dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/empty_message"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/history_empty_message"
|
||||
android:textColor="?attr/textSecondary"
|
||||
android:textSize="16sp"
|
||||
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,9 +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/. -->
|
||||
<org.mozilla.fenix.library.LibrarySiteItemView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/history_group_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="@dimen/library_item_height" />
|
@ -1,44 +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/. -->
|
||||
<FrameLayout 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="wrap_content"
|
||||
android:minHeight="48dp"
|
||||
android:paddingEnd="4dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?attr/layer1">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_title"
|
||||
style="@style/Header16TextStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/chevron"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Header and a super long something in the end for testing" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chevron"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_chevron" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
@ -1,9 +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/. -->
|
||||
<org.mozilla.fenix.library.LibrarySiteItemView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/history_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="@dimen/library_item_height" />
|
@ -1,46 +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:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sign_in_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="80dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/history_sign_in_message"
|
||||
android:textAppearance="@style/Body14TextStyle"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sign_in_button"
|
||||
style="@style/PositiveButton"
|
||||
android:layout_marginHorizontal="60dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/history_sign_in_button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sign_in_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/create_account"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="64dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/history_sign_in_create_account"
|
||||
android:textColor="?attr/textPrimary"
|
||||
android:textSize="10sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/sign_in_button" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,8 +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/. -->
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/separator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="32dp"/>
|
@ -1,8 +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/. -->
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/separator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"/>
|
@ -1,196 +0,0 @@
|
||||
package org.mozilla.fenix.library.history
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.concept.storage.HistoryMetadataKey
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.HistoryListGroupBinding
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.library.history.viewholders.HistoryGroupViewHolder
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class HistoryGroupViewHolderTest {
|
||||
|
||||
private lateinit var binding: HistoryListGroupBinding
|
||||
private lateinit var interactor: HistoryInteractor
|
||||
|
||||
private val metaDataItem = History.Metadata(
|
||||
position = 0,
|
||||
title = "Mozilla",
|
||||
url = "https://foundation.mozilla.org",
|
||||
visitedAt = 12398410293L,
|
||||
historyTimeGroup = HistoryItemTimeGroup.Today,
|
||||
totalViewTime = 1250,
|
||||
historyMetadataKey = HistoryMetadataKey(
|
||||
url = "https://foundation.mozilla.org",
|
||||
searchTerm = "mozilla"
|
||||
),
|
||||
selected = false
|
||||
)
|
||||
|
||||
private val historyGroupItem = HistoryViewItem.HistoryGroupItem(
|
||||
data = History.Group(
|
||||
position = 0,
|
||||
title = "Mozilla",
|
||||
visitedAt = 12398410293L,
|
||||
historyTimeGroup = HistoryItemTimeGroup.Today,
|
||||
items = listOf(
|
||||
metaDataItem,
|
||||
metaDataItem.copy(position = 1),
|
||||
metaDataItem.copy(position = 2),
|
||||
metaDataItem.copy(position = 3)
|
||||
),
|
||||
selected = false
|
||||
)
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
binding = HistoryListGroupBinding.inflate(LayoutInflater.from(testContext))
|
||||
interactor = mockk(relaxed = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a history group item has more than one item THEN viewHolder uses text for multiple sites`() {
|
||||
val viewHolder = testViewHolder()
|
||||
|
||||
val expectedText = String.format(testContext.resources.getString(R.string.history_search_group_sites), 5)
|
||||
val actualText = viewHolder.getGroupCountText(5, 0, testContext.resources)
|
||||
assertEquals(expectedText, actualText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a history group item has exactly one item THEN get text for single site`() {
|
||||
val viewHolder = testViewHolder()
|
||||
|
||||
val expectedText = String.format(testContext.resources.getString(R.string.history_search_group_site), 1)
|
||||
val actualText = viewHolder.getGroupCountText(1, 0, testContext.resources)
|
||||
assertEquals(expectedText, actualText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a new history group item on bind THEN set the history group name and items size`() {
|
||||
val viewHolder = testViewHolder()
|
||||
viewHolder.bind(historyGroupItem, HistoryFragmentState.Mode.Normal, 0)
|
||||
|
||||
val childrenSizeExpectedText = viewHolder.getGroupCountText(
|
||||
historyGroupItem.data.items.size,
|
||||
0,
|
||||
testContext.resources
|
||||
)
|
||||
assertEquals(historyGroupItem.data.title, binding.historyGroupLayout.titleView.text)
|
||||
assertEquals(childrenSizeExpectedText, binding.historyGroupLayout.urlView.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN pending deletion not zero THEN adjust items size`() {
|
||||
val viewHolder = testViewHolder()
|
||||
viewHolder.bind(historyGroupItem, HistoryFragmentState.Mode.Normal, 1)
|
||||
|
||||
val childrenSizeExpectedText = viewHolder.getGroupCountText(
|
||||
historyGroupItem.data.items.size,
|
||||
1,
|
||||
testContext.resources
|
||||
)
|
||||
assertEquals(childrenSizeExpectedText, binding.historyGroupLayout.urlView.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN a history item delete icon is clicked THEN onDeleteClicked is called`() {
|
||||
var isDeleteClicked = false
|
||||
testViewHolder(
|
||||
onDeleteClicked = { isDeleteClicked = true }
|
||||
).bind(historyGroupItem, HistoryFragmentState.Mode.Normal, 0)
|
||||
|
||||
binding.historyGroupLayout.overflowView.performClick()
|
||||
assertEquals(true, isDeleteClicked)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN a history item is clicked THEN interactor open is called`() {
|
||||
testViewHolder().bind(historyGroupItem, HistoryFragmentState.Mode.Normal, 0)
|
||||
|
||||
binding.historyGroupLayout.performClick()
|
||||
verify { interactor.open(historyGroupItem.data) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN selecting mode THEN delete button is not visible `() {
|
||||
testViewHolder().bind(historyGroupItem, HistoryFragmentState.Mode.Editing(setOf()), 0)
|
||||
|
||||
assertEquals(View.INVISIBLE, binding.historyGroupLayout.overflowView.visibility)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN normal mode THEN delete button is visible `() {
|
||||
testViewHolder().bind(historyGroupItem, HistoryFragmentState.Mode.Normal, 0)
|
||||
|
||||
assertEquals(View.VISIBLE, binding.historyGroupLayout.overflowView.visibility)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN editing mode WHEN item is selected THEN checkmark is visible `() {
|
||||
testViewHolder(
|
||||
selectedHistoryItems = setOf(historyGroupItem.data)
|
||||
).bind(historyGroupItem, HistoryFragmentState.Mode.Editing(setOf(historyGroupItem.data)), 0)
|
||||
|
||||
assertEquals(1, binding.historyGroupLayout.binding.icon.displayedChild)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN editing mode WHEN item is not selected THEN checkmark is not visible `() {
|
||||
testViewHolder().bind(historyGroupItem, HistoryFragmentState.Mode.Editing(setOf()), 0)
|
||||
|
||||
assertEquals(0, binding.historyGroupLayout.binding.icon.displayedChild)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN normal mode WHEN item is long pressed THEN interactor select is called`() {
|
||||
testViewHolder().bind(historyGroupItem, HistoryFragmentState.Mode.Normal, 0)
|
||||
|
||||
binding.historyGroupLayout.performLongClick()
|
||||
verify { interactor.select(historyGroupItem.data) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN editing mode and item is not selected WHEN item is clicked THEN interactor select is called`() {
|
||||
testViewHolder(
|
||||
selectedHistoryItems = setOf(historyGroupItem.data.copy(position = 1))
|
||||
).bind(historyGroupItem, HistoryFragmentState.Mode.Normal, 0)
|
||||
|
||||
binding.historyGroupLayout.performClick()
|
||||
verify { interactor.select(historyGroupItem.data) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN editing mode and item is selected WHEN item is clicked THEN interactor select is called`() {
|
||||
testViewHolder(
|
||||
selectedHistoryItems = setOf(historyGroupItem.data)
|
||||
).bind(historyGroupItem, HistoryFragmentState.Mode.Normal, 0)
|
||||
|
||||
binding.historyGroupLayout.performClick()
|
||||
verify { interactor.deselect(historyGroupItem.data) }
|
||||
}
|
||||
|
||||
private fun testViewHolder(
|
||||
view: View = binding.root,
|
||||
historyInteractor: HistoryInteractor = interactor,
|
||||
selectedHistoryItems: Set<History> = setOf(),
|
||||
onDeleteClicked: (Int) -> Unit = {}
|
||||
): HistoryGroupViewHolder {
|
||||
return HistoryGroupViewHolder(
|
||||
view = view,
|
||||
historyInteractor = historyInteractor,
|
||||
selectionHolder = mockk { every { selectedItems } returns selectedHistoryItems },
|
||||
onDeleteClicked = onDeleteClicked
|
||||
)
|
||||
}
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
package org.mozilla.fenix.library.history
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.browser.icons.BrowserIcons
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.databinding.HistoryListHistoryBinding
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.loadIntoView
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.library.history.viewholders.HistoryViewHolder
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class HistoryViewHolderTest {
|
||||
|
||||
private lateinit var binding: HistoryListHistoryBinding
|
||||
private lateinit var interactor: HistoryInteractor
|
||||
private lateinit var iconsLoader: BrowserIcons
|
||||
|
||||
private val historyItem = HistoryViewItem.HistoryItem(
|
||||
data = History.Regular(
|
||||
position = 0,
|
||||
title = "Mozilla",
|
||||
url = "https://foundation.mozilla.org",
|
||||
visitedAt = 12398410293L,
|
||||
historyTimeGroup = HistoryItemTimeGroup.Today,
|
||||
selected = false
|
||||
)
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
binding = HistoryListHistoryBinding.inflate(LayoutInflater.from(testContext))
|
||||
interactor = mockk(relaxed = true)
|
||||
iconsLoader = spyk(
|
||||
BrowserIcons(
|
||||
testContext,
|
||||
mockk(relaxed = true)
|
||||
)
|
||||
)
|
||||
every { testContext.components.core.icons } returns iconsLoader
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a new history item on bind THEN set the history title and url text`() {
|
||||
testViewHolder().bind(historyItem, HistoryFragmentState.Mode.Normal)
|
||||
|
||||
assertEquals(historyItem.data.title, binding.historyLayout.titleView.text)
|
||||
assertEquals(historyItem.data.url, binding.historyLayout.urlView.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN a new history item on bind THEN the icon is loaded`() {
|
||||
testViewHolder(
|
||||
selectedHistoryItems = setOf(historyItem.data)
|
||||
).bind(historyItem, HistoryFragmentState.Mode.Editing(setOf(historyItem.data)))
|
||||
|
||||
verify { iconsLoader.loadIntoView(binding.historyLayout.iconView, historyItem.data.url) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN the same history item on bind twice THEN the icon is not loaded again`() {
|
||||
testViewHolder(
|
||||
selectedHistoryItems = setOf(historyItem.data)
|
||||
).apply {
|
||||
bind(historyItem, HistoryFragmentState.Mode.Editing(setOf(historyItem.data)))
|
||||
bind(historyItem, HistoryFragmentState.Mode.Editing(setOf(historyItem.data)))
|
||||
}
|
||||
|
||||
verify(exactly = 1) {
|
||||
iconsLoader.loadIntoView(
|
||||
binding.historyLayout.iconView,
|
||||
historyItem.data.url
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN a history item delete icon is clicked THEN onDeleteClicked is called`() {
|
||||
var isDeleteClicked = false
|
||||
testViewHolder(
|
||||
onDeleteClicked = { isDeleteClicked = true }
|
||||
).bind(historyItem, HistoryFragmentState.Mode.Normal)
|
||||
|
||||
binding.historyLayout.overflowView.performClick()
|
||||
assertEquals(true, isDeleteClicked)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN a history item is clicked THEN interactor open is called`() {
|
||||
testViewHolder().bind(historyItem, HistoryFragmentState.Mode.Normal)
|
||||
|
||||
binding.historyLayout.performClick()
|
||||
verify { interactor.open(historyItem.data) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN selecting mode THEN delete button is not visible `() {
|
||||
testViewHolder().bind(historyItem, HistoryFragmentState.Mode.Editing(setOf()))
|
||||
|
||||
assertEquals(View.INVISIBLE, binding.historyLayout.overflowView.visibility)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN normal mode THEN delete button is visible `() {
|
||||
testViewHolder().bind(historyItem, HistoryFragmentState.Mode.Normal)
|
||||
|
||||
assertEquals(View.VISIBLE, binding.historyLayout.overflowView.visibility)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN editing mode WHEN item is selected THEN checkmark is visible `() {
|
||||
testViewHolder(
|
||||
selectedHistoryItems = setOf(historyItem.data)
|
||||
).bind(historyItem, HistoryFragmentState.Mode.Editing(setOf(historyItem.data)))
|
||||
|
||||
assertEquals(1, binding.historyLayout.binding.icon.displayedChild)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN editing mode WHEN item is not selected THEN checkmark is not visible `() {
|
||||
testViewHolder().bind(historyItem, HistoryFragmentState.Mode.Editing(setOf()))
|
||||
|
||||
assertEquals(0, binding.historyLayout.binding.icon.displayedChild)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN normal mode WHEN item is long pressed THEN interactor select is called`() {
|
||||
testViewHolder().bind(historyItem, HistoryFragmentState.Mode.Normal)
|
||||
|
||||
binding.historyLayout.performLongClick()
|
||||
verify { interactor.select(historyItem.data) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN editing mode and item is not selected WHEN item is clicked THEN interactor select is called`() {
|
||||
testViewHolder(
|
||||
selectedHistoryItems = setOf(historyItem.data.copy(position = 1))
|
||||
).bind(historyItem, HistoryFragmentState.Mode.Normal)
|
||||
|
||||
binding.historyLayout.performClick()
|
||||
verify { interactor.select(historyItem.data) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN editing mode and item is selected WHEN item is clicked THEN interactor select is called`() {
|
||||
testViewHolder(
|
||||
selectedHistoryItems = setOf(historyItem.data)
|
||||
).bind(historyItem, HistoryFragmentState.Mode.Normal)
|
||||
|
||||
binding.historyLayout.performClick()
|
||||
verify { interactor.deselect(historyItem.data) }
|
||||
}
|
||||
|
||||
private fun testViewHolder(
|
||||
view: View = binding.root,
|
||||
historyInteractor: HistoryInteractor = interactor,
|
||||
selectedHistoryItems: Set<History> = setOf(),
|
||||
onDeleteClicked: (Int) -> Unit = {}
|
||||
): HistoryViewHolder {
|
||||
return HistoryViewHolder(
|
||||
view = view,
|
||||
historyInteractor = historyInteractor,
|
||||
selectionHolder = mockk { every { selectedItems } returns selectedHistoryItems },
|
||||
onDeleteClicked = onDeleteClicked
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue