diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java index 559e0df351..db0ab2350d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java @@ -23,13 +23,11 @@ import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.color.MaterialColor; -import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.conversation.colors.ChatColors; -import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.groups.ui.managegroup.ManageGroupActivity; import org.thoughtcrime.securesms.mms.GlideApp; @@ -100,9 +98,9 @@ public final class AvatarImageView extends AppCompatImageView { typedArray.recycle(); } - outlinePaint = ThemeUtil.isDarkTheme(getContext()) ? DARK_THEME_OUTLINE_PAINT : LIGHT_THEME_OUTLINE_PAINT; + outlinePaint = ThemeUtil.isDarkTheme(context) ? DARK_THEME_OUTLINE_PAINT : LIGHT_THEME_OUTLINE_PAINT; - unknownRecipientDrawable = new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20).asDrawable(getContext(), ChatColorsPalette.UNKNOWN_CONTACT, inverted); + unknownRecipientDrawable = new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20).asDrawable(context, AvatarColor.UNKNOWN.colorInt(), inverted); blurred = false; chatColors = null; } @@ -213,7 +211,7 @@ public final class AvatarImageView extends AppCompatImageView { requestManager.clear(this); if (fallbackPhotoProvider != null) { setImageDrawable(fallbackPhotoProvider.getPhotoForRecipientWithoutName() - .asDrawable(getContext(), ChatColorsPalette.Bubbles.STEEL, inverted)); + .asDrawable(getContext(), AvatarColor.UNKNOWN.colorInt(), inverted)); } else { setImageDrawable(unknownRecipientDrawable); } @@ -246,11 +244,11 @@ public final class AvatarImageView extends AppCompatImageView { public void setImageBytesForGroup(@Nullable byte[] avatarBytes, @Nullable Recipient.FallbackPhotoProvider fallbackPhotoProvider, - @NonNull ChatColors color) + @NonNull AvatarColor color) { Drawable fallback = Util.firstNonNull(fallbackPhotoProvider, Recipient.DEFAULT_FALLBACK_PHOTO_PROVIDER) .getPhotoForGroup() - .asDrawable(getContext(), color); + .asDrawable(getContext(), color.colorInt()); GlideApp.with(this) .load(avatarBytes) diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackContactPhoto.java index 395b99959f..9a4e265c6f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackContactPhoto.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackContactPhoto.java @@ -3,13 +3,11 @@ package org.thoughtcrime.securesms.contacts.avatars; import android.content.Context; import android.graphics.drawable.Drawable; -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.conversation.colors.ChatColors; - public interface FallbackContactPhoto { - Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors); - Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted); - Drawable asSmallDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted); - Drawable asCallCard(@NonNull Context context); + + public Drawable asDrawable(Context context, int color); + public Drawable asDrawable(Context context, int color, boolean inverted); + public Drawable asSmallDrawable(Context context, int color, boolean inverted); + public Drawable asCallCard(Context context); + } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackPhoto20dp.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackPhoto20dp.java index 2da2846e4c..96c535e030 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackPhoto20dp.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackPhoto20dp.java @@ -7,9 +7,9 @@ import android.graphics.drawable.LayerDrawable; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.appcompat.content.res.AppCompatResources; +import androidx.core.graphics.drawable.DrawableCompat; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.conversation.colors.ChatColors; import org.thoughtcrime.securesms.util.ViewUtil; import java.util.Objects; @@ -26,18 +26,18 @@ public final class FallbackPhoto20dp implements FallbackContactPhoto { } @Override - public Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors) { - return buildDrawable(context, chatColors); + public Drawable asDrawable(Context context, int color) { + return buildDrawable(context, color); } @Override - public Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { - return buildDrawable(context, chatColors); + public Drawable asDrawable(Context context, int color, boolean inverted) { + return buildDrawable(context, color); } @Override - public Drawable asSmallDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { - return buildDrawable(context, chatColors); + public Drawable asSmallDrawable(Context context, int color, boolean inverted) { + return buildDrawable(context, color); } @Override @@ -45,13 +45,15 @@ public final class FallbackPhoto20dp implements FallbackContactPhoto { throw new UnsupportedOperationException(); } - private @NonNull Drawable buildDrawable(@NonNull Context context, @NonNull ChatColors backgroundColor) { - Drawable background = backgroundColor.asCircle(); + private @NonNull Drawable buildDrawable(@NonNull Context context, int color) { + Drawable background = DrawableCompat.wrap(Objects.requireNonNull(AppCompatResources.getDrawable(context, R.drawable.circle_tintable))).mutate(); Drawable foreground = AppCompatResources.getDrawable(context, drawable20dp); Drawable gradient = AppCompatResources.getDrawable(context, R.drawable.avatar_gradient); LayerDrawable drawable = new LayerDrawable(new Drawable[]{background, foreground, gradient}); int foregroundInset = ViewUtil.dpToPx(2); + DrawableCompat.setTint(background, color); + drawable.setLayerInset(1, foregroundInset, foregroundInset, foregroundInset, foregroundInset); return drawable; diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackPhoto80dp.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackPhoto80dp.java index c41bfc447b..e5b0aa7a97 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackPhoto80dp.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/FallbackPhoto80dp.java @@ -10,48 +10,49 @@ import androidx.appcompat.content.res.AppCompatResources; import androidx.core.graphics.drawable.DrawableCompat; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.conversation.colors.ChatColors; import org.thoughtcrime.securesms.util.ViewUtil; import java.util.Objects; public final class FallbackPhoto80dp implements FallbackContactPhoto { - @DrawableRes private final int drawable80dp; - private final ChatColors backgroundColor; + @DrawableRes private final int drawable80dp; + private final int backgroundColor; - public FallbackPhoto80dp(@DrawableRes int drawable80dp, @NonNull ChatColors backgroundColor) { + public FallbackPhoto80dp(@DrawableRes int drawable80dp, int backgroundColor) { this.drawable80dp = drawable80dp; this.backgroundColor = backgroundColor; } @Override - public Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors) { + public Drawable asDrawable(Context context, int color) { return buildDrawable(context); } @Override - public Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { + public Drawable asDrawable(Context context, int color, boolean inverted) { return buildDrawable(context); } @Override - public Drawable asSmallDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { + public Drawable asSmallDrawable(Context context, int color, boolean inverted) { throw new UnsupportedOperationException(); } @Override - public Drawable asCallCard(@NonNull Context context) { + public Drawable asCallCard(Context context) { throw new UnsupportedOperationException(); } private @NonNull Drawable buildDrawable(@NonNull Context context) { - Drawable background = backgroundColor.asCircle(); + Drawable background = DrawableCompat.wrap(Objects.requireNonNull(AppCompatResources.getDrawable(context, R.drawable.circle_tintable))).mutate(); Drawable foreground = AppCompatResources.getDrawable(context, drawable80dp); Drawable gradient = AppCompatResources.getDrawable(context, R.drawable.avatar_gradient); LayerDrawable drawable = new LayerDrawable(new Drawable[]{background, foreground, gradient}); int foregroundInset = ViewUtil.dpToPx(24); + DrawableCompat.setTint(background, backgroundColor); + drawable.setLayerInset(1, foregroundInset, foregroundInset, foregroundInset, foregroundInset); return drawable; diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java index 0d939bd6fb..eae791436f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java @@ -15,7 +15,6 @@ import androidx.appcompat.content.res.AppCompatResources; import com.amulyakhare.textdrawable.TextDrawable; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.conversation.colors.ChatColors; import org.thoughtcrime.securesms.util.ContextUtil; import org.thoughtcrime.securesms.util.ViewUtil; @@ -43,12 +42,12 @@ public class GeneratedContactPhoto implements FallbackContactPhoto { } @Override - public Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors) { - return asDrawable(context, chatColors, false); + public Drawable asDrawable(Context context, int color) { + return asDrawable(context, color,false); } @Override - public Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { + public Drawable asDrawable(Context context, int color, boolean inverted) { int targetSize = this.targetSize != -1 ? this.targetSize : context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size); @@ -56,36 +55,34 @@ public class GeneratedContactPhoto implements FallbackContactPhoto { String character = getAbbreviation(name); if (!TextUtils.isEmpty(character)) { - Drawable background = chatColors.asCircle(); - Drawable base = TextDrawable.builder() .beginConfig() .width(targetSize) .height(targetSize) .useFont(TYPEFACE) .fontSize(fontSize) - .textColor(inverted ? chatColors.asSingleColor() : Color.WHITE) + .textColor(inverted ? color : Color.WHITE) .endConfig() - .buildRound(character, inverted ? Color.WHITE : Color.TRANSPARENT); + .buildRound(character, inverted ? Color.WHITE : color); Drawable gradient = ContextUtil.requireDrawable(context, R.drawable.avatar_gradient); - return new LayerDrawable(new Drawable[] { background, base, gradient }); + return new LayerDrawable(new Drawable[] { base, gradient }); } - return newFallbackDrawable(context, chatColors, inverted); + return newFallbackDrawable(context, color, inverted); } @Override - public Drawable asSmallDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { - return asDrawable(context, chatColors, inverted); + public Drawable asSmallDrawable(Context context, int color, boolean inverted) { + return asDrawable(context, color, inverted); } protected @DrawableRes int getFallbackResId() { return fallbackResId; } - protected Drawable newFallbackDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { - return new ResourceContactPhoto(fallbackResId).asDrawable(context, chatColors, inverted); + protected Drawable newFallbackDrawable(@NonNull Context context, int color, boolean inverted) { + return new ResourceContactPhoto(fallbackResId).asDrawable(context, color, inverted); } private @Nullable String getAbbreviation(String name) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ResourceContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ResourceContactPhoto.java index 7267b977d8..272310d5ee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ResourceContactPhoto.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/ResourceContactPhoto.java @@ -16,7 +16,6 @@ import com.amulyakhare.textdrawable.TextDrawable; import com.makeramen.roundedimageview.RoundedDrawable; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.conversation.colors.ChatColors; import org.thoughtcrime.securesms.util.ContextUtil; public class ResourceContactPhoto implements FallbackContactPhoto { @@ -46,29 +45,29 @@ public class ResourceContactPhoto implements FallbackContactPhoto { } @Override - public @NonNull Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors) { - return asDrawable(context, chatColors, false); + public @NonNull Drawable asDrawable(@NonNull Context context, int color) { + return asDrawable(context, color, false); } @Override - public @NonNull Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { - return buildDrawable(context, resourceId, chatColors, inverted); + public @NonNull Drawable asDrawable(@NonNull Context context, int color, boolean inverted) { + return buildDrawable(context, resourceId, color, inverted); } @Override - public @NonNull Drawable asSmallDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { - return buildDrawable(context, smallResourceId, chatColors, inverted); + public @NonNull Drawable asSmallDrawable(@NonNull Context context, int color, boolean inverted) { + return buildDrawable(context, smallResourceId, color, inverted); } - private @NonNull Drawable buildDrawable(@NonNull Context context, int resourceId, @NonNull ChatColors chatColors, boolean inverted) { - Drawable background = chatColors.asCircle(); + private @NonNull Drawable buildDrawable(@NonNull Context context, int resourceId, int color, boolean inverted) { + Drawable background = TextDrawable.builder().buildRound(" ", inverted ? Color.WHITE : color); RoundedDrawable foreground = (RoundedDrawable) RoundedDrawable.fromDrawable(AppCompatResources.getDrawable(context, resourceId)); //noinspection ConstantConditions foreground.setScaleType(scaleType); if (inverted) { - foreground.setColorFilter(chatColors.asSingleColor(), PorterDuff.Mode.SRC_ATOP); + foreground.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); } Drawable gradient = ContextUtil.requireDrawable(context, R.drawable.avatar_gradient); diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/TransparentContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/TransparentContactPhoto.java index c63f9b69d7..f38e6f0f1f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/TransparentContactPhoto.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/TransparentContactPhoto.java @@ -3,35 +3,33 @@ package org.thoughtcrime.securesms.contacts.avatars; import android.content.Context; import android.graphics.drawable.Drawable; -import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import com.makeramen.roundedimageview.RoundedDrawable; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.conversation.colors.ChatColors; public class TransparentContactPhoto implements FallbackContactPhoto { public TransparentContactPhoto() {} @Override - public Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors) { - return asDrawable(context, chatColors, false); + public Drawable asDrawable(Context context, int color) { + return asDrawable(context, color, false); } @Override - public Drawable asDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { + public Drawable asDrawable(Context context, int color, boolean inverted) { return RoundedDrawable.fromDrawable(context.getResources().getDrawable(android.R.color.transparent)); } @Override - public Drawable asSmallDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { - return asDrawable(context, chatColors, inverted); + public Drawable asSmallDrawable(Context context, int color, boolean inverted) { + return asDrawable(context, color, inverted); } @Override - public Drawable asCallCard(@NonNull Context context) { + public Drawable asCallCard(Context context) { return ContextCompat.getDrawable(context, R.drawable.ic_contact_picture_large); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 8659e0221c..a329ff2be3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -1252,7 +1252,7 @@ public class ConversationActivity extends PassphraseRequiredActivity GlideApp.with(this) .asBitmap() .load(recipient.getContactPhoto()) - .error(recipient.getFallbackContactPhoto().asDrawable(this, recipient.getChatColors(), false)) + .error(recipient.getFallbackContactPhoto().asDrawable(this, recipient.getAvatarColor().colorInt(), false)) .into(new CustomTarget() { @Override public void onLoadFailed(@Nullable Drawable errorDrawable) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/AvatarColor.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/AvatarColor.java new file mode 100644 index 0000000000..ca1231eab8 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/AvatarColor.java @@ -0,0 +1,153 @@ +package org.thoughtcrime.securesms.conversation.colors; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * A serializable set of color constants that can be used for avatars. + */ +public enum AvatarColor { + C000("C000", 0xFFD00B0B), + C010("C010", 0xFFC72A0A), + C020("C020", 0xFFB34209), + C030("C030", 0xFF9C5711), + C040("C040", 0xFF866118), + C050("C050", 0xFF76681E), + C060("C060", 0xFF6C6C13), + C070("C070", 0xFF5E6E0C), + C080("C080", 0xFF507406), + C090("C090", 0xFF3D7406), + C100("C100", 0xFF2D7906), + C110("C110", 0xFF1A7906), + C120("C120", 0xFF067906), + C130("C130", 0xFF067919), + C140("C140", 0xFF06792D), + C150("C150", 0xFF067940), + C160("C160", 0xFF067953), + C170("C170", 0xFF067462), + C180("C180", 0xFF067474), + C190("C190", 0xFF077288), + C200("C200", 0xFF086DA0), + C210("C210", 0xFF0A69C7), + C220("C220", 0xFF0D59F2), + C230("C230", 0xFF3454F4), + C240("C240", 0xFF5151F6), + C250("C250", 0xFF6447F5), + C260("C260", 0xFF7A3DF5), + C270("C270", 0xFF8F2AF4), + C280("C280", 0xFFA20CED), + C290("C290", 0xFFAF0BD0), + C300("C300", 0xFFB80AB8), + C310("C310", 0xFFC20AA3), + C320("C320", 0xFFC70A88), + C330("C330", 0xFFCB0B6B), + C340("C340", 0xFFD00B4D), + C350("C350", 0xFFD00B2C), + CRIMSON("crimson", ChatColorsPalette.Bubbles.CRIMSON.asSingleColor()), + VERMILLION("vermillion", ChatColorsPalette.Bubbles.VERMILION.asSingleColor()), + BURLAP("burlap", ChatColorsPalette.Bubbles.BURLAP.asSingleColor()), + FOREST("forest", ChatColorsPalette.Bubbles.FOREST.asSingleColor()), + WINTERGREEN("wintergreen", ChatColorsPalette.Bubbles.WINTERGREEN.asSingleColor()), + TEAL("teal", ChatColorsPalette.Bubbles.TEAL.asSingleColor()), + BLUE("blue", ChatColorsPalette.Bubbles.BLUE.asSingleColor()), + INDIGO("indigo", ChatColorsPalette.Bubbles.INDIGO.asSingleColor()), + VIOLET("violet", ChatColorsPalette.Bubbles.VIOLET.asSingleColor()), + PLUM("plum", ChatColorsPalette.Bubbles.PLUM.asSingleColor()), + TAUPE("taupe", ChatColorsPalette.Bubbles.TAUPE.asSingleColor()), + STEEL("steel", ChatColorsPalette.Bubbles.STEEL.asSingleColor()), + ULTRAMARINE("ultramarine", ChatColorsPalette.Bubbles.ULTRAMARINE.asSingleColor()), + UNKNOWN("unknown", ChatColorsPalette.Bubbles.STEEL.asSingleColor()); + + /** Fast map of name to enum, while also giving us a location to map old colors to new ones. */ + private static final Map NAME_MAP = new HashMap<>(); + static { + for (AvatarColor color : AvatarColor.values()) { + NAME_MAP.put(color.serialize(), color); + } + + NAME_MAP.put("red", CRIMSON); + NAME_MAP.put("orange", VERMILLION); + NAME_MAP.put("deep_orange", VERMILLION); + NAME_MAP.put("brown", BURLAP); + NAME_MAP.put("green", FOREST); + NAME_MAP.put("light_green", WINTERGREEN); + NAME_MAP.put("teal", TEAL); + NAME_MAP.put("blue", BLUE); + NAME_MAP.put("indigo", INDIGO); + NAME_MAP.put("purple", VIOLET); + NAME_MAP.put("deep_purple", VIOLET); + NAME_MAP.put("pink", PLUM); + NAME_MAP.put("blue_grey", TAUPE); + NAME_MAP.put("grey", STEEL); + NAME_MAP.put("ultramarine", ULTRAMARINE); + } + + /** Colors that can be assigned via {@link #random()}. */ + private static final AvatarColor[] RANDOM_OPTIONS = new AvatarColor[] { + C000, + C010, + C020, + C030, + C040, + C050, + C060, + C070, + C080, + C090, + C100, + C110, + C120, + C130, + C140, + C150, + C160, + C170, + C180, + C190, + C200, + C210, + C220, + C230, + C240, + C250, + C260, + C270, + C280, + C290, + C300, + C310, + C320, + C330, + C340, + C350, + }; + + private final String name; + private final int color; + + AvatarColor(@NonNull String name, @ColorInt int color) { + this.name = name; + this.color = color; + } + + public @ColorInt int colorInt() { + return color; + } + + public static @NonNull AvatarColor random() { + int position = (int) Math.floor(Math.random() * RANDOM_OPTIONS.length); + return RANDOM_OPTIONS[position]; + } + + public @NonNull String serialize() { + return name; + } + + public static @NonNull AvatarColor deserialize(@NonNull String name) { + return Objects.requireNonNull(NAME_MAP.getOrDefault(name, C000)); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColors.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColors.kt index 91fed6252f..8cd6ac7d1e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColors.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColors.kt @@ -8,8 +8,8 @@ import android.graphics.PorterDuffColorFilter import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable import androidx.annotation.ColorInt -import androidx.core.graphics.ColorUtils import com.google.common.base.Objects +import org.signal.core.util.ColorUtil import org.thoughtcrime.securesms.components.RotatableGradientDrawable import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor import org.thoughtcrime.securesms.util.customizeOnDraw @@ -55,7 +55,7 @@ class ChatColors private constructor( val start = linearGradient.colors.first() val end = linearGradient.colors.last() - return ColorUtils.blendARGB(start, end, 0.5f) + return ColorUtil.blendARGB(start, end, 0.5f) } throw AssertionError() diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColorsPalette.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColorsPalette.kt index c60a86207c..5db0d4eee9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColorsPalette.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/colors/ChatColorsPalette.kt @@ -23,7 +23,7 @@ object ChatColorsPalette { // region Solids @JvmField - val CRIMSON = ChatColors.forColor(ChatColors.Id.BuiltIn, 0xFFCF16E3.toInt()) + val CRIMSON = ChatColors.forColor(ChatColors.Id.BuiltIn, 0xFFCF163E.toInt()) @JvmField val VERMILION = ChatColors.forColor(ChatColors.Id.BuiltIn, 0xFFC73F0A.toInt()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java index b27db631d5..6786842636 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -24,6 +24,7 @@ import org.signal.zkgroup.groups.GroupMasterKey; import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKeyCredential; import org.thoughtcrime.securesms.color.MaterialColor; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.conversation.colors.ChatColors; import org.thoughtcrime.securesms.conversation.colors.ChatColorsMapper; import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; @@ -112,7 +113,7 @@ public class RecipientDatabase extends Database { private static final String CALL_VIBRATE = "call_vibrate"; private static final String NOTIFICATION_CHANNEL = "notification_channel"; private static final String MUTE_UNTIL = "mute_until"; - //private static final String COLOR = "color"; + private static final String AVATAR_COLOR = "color"; private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder"; private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id"; private static final String MESSAGE_EXPIRATION_TIME = "message_expiration_time"; @@ -164,7 +165,7 @@ public class RecipientDatabase extends Database { private static final String[] RECIPIENT_PROJECTION = new String[] { ID, UUID, USERNAME, PHONE, EMAIL, GROUP_ID, GROUP_TYPE, - BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED, + BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, AVATAR_COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED, PROFILE_KEY, PROFILE_KEY_CREDENTIAL, SYSTEM_JOINED_NAME, SYSTEM_GIVEN_NAME, SYSTEM_FAMILY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI, PROFILE_GIVEN_NAME, PROFILE_FAMILY_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, LAST_PROFILE_FETCH, @@ -326,6 +327,7 @@ public class RecipientDatabase extends Database { CALL_VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " + NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL, " + MUTE_UNTIL + " INTEGER DEFAULT 0, " + + AVATAR_COLOR + " TEXT DEFAULT NULL, " + SEEN_INVITE_REMINDER + " INTEGER DEFAULT " + InsightsBannerTier.NO_TIER.getId() + ", " + DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + MESSAGE_EXPIRATION_TIME + " INTEGER DEFAULT 0, " + @@ -559,6 +561,7 @@ public class RecipientDatabase extends Database { values.put(UUID, uuid.toString().toLowerCase()); values.put(REGISTERED, RegisteredState.REGISTERED.getId()); values.put(STORAGE_SERVICE_ID, Base64.encodeBytes(StorageSyncHelper.generateKey())); + values.put(AVATAR_COLOR, AvatarColor.random().serialize()); } return values; @@ -589,6 +592,7 @@ public class RecipientDatabase extends Database { } else { ContentValues values = new ContentValues(); values.put(GROUP_ID, groupId.toString()); + values.put(AVATAR_COLOR, AvatarColor.random().serialize()); long id = databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values); @@ -866,7 +870,7 @@ public class RecipientDatabase extends Database { public void applyStorageSyncGroupV1Insert(@NonNull SignalGroupV1Record insert) { SQLiteDatabase db = databaseHelper.getWritableDatabase(); - long id = db.insertOrThrow(TABLE_NAME, null, getValuesForStorageGroupV1(insert)); + long id = db.insertOrThrow(TABLE_NAME, null, getValuesForStorageGroupV1(insert, true)); RecipientId recipientId = RecipientId.from(id); DatabaseFactory.getThreadDatabase(context).applyStorageSyncUpdate(recipientId, insert); @@ -877,7 +881,7 @@ public class RecipientDatabase extends Database { public void applyStorageSyncGroupV1Update(@NonNull StorageRecordUpdate update) { SQLiteDatabase db = databaseHelper.getWritableDatabase(); - ContentValues values = getValuesForStorageGroupV1(update.getNew()); + ContentValues values = getValuesForStorageGroupV1(update.getNew(), false); int updateCount = db.update(TABLE_NAME, values, STORAGE_SERVICE_ID + " = ?", new String[]{Base64.encodeBytes(update.getOld().getId().getRaw())}); if (updateCount < 1) { @@ -896,7 +900,7 @@ public class RecipientDatabase extends Database { GroupMasterKey masterKey = insert.getMasterKeyOrThrow(); GroupId.V2 groupId = GroupId.v2(masterKey); - ContentValues values = getValuesForStorageGroupV2(insert); + ContentValues values = getValuesForStorageGroupV2(insert, true); long id = db.insertOrThrow(TABLE_NAME, null, values); Recipient recipient = Recipient.externalGroupExact(context, groupId); @@ -919,7 +923,7 @@ public class RecipientDatabase extends Database { public void applyStorageSyncGroupV2Update(@NonNull StorageRecordUpdate update) { SQLiteDatabase db = databaseHelper.getWritableDatabase(); - ContentValues values = getValuesForStorageGroupV2(update.getNew()); + ContentValues values = getValuesForStorageGroupV2(update.getNew(), false); int updateCount = db.update(TABLE_NAME, values, STORAGE_SERVICE_ID + " = ?", new String[]{Base64.encodeBytes(update.getOld().getId().getRaw())}); if (updateCount < 1) { @@ -1037,10 +1041,14 @@ public class RecipientDatabase extends Database { values.putNull(STORAGE_PROTO); } + if (isInsert) { + values.put(AVATAR_COLOR, AvatarColor.random().serialize()); + } + return values; } - private static @NonNull ContentValues getValuesForStorageGroupV1(@NonNull SignalGroupV1Record groupV1) { + private static @NonNull ContentValues getValuesForStorageGroupV1(@NonNull SignalGroupV1Record groupV1, boolean isInsert) { ContentValues values = new ContentValues(); values.put(GROUP_ID, GroupId.v1orThrow(groupV1.getGroupId()).toString()); values.put(GROUP_TYPE, GroupType.SIGNAL_V1.getId()); @@ -1055,10 +1063,14 @@ public class RecipientDatabase extends Database { values.putNull(STORAGE_PROTO); } + if (isInsert) { + values.put(AVATAR_COLOR, AvatarColor.random().serialize()); + } + return values; } - private static @NonNull ContentValues getValuesForStorageGroupV2(@NonNull SignalGroupV2Record groupV2) { + private static @NonNull ContentValues getValuesForStorageGroupV2(@NonNull SignalGroupV2Record groupV2, boolean isInsert) { ContentValues values = new ContentValues(); values.put(GROUP_ID, GroupId.v2(groupV2.getMasterKeyOrThrow()).toString()); values.put(GROUP_TYPE, GroupType.SIGNAL_V2.getId()); @@ -1073,6 +1085,10 @@ public class RecipientDatabase extends Database { values.putNull(STORAGE_PROTO); } + if (isInsert) { + values.put(AVATAR_COLOR, AvatarColor.random().serialize()); + } + return values; } @@ -1198,6 +1214,7 @@ public class RecipientDatabase extends Database { byte[] wallpaper = CursorUtil.requireBlob(cursor, WALLPAPER); byte[] serializedChatColors = CursorUtil.requireBlob(cursor, CHAT_COLORS); long customChatColorsId = CursorUtil.requireLong(cursor, CUSTOM_CHAT_COLORS_ID); + String serializedAvatarColor = CursorUtil.requireString(cursor, AVATAR_COLOR); String about = CursorUtil.requireString(cursor, ABOUT); String aboutEmoji = CursorUtil.requireString(cursor, ABOUT_EMOJI); boolean hasGroupsInCommon = CursorUtil.requireBoolean(cursor, GROUPS_IN_COMMON); @@ -1288,6 +1305,7 @@ public class RecipientDatabase extends Database { MentionSetting.fromId(mentionSettingId), chatWallpaper, chatColors, + AvatarColor.deserialize(serializedAvatarColor), about, aboutEmoji, getSyncExtras(cursor), @@ -2838,6 +2856,7 @@ public class RecipientDatabase extends Database { } else { ContentValues values = new ContentValues(); values.put(column, value); + values.put(AVATAR_COLOR, AvatarColor.random().serialize()); long id = databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values); @@ -2883,6 +2902,7 @@ public class RecipientDatabase extends Database { uuidValues.put(NOTIFICATION_CHANNEL, uuidSettings.getNotificationChannel() != null ? uuidSettings.getNotificationChannel() : e164Settings.getNotificationChannel()); uuidValues.put(MUTE_UNTIL, uuidSettings.getMuteUntil() > 0 ? uuidSettings.getMuteUntil() : e164Settings.getMuteUntil()); uuidValues.put(CHAT_COLORS, Optional.fromNullable(uuidSettings.getChatColors()).or(Optional.fromNullable(e164Settings.getChatColors())).transform(colors -> colors.serialize().toByteArray()).orNull()); + uuidValues.put(AVATAR_COLOR, uuidSettings.getAvatarColor().serialize()); uuidValues.put(CUSTOM_CHAT_COLORS_ID, Optional.fromNullable(uuidSettings.getChatColors()).or(Optional.fromNullable(e164Settings.getChatColors())).transform(colors -> colors.getId().getLongValue()).orNull()); uuidValues.put(SEEN_INVITE_REMINDER, e164Settings.getInsightsBannerTier().getId()); uuidValues.put(DEFAULT_SUBSCRIPTION_ID, e164Settings.getDefaultSubscriptionId().or(-1)); @@ -3122,6 +3142,7 @@ public class RecipientDatabase extends Database { private final MentionSetting mentionSetting; private final ChatWallpaper wallpaper; private final ChatColors chatColors; + private final AvatarColor avatarColor; private final String about; private final String aboutEmoji; private final SyncExtras syncExtras; @@ -3165,6 +3186,7 @@ public class RecipientDatabase extends Database { @NonNull MentionSetting mentionSetting, @Nullable ChatWallpaper wallpaper, @Nullable ChatColors chatColors, + @NonNull AvatarColor avatarColor, @Nullable String about, @Nullable String aboutEmoji, @NonNull SyncExtras syncExtras, @@ -3210,6 +3232,7 @@ public class RecipientDatabase extends Database { this.mentionSetting = mentionSetting; this.wallpaper = wallpaper; this.chatColors = chatColors; + this.avatarColor = avatarColor; this.about = about; this.aboutEmoji = aboutEmoji; this.syncExtras = syncExtras; @@ -3369,6 +3392,10 @@ public class RecipientDatabase extends Database { return chatColors; } + public @NonNull AvatarColor getAvatarColor() { + return avatarColor; + } + public @Nullable String getAbout() { return about; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 987e3ea0c5..59c58ded0c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -24,6 +24,7 @@ import net.sqlcipher.database.SQLiteOpenHelper; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.contacts.avatars.ContactColorsLegacy; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.conversation.colors.ChatColors; import org.thoughtcrime.securesms.conversation.colors.ChatColorsMapper; import org.thoughtcrime.securesms.crypto.DatabaseSecret; @@ -188,8 +189,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab private static final int CLEAR_MMS_STORAGE_IDS = 98; private static final int SERVER_GUID = 99; private static final int CHAT_COLORS = 100; + private static final int AVATAR_COLORS = 101; - private static final int DATABASE_VERSION = 100; + private static final int DATABASE_VERSION = 101; private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -1491,6 +1493,19 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab } } + if (oldVersion < AVATAR_COLORS) { + try (Cursor cursor = db.query("recipient", new String[] { "_id" }, "color IS NULL", null, null, null, null)) { + while (cursor.moveToNext()) { + long id = cursor.getInt(cursor.getColumnIndexOrThrow("_id")); + + ContentValues values = new ContentValues(1); + values.put("color", AvatarColor.random().serialize()); + + db.update("recipient", values, "_id = ?", new String[] { String.valueOf(id) }); + } + } + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java index b8719648aa..4387038877 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java @@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.components.emoji.EmojiTextView; import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; import org.thoughtcrime.securesms.conversation.ConversationIntents; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.groups.ui.managegroup.dialogs.GroupDescriptionDialog; @@ -88,7 +89,7 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF groupCancelButton.setOnClickListener(v -> dismiss()); - avatar.setImageBytesForGroup(null, new FallbackPhotoProvider(), ChatColorsPalette.UNKNOWN_CONTACT); + avatar.setImageBytesForGroup(null, new FallbackPhotoProvider(), AvatarColor.UNKNOWN); return view; } @@ -130,7 +131,7 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF break; } - avatar.setImageBytesForGroup(details.getAvatarBytes(), new FallbackPhotoProvider(), ChatColorsPalette.UNKNOWN_CONTACT); + avatar.setImageBytesForGroup(details.getAvatarBytes(), new FallbackPhotoProvider(), AvatarColor.UNKNOWN); groupCancelButton.setVisibility(View.VISIBLE); }); diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java index b9e39bed82..60624baf13 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java @@ -39,8 +39,7 @@ import org.thoughtcrime.securesms.components.ThreadPhotoRailView; import org.thoughtcrime.securesms.components.emoji.EmojiTextView; import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto80dp; -import org.thoughtcrime.securesms.conversation.colors.ChatColors; -import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason; import org.thoughtcrime.securesms.groups.ui.GroupErrors; @@ -238,7 +237,7 @@ public class ManageGroupFragment extends LoggingFragment { viewModel.getMemberCountSummary().observe(getViewLifecycleOwner(), memberCountUnderAvatar::setText); viewModel.getFullMemberCountSummary().observe(getViewLifecycleOwner(), memberCountAboveList::setText); viewModel.getGroupRecipient().observe(getViewLifecycleOwner(), groupRecipient -> { - avatar.setFallbackPhotoProvider(new FallbackPhotoProvider(groupRecipient.getChatColors())); + avatar.setFallbackPhotoProvider(new FallbackPhotoProvider(groupRecipient.getAvatarColor())); avatar.setRecipient(groupRecipient); avatar.setOnClickListener(v -> { FragmentActivity activity = requireActivity(); @@ -508,15 +507,15 @@ public class ManageGroupFragment extends LoggingFragment { private final class FallbackPhotoProvider extends Recipient.FallbackPhotoProvider { - private final ChatColors groupColors; + private final AvatarColor groupColors; - private FallbackPhotoProvider(@NonNull ChatColors groupColors) { + private FallbackPhotoProvider(@NonNull AvatarColor groupColors) { this.groupColors = groupColors; } @Override public @NonNull FallbackContactPhoto getPhotoForGroup() { - return new FallbackPhoto80dp(R.drawable.ic_group_80, groupColors); + return new FallbackPhoto80dp(R.drawable.ic_group_80, groupColors.colorInt()); } }; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsRepository.java b/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsRepository.java index 84f4db4761..fb5d44ee37 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsRepository.java @@ -65,12 +65,11 @@ public class InsightsRepository implements InsightsDashboardViewModel.Repository @Override public void getUserAvatar(@NonNull Consumer avatarConsumer) { SimpleTask.run(() -> { - Recipient self = Recipient.self().resolve(); - String name = Optional.fromNullable(self.getDisplayName(context)).or(""); - ChatColors fallbackColor = self.getChatColors(); + Recipient self = Recipient.self().resolve(); + String name = Optional.fromNullable(self.getDisplayName(context)).or(""); return new InsightsUserAvatar(new ProfileContactPhoto(self, self.getProfileAvatar()), - fallbackColor, + self.getAvatarColor(), new GeneratedContactPhoto(name, R.drawable.ic_profile_outline_40)); }, avatarConsumer::accept); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsUserAvatar.java b/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsUserAvatar.java index b2acd9355f..5296bd13be 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsUserAvatar.java +++ b/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsUserAvatar.java @@ -10,22 +10,22 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; -import org.thoughtcrime.securesms.conversation.colors.ChatColors; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.mms.GlideApp; class InsightsUserAvatar { private final ProfileContactPhoto profileContactPhoto; - private final ChatColors fallbackColor; + private final AvatarColor fallbackColor; private final FallbackContactPhoto fallbackContactPhoto; - InsightsUserAvatar(@NonNull ProfileContactPhoto profileContactPhoto, @NonNull ChatColors fallbackColor, @NonNull FallbackContactPhoto fallbackContactPhoto) { + InsightsUserAvatar(@NonNull ProfileContactPhoto profileContactPhoto, @NonNull AvatarColor fallbackColor, @NonNull FallbackContactPhoto fallbackContactPhoto) { this.profileContactPhoto = profileContactPhoto; this.fallbackColor = fallbackColor; this.fallbackContactPhoto = fallbackContactPhoto; } private Drawable fallbackDrawable(@NonNull Context context) { - return fallbackContactPhoto.asDrawable(context, fallbackColor); + return fallbackContactPhoto.asDrawable(context, fallbackColor.colorInt()); } void load(ImageView into) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java index b08fdbf983..a659cd97a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -27,12 +27,11 @@ import com.bumptech.glide.load.resource.bitmap.CircleCrop; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto; import org.thoughtcrime.securesms.conversation.ConversationIntents; -import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; @@ -98,7 +97,7 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil } else { setContentTitle(context.getString(R.string.SingleRecipientNotificationBuilder_signal)); - setLargeIcon(new GeneratedContactPhoto("Unknown", R.drawable.ic_profile_outline_40).asDrawable(context, ChatColorsPalette.UNKNOWN_CONTACT)); + setLargeIcon(new GeneratedContactPhoto("Unknown", R.drawable.ic_profile_outline_40).asDrawable(context, AvatarColor.UNKNOWN.colorInt())); } setShortcutId(ConversationUtil.getShortcutId(recipient)); @@ -124,10 +123,10 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height)) .get(); } catch (InterruptedException | ExecutionException e) { - return fallbackContactPhoto.asDrawable(context, recipient.getChatColors()); + return fallbackContactPhoto.asDrawable(context, recipient.getAvatarColor().colorInt()); } } else { - return fallbackContactPhoto.asDrawable(context, recipient.getChatColors()); + return fallbackContactPhoto.asDrawable(context, recipient.getAvatarColor().colorInt()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationConversation.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationConversation.kt index e00b7192de..8f08b96571 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationConversation.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationConversation.kt @@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.contacts.TurnOffContactJoinedNotificationsActivity import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto import org.thoughtcrime.securesms.conversation.ConversationIntents -import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette +import org.thoughtcrime.securesms.conversation.colors.AvatarColor import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.notifications.DeleteNotificationReceiver import org.thoughtcrime.securesms.notifications.MarkReadReceiver @@ -52,7 +52,7 @@ data class NotificationConversation( return if (SignalStore.settings().messageNotificationsPrivacy.isDisplayContact) { recipient.getContactDrawable(context) } else { - GeneratedContactPhoto("Unknown", R.drawable.ic_profile_outline_40).asDrawable(context, ChatColorsPalette.UNKNOWN_CONTACT) + GeneratedContactPhoto("Unknown", R.drawable.ic_profile_outline_40).asDrawable(context, AvatarColor.UNKNOWN.colorInt()) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationExtensions.kt index e8545ea19a..2a6ecd132e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationExtensions.kt @@ -56,12 +56,12 @@ fun Recipient.getContactDrawable(context: Context): Drawable? { ) .get() } catch (e: InterruptedException) { - fallbackContactPhoto.asDrawable(context, chatColors) + fallbackContactPhoto.asDrawable(context, avatarColor.colorInt()) } catch (e: ExecutionException) { - fallbackContactPhoto.asDrawable(context, chatColors) + fallbackContactPhoto.asDrawable(context, avatarColor.colorInt()) } } else { - fallbackContactPhoto.asDrawable(context, chatColors) + fallbackContactPhoto.asDrawable(context, avatarColor.colorInt()) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileFragment.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileFragment.java index b5e8d5afa6..440722009a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileFragment.java @@ -30,7 +30,7 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.LoggingFragment; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; -import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.mediasend.AvatarSelectionActivity; import org.thoughtcrime.securesms.mediasend.AvatarSelectionBottomSheetDialogFragment; @@ -49,7 +49,6 @@ import java.io.IOException; import java.io.InputStream; import static android.app.Activity.RESULT_OK; -import static org.thoughtcrime.securesms.groups.v2.GroupDescriptionUtil.MAX_DESCRIPTION_LENGTH; import static org.thoughtcrime.securesms.profiles.edit.EditProfileActivity.EXCLUDE_SYSTEM; import static org.thoughtcrime.securesms.profiles.edit.EditProfileActivity.GROUP_ID; import static org.thoughtcrime.securesms.profiles.edit.EditProfileActivity.NEXT_BUTTON_TEXT; @@ -112,7 +111,7 @@ public class EditProfileFragment extends LoggingFragment { if (data != null && data.getBooleanExtra("delete", false)) { viewModel.setAvatar(null); - avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_camera_solid_white_24).asDrawable(requireActivity(), ChatColorsPalette.UNKNOWN_CONTACT)); + avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_camera_solid_white_24).asDrawable(requireActivity(), AvatarColor.UNKNOWN.colorInt())); return; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java index c539b5a614..a4c45946d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java @@ -21,7 +21,6 @@ import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto20dp; import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto; -import org.thoughtcrime.securesms.conversation.colors.ChatColors; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.ViewUtil; @@ -123,8 +122,8 @@ public class ReviewBannerView extends LinearLayout { } @Override - protected Drawable newFallbackDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { - return new FallbackPhoto20dp(getFallbackResId()).asDrawable(context, chatColors, inverted); + protected Drawable newFallbackDrawable(@NonNull Context context, int color, boolean inverted) { + return new FallbackPhoto20dp(getFallbackResId()).asDrawable(context, color, inverted); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java index 0f8598e00e..1352aac863 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java @@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.SystemContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.TransparentContactPhoto; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.conversation.colors.ChatColors; import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette; import org.thoughtcrime.securesms.database.DatabaseFactory; @@ -107,6 +108,7 @@ public class Recipient { private final MentionSetting mentionSetting; private final ChatWallpaper wallpaper; private final ChatColors chatColors; + private final AvatarColor avatarColor; private final String about; private final String aboutEmoji; private final ProfileName systemProfileName; @@ -353,6 +355,7 @@ public class Recipient { this.mentionSetting = MentionSetting.ALWAYS_NOTIFY; this.wallpaper = null; this.chatColors = null; + this.avatarColor = AvatarColor.UNKNOWN; this.about = null; this.aboutEmoji = null; this.systemProfileName = ProfileName.EMPTY; @@ -402,6 +405,7 @@ public class Recipient { this.mentionSetting = details.mentionSetting; this.wallpaper = details.wallpaper; this.chatColors = details.chatColors; + this.avatarColor = details.avatarColor; this.about = details.about; this.aboutEmoji = details.aboutEmoji; this.systemProfileName = details.systemProfileName; @@ -756,11 +760,11 @@ public class Recipient { } public @NonNull Drawable getFallbackContactPhotoDrawable(Context context, boolean inverted, @Nullable FallbackPhotoProvider fallbackPhotoProvider) { - return getFallbackContactPhoto(Util.firstNonNull(fallbackPhotoProvider, DEFAULT_FALLBACK_PHOTO_PROVIDER)).asDrawable(context, getChatColors(), inverted); + return getFallbackContactPhoto(Util.firstNonNull(fallbackPhotoProvider, DEFAULT_FALLBACK_PHOTO_PROVIDER)).asDrawable(context, avatarColor.colorInt(), inverted); } public @NonNull Drawable getSmallFallbackContactPhotoDrawable(Context context, boolean inverted, @Nullable FallbackPhotoProvider fallbackPhotoProvider) { - return getFallbackContactPhoto(Util.firstNonNull(fallbackPhotoProvider, DEFAULT_FALLBACK_PHOTO_PROVIDER)).asSmallDrawable(context, getChatColors(), inverted); + return getFallbackContactPhoto(Util.firstNonNull(fallbackPhotoProvider, DEFAULT_FALLBACK_PHOTO_PROVIDER)).asSmallDrawable(context, avatarColor.colorInt(), inverted); } public @NonNull FallbackContactPhoto getFallbackContactPhoto() { @@ -927,6 +931,10 @@ public class Recipient { } } + public @NonNull AvatarColor getAvatarColor() { + return avatarColor; + } + public boolean isSystemContact() { return contactUri != null; } @@ -1114,6 +1122,7 @@ public class Recipient { mentionSetting == other.mentionSetting && Objects.equals(wallpaper, other.wallpaper) && Objects.equals(chatColors, other.chatColors) && + Objects.equals(avatarColor, other.avatarColor) && Objects.equals(about, other.about) && Objects.equals(aboutEmoji, other.aboutEmoji) && Objects.equals(extras, other.extras); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java index f1a910dd71..af6da59740 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java @@ -7,6 +7,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.signal.zkgroup.profiles.ProfileKeyCredential; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.conversation.colors.ChatColors; import org.thoughtcrime.securesms.database.RecipientDatabase.InsightsBannerTier; import org.thoughtcrime.securesms.database.RecipientDatabase.MentionSetting; @@ -67,6 +68,7 @@ public class RecipientDetails { final MentionSetting mentionSetting; final ChatWallpaper wallpaper; final ChatColors chatColors; + final AvatarColor avatarColor; final String about; final String aboutEmoji; final ProfileName systemProfileName; @@ -120,6 +122,7 @@ public class RecipientDetails { this.mentionSetting = settings.getMentionSetting(); this.wallpaper = settings.getWallpaper(); this.chatColors = settings.getChatColors(); + this.avatarColor = settings.getAvatarColor(); this.about = settings.getAbout(); this.aboutEmoji = settings.getAboutEmoji(); this.systemProfileName = settings.getSystemProfileName(); @@ -172,6 +175,7 @@ public class RecipientDetails { this.mentionSetting = MentionSetting.ALWAYS_NOTIFY; this.wallpaper = null; this.chatColors = null; + this.avatarColor = AvatarColor.UNKNOWN; this.about = null; this.aboutEmoji = null; this.systemProfileName = ProfileName.EMPTY; diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java index ab153767ee..78c52ff3b6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java @@ -34,7 +34,6 @@ import org.thoughtcrime.securesms.recipients.RecipientExporter; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.util.BottomSheetUtil; -import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.Util; @@ -140,7 +139,7 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF avatar.setFallbackPhotoProvider(new Recipient.FallbackPhotoProvider() { @Override public @NonNull FallbackContactPhoto getPhotoForLocalNumber() { - return new FallbackPhoto80dp(R.drawable.ic_note_80, recipient.getChatColors()); + return new FallbackPhoto80dp(R.drawable.ic_note_80, recipient.getAvatarColor().colorInt()); } }); avatar.setAvatar(recipient); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java index 583ef4ac58..23941ff0b1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java @@ -32,7 +32,7 @@ import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.ThreadPhotoRailView; import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto80dp; -import org.thoughtcrime.securesms.conversation.colors.ChatColors; +import org.thoughtcrime.securesms.conversation.colors.AvatarColor; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.groups.ui.GroupMemberListView; import org.thoughtcrime.securesms.keyvalue.SignalStore; @@ -309,16 +309,16 @@ public class ManageRecipientFragment extends LoggingFragment { disappearingMessagesCard.setVisibility(recipient.isRegistered() ? View.VISIBLE : View.GONE); addToAGroup.setVisibility(recipient.isRegistered() ? View.VISIBLE : View.GONE); - ChatColors recipientColor = recipient.getChatColors(); + AvatarColor recipientColor = recipient.getAvatarColor(); avatar.setFallbackPhotoProvider(new Recipient.FallbackPhotoProvider() { @Override public @NonNull FallbackContactPhoto getPhotoForRecipientWithoutName() { - return new FallbackPhoto80dp(R.drawable.ic_profile_80, recipientColor); + return new FallbackPhoto80dp(R.drawable.ic_profile_80, recipientColor.colorInt()); } @Override public @NonNull FallbackContactPhoto getPhotoForLocalNumber() { - return new FallbackPhoto80dp(R.drawable.ic_note_80, recipientColor); + return new FallbackPhoto80dp(R.drawable.ic_note_80, recipientColor.colorInt()); } }); avatar.setAvatar(recipient); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/AvatarUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/AvatarUtil.java index 61435b1ddf..fc82c5e452 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/AvatarUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/AvatarUtil.java @@ -4,7 +4,6 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; -import android.text.TextUtils; import android.view.View; import android.widget.ImageView; @@ -21,13 +20,9 @@ import com.bumptech.glide.request.target.CustomViewTarget; import com.bumptech.glide.request.transition.Transition; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.color.MaterialColor; -import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; -import org.thoughtcrime.securesms.conversation.colors.ChatColors; -import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideRequest; import org.thoughtcrime.securesms.recipients.Recipient; @@ -173,9 +168,8 @@ public final class AvatarUtil { } private static Drawable getFallback(@NonNull Context context, @NonNull Recipient recipient) { - String name = Optional.fromNullable(recipient.getDisplayName(context)).or(""); - ChatColors fallbackColor = recipient.getChatColors(); + String name = Optional.fromNullable(recipient.getDisplayName(context)).or(""); - return new GeneratedContactPhoto(name, R.drawable.ic_profile_outline_40).asDrawable(context, fallbackColor); + return new GeneratedContactPhoto(name, R.drawable.ic_profile_outline_40).asDrawable(context, recipient.getAvatarColor().colorInt()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ConversationShortcutPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/util/ConversationShortcutPhoto.java index 9ca35e8a8a..cdfa75495e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ConversationShortcutPhoto.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ConversationShortcutPhoto.java @@ -23,9 +23,7 @@ import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto80dp; import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.SystemContactPhoto; -import org.thoughtcrime.securesms.conversation.colors.ChatColors; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideRequest; import org.thoughtcrime.securesms.profiles.AvatarHelper; @@ -178,9 +176,9 @@ public final class ConversationShortcutPhoto implements Key { photoSource = R.drawable.ic_profile_80; } - FallbackContactPhoto photo = recipient.isSelf() || recipient.isGroup() ? new FallbackPhoto80dp(photoSource, recipient.getChatColors()) + FallbackContactPhoto photo = recipient.isSelf() || recipient.isGroup() ? new FallbackPhoto80dp(photoSource, recipient.getAvatarColor().colorInt()) : new ShortcutGeneratedContactPhoto(recipient.getDisplayName(context), photoSource, ViewUtil.dpToPx(80), ViewUtil.dpToPx(28)); - Bitmap toWrap = DrawableUtil.toBitmap(photo.asDrawable(context, recipient.getChatColors()), ViewUtil.dpToPx(80), ViewUtil.dpToPx(80)); + Bitmap toWrap = DrawableUtil.toBitmap(photo.asDrawable(context, recipient.getAvatarColor().colorInt()), ViewUtil.dpToPx(80), ViewUtil.dpToPx(80)); Bitmap wrapped = DrawableUtil.wrapBitmapForShortcutInfo(toWrap); toWrap.recycle(); @@ -199,8 +197,8 @@ public final class ConversationShortcutPhoto implements Key { } @Override - protected Drawable newFallbackDrawable(@NonNull Context context, @NonNull ChatColors chatColors, boolean inverted) { - return new FallbackPhoto80dp(getFallbackResId(), chatColors).asDrawable(context, chatColors); + protected Drawable newFallbackDrawable(@NonNull Context context, int color, boolean inverted) { + return new FallbackPhoto80dp(getFallbackResId(), color).asDrawable(context, -1); } } } diff --git a/core-util/src/main/java/org/signal/core/util/ColorUtil.java b/core-util/src/main/java/org/signal/core/util/ColorUtil.java new file mode 100644 index 0000000000..bd063cd186 --- /dev/null +++ b/core-util/src/main/java/org/signal/core/util/ColorUtil.java @@ -0,0 +1,43 @@ +package org.signal.core.util; + +import androidx.annotation.ColorInt; +import androidx.annotation.FloatRange; + +public final class ColorUtil { + + private ColorUtil() {} + + public static int blendARGB(@ColorInt int color1, + @ColorInt int color2, + @FloatRange(from = 0.0, to = 1.0) float ratio) + { + final float inverseRatio = 1 - ratio; + + float a = alpha(color1) * inverseRatio + alpha(color2) * ratio; + float r = red(color1) * inverseRatio + red(color2) * ratio; + float g = green(color1) * inverseRatio + green(color2) * ratio; + float b = blue(color1) * inverseRatio + blue(color2) * ratio; + + return argb((int) a, (int) r, (int) g, (int) b); + } + + private static int alpha(int color) { + return color >>> 24; + } + + private static int red(int color) { + return (color >> 16) & 0xFF; + } + + private static int green(int color) { + return (color >> 8) & 0xFF; + } + + private static int blue(int color) { + return color & 0xFF; + } + + private static int argb(int alpha, int red, int green, int blue) { + return (alpha << 24) | (red << 16) | (green << 8) | blue; + } +}