diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java index 7094dee458..b84ece1c34 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java @@ -58,15 +58,22 @@ public final class LiveUpdateMessage { private static @NonNull Spannable toSpannable(@NonNull Context context, @NonNull UpdateDescription updateDescription, @NonNull String string) { boolean isDarkTheme = ThemeUtil.isDarkTheme(context); int drawableResource = isDarkTheme ? updateDescription.getDarkIconResource() : updateDescription.getLightIconResource(); + int tint = isDarkTheme ? updateDescription.getDarkTint() : updateDescription.getLightTint(); + + if (tint == 0) { + tint = ThemeUtil.getThemedColor(context, R.attr.conversation_item_update_text_color); + } if (drawableResource == 0) { return new SpannableString(string); } else { Drawable drawable = ContextCompat.getDrawable(context, drawableResource); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - drawable.setColorFilter(ThemeUtil.getThemedColor(context, R.attr.conversation_item_update_text_color), PorterDuff.Mode.SRC_ATOP); + drawable.setColorFilter(tint, PorterDuff.Mode.SRC_ATOP); - return new SpannableStringBuilder().append(SpanUtil.buildImageSpan(drawable)).append(" ").append(string); + Spannable stringWithImage = new SpannableStringBuilder().append(SpanUtil.buildImageSpan(drawable)).append(" ").append(string); + + return new SpannableString(SpanUtil.color(tint, stringWithImage)); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java index aa3726bbfc..95f8675a2e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -17,14 +17,17 @@ package org.thoughtcrime.securesms.database.model; import android.content.Context; +import android.graphics.Color; import android.text.Spannable; import android.text.SpannableString; import android.text.style.RelativeSizeSpan; import android.text.style.StyleSpan; +import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.thoughtcrime.securesms.R; @@ -138,11 +141,11 @@ public abstract class MessageRecord extends DisplayRecord { } else if (isGroupQuit()) { return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.ConversationItem_group_action_left, r.getDisplayName(context)), R.drawable.ic_update_group_leave_light_16, R.drawable.ic_update_group_leave_dark_16); } else if (isIncomingCall()) { - return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_s_called_you_date, r.getDisplayName(context), getCallDateString()), R.drawable.ic_update_audio_call_incoming_light_16, R.drawable.ic_update_audio_call_incoming_dark_16); + return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_s_called_you_date, r.getDisplayName(context), getCallDateString(context)), R.drawable.ic_update_audio_call_incoming_light_16, R.drawable.ic_update_audio_call_incoming_dark_16); } else if (isOutgoingCall()) { - return staticUpdateDescription(context.getString(R.string.MessageRecord_you_called_date, getCallDateString()), R.drawable.ic_update_audio_call_outgoing_light_16, R.drawable.ic_update_audio_call_outgoing_dark_16); + return staticUpdateDescription(context.getString(R.string.MessageRecord_you_called_date, getCallDateString(context)), R.drawable.ic_update_audio_call_outgoing_light_16, R.drawable.ic_update_audio_call_outgoing_dark_16); } else if (isMissedCall()) { - return staticUpdateDescription(context.getString(R.string.MessageRecord_missed_call_date, getCallDateString()), R.drawable.ic_update_audio_call_missed_light_16, R.drawable.ic_update_audio_call_missed_dark_16); + return staticUpdateDescription(context.getString(R.string.MessageRecord_missed_call_date, getCallDateString(context)), R.drawable.ic_update_audio_call_missed_light_16, R.drawable.ic_update_audio_call_missed_dark_16, ContextCompat.getColor(context, R.color.core_red_shade), ContextCompat.getColor(context, R.color.core_red)); } else if (isJoined()) { return staticUpdateDescription(context.getString(R.string.MessageRecord_s_joined_signal, getIndividualRecipient().getDisplayName(context)), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16); } else if (isExpirationTimerUpdate()) { @@ -213,8 +216,8 @@ public abstract class MessageRecord extends DisplayRecord { } } - private @NonNull String getCallDateString() { - return DateUtils.getBriefExactTimeString(Locale.getDefault(), getDateSent()); + private @NonNull String getCallDateString(@NonNull Context context) { + return DateUtils.getExtendedRelativeTimeSpanString(context, Locale.getDefault(), getDateSent()); } private static @NonNull UpdateDescription fromRecipient(@NonNull Recipient recipient, @@ -235,6 +238,15 @@ public abstract class MessageRecord extends DisplayRecord { return UpdateDescription.staticDescription(string, lightIconResource, darkIconResource); } + private static @NonNull UpdateDescription staticUpdateDescription(@NonNull String string, + @DrawableRes int lightIconResource, + @DrawableRes int darkIconResource, + @ColorInt int lightTint, + @ColorInt int darkTint) + { + return UpdateDescription.staticDescription(string, lightIconResource, darkIconResource, lightTint, darkTint); + } + private @NonNull String getProfileChangeDescription(@NonNull Context context) { try { byte[] decoded = Base64.decode(getBody()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java index d07ac1db4b..e39b82f6c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.database.model; import androidx.annotation.AnyThread; +import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -31,12 +32,16 @@ public final class UpdateDescription { private final String staticString; private final int lightIconResource; private final int darkIconResource; + private final int lightTint; + private final int darkTint; private UpdateDescription(@NonNull Collection mentioned, @Nullable StringFactory stringFactory, @Nullable String staticString, @DrawableRes int lightIconResource, - @DrawableRes int darkIconResource) + @DrawableRes int darkIconResource, + @ColorInt int lightTint, + @ColorInt int darkTint) { if (staticString == null && stringFactory == null) { throw new AssertionError(); @@ -46,6 +51,8 @@ public final class UpdateDescription { this.staticString = staticString; this.lightIconResource = lightIconResource; this.darkIconResource = darkIconResource; + this.lightTint = lightTint; + this.darkTint = darkTint; } /** @@ -64,7 +71,9 @@ public final class UpdateDescription { stringFactory, null, lightIconResource, - darkIconResource); + darkIconResource, + 0, + 0); } /** @@ -74,7 +83,19 @@ public final class UpdateDescription { @DrawableRes int lightIconResource, @DrawableRes int darkIconResource) { - return new UpdateDescription(Collections.emptyList(), null, staticString, lightIconResource, darkIconResource); + return new UpdateDescription(Collections.emptyList(), null, staticString, lightIconResource, darkIconResource, 0, 0); + } + + /** + * Create an update description that's string value is fixed with a specific tint color. + */ + public static UpdateDescription staticDescription(@NonNull String staticString, + @DrawableRes int lightIconResource, + @DrawableRes int darkIconResource, + @ColorInt int lightTint, + @ColorInt int darkTint) + { + return new UpdateDescription(Collections.emptyList(), null, staticString, lightIconResource, darkIconResource, lightTint, darkTint); } public boolean isStringStatic() { @@ -115,6 +136,14 @@ public final class UpdateDescription { return darkIconResource; } + public @ColorInt int getLightTint() { + return lightTint; + } + + public @ColorInt int getDarkTint() { + return darkTint; + } + public static UpdateDescription concatWithNewLines(@NonNull List updateDescriptions) { if (updateDescriptions.size() == 0) { throw new AssertionError(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java index 8f603180a6..a824088031 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java @@ -63,7 +63,7 @@ public class DateUtils extends android.text.format.DateUtils { private static String getFormattedDateTime(long time, String template, Locale locale) { final String localizedPattern = getLocalizedPattern(template, locale); - return new SimpleDateFormat(localizedPattern, locale).format(new Date(time)); + return setLowercaseAmPmStrings(new SimpleDateFormat(localizedPattern, locale), locale).format(new Date(time)); } public static String getBriefRelativeTimeSpanString(final Context c, final Locale locale, final long timestamp) { @@ -174,18 +174,17 @@ public class DateUtils extends android.text.format.DateUtils { return getExtendedRelativeTimeSpanString(context, locale, t1).equals(getExtendedRelativeTimeSpanString(context, locale, t2)); } - public static String getBriefExactTimeString(@NonNull Locale locale, long timestamp) { - SimpleDateFormat format = new SimpleDateFormat(getLocalizedPattern("MMM dd, hh:mm a", locale), locale); + private static String getLocalizedPattern(String template, Locale locale) { + return DateFormat.getBestDateTimePattern(locale, template); + } + + private static @NonNull SimpleDateFormat setLowercaseAmPmStrings(@NonNull SimpleDateFormat format, @NonNull Locale locale) { DateFormatSymbols symbols = new DateFormatSymbols(locale); symbols.setAmPmStrings(new String[] { "am", "pm"}); format.setDateFormatSymbols(symbols); - return format.format(timestamp); - } - - private static String getLocalizedPattern(String template, Locale locale) { - return DateFormat.getBestDateTimePattern(locale, template); + return format; } /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java index a307137b85..f108cc3fe3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java @@ -5,6 +5,7 @@ import android.graphics.PorterDuff; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Build; import android.text.Spannable; import android.text.SpannableString; import android.text.style.AbsoluteSizeSpan; @@ -60,7 +61,9 @@ public class SpanUtil { public static CharSequence buildImageSpan(@NonNull Drawable drawable) { SpannableString imageSpan = new SpannableString(" "); - imageSpan.setSpan(new ImageSpan(drawable, DynamicDrawableSpan.ALIGN_CENTER), 0, imageSpan.length(), 0); + int flag = Build.VERSION.SDK_INT >= 29 ? DynamicDrawableSpan.ALIGN_CENTER : DynamicDrawableSpan.ALIGN_BASELINE; + + imageSpan.setSpan(new ImageSpan(drawable, flag), 0, imageSpan.length(), 0); return imageSpan; } diff --git a/app/src/main/res/layout/conversation_fragment.xml b/app/src/main/res/layout/conversation_fragment.xml index 96af2808f4..a948b137d6 100644 --- a/app/src/main/res/layout/conversation_fragment.xml +++ b/app/src/main/res/layout/conversation_fragment.xml @@ -23,7 +23,7 @@ diff --git a/app/src/main/res/values/core_colors.xml b/app/src/main/res/values/core_colors.xml index 65afdde4dc..84850146ba 100644 --- a/app/src/main/res/values/core_colors.xml +++ b/app/src/main/res/values/core_colors.xml @@ -11,6 +11,7 @@ #ffd624 #f44336 #ef5350 + #e51d0e #ffffff #000000 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a86cc21826..9edbe16204 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -937,10 +937,10 @@ You have left the group. You updated the group. The group was updated. - You called • %1$s - Missed call • %1$s + You called · %1$s + Missed call · %1$s %s updated the group. - %1$s called you • %2$s + %1$s called you · %2$s Called %s %s is on Signal! You disabled disappearing messages.