mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 09:49:30 +01:00
Implement further features for badges.
* Add Subscriptions API * Add Accept-Language header to profile requests * Fix several UI bugs, add error dialogs, etc.
This commit is contained in:
committed by
Greyson Parrelli
parent
d88999d6d4
commit
c1820459b7
@@ -5,7 +5,6 @@ import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.core.content.res.use
|
||||
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.badges.glide.BadgeSpriteTransformation
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
@@ -15,8 +14,6 @@ import org.thoughtcrime.securesms.util.ThemeUtil
|
||||
import org.thoughtcrime.securesms.util.visible
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
private val TAG = Log.tag(BadgeImageView::class.java)
|
||||
|
||||
class BadgeImageView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
|
||||
@@ -1,19 +1,38 @@
|
||||
package org.thoughtcrime.securesms.badges
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.flexbox.AlignItems
|
||||
import com.google.android.flexbox.FlexDirection
|
||||
import com.google.android.flexbox.FlexboxLayoutManager
|
||||
import com.google.android.flexbox.JustifyContent
|
||||
import org.thoughtcrime.securesms.BuildConfig
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.thoughtcrime.securesms.badges.models.Badge.Category.Companion.fromCode
|
||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||
import org.thoughtcrime.securesms.util.ScreenDensity
|
||||
import org.whispersystems.libsignal.util.Pair
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile
|
||||
import java.math.BigDecimal
|
||||
import java.sql.Timestamp
|
||||
|
||||
object Badges {
|
||||
fun DSLConfiguration.displayBadges(context: Context, badges: List<Badge>, selectedBadge: Badge? = null) {
|
||||
fun DSLConfiguration.displayBadges(
|
||||
context: Context,
|
||||
badges: List<Badge>,
|
||||
selectedBadge: Badge? = null,
|
||||
fadedBadgeId: String? = null
|
||||
) {
|
||||
badges
|
||||
.map { Badge.Model(it, it == selectedBadge) }
|
||||
.map {
|
||||
Badge.Model(
|
||||
badge = it,
|
||||
isSelected = it == selectedBadge,
|
||||
isFaded = it.id == fadedBadgeId
|
||||
)
|
||||
}
|
||||
.forEach { customPref(it) }
|
||||
|
||||
val perRow = context.resources.getInteger(R.integer.badge_columns)
|
||||
@@ -32,4 +51,41 @@ object Badges {
|
||||
|
||||
return layoutManager
|
||||
}
|
||||
|
||||
private fun getBadgeImageUri(densityPath: String): Uri {
|
||||
return Uri.parse(BuildConfig.BADGE_STATIC_ROOT).buildUpon()
|
||||
.appendPath(densityPath)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun getBestBadgeImageUriForDevice(serviceBadge: SignalServiceProfile.Badge): Pair<Uri, String> {
|
||||
val bestDensity = ScreenDensity.getBestDensityBucketForDevice()
|
||||
return when (bestDensity) {
|
||||
"ldpi" -> Pair(getBadgeImageUri(serviceBadge.sprites6[0]), "ldpi")
|
||||
"mdpi" -> Pair(getBadgeImageUri(serviceBadge.sprites6[1]), "mdpi")
|
||||
"hdpi" -> Pair(getBadgeImageUri(serviceBadge.sprites6[2]), "hdpi")
|
||||
"xxhdpi" -> Pair(getBadgeImageUri(serviceBadge.sprites6[4]), "xxhdpi")
|
||||
"xxxhdpi" -> Pair(getBadgeImageUri(serviceBadge.sprites6[5]), "xxxhdpi")
|
||||
else -> Pair(getBadgeImageUri(serviceBadge.sprites6[3]), "xdpi")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTimestamp(bigDecimal: BigDecimal): Long {
|
||||
return Timestamp(bigDecimal.toLong() * 1000).time
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromServiceBadge(serviceBadge: SignalServiceProfile.Badge): Badge {
|
||||
val uriAndDensity: Pair<Uri, String> = getBestBadgeImageUriForDevice(serviceBadge)
|
||||
return Badge(
|
||||
serviceBadge.id,
|
||||
fromCode(serviceBadge.category),
|
||||
serviceBadge.name,
|
||||
serviceBadge.description,
|
||||
uriAndDensity.first(),
|
||||
uriAndDensity.second(),
|
||||
serviceBadge.expiration?.let { getTimestamp(it) } ?: 0,
|
||||
serviceBadge.isVisible
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,8 @@ package org.thoughtcrime.securesms.badges.glide
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.security.MessageDigest
|
||||
|
||||
/**
|
||||
@@ -19,7 +17,7 @@ class BadgeSpriteTransformation(
|
||||
) : BitmapTransformation() {
|
||||
|
||||
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
|
||||
messageDigest.update("BadgeSpriteTransformation(${size.code},$density,$isDarkTheme)".toByteArray(CHARSET))
|
||||
messageDigest.update("BadgeSpriteTransformation(${size.code},$density,$isDarkTheme).$VERSION".toByteArray(CHARSET))
|
||||
}
|
||||
|
||||
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
|
||||
@@ -33,11 +31,51 @@ class BadgeSpriteTransformation(
|
||||
return outBitmap
|
||||
}
|
||||
|
||||
enum class Size(val code: String) {
|
||||
SMALL("small"),
|
||||
MEDIUM("medium"),
|
||||
LARGE("large"),
|
||||
XLARGE("xlarge");
|
||||
enum class Size(val code: String, val frameMap: Map<Density, FrameSet>) {
|
||||
SMALL(
|
||||
"small",
|
||||
mapOf(
|
||||
Density.LDPI to FrameSet(Frame(124, 1, 13, 13), Frame(145, 31, 13, 13)),
|
||||
Density.MDPI to FrameSet(Frame(163, 1, 16, 16), Frame(189, 39, 16, 16)),
|
||||
Density.HDPI to FrameSet(Frame(244, 1, 25, 25), Frame(283, 58, 25, 25)),
|
||||
Density.XHDPI to FrameSet(Frame(323, 1, 32, 32), Frame(373, 75, 32, 32)),
|
||||
Density.XXHDPI to FrameSet(Frame(483, 1, 48, 48), Frame(557, 111, 48, 48)),
|
||||
Density.XXXHDPI to FrameSet(Frame(643, 1, 64, 64), Frame(741, 147, 64, 64))
|
||||
)
|
||||
),
|
||||
MEDIUM(
|
||||
"medium",
|
||||
mapOf(
|
||||
Density.LDPI to FrameSet(Frame(124, 16, 19, 19), Frame(160, 31, 19, 19)),
|
||||
Density.MDPI to FrameSet(Frame(163, 19, 24, 24), Frame(207, 39, 24, 24)),
|
||||
Density.HDPI to FrameSet(Frame(244, 28, 37, 37), Frame(310, 58, 37, 37)),
|
||||
Density.XHDPI to FrameSet(Frame(323, 35, 48, 48), Frame(407, 75, 48, 48)),
|
||||
Density.XXHDPI to FrameSet(Frame(483, 51, 72, 72), Frame(607, 111, 72, 72)),
|
||||
Density.XXXHDPI to FrameSet(Frame(643, 67, 96, 96), Frame(807, 147, 96, 96))
|
||||
)
|
||||
),
|
||||
LARGE(
|
||||
"large",
|
||||
mapOf(
|
||||
Density.LDPI to FrameSet(Frame(145, 1, 28, 28), Frame(124, 46, 28, 28)),
|
||||
Density.MDPI to FrameSet(Frame(189, 1, 36, 36), Frame(163, 57, 36, 36)),
|
||||
Density.HDPI to FrameSet(Frame(283, 1, 55, 55), Frame(244, 85, 55, 55)),
|
||||
Density.XHDPI to FrameSet(Frame(373, 1, 72, 72), Frame(323, 109, 72, 72)),
|
||||
Density.XXHDPI to FrameSet(Frame(557, 1, 108, 108), Frame(483, 161, 108, 108)),
|
||||
Density.XXXHDPI to FrameSet(Frame(741, 1, 144, 144), Frame(643, 213, 144, 144))
|
||||
)
|
||||
),
|
||||
XLARGE(
|
||||
"xlarge",
|
||||
mapOf(
|
||||
Density.LDPI to FrameSet(Frame(1, 1, 121, 121), Frame(1, 1, 121, 121)),
|
||||
Density.MDPI to FrameSet(Frame(1, 1, 160, 160), Frame(1, 1, 160, 160)),
|
||||
Density.HDPI to FrameSet(Frame(1, 1, 241, 241), Frame(1, 1, 241, 241)),
|
||||
Density.XHDPI to FrameSet(Frame(1, 1, 320, 320), Frame(1, 1, 320, 320)),
|
||||
Density.XXHDPI to FrameSet(Frame(1, 1, 480, 480), Frame(1, 1, 480, 480)),
|
||||
Density.XXXHDPI to FrameSet(Frame(1, 1, 640, 640), Frame(1, 1, 640, 640))
|
||||
)
|
||||
);
|
||||
|
||||
companion object {
|
||||
fun fromInteger(integer: Int): Size {
|
||||
@@ -52,55 +90,42 @@ class BadgeSpriteTransformation(
|
||||
}
|
||||
}
|
||||
|
||||
enum class Density(val density: String) {
|
||||
LDPI("ldpi"),
|
||||
MDPI("mdpi"),
|
||||
HDPI("hdpi"),
|
||||
XHDPI("xhdpi"),
|
||||
XXHDPI("xxhdpi"),
|
||||
XXXHDPI("xxxhdpi")
|
||||
}
|
||||
|
||||
data class FrameSet(val light: Frame, val dark: Frame)
|
||||
|
||||
data class Frame(
|
||||
val x: Int,
|
||||
val y: Int,
|
||||
val width: Int,
|
||||
val height: Int
|
||||
) {
|
||||
fun toBounds(): Rect {
|
||||
return Rect(x, y, x + width, y + height)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PADDING = 1
|
||||
private const val VERSION = 1
|
||||
|
||||
@VisibleForTesting
|
||||
fun getInBounds(density: String, size: Size, isDarkTheme: Boolean): Rect {
|
||||
val scaleFactor: Int = when (density) {
|
||||
"ldpi" -> 75
|
||||
"mdpi" -> 100
|
||||
"hdpi" -> 150
|
||||
"xhdpi" -> 200
|
||||
"xxhdpi" -> 300
|
||||
"xxxhdpi" -> 400
|
||||
else -> throw IllegalArgumentException("Unexpected density $density")
|
||||
}
|
||||
private fun getDensity(density: String): Density {
|
||||
return Density.values().first { it.density == density }
|
||||
}
|
||||
|
||||
val smallLength = 8 * scaleFactor / 100
|
||||
val mediumLength = 12 * scaleFactor / 100
|
||||
val largeLength = 18 * scaleFactor / 100
|
||||
val xlargeLength = 80 * scaleFactor / 100
|
||||
private fun getFrame(size: Size, density: Density, isDarkTheme: Boolean): Frame {
|
||||
val frameSet: FrameSet = size.frameMap[density]!!
|
||||
return if (isDarkTheme) frameSet.dark else frameSet.light
|
||||
}
|
||||
|
||||
val sideLength: Int = when (size) {
|
||||
Size.SMALL -> smallLength
|
||||
Size.MEDIUM -> mediumLength
|
||||
Size.LARGE -> largeLength
|
||||
Size.XLARGE -> xlargeLength
|
||||
}
|
||||
|
||||
val lightOffset: Int = when (size) {
|
||||
Size.LARGE -> PADDING
|
||||
Size.MEDIUM -> (largeLength + PADDING * 2) * 2 + PADDING
|
||||
Size.SMALL -> (largeLength + PADDING * 2) * 2 + (mediumLength + PADDING * 2) * 2 + PADDING
|
||||
Size.XLARGE -> (largeLength + PADDING * 2) * 2 + (mediumLength + PADDING * 2) * 2 + (smallLength + PADDING * 2) * 2 + PADDING
|
||||
}
|
||||
|
||||
val darkOffset = if (isDarkTheme) {
|
||||
when (size) {
|
||||
Size.XLARGE -> 0
|
||||
else -> sideLength + PADDING * 2
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
return Rect(
|
||||
lightOffset + darkOffset,
|
||||
PADDING,
|
||||
lightOffset + darkOffset + sideLength,
|
||||
sideLength + PADDING
|
||||
)
|
||||
private fun getInBounds(density: String, size: Size, isDarkTheme: Boolean): Rect {
|
||||
return getFrame(size, getDensity(density), isDarkTheme).toBounds()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.thoughtcrime.securesms.util.MappingViewHolder
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil
|
||||
import java.security.MessageDigest
|
||||
|
||||
typealias OnBadgeClicked = (Badge, Boolean) -> Unit
|
||||
typealias OnBadgeClicked = (Badge, Boolean, Boolean) -> Unit
|
||||
|
||||
/**
|
||||
* A Badge that can be collected and displayed by a user.
|
||||
@@ -70,14 +70,18 @@ data class Badge(
|
||||
|
||||
class Model(
|
||||
val badge: Badge,
|
||||
val isSelected: Boolean = false
|
||||
val isSelected: Boolean = false,
|
||||
val isFaded: Boolean = false
|
||||
) : PreferenceModel<Model>() {
|
||||
override fun areItemsTheSame(newItem: Model): Boolean {
|
||||
return newItem.badge.id == badge.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(newItem: Model): Boolean {
|
||||
return super.areContentsTheSame(newItem) && badge == newItem.badge && isSelected == newItem.isSelected
|
||||
return super.areContentsTheSame(newItem) &&
|
||||
badge == newItem.badge &&
|
||||
isSelected == newItem.isSelected &&
|
||||
isFaded == newItem.isFaded
|
||||
}
|
||||
|
||||
override fun getChangePayload(newItem: Model): Any? {
|
||||
@@ -103,7 +107,7 @@ data class Badge(
|
||||
|
||||
override fun bind(model: Model) {
|
||||
itemView.setOnClickListener {
|
||||
onBadgeClicked(model.badge, model.isSelected)
|
||||
onBadgeClicked(model.badge, model.isSelected, model.isFaded)
|
||||
}
|
||||
|
||||
checkAnimator?.cancel()
|
||||
@@ -117,7 +121,7 @@ data class Badge(
|
||||
return
|
||||
}
|
||||
|
||||
badge.alpha = if (model.badge.isExpired()) 0.5f else 1f
|
||||
badge.alpha = if (model.badge.isExpired() || model.isFaded) 0.5f else 1f
|
||||
|
||||
GlideApp.with(badge)
|
||||
.load(model.badge)
|
||||
@@ -162,26 +166,4 @@ data class Badge(
|
||||
mappingAdapter.registerFactory(EmptyModel::class.java, MappingAdapter.LayoutFactory({ EmptyViewHolder(it) }, R.layout.badge_preference_view))
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class ImageSet(
|
||||
val ldpi: String,
|
||||
val mdpi: String,
|
||||
val hdpi: String,
|
||||
val xhdpi: String,
|
||||
val xxhdpi: String,
|
||||
val xxxhdpi: String
|
||||
) : Parcelable {
|
||||
fun getByDensity(density: String): String {
|
||||
return when (density) {
|
||||
"ldpi" -> ldpi
|
||||
"mdpi" -> mdpi
|
||||
"hdpi" -> hdpi
|
||||
"xhdpi" -> xhdpi
|
||||
"xxhdpi" -> xxhdpi
|
||||
"xxxhdpi" -> xxxhdpi
|
||||
else -> xhdpi
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,22 +22,30 @@ object BadgePreview {
|
||||
|
||||
data class Model(override val badge: Badge?) : BadgeModel<Model>() {
|
||||
override fun areItemsTheSame(newItem: Model): Boolean {
|
||||
return newItem.badge?.id == badge?.id
|
||||
return true
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(newItem: Model): Boolean {
|
||||
return super.areContentsTheSame(newItem) && badge == newItem.badge
|
||||
}
|
||||
|
||||
override fun getChangePayload(newItem: Model): Any? {
|
||||
return Unit
|
||||
}
|
||||
}
|
||||
|
||||
data class SubscriptionModel(override val badge: Badge?) : BadgeModel<SubscriptionModel>() {
|
||||
override fun areItemsTheSame(newItem: SubscriptionModel): Boolean {
|
||||
return newItem.badge?.id == badge?.id
|
||||
return true
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(newItem: SubscriptionModel): Boolean {
|
||||
return super.areContentsTheSame(newItem) && badge == newItem.badge
|
||||
}
|
||||
|
||||
override fun getChangePayload(newItem: SubscriptionModel): Any? {
|
||||
return Unit
|
||||
}
|
||||
}
|
||||
|
||||
class ViewHolder<T : BadgeModel<T>>(itemView: View) : MappingViewHolder<T>(itemView) {
|
||||
@@ -46,8 +54,11 @@ object BadgePreview {
|
||||
private val badge: BadgeImageView = itemView.findViewById(R.id.badge)
|
||||
|
||||
override fun bind(model: T) {
|
||||
avatar.setRecipient(Recipient.self())
|
||||
avatar.disableQuickContact()
|
||||
if (payload.isEmpty()) {
|
||||
avatar.setRecipient(Recipient.self())
|
||||
avatar.disableQuickContact()
|
||||
}
|
||||
|
||||
badge.setBadge(model.badge)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class SelectFeaturedBadgeFragment : DSLSettingsFragment(
|
||||
}
|
||||
|
||||
override fun bindAdapter(adapter: DSLSettingsAdapter) {
|
||||
Badge.register(adapter) { badge, isSelected ->
|
||||
Badge.register(adapter) { badge, isSelected, _ ->
|
||||
if (!isSelected) {
|
||||
viewModel.setSelectedBadge(badge)
|
||||
}
|
||||
@@ -69,8 +69,16 @@ class SelectFeaturedBadgeFragment : DSLSettingsFragment(
|
||||
}
|
||||
}
|
||||
|
||||
var hasBoundPreview = false
|
||||
viewModel.state.observe(viewLifecycleOwner) { state ->
|
||||
save.isEnabled = state.stage == SelectFeaturedBadgeState.Stage.READY
|
||||
|
||||
if (hasBoundPreview) {
|
||||
previewViewHolder.setPayload(listOf(Unit))
|
||||
} else {
|
||||
hasBoundPreview = true
|
||||
}
|
||||
|
||||
previewViewHolder.bind(BadgePreview.Model(state.selectedBadge))
|
||||
adapter.submitList(getConfiguration(state).toMappingModelList())
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||
|
||||
@@ -26,11 +28,15 @@ class BadgesOverviewFragment : DSLSettingsFragment(
|
||||
) {
|
||||
|
||||
private val lifecycleDisposable = LifecycleDisposable()
|
||||
private val viewModel: BadgesOverviewViewModel by viewModels(factoryProducer = { BadgesOverviewViewModel.Factory(BadgeRepository(requireContext())) })
|
||||
private val viewModel: BadgesOverviewViewModel by viewModels(
|
||||
factoryProducer = {
|
||||
BadgesOverviewViewModel.Factory(BadgeRepository(requireContext()), SubscriptionsRepository(ApplicationDependencies.getDonationsService()))
|
||||
}
|
||||
)
|
||||
|
||||
override fun bindAdapter(adapter: DSLSettingsAdapter) {
|
||||
Badge.register(adapter) { badge, _ ->
|
||||
if (badge.isExpired()) {
|
||||
Badge.register(adapter) { badge, _, isFaded ->
|
||||
if (badge.isExpired() || isFaded) {
|
||||
findNavController().navigate(BadgesOverviewFragmentDirections.actionBadgeManageFragmentToExpiredBadgeDialog(badge))
|
||||
} else {
|
||||
ViewBadgeBottomSheetDialogFragment.show(parentFragmentManager, Recipient.self().id, badge)
|
||||
@@ -56,7 +62,11 @@ class BadgesOverviewFragment : DSLSettingsFragment(
|
||||
return configure {
|
||||
sectionHeaderPref(R.string.BadgesOverviewFragment__my_badges)
|
||||
|
||||
displayBadges(requireContext(), state.allUnlockedBadges)
|
||||
displayBadges(
|
||||
context = requireContext(),
|
||||
badges = state.allUnlockedBadges,
|
||||
fadedBadgeId = state.fadedBadgeId
|
||||
)
|
||||
|
||||
switchPref(
|
||||
title = DSLSettingsText.from(R.string.BadgesOverviewFragment__display_badges_on_profile),
|
||||
|
||||
@@ -7,6 +7,7 @@ data class BadgesOverviewState(
|
||||
val allUnlockedBadges: List<Badge> = listOf(),
|
||||
val featuredBadge: Badge? = null,
|
||||
val displayBadgesOnProfile: Boolean = false,
|
||||
val fadedBadgeId: String? = null
|
||||
) {
|
||||
|
||||
val hasUnexpiredBadges = allUnlockedBadges.any { it.expirationTimestamp > System.currentTimeMillis() }
|
||||
|
||||
@@ -5,17 +5,25 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.badges.BadgeRepository
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
import org.whispersystems.libsignal.util.guava.Optional
|
||||
|
||||
private val TAG = Log.tag(BadgesOverviewViewModel::class.java)
|
||||
|
||||
class BadgesOverviewViewModel(private val badgeRepository: BadgeRepository) : ViewModel() {
|
||||
class BadgesOverviewViewModel(
|
||||
private val badgeRepository: BadgeRepository,
|
||||
private val subscriptionsRepository: SubscriptionsRepository
|
||||
) : ViewModel() {
|
||||
private val store = Store(BadgesOverviewState())
|
||||
private val eventSubject = PublishSubject.create<BadgesOverviewEvent>()
|
||||
|
||||
@@ -33,6 +41,19 @@ class BadgesOverviewViewModel(private val badgeRepository: BadgeRepository) : Vi
|
||||
featuredBadge = recipient.featuredBadge
|
||||
)
|
||||
}
|
||||
|
||||
disposables += Single.zip(
|
||||
subscriptionsRepository.getActiveSubscription(),
|
||||
subscriptionsRepository.getSubscriptions(SignalStore.donationsValues().getSubscriptionCurrency())
|
||||
) { active, all ->
|
||||
if (!active.isActive && active.activeSubscription?.willCancelAtPeriodEnd() == true) {
|
||||
Optional.fromNullable<String>(all.firstOrNull { it.level == active.activeSubscription?.level }?.badge?.id)
|
||||
} else {
|
||||
Optional.absent()
|
||||
}
|
||||
}.subscribeBy { badgeId ->
|
||||
store.update { it.copy(fadedBadgeId = badgeId.orNull()) }
|
||||
}
|
||||
}
|
||||
|
||||
fun setDisplayBadgesOnProfile(displayBadgesOnProfile: Boolean) {
|
||||
@@ -53,9 +74,12 @@ class BadgesOverviewViewModel(private val badgeRepository: BadgeRepository) : Vi
|
||||
disposables.clear()
|
||||
}
|
||||
|
||||
class Factory(private val badgeRepository: BadgeRepository) : ViewModelProvider.Factory {
|
||||
class Factory(
|
||||
private val badgeRepository: BadgeRepository,
|
||||
private val subscriptionsRepository: SubscriptionsRepository
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return requireNotNull(modelClass.cast(BadgesOverviewViewModel(badgeRepository)))
|
||||
return requireNotNull(modelClass.cast(BadgesOverviewViewModel(badgeRepository, subscriptionsRepository)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user