Add support for updated server badge image url formats.

This commit is contained in:
Alex Hart
2021-09-28 16:55:07 -03:00
committed by Greyson Parrelli
parent 6e00920c95
commit 8d0acb277c
32 changed files with 602 additions and 214 deletions

View File

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

View File

@@ -11,46 +11,23 @@ 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.R
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.badges.models.BadgeAnimator
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.util.customizeOnDraw
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(
@Px outlineWidth: Float,
@ColorInt outlineColor: Int,
@ColorInt gapColor: Int,
animator: BadgeAnimator
): Drawable {
val outline = mutate().constantState?.newDrawable()?.mutate()
outline?.colorFilter = SimpleColorFilter(outlineColor)
val gap = mutate().constantState?.newDrawable()?.mutate()
gap?.colorFilter = SimpleColorFilter(gapColor)
return customizeOnDraw { wrapped, canvas ->
outline?.bounds = wrapped.bounds
gap?.bounds = wrapped.bounds
outline?.draw(canvas)
@@ -58,11 +35,7 @@ object Badges {
val interpolatedScale = scale + (1f - scale) * animator.getFraction()
canvas.withScale(x = interpolatedScale, y = interpolatedScale, wrapped.bounds.width() / 2f, wrapped.bounds.height() / 2f) {
gap?.draw(canvas)
canvas.withScale(x = interpolatedScale, y = interpolatedScale, wrapped.bounds.width() / 2f, wrapped.bounds.height() / 2f) {
wrapped.draw(canvas)
}
wrapped.draw(canvas)
}
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
.map { Badge.Model(it, it == selectedBadge) }
.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) {
customPref(Badge.EmptyModel())
}

View File

@@ -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
)
}
}
}

View File

@@ -2,22 +2,26 @@ package org.thoughtcrime.securesms.badges.models
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Parcel
import android.os.Parcelable
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
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.transition.Transition
import kotlinx.parcelize.Parcelize
import org.signal.core.util.DimensionUnit
import org.thoughtcrime.securesms.R
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.mms.GlideApp
import org.thoughtcrime.securesms.util.MappingAdapter
import org.thoughtcrime.securesms.util.MappingViewHolder
import org.thoughtcrime.securesms.util.ThemeUtil
import java.security.MessageDigest
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.
*/
@Parcelize
data class Badge(
val id: String,
val category: Category,
val imageUrl: Uri,
val name: String,
val description: String,
val imageUrl: Uri,
val imageDensity: String,
val expirationTimestamp: Long,
val visible: Boolean
val visible: Boolean,
) : 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) {
messageDigest.update(id.toByteArray(Key.CHARSET))
messageDigest.update(imageUrl.toString().toByteArray(Key.CHARSET))
messageDigest.update(imageDensity.toByteArray(Key.CHARSET))
}
fun resolveDescription(shortName: String): String {
@@ -130,6 +114,9 @@ data class Badge(
GlideApp.with(badge)
.load(model.badge)
.downsample(DownsampleStrategy.NONE)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.transform(BadgeSpriteTransformation(BadgeSpriteTransformation.Size.XLARGE, model.badge.imageDensity, ThemeUtil.isDarkTheme(context)))
.into(target)
if (model.isSelected) {
@@ -170,7 +157,6 @@ data class Badge(
val drawable = resource.selectable(
DimensionUnit.DP.toPixels(2.5f),
ContextCompat.getColor(view.context, R.color.signal_inverse_primary),
ContextCompat.getColor(view.context, R.color.signal_background_primary),
animator
)
@@ -202,20 +188,34 @@ data class Badge(
}
}
companion object CREATOR : Parcelable.Creator<Badge> {
companion object {
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) {
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))
}
}
@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
}
}
}
}

View File

@@ -1,17 +1,10 @@
package org.thoughtcrime.securesms.badges.models
import android.graphics.drawable.Drawable
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.badges.Badges.insetWithOutline
import org.thoughtcrime.securesms.badges.BadgeImageView
import org.thoughtcrime.securesms.components.AvatarImageView
import org.thoughtcrime.securesms.components.settings.PreferenceModel
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.MappingAdapter
import org.thoughtcrime.securesms.util.MappingViewHolder
@@ -35,40 +28,12 @@ object FeaturedBadgePreview {
class ViewHolder(itemView: View) : MappingViewHolder<Model>(itemView) {
private val avatar: AvatarImageView = itemView.findViewById(R.id.avatar)
private val badge: ImageView = itemView.findViewById(R.id.badge)
private val target: Target = Target(badge)
private val badge: BadgeImageView = itemView.findViewById(R.id.badge)
override fun bind(model: Model) {
avatar.setRecipient(Recipient.self())
avatar.disableQuickContact()
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)
badge.setBadge(model.badge)
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ import okhttp3.ConnectionSpec;
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> {
@@ -40,12 +40,12 @@ public class BadgeLoader implements ModelLoader<Badge, InputStream> {
}
@Override
public @Nullable LoadData<InputStream> buildLoadData(@NonNull Badge badge, int width, int height, @NonNull Options options) {
return new LoadData<>(badge, new OkHttpStreamFetcher(client, new GlideUrl(badge.getImageUrl().toString())));
public @Nullable LoadData<InputStream> buildLoadData(@NonNull Badge request, int width, int height, @NonNull Options options) {
return new LoadData<>(request, new OkHttpStreamFetcher(client, new GlideUrl(request.getImageUrl().toString())));
}
@Override
public boolean handles(@NonNull Badge badge) {
public boolean handles(@NonNull Badge badgeSpriteSheetRequest) {
return true;
}

View File

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

View File

@@ -16,6 +16,7 @@ import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.badges.models.Badge;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
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.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.ProfileUtil;
import org.thoughtcrime.securesms.util.ScreenDensity;
import org.thoughtcrime.securesms.util.SetUtil;
import org.thoughtcrime.securesms.util.Stopwatch;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -357,17 +358,45 @@ public class RetrieveProfileJob extends BaseJob {
}
private static Badge adaptFromServiceBadge(@NonNull SignalServiceProfile.Badge serviceBadge) {
Pair<Uri, String> uriAndDensity = RetrieveProfileJob.getBestBadgeImageUriForDevice(serviceBadge);
return new Badge(
serviceBadge.getId(),
Badge.Category.Companion.fromCode(serviceBadge.getCategory()),
Uri.parse(serviceBadge.getImageUrl()),
serviceBadge.getName(),
serviceBadge.getDescription(),
uriAndDensity.first(),
uriAndDensity.second(),
0L,
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,
@NonNull ProfileKey recipientProfileKey,
@NonNull ProfileKeyCredential credential)

View File

@@ -5,6 +5,8 @@ import android.util.DisplayMetrics;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -51,6 +53,16 @@ public final class ScreenDensity {
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() {
return bucket;
}