For #8312 - Show Top Sites in a ViewPager (#14116)

pull/35/head
Gabriel Luong 4 years ago committed by GitHub
parent 9430546d12
commit 04dcfa5cab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -432,8 +432,6 @@ dependencies {
implementation Deps.androidx_work_ktx
implementation Deps.google_material
implementation Deps.google_flexbox
implementation Deps.lottie
implementation Deps.adjust

@ -22,7 +22,7 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoCollectionsMessageViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.PrivateBrowsingDescriptionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSiteViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSitePagerViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingAutomaticSignInViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingFinishViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingHeaderViewHolder
@ -42,14 +42,14 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
ButtonTipViewHolder.LAYOUT_ID
)
data class TopSiteList(val topSites: List<TopSite>) : AdapterItem(TopSiteViewHolder.LAYOUT_ID) {
data class TopSitePager(val topSites: List<TopSite>) : AdapterItem(TopSitePagerViewHolder.LAYOUT_ID) {
override fun sameAs(other: AdapterItem): Boolean {
val newTopSites = (other as? TopSiteList) ?: return false
val newTopSites = (other as? TopSitePager) ?: return false
return newTopSites.topSites == this.topSites
}
override fun contentsSameAs(other: AdapterItem): Boolean {
val newTopSites = (other as? TopSiteList) ?: return false
val newTopSites = (other as? TopSitePager) ?: return false
if (newTopSites.topSites.size != this.topSites.size) return false
val newSitesSequence = newTopSites.topSites.asSequence()
val oldTopSites = this.topSites.asSequence()
@ -147,7 +147,7 @@ class SessionControlAdapter(
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return when (viewType) {
ButtonTipViewHolder.LAYOUT_ID -> ButtonTipViewHolder(view, interactor)
TopSiteViewHolder.LAYOUT_ID -> TopSiteViewHolder(view, interactor)
TopSitePagerViewHolder.LAYOUT_ID -> TopSitePagerViewHolder(view, interactor)
PrivateBrowsingDescriptionViewHolder.LAYOUT_ID -> PrivateBrowsingDescriptionViewHolder(
view,
interactor
@ -203,8 +203,8 @@ class SessionControlAdapter(
val tipItem = item as AdapterItem.TipItem
holder.bind(tipItem.tip)
}
is TopSiteViewHolder -> {
holder.bind((item as AdapterItem.TopSiteList).topSites)
is TopSitePagerViewHolder -> {
holder.bind((item as AdapterItem.TopSitePager).topSites)
}
is CollectionViewHolder -> {
val (collection, expanded) = item as AdapterItem.CollectionItem

@ -35,7 +35,7 @@ private fun normalModeAdapterItems(
tip?.let { items.add(AdapterItem.TipItem(it)) }
if (topSites.isNotEmpty()) {
items.add(AdapterItem.TopSiteList(topSites))
items.add(AdapterItem.TopSitePager(topSites))
}
if (collections.isEmpty()) {
@ -160,7 +160,7 @@ class SessionControlView(
sessionControlAdapter.submitList(stateAdapterList) {
val loadedTopSites = stateAdapterList.find { adapterItem ->
adapterItem is AdapterItem.TopSiteList && adapterItem.topSites.isNotEmpty()
adapterItem is AdapterItem.TopSitePager && adapterItem.topSites.isNotEmpty()
}
loadedTopSites?.run {
homeScreenViewModel.shouldScrollToTopSites = false

@ -0,0 +1,57 @@
/* 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
import android.view.View
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import kotlinx.android.synthetic.main.component_top_sites_pager.view.*
import mozilla.components.feature.top.sites.TopSite
import org.mozilla.fenix.R
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSitesPagerAdapter
class TopSitePagerViewHolder(
view: View,
interactor: TopSiteInteractor
) : RecyclerView.ViewHolder(view) {
private val topSitesPagerAdapter = TopSitesPagerAdapter(interactor)
private val pageIndicator = view.page_indicator
private val topSitesPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
pageIndicator.setSelection(position)
}
}
init {
view.top_sites_pager.apply {
adapter = topSitesPagerAdapter
registerOnPageChangeCallback(topSitesPageChangeCallback)
}
}
fun bind(topSites: List<TopSite>) {
topSitesPagerAdapter.updateData(topSites)
// Don't show any page indicator if there is only 1 page.
val numPages = if (topSites.size > TOP_SITES_PER_PAGE) {
TOP_SITES_MAX_PAGE_SIZE
} else {
0
}
pageIndicator.isVisible = numPages > 1
pageIndicator.setSize(numPages)
}
companion object {
const val LAYOUT_ID = R.layout.component_top_sites_pager
const val TOP_SITES_MAX_PAGE_SIZE = 2
const val TOP_SITES_PER_PAGE = 8
}
}

@ -6,7 +6,6 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.google.android.flexbox.FlexboxLayoutManager
import kotlinx.android.synthetic.main.component_top_sites.view.*
import mozilla.components.feature.top.sites.TopSite
import org.mozilla.fenix.R
@ -23,8 +22,6 @@ class TopSiteViewHolder(
init {
view.top_sites_list.apply {
adapter = topSitesAdapter
layoutManager = FlexboxLayoutManager(view.context)
isNestedScrollingEnabled = false
}
}

@ -0,0 +1,82 @@
/* 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 android.content.Context
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
import android.widget.LinearLayout
import androidx.core.view.MarginLayoutParamsCompat
import org.mozilla.fenix.R
/**
* A pager indicator widget to display the number of pages and the current selected page.
*/
class PagerIndicator : LinearLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
private var selectedIndex = 0
/**
* Set the number of pager dots to display.
*/
fun setSize(size: Int) {
if (childCount == size) {
return
}
if (selectedIndex >= size) {
selectedIndex = size - 1
}
removeAllViews()
for (i in 0 until size) {
val isLast = i == size - 1
addView(
View(context).apply {
setBackgroundResource(R.drawable.pager_dot)
isSelected = i == selectedIndex
},
LayoutParams(dpToPx(DOT_SIZE_IN_DP), dpToPx(DOT_SIZE_IN_DP)).apply {
if (!isLast) {
MarginLayoutParamsCompat.setMarginEnd(this, dpToPx(DOT_MARGIN))
}
}
)
}
}
/**
* Set the current selected pager dot.
*/
fun setSelection(index: Int) {
if (selectedIndex == index) {
return
}
getChildAt(selectedIndex)?.run {
isSelected = false
}
getChildAt(index)?.run {
isSelected = true
}
selectedIndex = index
}
companion object {
private const val DOT_SIZE_IN_DP = 6f
private const val DOT_MARGIN = 4f
}
}
fun Context.dpToPx(value: Float): Int =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, resources.displayMetrics).toInt()
fun View.dpToPx(value: Float): Int = context.dpToPx(value)

@ -0,0 +1,40 @@
/* 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 android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import mozilla.components.feature.top.sites.TopSite
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSiteViewHolder
class TopSitesPagerAdapter(
private val interactor: TopSiteInteractor
) : RecyclerView.Adapter<TopSiteViewHolder>() {
private var topSites: List<List<TopSite>> = listOf()
fun updateData(topSites: List<TopSite>) {
this.topSites = topSites.chunked(TOP_SITES_PER_PAGE)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TopSiteViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(TopSiteViewHolder.LAYOUT_ID, parent, false)
return TopSiteViewHolder(view, interactor)
}
override fun onBindViewHolder(holder: TopSiteViewHolder, position: Int) {
holder.bind(this.topSites[position])
}
override fun getItemCount(): Int = this.topSites.size
companion object {
const val TOP_SITES_PER_PAGE = 8
}
}

@ -0,0 +1,8 @@
<?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/. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/top_site_pager_dot_selected" android:state_selected="true" />
<item android:color="@color/top_site_pager_dot" />
</selector>

@ -0,0 +1,12 @@
<?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/. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/pager_dot"/>
<size
android:width="6dp"
android:height="6dp"/>
</shape>

@ -2,12 +2,19 @@
<!-- 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.recyclerview.widget.RecyclerView 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/top_sites_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="8dp"
android:clipChildren="false"
android:clipToPadding="false"
android:overScrollMode="never"
android:nestedScrollingEnabled="false"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="4"
tools:listitem="@layout/top_site_item" />

@ -0,0 +1,24 @@
<?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/. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/top_sites_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.PagerIndicator
android:id="@+id/page_indicator"
android:layout_width="wrap_content"
android:layout_height="6dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="8dp"
android:orientation="horizontal" />
</LinearLayout>

@ -72,6 +72,8 @@
<!--Top site colors -->
<color name="top_site_title_text">@color/top_site_title_text_dark_theme</color>
<color name="top_site_pager_dot">#3A3944</color>
<color name="top_site_pager_dot_selected">#5B5B66</color>
<!-- Synced tabs colors-->
<color name="synced_tabs_separator">@color/synced_tabs_separator_dark_theme</color>

@ -4,6 +4,7 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Photon colors -->
<color name="light_grey_50">#BFBFC9</color>
<color name="ink_20">#312A65</color>
<color name="ink_20_48a">#7A312A65</color>
<color name="violet_50">#9059FF</color>
@ -42,7 +43,7 @@
<color name="toolbar_start_gradient_light_theme">@color/foundation_light_theme</color>
<color name="toolbar_center_gradient_light_theme">@color/foundation_light_theme</color>
<color name="toolbar_end_gradient_light_theme">@color/foundation_light_theme</color>
<color name="toolbar_divider_color_light_theme">#BFBFC9</color>
<color name="toolbar_divider_color_light_theme">@color/light_grey_50</color>
<color name="fill_link_from_clipboard_light_theme">@color/accent_light_theme</color>
<color name="sync_disconnected_icon_fill_light_theme">#C45A27</color>
<color name="sync_disconnected_background_light_theme">#FFFDE2</color>
@ -271,6 +272,8 @@
<!-- Top site colors -->
<color name="top_site_title_text">@color/top_site_title_text_light_theme</color>
<color name="top_site_pager_dot">@color/photonLightGrey30</color>
<color name="top_site_pager_dot_selected">@color/light_grey_50</color>
<!-- Synced tabs colors-->
<color name="synced_tabs_separator">@color/synced_tabs_separator_light_theme</color>

@ -28,7 +28,6 @@ object Versions {
const val androidx_transition = "1.3.0"
const val androidx_work = "2.2.0"
const val google_material = "1.1.0"
const val google_flexbox = "2.0.1"
const val mozilla_android_components = AndroidComponents.VERSION
@ -175,7 +174,6 @@ object Deps {
const val androidx_work_ktx = "androidx.work:work-runtime-ktx:${Versions.androidx_work}"
const val androidx_work_testing = "androidx.work:work-testing:${Versions.androidx_work}"
const val google_material = "com.google.android.material:material:${Versions.google_material}"
const val google_flexbox = "com.google.android:flexbox:${Versions.google_flexbox}"
const val adjust = "com.adjust.sdk:adjust-android:${Versions.adjust}"
const val installreferrer = "com.android.installreferrer:installreferrer:${Versions.installreferrer}"

Loading…
Cancel
Save