Various UI adjustments to conversation updates.

This commit is contained in:
Greyson Parrelli 2020-10-23 13:29:29 -04:00 committed by Cody Henthorne
parent 9743e3689a
commit 3f983a5c82
9 changed files with 75 additions and 24 deletions

View File

@ -58,15 +58,22 @@ public final class LiveUpdateMessage {
private static @NonNull Spannable toSpannable(@NonNull Context context, @NonNull UpdateDescription updateDescription, @NonNull String string) { private static @NonNull Spannable toSpannable(@NonNull Context context, @NonNull UpdateDescription updateDescription, @NonNull String string) {
boolean isDarkTheme = ThemeUtil.isDarkTheme(context); boolean isDarkTheme = ThemeUtil.isDarkTheme(context);
int drawableResource = isDarkTheme ? updateDescription.getDarkIconResource() : updateDescription.getLightIconResource(); 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) { if (drawableResource == 0) {
return new SpannableString(string); return new SpannableString(string);
} else { } else {
Drawable drawable = ContextCompat.getDrawable(context, drawableResource); Drawable drawable = ContextCompat.getDrawable(context, drawableResource);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); 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));
} }
} }
} }

View File

@ -17,14 +17,17 @@
package org.thoughtcrime.securesms.database.model; package org.thoughtcrime.securesms.database.model;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.style.RelativeSizeSpan; import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
@ -138,11 +141,11 @@ public abstract class MessageRecord extends DisplayRecord {
} else if (isGroupQuit()) { } 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); 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()) { } 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()) { } 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()) { } 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()) { } 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); 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()) { } else if (isExpirationTimerUpdate()) {
@ -213,8 +216,8 @@ public abstract class MessageRecord extends DisplayRecord {
} }
} }
private @NonNull String getCallDateString() { private @NonNull String getCallDateString(@NonNull Context context) {
return DateUtils.getBriefExactTimeString(Locale.getDefault(), getDateSent()); return DateUtils.getExtendedRelativeTimeSpanString(context, Locale.getDefault(), getDateSent());
} }
private static @NonNull UpdateDescription fromRecipient(@NonNull Recipient recipient, private static @NonNull UpdateDescription fromRecipient(@NonNull Recipient recipient,
@ -235,6 +238,15 @@ public abstract class MessageRecord extends DisplayRecord {
return UpdateDescription.staticDescription(string, lightIconResource, darkIconResource); 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) { private @NonNull String getProfileChangeDescription(@NonNull Context context) {
try { try {
byte[] decoded = Base64.decode(getBody()); byte[] decoded = Base64.decode(getBody());

View File

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.database.model; package org.thoughtcrime.securesms.database.model;
import androidx.annotation.AnyThread; import androidx.annotation.AnyThread;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -31,12 +32,16 @@ public final class UpdateDescription {
private final String staticString; private final String staticString;
private final int lightIconResource; private final int lightIconResource;
private final int darkIconResource; private final int darkIconResource;
private final int lightTint;
private final int darkTint;
private UpdateDescription(@NonNull Collection<UUID> mentioned, private UpdateDescription(@NonNull Collection<UUID> mentioned,
@Nullable StringFactory stringFactory, @Nullable StringFactory stringFactory,
@Nullable String staticString, @Nullable String staticString,
@DrawableRes int lightIconResource, @DrawableRes int lightIconResource,
@DrawableRes int darkIconResource) @DrawableRes int darkIconResource,
@ColorInt int lightTint,
@ColorInt int darkTint)
{ {
if (staticString == null && stringFactory == null) { if (staticString == null && stringFactory == null) {
throw new AssertionError(); throw new AssertionError();
@ -46,6 +51,8 @@ public final class UpdateDescription {
this.staticString = staticString; this.staticString = staticString;
this.lightIconResource = lightIconResource; this.lightIconResource = lightIconResource;
this.darkIconResource = darkIconResource; this.darkIconResource = darkIconResource;
this.lightTint = lightTint;
this.darkTint = darkTint;
} }
/** /**
@ -64,7 +71,9 @@ public final class UpdateDescription {
stringFactory, stringFactory,
null, null,
lightIconResource, lightIconResource,
darkIconResource); darkIconResource,
0,
0);
} }
/** /**
@ -74,7 +83,19 @@ public final class UpdateDescription {
@DrawableRes int lightIconResource, @DrawableRes int lightIconResource,
@DrawableRes int darkIconResource) @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() { public boolean isStringStatic() {
@ -115,6 +136,14 @@ public final class UpdateDescription {
return darkIconResource; return darkIconResource;
} }
public @ColorInt int getLightTint() {
return lightTint;
}
public @ColorInt int getDarkTint() {
return darkTint;
}
public static UpdateDescription concatWithNewLines(@NonNull List<UpdateDescription> updateDescriptions) { public static UpdateDescription concatWithNewLines(@NonNull List<UpdateDescription> updateDescriptions) {
if (updateDescriptions.size() == 0) { if (updateDescriptions.size() == 0) {
throw new AssertionError(); throw new AssertionError();

View File

@ -63,7 +63,7 @@ public class DateUtils extends android.text.format.DateUtils {
private static String getFormattedDateTime(long time, String template, Locale locale) { private static String getFormattedDateTime(long time, String template, Locale locale) {
final String localizedPattern = getLocalizedPattern(template, 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) { 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)); return getExtendedRelativeTimeSpanString(context, locale, t1).equals(getExtendedRelativeTimeSpanString(context, locale, t2));
} }
public static String getBriefExactTimeString(@NonNull Locale locale, long timestamp) { private static String getLocalizedPattern(String template, Locale locale) {
SimpleDateFormat format = new SimpleDateFormat(getLocalizedPattern("MMM dd, hh:mm a", locale), locale); return DateFormat.getBestDateTimePattern(locale, template);
}
private static @NonNull SimpleDateFormat setLowercaseAmPmStrings(@NonNull SimpleDateFormat format, @NonNull Locale locale) {
DateFormatSymbols symbols = new DateFormatSymbols(locale); DateFormatSymbols symbols = new DateFormatSymbols(locale);
symbols.setAmPmStrings(new String[] { "am", "pm"}); symbols.setAmPmStrings(new String[] { "am", "pm"});
format.setDateFormatSymbols(symbols); format.setDateFormatSymbols(symbols);
return format.format(timestamp); return format;
}
private static String getLocalizedPattern(String template, Locale locale) {
return DateFormat.getBestDateTimePattern(locale, template);
} }
/** /**

View File

@ -5,6 +5,7 @@ import android.graphics.PorterDuff;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.style.AbsoluteSizeSpan; import android.text.style.AbsoluteSizeSpan;
@ -60,7 +61,9 @@ public class SpanUtil {
public static CharSequence buildImageSpan(@NonNull Drawable drawable) { public static CharSequence buildImageSpan(@NonNull Drawable drawable) {
SpannableString imageSpan = new SpannableString(" "); 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; return imageSpan;
} }

View File

@ -23,7 +23,7 @@
<TextView <TextView
android:id="@+id/scroll_date_header" android:id="@+id/scroll_date_header"
style="@style/Signal.Text.Caption" style="@style/Signal.Text.Preview"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"

View File

@ -15,7 +15,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
style="@style/Signal.Text.Caption" style="@style/Signal.Text.Preview"
android:textColor="?conversation_item_update_text_color" android:textColor="?conversation_item_update_text_color"
tools:text="March 1, 2015" /> tools:text="March 1, 2015" />

View File

@ -11,6 +11,7 @@
<color name="core_yellow">#ffd624</color> <color name="core_yellow">#ffd624</color>
<color name="core_red">#f44336</color> <color name="core_red">#f44336</color>
<color name="core_red_highlight">#ef5350</color> <color name="core_red_highlight">#ef5350</color>
<color name="core_red_shade">#e51d0e</color>
<color name="core_white">#ffffff</color> <color name="core_white">#ffffff</color>
<color name="core_black">#000000</color> <color name="core_black">#000000</color>

View File

@ -937,10 +937,10 @@
<string name="MessageRecord_left_group">You have left the group.</string> <string name="MessageRecord_left_group">You have left the group.</string>
<string name="MessageRecord_you_updated_group">You updated the group.</string> <string name="MessageRecord_you_updated_group">You updated the group.</string>
<string name="MessageRecord_the_group_was_updated">The group was updated.</string> <string name="MessageRecord_the_group_was_updated">The group was updated.</string>
<string name="MessageRecord_you_called_date">You called %1$s</string> <string name="MessageRecord_you_called_date">You called · %1$s</string>
<string name="MessageRecord_missed_call_date">Missed call %1$s</string> <string name="MessageRecord_missed_call_date">Missed call · %1$s</string>
<string name="MessageRecord_s_updated_group">%s updated the group.</string> <string name="MessageRecord_s_updated_group">%s updated the group.</string>
<string name="MessageRecord_s_called_you_date">%1$s called you %2$s</string> <string name="MessageRecord_s_called_you_date">%1$s called you · %2$s</string>
<string name="MessageRecord_called_s">Called %s</string> <string name="MessageRecord_called_s">Called %s</string>
<string name="MessageRecord_s_joined_signal">%s is on Signal!</string> <string name="MessageRecord_s_joined_signal">%s is on Signal!</string>
<string name="MessageRecord_you_disabled_disappearing_messages">You disabled disappearing messages.</string> <string name="MessageRecord_you_disabled_disappearing_messages">You disabled disappearing messages.</string>