Close #22402: Add top placeholder for home
This commit is contained in:
parent
dde916038d
commit
24e4452cb5
|
@ -25,7 +25,7 @@ private const val EXPECTED_SUPPRESSION_COUNT = 19
|
|||
@Suppress("TopLevelPropertyNaming") // it's silly this would have a different naming convention b/c no const
|
||||
private val EXPECTED_RUNBLOCKING_RANGE = 0..1 // CI has +1 counts compared to local runs: increment these together
|
||||
private const val EXPECTED_RECYCLER_VIEW_CONSTRAINT_LAYOUT_CHILDREN = 4
|
||||
private const val EXPECTED_NUMBER_OF_INFLATION = 12
|
||||
private const val EXPECTED_NUMBER_OF_INFLATION = 13
|
||||
|
||||
private val failureMsgStrictMode = getErrorMessage(
|
||||
shortName = "StrictMode suppression",
|
||||
|
|
|
@ -377,8 +377,7 @@ class HomeFragment : Fragment() {
|
|||
homeFragmentStore,
|
||||
binding.sessionControlRecyclerView,
|
||||
viewLifecycleOwner,
|
||||
sessionControlInteractor,
|
||||
homeViewModel
|
||||
sessionControlInteractor
|
||||
)
|
||||
|
||||
updateSessionControlView()
|
||||
|
@ -546,8 +545,6 @@ class HomeFragment : Fragment() {
|
|||
if (bundleArgs.getBoolean(FOCUS_ON_ADDRESS_BAR)) {
|
||||
navigateToSearch()
|
||||
} else if (bundleArgs.getLong(FOCUS_ON_COLLECTION, -1) >= 0) {
|
||||
// No need to scroll to async'd loaded TopSites if we want to scroll to collections.
|
||||
homeViewModel.shouldScrollToTopSites = false
|
||||
/* Triggered when the user has added a tab to a collection and has tapped
|
||||
* the View action on the [TabsTrayDialogFragment] snackbar.*/
|
||||
scrollAndAnimateCollection(bundleArgs.getLong(FOCUS_ON_COLLECTION, -1))
|
||||
|
|
|
@ -11,9 +11,4 @@ class HomeScreenViewModel : ViewModel() {
|
|||
* Used to delete a specific session once the home screen is resumed
|
||||
*/
|
||||
var sessionToDelete: String? = null
|
||||
|
||||
/**
|
||||
* Used to remember if we need to scroll to top of the homeFragment's recycleView (top sites) see #8561
|
||||
* */
|
||||
var shouldScrollToTopSites: Boolean = true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/* 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
|
||||
|
||||
import android.view.View
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.utils.view.ViewHolder
|
||||
|
||||
/**
|
||||
* View holder for a synchronous, unconditional and invisible placeholder. This is to anchor home to
|
||||
* the top when home is created.
|
||||
*/
|
||||
class TopPlaceholderViewHolder(
|
||||
view: View
|
||||
) : ViewHolder(view) {
|
||||
|
||||
fun bind() = Unit
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.top_placeholder_item
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import org.mozilla.fenix.components.tips.Tip
|
|||
import org.mozilla.fenix.historymetadata.view.HistoryMetadataGroupViewHolder
|
||||
import org.mozilla.fenix.historymetadata.view.HistoryMetadataHeaderViewHolder
|
||||
import org.mozilla.fenix.home.HomeFragmentStore
|
||||
import org.mozilla.fenix.home.TopPlaceholderViewHolder
|
||||
import org.mozilla.fenix.home.OnboardingState
|
||||
import org.mozilla.fenix.home.recentbookmarks.view.RecentBookmarksViewHolder
|
||||
import org.mozilla.fenix.home.recenttabs.view.RecentTabViewHolder
|
||||
|
@ -49,6 +50,7 @@ import org.mozilla.fenix.home.topsites.TopSitePagerViewHolder
|
|||
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||
|
||||
sealed class AdapterItem(@LayoutRes val viewType: Int) {
|
||||
object TopPlaceholderItem : AdapterItem(TopPlaceholderViewHolder.LAYOUT_ID)
|
||||
data class TipItem(val tip: Tip) : AdapterItem(
|
||||
ButtonTipViewHolder.LAYOUT_ID
|
||||
)
|
||||
|
@ -255,6 +257,7 @@ class SessionControlAdapter(
|
|||
|
||||
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||
return when (viewType) {
|
||||
TopPlaceholderViewHolder.LAYOUT_ID -> TopPlaceholderViewHolder(view)
|
||||
ButtonTipViewHolder.LAYOUT_ID -> ButtonTipViewHolder(view, interactor)
|
||||
TopSitePagerViewHolder.LAYOUT_ID -> TopSitePagerViewHolder(view, interactor)
|
||||
PrivateBrowsingDescriptionViewHolder.LAYOUT_ID -> PrivateBrowsingDescriptionViewHolder(
|
||||
|
@ -350,6 +353,9 @@ class SessionControlAdapter(
|
|||
val tipItem = item as AdapterItem.TipItem
|
||||
holder.bind(tipItem.tip)
|
||||
}
|
||||
is TopPlaceholderViewHolder -> {
|
||||
holder.bind()
|
||||
}
|
||||
is TopSitePagerViewHolder -> {
|
||||
holder.bind((item as AdapterItem.TopSitePager).topSites)
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import org.mozilla.fenix.ext.settings
|
|||
import org.mozilla.fenix.historymetadata.HistoryMetadataGroup
|
||||
import org.mozilla.fenix.home.HomeFragmentState
|
||||
import org.mozilla.fenix.home.HomeFragmentStore
|
||||
import org.mozilla.fenix.home.HomeScreenViewModel
|
||||
import org.mozilla.fenix.home.Mode
|
||||
import org.mozilla.fenix.home.OnboardingState
|
||||
import org.mozilla.fenix.home.recenttabs.RecentTab
|
||||
|
@ -46,6 +45,9 @@ internal fun normalModeAdapterItems(
|
|||
val items = mutableListOf<AdapterItem>()
|
||||
var shouldShowCustomizeHome = false
|
||||
|
||||
// Add a synchronous, unconditional and invisible placeholder so home is anchored to the top when created.
|
||||
items.add(AdapterItem.TopPlaceholderItem)
|
||||
|
||||
tip?.let { items.add(AdapterItem.TipItem(it)) }
|
||||
|
||||
if (showSetAsDefaultBrowserCard) {
|
||||
|
@ -182,8 +184,7 @@ class SessionControlView(
|
|||
store: HomeFragmentStore,
|
||||
val containerView: View,
|
||||
viewLifecycleOwner: LifecycleOwner,
|
||||
internal val interactor: SessionControlInteractor,
|
||||
private var homeScreenViewModel: HomeScreenViewModel
|
||||
internal val interactor: SessionControlInteractor
|
||||
) {
|
||||
|
||||
val view: RecyclerView = containerView as RecyclerView
|
||||
|
@ -222,20 +223,6 @@ class SessionControlView(
|
|||
|
||||
if (shouldReportMetrics) interactor.reportSessionMetrics(state)
|
||||
|
||||
val stateAdapterList = state.toAdapterList()
|
||||
if (homeScreenViewModel.shouldScrollToTopSites) {
|
||||
sessionControlAdapter.submitList(stateAdapterList) {
|
||||
|
||||
val loadedTopSites = stateAdapterList.find { adapterItem ->
|
||||
adapterItem is AdapterItem.TopSitePager && adapterItem.topSites.isNotEmpty()
|
||||
}
|
||||
loadedTopSites?.run {
|
||||
homeScreenViewModel.shouldScrollToTopSites = false
|
||||
view.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sessionControlAdapter.submitList(stateAdapterList)
|
||||
}
|
||||
sessionControlAdapter.submitList(state.toAdapterList())
|
||||
}
|
||||
}
|
||||
|
|
7
app/src/main/res/layout/top_placeholder_item.xml
Normal file
7
app/src/main/res/layout/top_placeholder_item.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?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:layout_width="match_parent"
|
||||
android:layout_height="0dp"/>
|
|
@ -14,6 +14,7 @@ import mozilla.components.feature.tab.collections.TabCollection
|
|||
import mozilla.components.feature.top.sites.TopSite
|
||||
import mozilla.components.service.pocket.PocketRecommendedStory
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Test
|
||||
|
@ -96,8 +97,7 @@ class SessionControlViewTest {
|
|||
mockk(relaxed = true),
|
||||
view,
|
||||
mockk(relaxed = true),
|
||||
interactor,
|
||||
mockk(relaxed = true)
|
||||
interactor
|
||||
)
|
||||
val recentTabs = listOf<RecentTab>(mockk(relaxed = true))
|
||||
|
||||
|
@ -118,8 +118,7 @@ class SessionControlViewTest {
|
|||
mockk(relaxed = true),
|
||||
view,
|
||||
mockk(relaxed = true),
|
||||
interactor,
|
||||
mockk(relaxed = true)
|
||||
interactor
|
||||
)
|
||||
|
||||
val state = HomeFragmentState()
|
||||
|
@ -155,8 +154,9 @@ class SessionControlViewTest {
|
|||
pocketArticles
|
||||
)
|
||||
|
||||
assertTrue(results[0] is AdapterItem.RecentBookmarks)
|
||||
assertTrue(results[1] is AdapterItem.CustomizeHomeButton)
|
||||
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
|
||||
assertTrue(results[1] is AdapterItem.RecentBookmarks)
|
||||
assertTrue(results[2] is AdapterItem.CustomizeHomeButton)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -182,9 +182,10 @@ class SessionControlViewTest {
|
|||
pocketArticles
|
||||
)
|
||||
|
||||
assertTrue(results[0] is AdapterItem.RecentTabsHeader)
|
||||
assertTrue(results[1] is AdapterItem.RecentTabItem)
|
||||
assertTrue(results[2] is AdapterItem.CustomizeHomeButton)
|
||||
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
|
||||
assertTrue(results[1] is AdapterItem.RecentTabsHeader)
|
||||
assertTrue(results[2] is AdapterItem.RecentTabItem)
|
||||
assertTrue(results[3] is AdapterItem.CustomizeHomeButton)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -210,9 +211,10 @@ class SessionControlViewTest {
|
|||
pocketArticles
|
||||
)
|
||||
|
||||
assertTrue(results[0] is AdapterItem.HistoryMetadataHeader)
|
||||
assertTrue(results[1] is AdapterItem.HistoryMetadataGroup)
|
||||
assertTrue(results[2] is AdapterItem.CustomizeHomeButton)
|
||||
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
|
||||
assertTrue(results[1] is AdapterItem.HistoryMetadataHeader)
|
||||
assertTrue(results[2] is AdapterItem.HistoryMetadataGroup)
|
||||
assertTrue(results[3] is AdapterItem.CustomizeHomeButton)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -238,8 +240,9 @@ class SessionControlViewTest {
|
|||
pocketArticles
|
||||
)
|
||||
|
||||
assertTrue(results[0] is AdapterItem.PocketStoriesItem)
|
||||
assertTrue(results[1] is AdapterItem.CustomizeHomeButton)
|
||||
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
|
||||
assertTrue(results[1] is AdapterItem.PocketStoriesItem)
|
||||
assertTrue(results[2] is AdapterItem.CustomizeHomeButton)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -264,6 +267,36 @@ class SessionControlViewTest {
|
|||
historyMetadata,
|
||||
pocketArticles
|
||||
)
|
||||
assertTrue(results.isEmpty())
|
||||
assertEquals(results.size, 1)
|
||||
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN all items THEN top placeholder item is always the first item`() {
|
||||
val collection = mockk<TabCollection> {
|
||||
every { id } returns 123L
|
||||
}
|
||||
val topSites = listOf<TopSite>(mockk())
|
||||
val collections = listOf(collection)
|
||||
val expandedCollections = emptySet<Long>()
|
||||
val recentBookmarks = listOf<BookmarkNode>(mockk())
|
||||
val recentTabs = listOf<RecentTab.Tab>(mockk())
|
||||
val historyMetadata = listOf<HistoryMetadataGroup>(mockk())
|
||||
val pocketArticles = listOf<PocketRecommendedStory>(mockk())
|
||||
|
||||
val results = normalModeAdapterItems(
|
||||
topSites,
|
||||
collections,
|
||||
expandedCollections,
|
||||
null,
|
||||
recentBookmarks,
|
||||
false,
|
||||
false,
|
||||
recentTabs,
|
||||
historyMetadata,
|
||||
pocketArticles
|
||||
)
|
||||
|
||||
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user