Add support for updated server badge image url formats.

fork-5.53.8
Alex Hart 2021-09-28 16:55:07 -03:00 zatwierdzone przez Greyson Parrelli
rodzic 6e00920c95
commit 8d0acb277c
32 zmienionych plików z 602 dodań i 214 usunięć

Wyświetl plik

@ -9,6 +9,7 @@ apply from: 'translations.gradle'
apply from: 'witness-verifications.gradle' apply from: 'witness-verifications.gradle'
apply plugin: 'org.jetbrains.kotlin.android' apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'app.cash.exhaustive' apply plugin: 'app.cash.exhaustive'
apply plugin: 'kotlin-parcelize'
repositories { repositories {
maven { maven {
@ -177,6 +178,7 @@ android {
buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"unset\"" buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"unset\""
buildConfigField "String", "BUILD_ENVIRONMENT_TYPE", "\"unset\"" buildConfigField "String", "BUILD_ENVIRONMENT_TYPE", "\"unset\""
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"unset\"" buildConfigField "String", "BUILD_VARIANT_TYPE", "\"unset\""
buildConfigField "String", "BADGE_STATIC_ROOT", "\"https://updates2.signal.org/static/badges/\""
ndk { ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'

Wyświetl plik

@ -1,20 +1,18 @@
package org.thoughtcrime.securesms.badges package org.thoughtcrime.securesms.badges
import android.content.Context import android.content.Context
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.util.AttributeSet import android.util.AttributeSet
import androidx.annotation.ColorInt
import androidx.annotation.Px
import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.res.use import androidx.core.content.res.use
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
import org.signal.core.util.logging.Log import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.Badges.insetWithOutline import org.thoughtcrime.securesms.badges.glide.BadgeSpriteTransformation
import org.thoughtcrime.securesms.badges.models.Badge import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.ThemeUtil
import org.thoughtcrime.securesms.util.ViewUtil import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.visible import org.thoughtcrime.securesms.util.visible
@ -25,16 +23,11 @@ class BadgeImageView @JvmOverloads constructor(
attrs: AttributeSet? = null attrs: AttributeSet? = null
) : AppCompatImageView(context, attrs) { ) : AppCompatImageView(context, attrs) {
@Px private var badgeSize: Int = 0
private var outlineWidth: Float = 0f
@ColorInt
private var outlineColor: Int = Color.BLACK
init { init {
context.obtainStyledAttributes(attrs, R.styleable.BadgeImageView).use { context.obtainStyledAttributes(attrs, R.styleable.BadgeImageView).use {
outlineWidth = it.getDimension(R.styleable.BadgeImageView_badge_outline_width, 0f) badgeSize = it.getInt(R.styleable.BadgeImageView_badge_size, 0)
outlineColor = it.getColor(R.styleable.BadgeImageView_badge_outline_color, Color.BLACK)
} }
} }
@ -55,21 +48,17 @@ class BadgeImageView @JvmOverloads constructor(
return return
} }
GlideApp if (badge != null) {
.with(this) GlideApp
.load(badge) .with(this)
.into(this) .load(badge)
} .downsample(DownsampleStrategy.NONE)
.transform(BadgeSpriteTransformation(BadgeSpriteTransformation.Size.fromInteger(badgeSize), badge.imageDensity, ThemeUtil.isDarkTheme(context)))
override fun setImageDrawable(drawable: Drawable?) { .into(this)
if (drawable == null || outlineWidth == 0f) {
super.setImageDrawable(drawable)
} else { } else {
super.setImageDrawable( GlideApp
drawable.insetWithOutline( .with(this)
outlineWidth, outlineColor .clear(this)
)
)
} }
} }
} }

Wyświetl plik

@ -11,46 +11,23 @@ import com.google.android.flexbox.AlignItems
import com.google.android.flexbox.FlexDirection import com.google.android.flexbox.FlexDirection
import com.google.android.flexbox.FlexboxLayoutManager import com.google.android.flexbox.FlexboxLayoutManager
import com.google.android.flexbox.JustifyContent import com.google.android.flexbox.JustifyContent
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.models.Badge import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.badges.models.BadgeAnimator import org.thoughtcrime.securesms.badges.models.BadgeAnimator
import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.util.customizeOnDraw import org.thoughtcrime.securesms.util.customizeOnDraw
object Badges { object Badges {
fun Drawable.insetWithOutline(
@Px outlineWidth: Float,
@ColorInt outlineColor: Int
): Drawable {
val clone = mutate().constantState?.newDrawable()?.mutate()
clone?.colorFilter = SimpleColorFilter(outlineColor)
return customizeOnDraw { wrapped, canvas ->
clone?.bounds = wrapped.bounds
clone?.draw(canvas)
val scale = 1 - ((outlineWidth * 2) / canvas.width)
canvas.withScale(x = scale, y = scale, canvas.width / 2f, canvas.height / 2f) {
wrapped.draw(canvas)
}
}
}
fun Drawable.selectable( fun Drawable.selectable(
@Px outlineWidth: Float, @Px outlineWidth: Float,
@ColorInt outlineColor: Int, @ColorInt outlineColor: Int,
@ColorInt gapColor: Int,
animator: BadgeAnimator animator: BadgeAnimator
): Drawable { ): Drawable {
val outline = mutate().constantState?.newDrawable()?.mutate() val outline = mutate().constantState?.newDrawable()?.mutate()
outline?.colorFilter = SimpleColorFilter(outlineColor) outline?.colorFilter = SimpleColorFilter(outlineColor)
val gap = mutate().constantState?.newDrawable()?.mutate()
gap?.colorFilter = SimpleColorFilter(gapColor)
return customizeOnDraw { wrapped, canvas -> return customizeOnDraw { wrapped, canvas ->
outline?.bounds = wrapped.bounds outline?.bounds = wrapped.bounds
gap?.bounds = wrapped.bounds
outline?.draw(canvas) outline?.draw(canvas)
@ -58,11 +35,7 @@ object Badges {
val interpolatedScale = scale + (1f - scale) * animator.getFraction() val interpolatedScale = scale + (1f - scale) * animator.getFraction()
canvas.withScale(x = interpolatedScale, y = interpolatedScale, wrapped.bounds.width() / 2f, wrapped.bounds.height() / 2f) { canvas.withScale(x = interpolatedScale, y = interpolatedScale, wrapped.bounds.width() / 2f, wrapped.bounds.height() / 2f) {
gap?.draw(canvas) wrapped.draw(canvas)
canvas.withScale(x = interpolatedScale, y = interpolatedScale, wrapped.bounds.width() / 2f, wrapped.bounds.height() / 2f) {
wrapped.draw(canvas)
}
} }
if (animator.shouldInvalidate()) { if (animator.shouldInvalidate()) {
@ -71,12 +44,13 @@ object Badges {
} }
} }
fun DSLConfiguration.displayBadges(badges: List<Badge>, selectedBadge: Badge? = null) { fun DSLConfiguration.displayBadges(context: Context, badges: List<Badge>, selectedBadge: Badge? = null) {
badges badges
.map { Badge.Model(it, it == selectedBadge) } .map { Badge.Model(it, it == selectedBadge) }
.forEach { customPref(it) } .forEach { customPref(it) }
val empties = (4 - (badges.size % 4)) % 4 val perRow = context.resources.getInteger(R.integer.badge_columns)
val empties = (perRow - (badges.size % perRow)) % perRow
repeat(empties) { repeat(empties) {
customPref(Badge.EmptyModel()) customPref(Badge.EmptyModel())
} }

Wyświetl plik

@ -0,0 +1,106 @@
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
/**
* Cuts out the badge of the requested size from the sprite sheet.
*/
class BadgeSpriteTransformation(
private val size: Size,
private val density: String,
private val isDarkTheme: Boolean
) : BitmapTransformation() {
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update("BadgeSpriteTransformation(${size.code},$density,$isDarkTheme)".toByteArray(CHARSET))
}
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
val outBitmap = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(outBitmap)
val inBounds = getInBounds(density, size, isDarkTheme)
val outBounds = Rect(0, 0, outWidth, outHeight)
canvas.drawBitmap(toTransform, inBounds, outBounds, null)
return outBitmap
}
enum class Size(val code: String) {
SMALL("small"),
MEDIUM("medium"),
LARGE("large"),
XLARGE("xlarge");
companion object {
fun fromInteger(integer: Int): Size {
return when (integer) {
0 -> SMALL
1 -> MEDIUM
2 -> LARGE
3 -> XLARGE
else -> LARGE
}
}
}
}
companion object {
private const val PADDING = 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")
}
val smallLength = 8 * scaleFactor / 100
val mediumLength = 12 * scaleFactor / 100
val largeLength = 18 * scaleFactor / 100
val xlargeLength = 80 * scaleFactor / 100
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
)
}
}
}

Wyświetl plik

@ -2,22 +2,26 @@ package org.thoughtcrime.securesms.badges.models
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.bumptech.glide.load.Key import com.bumptech.glide.load.Key
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
import com.bumptech.glide.request.target.CustomViewTarget import com.bumptech.glide.request.target.CustomViewTarget
import com.bumptech.glide.request.transition.Transition import com.bumptech.glide.request.transition.Transition
import kotlinx.parcelize.Parcelize
import org.signal.core.util.DimensionUnit import org.signal.core.util.DimensionUnit
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.Badges.selectable import org.thoughtcrime.securesms.badges.Badges.selectable
import org.thoughtcrime.securesms.badges.glide.BadgeSpriteTransformation
import org.thoughtcrime.securesms.components.settings.PreferenceModel import org.thoughtcrime.securesms.components.settings.PreferenceModel
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.util.MappingAdapter import org.thoughtcrime.securesms.util.MappingAdapter
import org.thoughtcrime.securesms.util.MappingViewHolder import org.thoughtcrime.securesms.util.MappingViewHolder
import org.thoughtcrime.securesms.util.ThemeUtil
import java.security.MessageDigest import java.security.MessageDigest
typealias OnBadgeClicked = (Badge, Boolean) -> Unit typealias OnBadgeClicked = (Badge, Boolean) -> Unit
@ -25,42 +29,22 @@ typealias OnBadgeClicked = (Badge, Boolean) -> Unit
/** /**
* A Badge that can be collected and displayed by a user. * A Badge that can be collected and displayed by a user.
*/ */
@Parcelize
data class Badge( data class Badge(
val id: String, val id: String,
val category: Category, val category: Category,
val imageUrl: Uri,
val name: String, val name: String,
val description: String, val description: String,
val imageUrl: Uri,
val imageDensity: String,
val expirationTimestamp: Long, val expirationTimestamp: Long,
val visible: Boolean val visible: Boolean,
) : Parcelable, Key { ) : Parcelable, Key {
constructor(parcel: Parcel) : this(
requireNotNull(parcel.readString()),
Category.fromCode(requireNotNull(parcel.readString())),
requireNotNull(parcel.readParcelable(Uri::class.java.classLoader)),
requireNotNull(parcel.readString()),
requireNotNull(parcel.readString()),
parcel.readLong(),
parcel.readByte() == 1.toByte()
)
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(id)
parcel.writeString(category.code)
parcel.writeParcelable(imageUrl, flags)
parcel.writeString(name)
parcel.writeString(description)
parcel.writeLong(expirationTimestamp)
parcel.writeByte(if (visible) 1 else 0)
}
override fun updateDiskCacheKey(messageDigest: MessageDigest) { override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update(id.toByteArray(Key.CHARSET)) messageDigest.update(id.toByteArray(Key.CHARSET))
messageDigest.update(imageUrl.toString().toByteArray(Key.CHARSET))
messageDigest.update(imageDensity.toByteArray(Key.CHARSET))
} }
fun resolveDescription(shortName: String): String { fun resolveDescription(shortName: String): String {
@ -130,6 +114,9 @@ data class Badge(
GlideApp.with(badge) GlideApp.with(badge)
.load(model.badge) .load(model.badge)
.downsample(DownsampleStrategy.NONE)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.transform(BadgeSpriteTransformation(BadgeSpriteTransformation.Size.XLARGE, model.badge.imageDensity, ThemeUtil.isDarkTheme(context)))
.into(target) .into(target)
if (model.isSelected) { if (model.isSelected) {
@ -170,7 +157,6 @@ data class Badge(
val drawable = resource.selectable( val drawable = resource.selectable(
DimensionUnit.DP.toPixels(2.5f), DimensionUnit.DP.toPixels(2.5f),
ContextCompat.getColor(view.context, R.color.signal_inverse_primary), ContextCompat.getColor(view.context, R.color.signal_inverse_primary),
ContextCompat.getColor(view.context, R.color.signal_background_primary),
animator animator
) )
@ -202,20 +188,34 @@ data class Badge(
} }
} }
companion object CREATOR : Parcelable.Creator<Badge> { companion object {
private val SELECTION_CHANGED = Any() private val SELECTION_CHANGED = Any()
override fun createFromParcel(parcel: Parcel): Badge {
return Badge(parcel)
}
override fun newArray(size: Int): Array<Badge?> {
return arrayOfNulls(size)
}
fun register(mappingAdapter: MappingAdapter, onBadgeClicked: OnBadgeClicked) { fun register(mappingAdapter: MappingAdapter, onBadgeClicked: OnBadgeClicked) {
mappingAdapter.registerFactory(Model::class.java, MappingAdapter.LayoutFactory({ ViewHolder(it, onBadgeClicked) }, R.layout.badge_preference_view)) mappingAdapter.registerFactory(Model::class.java, MappingAdapter.LayoutFactory({ ViewHolder(it, onBadgeClicked) }, R.layout.badge_preference_view))
mappingAdapter.registerFactory(EmptyModel::class.java, MappingAdapter.LayoutFactory({ EmptyViewHolder(it) }, R.layout.badge_preference_view)) 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
}
}
}
} }

Wyświetl plik

@ -1,17 +1,10 @@
package org.thoughtcrime.securesms.badges.models package org.thoughtcrime.securesms.badges.models
import android.graphics.drawable.Drawable
import android.view.View import android.view.View
import android.widget.ImageView
import androidx.core.content.ContextCompat
import com.bumptech.glide.request.target.CustomViewTarget
import com.bumptech.glide.request.transition.Transition
import org.signal.core.util.DimensionUnit
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.Badges.insetWithOutline import org.thoughtcrime.securesms.badges.BadgeImageView
import org.thoughtcrime.securesms.components.AvatarImageView import org.thoughtcrime.securesms.components.AvatarImageView
import org.thoughtcrime.securesms.components.settings.PreferenceModel import org.thoughtcrime.securesms.components.settings.PreferenceModel
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.MappingAdapter import org.thoughtcrime.securesms.util.MappingAdapter
import org.thoughtcrime.securesms.util.MappingViewHolder import org.thoughtcrime.securesms.util.MappingViewHolder
@ -35,40 +28,12 @@ object FeaturedBadgePreview {
class ViewHolder(itemView: View) : MappingViewHolder<Model>(itemView) { class ViewHolder(itemView: View) : MappingViewHolder<Model>(itemView) {
private val avatar: AvatarImageView = itemView.findViewById(R.id.avatar) private val avatar: AvatarImageView = itemView.findViewById(R.id.avatar)
private val badge: ImageView = itemView.findViewById(R.id.badge) private val badge: BadgeImageView = itemView.findViewById(R.id.badge)
private val target: Target = Target(badge)
override fun bind(model: Model) { override fun bind(model: Model) {
avatar.setRecipient(Recipient.self()) avatar.setRecipient(Recipient.self())
avatar.disableQuickContact() avatar.disableQuickContact()
badge.setBadge(model.badge)
if (model.badge != null) {
GlideApp.with(badge)
.load(model.badge)
.into(target)
} else {
GlideApp.with(badge).clear(badge)
badge.setImageDrawable(null)
}
}
}
private class Target(view: ImageView) : CustomViewTarget<ImageView, Drawable>(view) {
override fun onLoadFailed(errorDrawable: Drawable?) {
view.setImageDrawable(errorDrawable)
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
view.setImageDrawable(
resource.insetWithOutline(
DimensionUnit.DP.toPixels(2.5f),
ContextCompat.getColor(view.context, R.color.signal_background_primary)
)
)
}
override fun onResourceCleared(placeholder: Drawable?) {
view.setImageDrawable(placeholder)
} }
} }
} }

Wyświetl plik

@ -1,10 +1,9 @@
package org.thoughtcrime.securesms.badges.models package org.thoughtcrime.securesms.badges.models
import android.view.View import android.view.View
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.badges.BadgeImageView
import org.thoughtcrime.securesms.util.MappingAdapter import org.thoughtcrime.securesms.util.MappingAdapter
import org.thoughtcrime.securesms.util.MappingModel import org.thoughtcrime.securesms.util.MappingModel
import org.thoughtcrime.securesms.util.MappingViewHolder import org.thoughtcrime.securesms.util.MappingViewHolder
@ -35,14 +34,12 @@ data class LargeBadge(
class ViewHolder(itemView: View) : MappingViewHolder<Model>(itemView) { class ViewHolder(itemView: View) : MappingViewHolder<Model>(itemView) {
private val badge: ImageView = itemView.findViewById(R.id.badge) private val badge: BadgeImageView = itemView.findViewById(R.id.badge)
private val name: TextView = itemView.findViewById(R.id.name) private val name: TextView = itemView.findViewById(R.id.name)
private val description: TextView = itemView.findViewById(R.id.description) private val description: TextView = itemView.findViewById(R.id.description)
override fun bind(model: Model) { override fun bind(model: Model) {
GlideApp.with(badge) badge.setBadge(model.largeBadge.badge)
.load(model.largeBadge.badge)
.into(badge)
name.text = model.largeBadge.badge.name name.text = model.largeBadge.badge.name
description.text = model.largeBadge.badge.resolveDescription(model.shortName) description.text = model.largeBadge.badge.resolveDescription(model.shortName)

Wyświetl plik

@ -79,7 +79,7 @@ class SelectFeaturedBadgeFragment : DSLSettingsFragment(
private fun getConfiguration(state: SelectFeaturedBadgeState): DSLConfiguration { private fun getConfiguration(state: SelectFeaturedBadgeState): DSLConfiguration {
return configure { return configure {
sectionHeaderPref(R.string.SelectFeaturedBadgeFragment__select_a_badge) sectionHeaderPref(R.string.SelectFeaturedBadgeFragment__select_a_badge)
displayBadges(state.allUnlockedBadges, state.selectedBadge) displayBadges(requireContext(), state.allUnlockedBadges, state.selectedBadge)
} }
} }
} }

Wyświetl plik

@ -52,7 +52,7 @@ class BadgesOverviewFragment : DSLSettingsFragment(
return configure { return configure {
sectionHeaderPref(R.string.BadgesOverviewFragment__my_badges) sectionHeaderPref(R.string.BadgesOverviewFragment__my_badges)
displayBadges(state.allUnlockedBadges) displayBadges(requireContext(), state.allUnlockedBadges)
switchPref( switchPref(
title = DSLSettingsText.from(R.string.BadgesOverviewFragment__display_badges_on_profile), title = DSLSettingsText.from(R.string.BadgesOverviewFragment__display_badges_on_profile),

Wyświetl plik

@ -489,7 +489,7 @@ class ConversationSettingsFragment : DSLSettingsFragment(
sectionHeaderPref(R.string.ManageProfileFragment_badges) sectionHeaderPref(R.string.ManageProfileFragment_badges)
displayBadges(state.recipient.badges) displayBadges(requireContext(), state.recipient.badges)
} }
if (recipientSettingsState.selfHasGroups) { if (recipientSettingsState.selfHasGroups) {

Wyświetl plik

@ -1373,9 +1373,10 @@ public class RecipientDatabase extends Database {
badges.add(new Badge( badges.add(new Badge(
protoBadge.getId(), protoBadge.getId(),
Badge.Category.Companion.fromCode(protoBadge.getCategory()), Badge.Category.Companion.fromCode(protoBadge.getCategory()),
Uri.parse(protoBadge.getImageUrl()),
protoBadge.getName(), protoBadge.getName(),
protoBadge.getDescription(), protoBadge.getDescription(),
Uri.parse(protoBadge.getImageUrl()),
protoBadge.getImageDensity(),
protoBadge.getExpiration(), protoBadge.getExpiration(),
protoBadge.getVisible() protoBadge.getVisible()
)); ));
@ -1691,7 +1692,8 @@ public class RecipientDatabase extends Database {
.setExpiration(badge.getExpirationTimestamp()) .setExpiration(badge.getExpirationTimestamp())
.setVisible(badge.getVisible()) .setVisible(badge.getVisible())
.setName(badge.getName()) .setName(badge.getName())
.setImageUrl(badge.getImageUrl().toString())); .setImageUrl(badge.getImageUrl().toString())
.setImageDensity(badge.getImageDensity()));
} }
ContentValues values = new ContentValues(1); ContentValues values = new ContentValues(1);

Wyświetl plik

@ -29,7 +29,7 @@ import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
/** /**
* A simple model loader for fetching media over http/https using OkHttp. * A loader which will load a sprite sheet for a particular badge at the correct dpi for this device.
*/ */
public class BadgeLoader implements ModelLoader<Badge, InputStream> { public class BadgeLoader implements ModelLoader<Badge, InputStream> {
@ -40,12 +40,12 @@ public class BadgeLoader implements ModelLoader<Badge, InputStream> {
} }
@Override @Override
public @Nullable LoadData<InputStream> buildLoadData(@NonNull Badge badge, int width, int height, @NonNull Options options) { public @Nullable LoadData<InputStream> buildLoadData(@NonNull Badge request, int width, int height, @NonNull Options options) {
return new LoadData<>(badge, new OkHttpStreamFetcher(client, new GlideUrl(badge.getImageUrl().toString()))); return new LoadData<>(request, new OkHttpStreamFetcher(client, new GlideUrl(request.getImageUrl().toString())));
} }
@Override @Override
public boolean handles(@NonNull Badge badge) { public boolean handles(@NonNull Badge badgeSpriteSheetRequest) {
return true; return true;
} }

Wyświetl plik

@ -9,6 +9,7 @@ import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential; import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.badges.models.Badge; import org.thoughtcrime.securesms.badges.models.Badge;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -21,8 +22,10 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.profiles.ProfileName; import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ProfileUtil; import org.thoughtcrime.securesms.util.ProfileUtil;
import org.thoughtcrime.securesms.util.ScreenDensity;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException;
import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
@ -177,12 +180,14 @@ public class RefreshOwnProfileJob extends BaseJob {
} }
private static Badge adaptFromServiceBadge(@NonNull SignalServiceProfile.Badge serviceBadge) { private static Badge adaptFromServiceBadge(@NonNull SignalServiceProfile.Badge serviceBadge) {
Pair<Uri, String> uriAndDensity = RetrieveProfileJob.getBestBadgeImageUriForDevice(serviceBadge);
return new Badge( return new Badge(
serviceBadge.getId(), serviceBadge.getId(),
Badge.Category.Companion.fromCode(serviceBadge.getCategory()), Badge.Category.Companion.fromCode(serviceBadge.getCategory()),
Uri.parse(serviceBadge.getImageUrl()),
serviceBadge.getName(), serviceBadge.getName(),
serviceBadge.getDescription(), serviceBadge.getDescription(),
uriAndDensity.first(),
uriAndDensity.second(),
getTimestamp(serviceBadge.getExpiration()), getTimestamp(serviceBadge.getExpiration()),
serviceBadge.isVisible() serviceBadge.isVisible()
); );

Wyświetl plik

@ -16,6 +16,7 @@ import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential; import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.badges.models.Badge; import org.thoughtcrime.securesms.badges.models.Badge;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -34,9 +35,9 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.ProfileUtil; import org.thoughtcrime.securesms.util.ProfileUtil;
import org.thoughtcrime.securesms.util.ScreenDensity;
import org.thoughtcrime.securesms.util.SetUtil; import org.thoughtcrime.securesms.util.SetUtil;
import org.thoughtcrime.securesms.util.Stopwatch; import org.thoughtcrime.securesms.util.Stopwatch;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -357,17 +358,45 @@ public class RetrieveProfileJob extends BaseJob {
} }
private static Badge adaptFromServiceBadge(@NonNull SignalServiceProfile.Badge serviceBadge) { private static Badge adaptFromServiceBadge(@NonNull SignalServiceProfile.Badge serviceBadge) {
Pair<Uri, String> uriAndDensity = RetrieveProfileJob.getBestBadgeImageUriForDevice(serviceBadge);
return new Badge( return new Badge(
serviceBadge.getId(), serviceBadge.getId(),
Badge.Category.Companion.fromCode(serviceBadge.getCategory()), Badge.Category.Companion.fromCode(serviceBadge.getCategory()),
Uri.parse(serviceBadge.getImageUrl()),
serviceBadge.getName(), serviceBadge.getName(),
serviceBadge.getDescription(), serviceBadge.getDescription(),
uriAndDensity.first(),
uriAndDensity.second(),
0L, 0L,
true true
); );
} }
public static @NonNull Pair<Uri, String> getBestBadgeImageUriForDevice(@NonNull SignalServiceProfile.Badge serviceBadge) {
String bestDensity = ScreenDensity.getBestDensityBucketForDevice();
switch (bestDensity) {
case "ldpi":
return new Pair<>(getBadgeImageUri(serviceBadge.getLdpiUri()), "ldpi");
case "mdpi":
return new Pair<>(getBadgeImageUri(serviceBadge.getMdpiUri()), "mdpi");
case "hdpi":
return new Pair<>(getBadgeImageUri(serviceBadge.getHdpiUri()), "hdpi");
case "xxhdpi":
return new Pair<>(getBadgeImageUri(serviceBadge.getXxhdpiUri()), "xxhdpi");
case "xxxhdpi":
return new Pair<>(getBadgeImageUri(serviceBadge.getXxxhdpiUri()), "xxxhdpi");
default:
return new Pair<>(getBadgeImageUri(serviceBadge.getXhdpiUri()), "xdpi");
}
}
private static @NonNull Uri getBadgeImageUri(@NonNull String densityPath) {
return Uri.parse(BuildConfig.BADGE_STATIC_ROOT).buildUpon()
.appendPath(densityPath)
.build();
}
private void setProfileKeyCredential(@NonNull Recipient recipient, private void setProfileKeyCredential(@NonNull Recipient recipient,
@NonNull ProfileKey recipientProfileKey, @NonNull ProfileKey recipientProfileKey,
@NonNull ProfileKeyCredential credential) @NonNull ProfileKeyCredential credential)

Wyświetl plik

@ -5,6 +5,8 @@ import android.util.DisplayMetrics;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -51,6 +53,16 @@ public final class ScreenDensity {
return new ScreenDensity(bucket, density); return new ScreenDensity(bucket, density);
} }
public static @NonNull String getBestDensityBucketForDevice() {
ScreenDensity density = get(ApplicationDependencies.getApplication());
if (density.isKnownDensity()) {
return density.bucket;
} else {
return "xhdpi";
}
}
public String getBucket() { public String getBucket() {
return bucket; return bucket;
} }

Wyświetl plik

@ -25,13 +25,14 @@ message ReactionList {
message BadgeList { message BadgeList {
message Badge { message Badge {
string id = 1; string id = 1;
string category = 2; string category = 2;
string name = 3; string name = 3;
string description = 4; string description = 4;
string imageUrl = 5; string imageUrl = 5;
uint64 expiration = 6; uint64 expiration = 6;
bool visible = 7; bool visible = 7;
string imageDensity = 8;
} }
repeated Badge badges = 1; repeated Badge badges = 1;

Wyświetl plik

@ -21,14 +21,13 @@
<org.thoughtcrime.securesms.badges.BadgeImageView <org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/badge" android:id="@+id/badge"
android:layout_width="26dp" android:layout_width="24dp"
android:layout_height="26dp" android:layout_height="24dp"
android:layout_marginStart="39dp" android:layout_marginStart="40dp"
android:layout_marginTop="39dp" android:layout_marginTop="40dp"
android:contentDescription="@string/ImageView__badge" android:contentDescription="@string/ImageView__badge"
android:visibility="gone" android:visibility="gone"
app:badge_outline_color="@color/signal_background_primary" app:badge_size="medium"
app:badge_outline_width="1dp"
app:layout_constraintStart_toStartOf="@id/icon" app:layout_constraintStart_toStartOf="@id/icon"
app:layout_constraintTop_toTopOf="@id/icon" app:layout_constraintTop_toTopOf="@id/icon"
tools:visibility="visible" /> tools:visibility="visible" />

Wyświetl plik

@ -25,14 +25,13 @@
<org.thoughtcrime.securesms.badges.BadgeImageView <org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/contact_badge" android:id="@+id/contact_badge"
android:layout_width="@dimen/badge_size_small" android:layout_width="16dp"
android:layout_height="@dimen/badge_size_small" android:layout_height="16dp"
android:layout_marginStart="23dp" android:layout_marginStart="24dp"
android:layout_marginTop="23dp" android:layout_marginTop="24dp"
android:contentDescription="@string/ImageView__badge" android:contentDescription="@string/ImageView__badge"
android:visibility="gone" android:visibility="gone"
app:badge_outline_color="@color/signal_background_primary" app:badge_size="small"
app:badge_outline_width="1dp"
app:layout_constraintStart_toStartOf="@id/contact_photo_image" app:layout_constraintStart_toStartOf="@id/contact_photo_image"
app:layout_constraintTop_toTopOf="@id/contact_photo_image" app:layout_constraintTop_toTopOf="@id/contact_photo_image"
tools:visibility="visible" /> tools:visibility="visible" />

Wyświetl plik

@ -18,14 +18,13 @@
<org.thoughtcrime.securesms.badges.BadgeImageView <org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/message_request_badge" android:id="@+id/message_request_badge"
android:layout_width="34dp" android:layout_width="32dp"
android:layout_height="34dp" android:layout_height="32dp"
android:layout_marginStart="46dp" android:layout_marginStart="47dp"
android:layout_marginTop="47dp" android:layout_marginTop="48dp"
android:contentDescription="@string/ImageView__badge" android:contentDescription="@string/ImageView__badge"
android:visibility="gone" android:visibility="gone"
app:badge_outline_color="@color/signal_background_primary" app:badge_size="large"
app:badge_outline_width="1dp"
app:layout_constraintStart_toStartOf="@id/message_request_avatar" app:layout_constraintStart_toStartOf="@id/message_request_avatar"
app:layout_constraintTop_toTopOf="@id/message_request_avatar" app:layout_constraintTop_toTopOf="@id/message_request_avatar"
tools:visibility="visible" /> tools:visibility="visible" />

Wyświetl plik

@ -191,14 +191,13 @@
<org.thoughtcrime.securesms.badges.BadgeImageView <org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/conversation_list_item_badge" android:id="@+id/conversation_list_item_badge"
android:layout_width="26dp" android:layout_width="24dp"
android:layout_height="26dp" android:layout_height="24dp"
android:layout_marginStart="@dimen/conversation_list_badge_offset" android:layout_marginStart="26dp"
android:layout_marginTop="@dimen/conversation_list_badge_offset" android:layout_marginTop="26dp"
android:contentDescription="@string/ImageView__badge" android:contentDescription="@string/ImageView__badge"
android:visibility="gone" android:visibility="gone"
app:badge_outline_color="@color/signal_background_primary" app:badge_size="medium"
app:badge_outline_width="1dp"
app:layout_constraintStart_toStartOf="@id/conversation_list_item_avatar" app:layout_constraintStart_toStartOf="@id/conversation_list_item_avatar"
app:layout_constraintTop_toTopOf="@id/conversation_list_item_avatar" app:layout_constraintTop_toTopOf="@id/conversation_list_item_avatar"
tools:visibility="visible" /> tools:visibility="visible" />

Wyświetl plik

@ -23,14 +23,13 @@
<org.thoughtcrime.securesms.badges.BadgeImageView <org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/bio_preference_badge" android:id="@+id/bio_preference_badge"
android:layout_width="34dp" android:layout_width="32dp"
android:layout_height="34dp" android:layout_height="32dp"
android:layout_marginStart="46dp" android:layout_marginStart="47dp"
android:layout_marginTop="47dp" android:layout_marginTop="48dp"
android:contentDescription="@string/ImageView__badge" android:contentDescription="@string/ImageView__badge"
android:visibility="gone" android:visibility="gone"
app:badge_outline_color="@color/signal_background_primary" app:badge_size="large"
app:badge_outline_width="1dp"
app:layout_constraintStart_toStartOf="@id/bio_preference_avatar" app:layout_constraintStart_toStartOf="@id/bio_preference_avatar"
app:layout_constraintTop_toTopOf="@id/bio_preference_avatar" app:layout_constraintTop_toTopOf="@id/bio_preference_avatar"
tools:visibility="visible" /> tools:visibility="visible" />

Wyświetl plik

@ -31,16 +31,15 @@
<org.thoughtcrime.securesms.badges.BadgeImageView <org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/badge" android:id="@+id/badge"
android:layout_width="@dimen/badge_size_small" android:layout_width="16dp"
android:layout_height="@dimen/badge_size_small" android:layout_height="16dp"
android:layout_alignStart="@id/contact_photo_image" android:layout_alignStart="@id/contact_photo_image"
android:layout_alignTop="@id/contact_photo_image" android:layout_alignTop="@id/contact_photo_image"
android:layout_marginStart="21dp" android:layout_marginStart="22dp"
android:layout_marginTop="21dp" android:layout_marginTop="22dp"
android:contentDescription="@string/ImageView__badge" android:contentDescription="@string/ImageView__badge"
android:visibility="gone" android:visibility="gone"
app:badge_outline_color="@color/signal_background_primary" app:badge_size="small"
app:badge_outline_width="1dp"
tools:visibility="visible" /> tools:visibility="visible" />
</RelativeLayout> </RelativeLayout>

Wyświetl plik

@ -22,11 +22,12 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<ImageView <org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/badge" android:id="@+id/badge"
android:layout_width="33dp" android:layout_width="32dp"
android:layout_height="33dp" android:layout_height="32dp"
android:contentDescription="@string/BadgesOverviewFragment__featured_badge" android:contentDescription="@string/BadgesOverviewFragment__featured_badge"
app:badge_size="large"
app:layout_constraintBottom_toBottomOf="@id/avatar" app:layout_constraintBottom_toBottomOf="@id/avatar"
app:layout_constraintEnd_toEndOf="@id/avatar" /> app:layout_constraintEnd_toEndOf="@id/avatar" />

Wyświetl plik

@ -23,14 +23,13 @@
<org.thoughtcrime.securesms.badges.BadgeImageView <org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/rbs_badge" android:id="@+id/rbs_badge"
android:layout_width="34dp" android:layout_width="32dp"
android:layout_height="34dp" android:layout_height="32dp"
android:layout_marginStart="46dp" android:layout_marginStart="47dp"
android:layout_marginTop="47dp" android:layout_marginTop="48dp"
android:contentDescription="@string/ImageView__badge" android:contentDescription="@string/ImageView__badge"
android:visibility="gone" android:visibility="gone"
app:badge_outline_color="@color/signal_background_primary" app:badge_size="large"
app:badge_outline_width="1dp"
app:layout_constraintStart_toStartOf="@id/rbs_recipient_avatar" app:layout_constraintStart_toStartOf="@id/rbs_recipient_avatar"
app:layout_constraintTop_toTopOf="@id/rbs_recipient_avatar" app:layout_constraintTop_toTopOf="@id/rbs_recipient_avatar"
tools:visibility="visible" /> tools:visibility="visible" />

Wyświetl plik

@ -3,14 +3,16 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"> android:orientation="vertical">
<ImageView <org.thoughtcrime.securesms.badges.BadgeImageView
android:id="@+id/badge" android:id="@+id/badge"
android:layout_width="200dp" android:layout_width="200dp"
android:layout_height="200dp" android:layout_height="200dp"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:contentDescription="@string/BadgesOverviewFragment__featured_badge" android:contentDescription="@string/BadgesOverviewFragment__featured_badge"
app:badge_size="xlarge"
tools:src="@drawable/test_gradient" /> tools:src="@drawable/test_gradient" />
<TextView <TextView

Wyświetl plik

@ -33,4 +33,6 @@
<dimen name="toolbar_avatar_margin">34dp</dimen> <dimen name="toolbar_avatar_margin">34dp</dimen>
<dimen name="verify_identity_vertical_margin">32dp</dimen> <dimen name="verify_identity_vertical_margin">32dp</dimen>
<integer name="badge_columns">4</integer>
</resources> </resources>

Wyświetl plik

@ -2,4 +2,6 @@
<resources> <resources>
<dimen name="media_bubble_max_width">350dp</dimen> <dimen name="media_bubble_max_width">350dp</dimen>
<dimen name="media_bubble_max_height">300dp</dimen> <dimen name="media_bubble_max_height">300dp</dimen>
<integer name="badge_columns">5</integer>
</resources> </resources>

Wyświetl plik

@ -328,7 +328,11 @@
</declare-styleable> </declare-styleable>
<declare-styleable name="BadgeImageView"> <declare-styleable name="BadgeImageView">
<attr name="badge_outline_width" format="dimension" /> <attr name="badge_size" format="enum">
<attr name="badge_outline_color" format="color" /> <enum name="small" value="0" />
<enum name="medium" value="1" />
<enum name="large" value="2" />
<enum name="xlarge" value="3" />
</attr>
</declare-styleable> </declare-styleable>
</resources> </resources>

Wyświetl plik

@ -205,9 +205,8 @@
<dimen name="toolbar_avatar_size">28dp</dimen> <dimen name="toolbar_avatar_size">28dp</dimen>
<dimen name="toolbar_avatar_margin">26dp</dimen> <dimen name="toolbar_avatar_margin">26dp</dimen>
<dimen name="conversation_list_avatar_size">48dp</dimen> <dimen name="conversation_list_avatar_size">48dp</dimen>
<dimen name="conversation_list_badge_offset">25dp</dimen>
<dimen name="verify_identity_vertical_margin">16dp</dimen> <dimen name="verify_identity_vertical_margin">16dp</dimen>
<dimen name="badge_size_small">18dp</dimen> <integer name="badge_columns">3</integer>
</resources> </resources>

Wyświetl plik

@ -0,0 +1,134 @@
package org.thoughtcrime.securesms.badges.glide
import android.app.Application
import android.graphics.Rect
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@Suppress("ClassName")
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE, application = Application::class)
class BadgeSpriteTransformationTest__mdpi {
@Test
fun `Given request for large mdpi in light theme, when I getInBounds, then I expect 18x18@1,1`() {
// GIVEN
val density = "mdpi"
val size = BadgeSpriteTransformation.Size.LARGE
val isDarkTheme = false
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 1, 1, 18, 18)
}
@Test
fun `Given request for large mdpi in dark theme, when I getInBounds, then I expect 18x18@21,1`() {
// GIVEN
val density = "mdpi"
val size = BadgeSpriteTransformation.Size.LARGE
val isDarkTheme = true
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 21, 1, 18, 18)
}
@Test
fun `Given request for medium mdpi in light theme, when I getInBounds, then I expect 12x12@41,1`() {
// GIVEN
val density = "mdpi"
val size = BadgeSpriteTransformation.Size.MEDIUM
val isDarkTheme = false
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 41, 1, 12, 12)
}
@Test
fun `Given request for medium mdpi in dark theme, when I getInBounds, then I expect 12x12@55,1`() {
// GIVEN
val density = "mdpi"
val size = BadgeSpriteTransformation.Size.MEDIUM
val isDarkTheme = true
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 55, 1, 12, 12)
}
@Test
fun `Given request for small mdpi in light theme, when I getInBounds, then I expect 8x8@69,1`() {
// GIVEN
val density = "mdpi"
val size = BadgeSpriteTransformation.Size.SMALL
val isDarkTheme = false
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 69, 1, 8, 8)
}
@Test
fun `Given request for small mdpi in dark theme, when I getInBounds, then I expect 8x8@79,1`() {
// GIVEN
val density = "mdpi"
val size = BadgeSpriteTransformation.Size.SMALL
val isDarkTheme = true
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 79, 1, 8, 8)
}
@Test
fun `Given request for xlarge mdpi in light theme, when I getInBounds, then I expect 80x80@89,1`() {
// GIVEN
val density = "mdpi"
val size = BadgeSpriteTransformation.Size.XLARGE
val isDarkTheme = false
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 89, 1, 80, 80)
}
@Test
fun `Given request for xlarge mdpi in dark theme, when I getInBounds, then I expect 80x80@89,1`() {
// GIVEN
val density = "mdpi"
val size = BadgeSpriteTransformation.Size.XLARGE
val isDarkTheme = true
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 89, 1, 80, 80)
}
private fun assertRectMatches(rect: Rect, x: Int, y: Int, width: Int, height: Int) {
assertEquals("Rect has wrong x value", x, rect.left)
assertEquals("Rect has wrong y value", rect.top, y)
assertEquals("Rect has wrong width", width, rect.width())
assertEquals("Rect has wrong height", height, rect.height())
}
}

Wyświetl plik

@ -0,0 +1,134 @@
package org.thoughtcrime.securesms.badges.glide
import android.app.Application
import android.graphics.Rect
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@Suppress("ClassName")
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE, application = Application::class)
class BadgeSpriteTransformationTest__xxxhdpi {
@Test
fun `Given request for large xxxhdpi in light theme, when I getInBounds, then I expect 72x72@1,1`() {
// GIVEN
val density = "xxxhdpi"
val size = BadgeSpriteTransformation.Size.LARGE
val isDarkTheme = false
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 1, 1, 72, 72)
}
@Test
fun `Given request for large xxxhdpi in dark theme, when I getInBounds, then I expect 72x72@21,1`() {
// GIVEN
val density = "xxxhdpi"
val size = BadgeSpriteTransformation.Size.LARGE
val isDarkTheme = true
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 75, 1, 72, 72)
}
@Test
fun `Given request for medium xxxhdpi in light theme, when I getInBounds, then I expect 48x48@149,1`() {
// GIVEN
val density = "xxxhdpi"
val size = BadgeSpriteTransformation.Size.MEDIUM
val isDarkTheme = false
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 149, 1, 48, 48)
}
@Test
fun `Given request for medium xxxhdpi in dark theme, when I getInBounds, then I expect 48x48@199,1`() {
// GIVEN
val density = "xxxhdpi"
val size = BadgeSpriteTransformation.Size.MEDIUM
val isDarkTheme = true
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 199, 1, 48, 48)
}
@Test
fun `Given request for small xxxhdpi in light theme, when I getInBounds, then I expect 32x32@249,1`() {
// GIVEN
val density = "xxxhdpi"
val size = BadgeSpriteTransformation.Size.SMALL
val isDarkTheme = false
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 249, 1, 32, 32)
}
@Test
fun `Given request for small xxxhdpi in dark theme, when I getInBounds, then I expect 32x32@283,1`() {
// GIVEN
val density = "xxxhdpi"
val size = BadgeSpriteTransformation.Size.SMALL
val isDarkTheme = true
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 283, 1, 32, 32)
}
@Test
fun `Given request for xlarge xxxhdpi in light theme, when I getInBounds, then I expect 320x320@317,1`() {
// GIVEN
val density = "xxxhdpi"
val size = BadgeSpriteTransformation.Size.XLARGE
val isDarkTheme = false
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 317, 1, 320, 320)
}
@Test
fun `Given request for xlarge xxxhdpi in dark theme, when I getInBounds, then I expect 320x320@317,1`() {
// GIVEN
val density = "xxxhdpi"
val size = BadgeSpriteTransformation.Size.XLARGE
val isDarkTheme = true
// WHEN
val inBounds = BadgeSpriteTransformation.getInBounds(density, size, isDarkTheme)
// THEN
assertRectMatches(inBounds, 317, 1, 320, 320)
}
private fun assertRectMatches(rect: Rect, x: Int, y: Int, width: Int, height: Int) {
assertEquals("Rect has wrong x value", x, rect.left)
assertEquals("Rect has wrong y value", rect.top, y)
assertEquals("Rect has wrong width", width, rect.width())
assertEquals("Rect has wrong height", height, rect.height())
}
}

