You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
iceraven-browser/app/src/main/java/org/mozilla/fenix/home/topsites/TopSiteItemViewHolder.kt

220 lines
9.0 KiB
Kotlin

/* 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.topsites
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.view.MotionEvent
import android.view.View
import android.widget.PopupWindow
import androidx.annotation.VisibleForTesting
import androidx.appcompat.content.res.AppCompatResources.getDrawable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.widget.TextViewCompat
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.components.feature.top.sites.TopSite
import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.support.ktx.android.content.getColorFromAttr
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.GleanMetrics.Pings
import org.mozilla.fenix.GleanMetrics.TopSites
import org.mozilla.fenix.R
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.databinding.TopSiteItemBinding
import org.mozilla.fenix.ext.bitmapForUrl
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.isSystemInDarkTheme
import org.mozilla.fenix.ext.loadIntoView
import org.mozilla.fenix.ext.name
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.utils.view.ViewHolder
@SuppressLint("ClickableViewAccessibility")
class TopSiteItemViewHolder(
view: View,
appStore: AppStore,
private val viewLifecycleOwner: LifecycleOwner,
private val interactor: TopSiteInteractor,
) : ViewHolder(view) {
private lateinit var topSite: TopSite
private val binding = TopSiteItemBinding.bind(view)
init {
itemView.setOnLongClickListener {
TopSites.longPress.record(TopSites.LongPressExtra(topSite.name()))
val topSiteMenu = TopSiteItemMenu(
context = view.context,
topSite = topSite,
) { item ->
when (item) {
is TopSiteItemMenu.Item.OpenInPrivateTab -> interactor.onOpenInPrivateTabClicked(
topSite,
)
is TopSiteItemMenu.Item.RenameTopSite -> interactor.onRenameTopSiteClicked(
topSite,
)
is TopSiteItemMenu.Item.RemoveTopSite -> {
interactor.onRemoveTopSiteClicked(topSite)
FenixSnackbar.make(
view = it,
duration = FenixSnackbar.LENGTH_LONG,
isDisplayedWithBrowserToolbar = false,
)
.setText(it.context.getString(R.string.snackbar_top_site_removed))
.setAction(it.context.getString(R.string.snackbar_deleted_undo)) {
it.context.components.useCases.topSitesUseCase.addPinnedSites(
topSite.title.toString(),
topSite.url,
)
}
.show()
}
is TopSiteItemMenu.Item.Settings -> interactor.onSettingsClicked()
is TopSiteItemMenu.Item.SponsorPrivacy -> interactor.onSponsorPrivacyClicked()
}
}
val menu = topSiteMenu.menuBuilder.build(view.context).show(anchor = it)
it.setOnTouchListener { v, event ->
onTouchEvent(v, event, menu)
}
true
}
appStore.flowScoped(viewLifecycleOwner) { flow ->
flow.map { state -> state.wallpaperState }
.ifChanged()
.collect { currentState ->
var backgroundColor = ContextCompat.getColor(view.context, R.color.fx_mobile_layer_color_2)
currentState.runIfWallpaperCardColorsAreAvailable { cardColorLight, cardColorDark ->
backgroundColor = if (view.context.isSystemInDarkTheme()) {
cardColorDark
} else {
cardColorLight
}
}
binding.faviconCard.setCardBackgroundColor(backgroundColor)
val textColor = currentState.currentWallpaper.textColor
if (textColor != null) {
val color = Color(textColor).toArgb()
val colorList = ColorStateList.valueOf(color)
binding.topSiteTitle.setTextColor(color)
binding.topSiteSubtitle.setTextColor(color)
TextViewCompat.setCompoundDrawableTintList(binding.topSiteTitle, colorList)
} else {
binding.topSiteTitle.setTextColor(
view.context.getColorFromAttr(R.attr.textPrimary),
)
binding.topSiteSubtitle.setTextColor(
view.context.getColorFromAttr(R.attr.textSecondary),
)
TextViewCompat.setCompoundDrawableTintList(binding.topSiteTitle, null)
}
}
}
}
fun bind(topSite: TopSite, position: Int) {
itemView.setOnClickListener {
interactor.onSelectTopSite(topSite, position)
}
binding.topSiteTitle.text = topSite.title
if (topSite is TopSite.Pinned || topSite is TopSite.Default) {
val pinIndicator = getDrawable(itemView.context, R.drawable.ic_new_pin)
binding.topSiteTitle.setCompoundDrawablesWithIntrinsicBounds(pinIndicator, null, null, null)
} else {
binding.topSiteTitle.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
}
if (topSite is TopSite.Provided) {
binding.topSiteSubtitle.isVisible = true
viewLifecycleOwner.lifecycleScope.launch(IO) {
itemView.context.components.core.client.bitmapForUrl(topSite.imageUrl)?.let { bitmap ->
withContext(Main) {
binding.faviconImage.setImageBitmap(bitmap)
submitTopSitesImpressionPing(topSite, position)
}
}
}
} else {
when (topSite.url) {
SupportUtils.POCKET_TRENDING_URL -> {
binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_pocket))
}
SupportUtils.BAIDU_URL -> {
binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_baidu))
}
SupportUtils.JD_URL -> {
binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_jd))
}
SupportUtils.PDD_URL -> {
binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_pdd))
}
SupportUtils.TC_URL -> {
binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_tc))
}
SupportUtils.MEITUAN_URL -> {
binding.faviconImage.setImageDrawable(getDrawable(itemView.context, R.drawable.ic_meituan))
}
else -> {
itemView.context.components.core.icons.loadIntoView(binding.faviconImage, topSite.url)
}
}
}
this.topSite = topSite
}
@VisibleForTesting
internal fun submitTopSitesImpressionPing(topSite: TopSite.Provided, position: Int) {
TopSites.contileImpression.record(
TopSites.ContileImpressionExtra(
position = position + 1,
source = "newtab",
),
)
topSite.id?.let { TopSites.contileTileId.set(it) }
topSite.title?.let { TopSites.contileAdvertiser.set(it.lowercase()) }
TopSites.contileReportingUrl.set(topSite.impressionUrl)
Pings.topsitesImpression.submit()
}
@SuppressLint("ClickableViewAccessibility")
private fun onTouchEvent(
v: View,
event: MotionEvent,
menu: PopupWindow,
): Boolean {
if (event.action == MotionEvent.ACTION_CANCEL) {
menu.dismiss()
}
return v.onTouchEvent(event)
}
companion object {
const val LAYOUT_ID = R.layout.top_site_item
}
}