2
0
mirror of https://github.com/fork-maintainers/iceraven-browser synced 2024-11-17 15:26:23 +00:00

[fenix] For https://github.com/mozilla-mobile/fenix/issues/17542 - Removes flash on renaming top site and fixes title not being updated

This commit is contained in:
ekager 2021-01-20 20:10:37 -07:00
parent cd40b8510e
commit 42c74bcd44
7 changed files with 211 additions and 10 deletions

View File

@ -703,7 +703,7 @@ private fun assertExistingTopSitesList() =
private fun assertExistingTopSitesTabs(title: String) = private fun assertExistingTopSitesTabs(title: String) =
onView(allOf(withId(R.id.top_sites_list))) onView(allOf(withId(R.id.top_sites_list)))
.check(matches(hasItem(hasDescendant(withText(title))))) .check(matches(hasDescendant(withText(title))))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertNotExistingTopSitesList(title: String) = private fun assertNotExistingTopSitesList(title: String) =

View File

@ -43,7 +43,12 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
ButtonTipViewHolder.LAYOUT_ID ButtonTipViewHolder.LAYOUT_ID
) )
data class TopSitePager(val topSites: List<TopSite>) : AdapterItem(TopSitePagerViewHolder.LAYOUT_ID) { data class TopSitePagerPayload(
val changed: Set<Pair<Int, TopSite>>
)
data class TopSitePager(val topSites: List<TopSite>) :
AdapterItem(TopSitePagerViewHolder.LAYOUT_ID) {
override fun sameAs(other: AdapterItem): Boolean { override fun sameAs(other: AdapterItem): Boolean {
val newTopSites = (other as? TopSitePager) ?: return false val newTopSites = (other as? TopSitePager) ?: return false
return newTopSites.topSites.size == this.topSites.size return newTopSites.topSites.size == this.topSites.size
@ -56,6 +61,19 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
val oldTopSites = this.topSites.asSequence() val oldTopSites = this.topSites.asSequence()
return newSitesSequence.zip(oldTopSites).all { (new, old) -> new == old } return newSitesSequence.zip(oldTopSites).all { (new, old) -> new == old }
} }
override fun getChangePayload(newItem: AdapterItem): Any? {
val newTopSites = (newItem as? TopSitePager) ?: return null
val oldTopSites = (this as? TopSitePager) ?: return null
val changed = mutableSetOf<Pair<Int, TopSite>>()
for ((index, item) in newTopSites.topSites.withIndex()) {
if (oldTopSites.topSites.getOrNull(index) != item) {
changed.add(Pair(index, item))
}
}
return if (changed.isNotEmpty()) TopSitePagerPayload(changed) else null
}
} }
object PrivateBrowsingDescription : AdapterItem(PrivateBrowsingDescriptionViewHolder.LAYOUT_ID) object PrivateBrowsingDescription : AdapterItem(PrivateBrowsingDescriptionViewHolder.LAYOUT_ID)
@ -195,6 +213,25 @@ class SessionControlAdapter(
override fun getItemViewType(position: Int) = getItem(position).viewType override fun getItemViewType(position: Int) = getItem(position).viewType
override fun onBindViewHolder(
holder: RecyclerView.ViewHolder,
position: Int,
payloads: MutableList<Any>
) {
if (payloads.isNullOrEmpty()) {
onBindViewHolder(holder, position)
} else {
when (holder) {
is TopSitePagerViewHolder -> {
if (payloads[0] is AdapterItem.TopSitePagerPayload) {
val payload = payloads[0] as AdapterItem.TopSitePagerPayload
holder.update(payload)
}
}
}
}
}
@SuppressWarnings("ComplexMethod") @SuppressWarnings("ComplexMethod")
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = getItem(position) val item = getItem(position)

View File

@ -13,6 +13,7 @@ import mozilla.components.feature.top.sites.TopSite
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.home.sessioncontrol.AdapterItem
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSitesPagerAdapter import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSitesPagerAdapter
@ -28,7 +29,11 @@ class TopSitePagerViewHolder(
private val topSitesPageChangeCallback = object : ViewPager2.OnPageChangeCallback() { private val topSitesPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
if (currentPage != position) { if (currentPage != position) {
pageIndicator.context.components.analytics.metrics.track(Event.TopSiteSwipeCarousel(position)) pageIndicator.context.components.analytics.metrics.track(
Event.TopSiteSwipeCarousel(
position
)
)
} }
pageIndicator.setSelection(position) pageIndicator.setSelection(position)
@ -43,6 +48,12 @@ class TopSitePagerViewHolder(
} }
} }
fun update(payload: AdapterItem.TopSitePagerPayload) {
for (item in payload.changed) {
topSitesPagerAdapter.notifyItemChanged(currentPage, payload)
}
}
fun bind(topSites: List<TopSite>) { fun bind(topSites: List<TopSite>) {
val chunkedTopSites = topSites.chunked(TOP_SITES_PER_PAGE) val chunkedTopSites = topSites.chunked(TOP_SITES_PER_PAGE)
topSitesPagerAdapter.submitList(chunkedTopSites) topSitesPagerAdapter.submitList(chunkedTopSites)

View File

@ -8,13 +8,14 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import kotlinx.android.synthetic.main.top_site_item.view.*
import mozilla.components.feature.top.sites.TopSite import mozilla.components.feature.top.sites.TopSite
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
import org.mozilla.fenix.perf.StartupTimeline import org.mozilla.fenix.perf.StartupTimeline
class TopSitesAdapter( class TopSitesAdapter(
private val interactor: TopSiteInteractor private val interactor: TopSiteInteractor
) : ListAdapter<TopSite, TopSiteItemViewHolder>(DiffCallback) { ) : ListAdapter<TopSite, TopSiteItemViewHolder>(TopSitesDiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TopSiteItemViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TopSiteItemViewHolder {
val view = LayoutInflater.from(parent.context) val view = LayoutInflater.from(parent.context)
.inflate(TopSiteItemViewHolder.LAYOUT_ID, parent, false) .inflate(TopSiteItemViewHolder.LAYOUT_ID, parent, false)
@ -26,11 +27,39 @@ class TopSitesAdapter(
holder.bind(getItem(position)) holder.bind(getItem(position))
} }
private object DiffCallback : DiffUtil.ItemCallback<TopSite>() { override fun onBindViewHolder(
override fun areItemsTheSame(oldItem: TopSite, newItem: TopSite) = holder: TopSiteItemViewHolder,
oldItem.id == newItem.id || oldItem.title == newItem.title || oldItem.url == newItem.url position: Int,
payloads: MutableList<Any>
) {
if (payloads.isNullOrEmpty()) {
onBindViewHolder(holder, position)
} else {
when (payloads[0]) {
is TopSite -> {
holder.bind((payloads[0] as TopSite))
}
is TopSitePayload -> {
holder.itemView.top_site_title.text = (payloads[0] as TopSitePayload).newTitle
}
}
}
}
data class TopSitePayload(
val newTitle: String?
)
internal object TopSitesDiffCallback : DiffUtil.ItemCallback<TopSite>() {
override fun areItemsTheSame(oldItem: TopSite, newItem: TopSite) = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: TopSite, newItem: TopSite) = override fun areContentsTheSame(oldItem: TopSite, newItem: TopSite) =
oldItem.id == newItem.id || oldItem.title == newItem.title || oldItem.url == newItem.url oldItem.id == newItem.id && oldItem.title == newItem.title && oldItem.url == newItem.url
override fun getChangePayload(oldItem: TopSite, newItem: TopSite): Any? {
return if (oldItem.id == newItem.id && oldItem.url == newItem.url && oldItem.title != newItem.title) {
TopSitePayload(newItem.title)
} else null
}
} }
} }

View File

@ -10,12 +10,13 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import kotlinx.android.synthetic.main.component_top_sites.view.* import kotlinx.android.synthetic.main.component_top_sites.view.*
import mozilla.components.feature.top.sites.TopSite import mozilla.components.feature.top.sites.TopSite
import org.mozilla.fenix.home.sessioncontrol.AdapterItem
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSiteViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSiteViewHolder
class TopSitesPagerAdapter( class TopSitesPagerAdapter(
private val interactor: TopSiteInteractor private val interactor: TopSiteInteractor
) : ListAdapter<List<TopSite>, TopSiteViewHolder>(DiffCallback) { ) : ListAdapter<List<TopSite>, TopSiteViewHolder>(TopSiteListDiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TopSiteViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TopSiteViewHolder {
val view = LayoutInflater.from(parent.context) val view = LayoutInflater.from(parent.context)
@ -23,12 +24,30 @@ class TopSitesPagerAdapter(
return TopSiteViewHolder(view, interactor) return TopSiteViewHolder(view, interactor)
} }
override fun onBindViewHolder(
holder: TopSiteViewHolder,
position: Int,
payloads: MutableList<Any>
) {
if (payloads.isNullOrEmpty()) {
onBindViewHolder(holder, position)
} else {
if (payloads[0] is AdapterItem.TopSitePagerPayload) {
val adapter = holder.itemView.top_sites_list.adapter as TopSitesAdapter
val payload = payloads[0] as AdapterItem.TopSitePagerPayload
for (item in payload.changed) {
adapter.notifyItemChanged(item.first, item.second)
}
}
}
}
override fun onBindViewHolder(holder: TopSiteViewHolder, position: Int) { override fun onBindViewHolder(holder: TopSiteViewHolder, position: Int) {
val adapter = holder.itemView.top_sites_list.adapter as TopSitesAdapter val adapter = holder.itemView.top_sites_list.adapter as TopSitesAdapter
adapter.submitList(getItem(position)) adapter.submitList(getItem(position))
} }
private object DiffCallback : DiffUtil.ItemCallback<List<TopSite>>() { internal object TopSiteListDiffCallback : DiffUtil.ItemCallback<List<TopSite>>() {
override fun areItemsTheSame(oldItem: List<TopSite>, newItem: List<TopSite>): Boolean { override fun areItemsTheSame(oldItem: List<TopSite>, newItem: List<TopSite>): Boolean {
return oldItem.size == newItem.size return oldItem.size == newItem.size
} }
@ -36,5 +55,15 @@ class TopSitesPagerAdapter(
override fun areContentsTheSame(oldItem: List<TopSite>, newItem: List<TopSite>): Boolean { override fun areContentsTheSame(oldItem: List<TopSite>, newItem: List<TopSite>): Boolean {
return newItem.zip(oldItem).all { (new, old) -> new == old } return newItem.zip(oldItem).all { (new, old) -> new == old }
} }
override fun getChangePayload(oldItem: List<TopSite>, newItem: List<TopSite>): Any? {
val changed = mutableSetOf<Pair<Int, TopSite>>()
for ((index, item) in newItem.withIndex()) {
if (oldItem.getOrNull(index) != item) {
changed.add(Pair(index, item))
}
}
return if (changed.isNotEmpty()) AdapterItem.TopSitePagerPayload(changed) else null
}
} }
} }

View File

@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.home.sessioncontrol.viewholders.topsites
import mozilla.components.feature.top.sites.TopSite
import org.junit.Assert.assertEquals
import org.junit.Test
class TopSitesAdapterTest {
@Test
fun testDiffCallback() {
val topSite = TopSite(
id = 1L,
title = "Title1",
url = "https://mozilla.org",
null,
TopSite.Type.DEFAULT
)
val topSite2 = TopSite(
id = 1L,
title = "Title2",
url = "https://mozilla.org",
null,
TopSite.Type.DEFAULT
)
assertEquals(
TopSitesAdapter.TopSitesDiffCallback.getChangePayload(topSite, topSite2),
TopSitesAdapter.TopSitePayload("Title2")
)
val topSite3 = TopSite(
id = 2L,
title = "Title2",
url = "https://firefox.org",
null,
TopSite.Type.DEFAULT
)
assertEquals(
TopSitesAdapter.TopSitesDiffCallback.getChangePayload(topSite, topSite3),
null
)
}
}

View File

@ -0,0 +1,47 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.home.sessioncontrol.viewholders.topsites
import mozilla.components.feature.top.sites.TopSite
import org.junit.Assert.assertEquals
import org.junit.Test
import org.mozilla.fenix.home.sessioncontrol.AdapterItem
class TopSitesPagerAdapterTest {
@Test
fun testDiffCallback() {
val topSite = TopSite(
id = 1L,
title = "Title1",
url = "https://mozilla.org",
null,
TopSite.Type.DEFAULT
)
val topSite2 = TopSite(
id = 2L,
title = "Title2",
url = "https://mozilla.org",
null,
TopSite.Type.DEFAULT
)
val topSite3 = TopSite(
id = 3L,
title = "Title2",
url = "https://firefox.org",
null,
TopSite.Type.DEFAULT
)
assertEquals(
TopSitesPagerAdapter.TopSiteListDiffCallback.getChangePayload(
listOf(topSite, topSite3),
listOf(topSite, topSite2)
),
AdapterItem.TopSitePagerPayload(setOf(Pair(1, topSite2)))
)
}
}