Wyświetl plik

@ -128,15 +128,30 @@ public class SignalServiceProfile {
@JsonProperty @JsonProperty
private String category; private String category;
@JsonProperty
private String imageUrl;
@JsonProperty @JsonProperty
private String name; private String name;
@JsonProperty @JsonProperty
private String description; private String description;
@JsonProperty
private String ldpi;
@JsonProperty
private String mdpi;
@JsonProperty
private String hdpi;
@JsonProperty
private String xhdpi;
@JsonProperty
private String xxhdpi;
@JsonProperty
private String xxxhdpi;
@JsonProperty @JsonProperty
private BigDecimal expiration; private BigDecimal expiration;
@ -159,12 +174,32 @@ public class SignalServiceProfile {
return description; return description;
} }
public BigDecimal getExpiration() { public String getLdpiUri() {
return expiration; return ldpi;
} }
public String getImageUrl() { public String getMdpiUri() {
return imageUrl; return mdpi;
}
public String getHdpiUri() {
return hdpi;
}
public String getXhdpiUri() {
return xhdpi;
}
public String getXxhdpiUri() {
return xxhdpi;
}
public String getXxxhdpiUri() {
return xxxhdpi;
}
public BigDecimal getExpiration() {
return expiration;
} }
public boolean isVisible() { public boolean isVisible() {