Show date break header by hour instead of by day

Also ditch relative timestamps in favor of absolute ones
pull/645/head
Niels Andriesse 5 years ago
parent 7aefa288d9
commit 183f013c31

@ -189,7 +189,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
CharSequence relativeTimeSpan;
if (mediaItem.date > 0) {
relativeTimeSpan = DateUtils.getExtendedRelativeTimeSpanString(this, Locale.getDefault(), mediaItem.date);
relativeTimeSpan = DateUtils.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), mediaItem.date);
} else {
relativeTimeSpan = getString(R.string.MediaPreviewActivity_draft);
}

@ -1141,7 +1141,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
for (message in sortedMessages) {
val body = MentionUtilities.highlightMentions(message.body, message.threadId, this)
if (TextUtils.isEmpty(body)) { continue }
val formattedTimestamp = DateUtils.getExtendedRelativeTimeSpanString(this, Locale.getDefault(), message.timestamp)
val formattedTimestamp = DateUtils.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), message.timestamp)
builder.append("$formattedTimestamp: $body").append('\n')
}
if (builder.isNotEmpty() && builder[builder.length - 1] == '\n') {

@ -110,11 +110,11 @@ class VisibleMessageView : LinearLayout {
senderNameTextView.visibility = View.GONE
}
// Date break
val showDateBreak = (previous == null || !DateUtils.isSameDay(message.timestamp, previous.timestamp))
val showDateBreak = (previous == null || !DateUtils.isSameHour(message.timestamp, previous.timestamp))
dateBreakTextView.isVisible = showDateBreak
dateBreakTextView.text = if (showDateBreak) DateUtils.getRelativeDate(context, Locale.getDefault(), message.timestamp) else ""
dateBreakTextView.text = if (showDateBreak) DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), message.timestamp) else ""
// Timestamp
messageTimestampTextView.text = DateUtils.getExtendedRelativeTimeSpanString(context, Locale.getDefault(), message.timestamp)
messageTimestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), message.timestamp)
// Margins
val startPadding: Int
if (isGroupThread) {
@ -177,10 +177,10 @@ class VisibleMessageView : LinearLayout {
private fun isEndOfMessageCluster(current: MessageRecord, next: MessageRecord?, isGroupThread: Boolean): Boolean {
return if (isGroupThread) {
next == null || next.isUpdate || !DateUtils.isSameDay(current.timestamp, next.timestamp)
next == null || next.isUpdate || !DateUtils.isSameHour(current.timestamp, next.timestamp)
|| current.recipient.address != next.recipient.address
} else {
next == null || next.isUpdate || !DateUtils.isSameDay(current.timestamp, next.timestamp)
next == null || next.isUpdate || !DateUtils.isSameHour(current.timestamp, next.timestamp)
|| current.isOutgoing != next.isOutgoing
}
}

@ -58,7 +58,7 @@ class ConversationView : LinearLayout {
profilePictureView.update(thread.recipient, thread.threadId)
val senderDisplayName = getUserDisplayName(thread.recipient) ?: thread.recipient.address.toString()
conversationViewDisplayNameTextView.text = senderDisplayName
timestampTextView.text = DateUtils.getBriefRelativeTimeSpanString(context, Locale.getDefault(), thread.date)
timestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), thread.date)
muteIndicatorImageView.visibility = if (thread.recipient.isMuted) VISIBLE else GONE
val rawSnippet = thread.getDisplayBody(context)
val snippet = highlightMentions(rawSnippet, thread.threadId, context)

@ -118,7 +118,7 @@ object BackupUtil {
if (timestamp == null) {
return context.getString(R.string.BackupUtil_never)
}
return DateUtils.getExtendedRelativeTimeSpanString(context, locale, timestamp.time)
return DateUtils.getDisplayFormattedTimeSpanString(context, locale, timestamp.time)
}
@JvmStatic

@ -28,6 +28,7 @@ import org.session.libsignal.utilities.Log;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@ -40,8 +41,9 @@ import network.loki.messenger.R;
public class DateUtils extends android.text.format.DateUtils {
@SuppressWarnings("unused")
private static final String TAG = DateUtils.class.getSimpleName();
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");
private static final String TAG = DateUtils.class.getSimpleName();
private static final SimpleDateFormat DAY_PRECISION_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");
private static final SimpleDateFormat HOUR_PRECISION_DATE_FORMAT = new SimpleDateFormat("yyyyMMddHH");
private static boolean isWithin(final long millis, final long span, final TimeUnit unit) {
return System.currentTimeMillis() - millis <= unit.toMillis(span);
@ -60,56 +62,21 @@ public class DateUtils extends android.text.format.DateUtils {
return new SimpleDateFormat(localizedPattern, locale).format(new Date(time));
}
public static String getBriefRelativeTimeSpanString(final Context c, final Locale locale, final long timestamp) {
if (isWithin(timestamp, 1, TimeUnit.MINUTES)) {
return c.getString(R.string.DateUtils_just_now);
} else if (isWithin(timestamp, 1, TimeUnit.HOURS)) {
int mins = convertDelta(timestamp, TimeUnit.MINUTES);
return c.getResources().getString(R.string.DateUtils_minutes_ago, mins);
} else if (isWithin(timestamp, 1, TimeUnit.DAYS)) {
int hours = convertDelta(timestamp, TimeUnit.HOURS);
return c.getResources().getQuantityString(R.plurals.hours_ago, hours, hours);
} else if (isWithin(timestamp, 6, TimeUnit.DAYS)) {
return getFormattedDateTime(timestamp, "EEE", locale);
} else if (isWithin(timestamp, 365, TimeUnit.DAYS)) {
return getFormattedDateTime(timestamp, "MMM d", locale);
} else {
return getFormattedDateTime(timestamp, "MMM d, yyyy", locale);
}
public static String getHourFormat(Context c) {
return (DateFormat.is24HourFormat(c)) ? "HH:mm" : "hh:mm a";
}
public static String getExtendedRelativeTimeSpanString(final Context c, final Locale locale, final long timestamp) {
public static String getDisplayFormattedTimeSpanString(final Context c, final Locale locale, final long timestamp) {
if (isWithin(timestamp, 1, TimeUnit.MINUTES)) {
return c.getString(R.string.DateUtils_just_now);
} else if (isWithin(timestamp, 1, TimeUnit.HOURS)) {
int mins = (int)TimeUnit.MINUTES.convert(System.currentTimeMillis() - timestamp, TimeUnit.MILLISECONDS);
return c.getResources().getString(R.string.DateUtils_minutes_ago, mins);
} else {
StringBuilder format = new StringBuilder();
if (isWithin(timestamp, 6, TimeUnit.DAYS)) format.append("EEE ");
else if (isWithin(timestamp, 365, TimeUnit.DAYS)) format.append("MMM d, ");
else format.append("MMM d, yyyy, ");
if (DateFormat.is24HourFormat(c)) format.append("HH:mm");
else format.append("hh:mm a");
return getFormattedDateTime(timestamp, format.toString(), locale);
}
}
public static String getDayPrecisionTimeSpanString(Context context, Locale locale, long timestamp) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
if (simpleDateFormat.format(System.currentTimeMillis()).equals(simpleDateFormat.format(timestamp))) {
return context.getString(R.string.DeviceListItem_today);
} else if (isToday(timestamp)) {
return getFormattedDateTime(timestamp, getHourFormat(c), locale);
} else if (isWithin(timestamp, 6, TimeUnit.DAYS)) {
return getFormattedDateTime(timestamp, "EEE " + getHourFormat(c), locale);
} else if (isWithin(timestamp, 365, TimeUnit.DAYS)) {
return getFormattedDateTime(timestamp, "MMM d " + getHourFormat(c), locale);
} else {
String format;
if (isWithin(timestamp, 6, TimeUnit.DAYS)) format = "EEE ";
else if (isWithin(timestamp, 365, TimeUnit.DAYS)) format = "MMM d";
else format = "MMM d, yyy";
return getFormattedDateTime(timestamp, format, locale);
return getFormattedDateTime(timestamp, "MMM d " + getHourFormat(c) + ", yyyy", locale);
}
}
@ -139,11 +106,11 @@ public class DateUtils extends android.text.format.DateUtils {
}
public static boolean isSameDay(long t1, long t2) {
return DATE_FORMAT.format(new Date(t1)).equals(DATE_FORMAT.format(new Date(t2)));
return DAY_PRECISION_DATE_FORMAT.format(new Date(t1)).equals(DAY_PRECISION_DATE_FORMAT.format(new Date(t2)));
}
public static boolean isSameExtendedRelativeTimestamp(@NonNull Context context, @NonNull Locale locale, long t1, long t2) {
return getExtendedRelativeTimeSpanString(context, locale, t1).equals(getExtendedRelativeTimeSpanString(context, locale, t2));
public static boolean isSameHour(long t1, long t2) {
return HOUR_PRECISION_DATE_FORMAT.format(new Date(t1)).equals(HOUR_PRECISION_DATE_FORMAT.format(new Date(t2)));
}
private static String getLocalizedPattern(String template, Locale locale) {
@ -178,4 +145,43 @@ public class DateUtils extends android.text.format.DateUtils {
return -1;
}
}
// region Deprecated
public static String getBriefRelativeTimeSpanString(final Context c, final Locale locale, final long timestamp) {
if (isWithin(timestamp, 1, TimeUnit.MINUTES)) {
return c.getString(R.string.DateUtils_just_now);
} else if (isWithin(timestamp, 1, TimeUnit.HOURS)) {
int mins = convertDelta(timestamp, TimeUnit.MINUTES);
return c.getResources().getString(R.string.DateUtils_minutes_ago, mins);
} else if (isWithin(timestamp, 1, TimeUnit.DAYS)) {
int hours = convertDelta(timestamp, TimeUnit.HOURS);
return c.getResources().getQuantityString(R.plurals.hours_ago, hours, hours);
} else if (isWithin(timestamp, 6, TimeUnit.DAYS)) {
return getFormattedDateTime(timestamp, "EEE", locale);
} else if (isWithin(timestamp, 365, TimeUnit.DAYS)) {
return getFormattedDateTime(timestamp, "MMM d", locale);
} else {
return getFormattedDateTime(timestamp, "MMM d, yyyy", locale);
}
}
public static String getExtendedRelativeTimeSpanString(final Context c, final Locale locale, final long timestamp) {
if (isWithin(timestamp, 1, TimeUnit.MINUTES)) {
return c.getString(R.string.DateUtils_just_now);
} else if (isWithin(timestamp, 1, TimeUnit.HOURS)) {
int mins = (int)TimeUnit.MINUTES.convert(System.currentTimeMillis() - timestamp, TimeUnit.MILLISECONDS);
return c.getResources().getString(R.string.DateUtils_minutes_ago, mins);
} else {
StringBuilder format = new StringBuilder();
if (isWithin(timestamp, 6, TimeUnit.DAYS)) format.append("EEE ");
else if (isWithin(timestamp, 365, TimeUnit.DAYS)) format.append("MMM d, ");
else format.append("MMM d, yyyy, ");
if (DateFormat.is24HourFormat(c)) format.append("HH:mm");
else format.append("hh:mm a");
return getFormattedDateTime(timestamp, format.toString(), locale);
}
}
// endregion
}

Loading…
Cancel
Save