Add support for scheduled message sends.

This commit is contained in:
Clark
2023-01-26 10:37:08 -05:00
committed by Greyson Parrelli
parent df695f7611
commit f3e715e069
59 changed files with 1948 additions and 90 deletions

View File

@@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.R;
import java.text.DateFormatSymbols;
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;
@@ -44,6 +45,7 @@ public class DateUtils extends android.text.format.DateUtils {
private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = new ThreadLocal<>();
private static final ThreadLocal<SimpleDateFormat> BRIEF_EXACT_FORMAT = new ThreadLocal<>();
private static final long MAX_RELATIVE_TIMESTAMP = TimeUnit.MINUTES.toMillis(3);
private static final int HALF_A_YEAR_IN_DAYS = 182;
private static boolean isWithin(final long millis, final long span, final TimeUnit unit) {
return System.currentTimeMillis() - millis <= unit.toMillis(span);
@@ -110,11 +112,22 @@ public class DateUtils extends android.text.format.DateUtils {
int mins = (int) TimeUnit.MINUTES.convert(System.currentTimeMillis() - timestamp, TimeUnit.MILLISECONDS);
return context.getResources().getString(R.string.DateUtils_minutes_ago, mins);
} else {
String format = DateFormat.is24HourFormat(context) ? "HH:mm" : "hh:mm a";
return getFormattedDateTime(timestamp, format, locale);
return getOnlyTimeString(context, locale, timestamp);
}
}
/**
* Formats a given timestamp as just the time.
*
* For example:
* For 12 hour locale: 7:23 pm
* For 24 hour locale: 19:23
*/
public static String getOnlyTimeString(final Context context, final Locale locale, final long timestamp) {
String format = DateFormat.is24HourFormat(context) ? "HH:mm" : "hh:mm a";
return getFormattedDateTime(timestamp, format, locale);
}
public static String getTimeString(final Context c, final Locale locale, final long timestamp) {
StringBuilder format = new StringBuilder();
@@ -129,6 +142,37 @@ public class DateUtils extends android.text.format.DateUtils {
return getFormattedDateTime(timestamp, format.toString(), locale);
}
/**
* Formats the passed timestamp based on the current time at a day precision.
*
* For example:
* - Today
* - Wed
* - Mon
* - Jan 31
* - Feb 4
* - Jan 12, 2033
*/
public static String getDayPrecisionTimeString(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 {
String format;
if (isWithinAbs(timestamp, 6, TimeUnit.DAYS)) {
format = "EEE ";
} else if (isWithinAbs(timestamp, 365, TimeUnit.DAYS)) {
format = "MMM d";
} else {
format = "MMM d, yyy";
}
return getFormattedDateTime(timestamp, format, locale);
}
}
public static String getDayPrecisionTimeSpanString(Context context, Locale locale, long timestamp) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
@@ -165,13 +209,44 @@ public class DateUtils extends android.text.format.DateUtils {
return context.getString(R.string.DateUtils_today);
} else if (isYesterday(timestamp)) {
return context.getString(R.string.DateUtils_yesterday);
} else if (isWithin(timestamp, 182, TimeUnit.DAYS)) {
} else if (isWithin(timestamp, HALF_A_YEAR_IN_DAYS, TimeUnit.DAYS)) {
return formatDateWithDayOfWeek(locale, timestamp);
} else {
return formatDateWithYear(locale, timestamp);
}
}
public static String getScheduledMessagesDateHeaderString(@NonNull Context context,
@NonNull Locale locale,
long timestamp)
{
if (isToday(timestamp)) {
return context.getString(R.string.DateUtils_today);
} else if (isWithinAbs(timestamp, HALF_A_YEAR_IN_DAYS, TimeUnit.DAYS)) {
return formatDateWithDayOfWeek(locale, timestamp);
} else {
return formatDateWithYear(locale, timestamp);
}
}
public static String getScheduledMessageDateString(@NonNull Context context, @NonNull Locale locale, long timestamp) {
String dayModifier;
if (isToday(timestamp)) {
Calendar calendar = Calendar.getInstance(locale);
if (calendar.get(Calendar.HOUR_OF_DAY) >= 19) {
dayModifier = context.getString(R.string.DateUtils_tonight);
} else {
dayModifier = context.getString(R.string.DateUtils_today);
}
} else {
dayModifier = context.getString(R.string.DateUtils_tomorrow);
}
String format = DateFormat.is24HourFormat(context) ? "HH:mm" : "hh:mm a";
String time = getFormattedDateTime(timestamp, format, locale);
return context.getString(R.string.DateUtils_schedule_at, dayModifier, time);
}
public static String formatDateWithDayOfWeek(@NonNull Locale locale, long timestamp) {
return getFormattedDateTime(timestamp, "EEE, MMM d", locale);
}

View File

@@ -105,6 +105,7 @@ public final class FeatureFlags {
private static final String PAYPAL_ONE_TIME_DONATIONS = "android.oneTimePayPalDonations.2";
private static final String PAYPAL_RECURRING_DONATIONS = "android.recurringPayPalDonations.2";
private static final String TEXT_FORMATTING = "android.textFormatting";
private static final String SCHEDULED_MESSAGE_SENDS = "android.scheduledMessageSends";
/**
* We will only store remote values for flags in this set. If you want a flag to be controllable
@@ -160,7 +161,8 @@ public final class FeatureFlags {
CHAT_FILTERS,
PAYPAL_ONE_TIME_DONATIONS,
PAYPAL_RECURRING_DONATIONS,
TEXT_FORMATTING
TEXT_FORMATTING,
SCHEDULED_MESSAGE_SENDS
);
@VisibleForTesting
@@ -574,6 +576,13 @@ public final class FeatureFlags {
return getBoolean(TEXT_FORMATTING, false);
}
/**
* Whether or not we allow the user to schedule message sends. This takes over the entry point for SMS message sends
*/
public static boolean scheduledMessageSends() {
return getBoolean(SCHEDULED_MESSAGE_SENDS, false);
}
/** Only for rendering debug info. */
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
return new TreeMap<>(REMOTE_VALUES);

View File

@@ -10,6 +10,7 @@ import java.time.LocalTime
import java.time.OffsetDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.time.temporal.WeekFields
@@ -31,6 +32,28 @@ fun LocalDateTime.toMillis(zoneOffset: ZoneOffset = ZoneId.systemDefault().toOff
return TimeUnit.SECONDS.toMillis(toEpochSecond(zoneOffset))
}
/**
* Convert [ZonedDateTime] to be same as [System.currentTimeMillis]
*/
fun ZonedDateTime.toMillis(): Long {
return TimeUnit.SECONDS.toMillis(toEpochSecond())
}
/**
* Convert [LocalDateTime] to a [ZonedDateTime] at the UTC offset
*/
fun LocalDateTime.atUTC(): ZonedDateTime {
return atZone(ZoneId.ofOffset("UTC", ZoneOffset.UTC))
}
/**
* Create a LocalDateTime with the same year, month, and day, but set
* to midnight.
*/
fun LocalDateTime.atMidnight(): LocalDateTime {
return LocalDateTime.of(year, month, dayOfMonth, 0, 0)
}
/**
* Return true if the [LocalDateTime] is within [start] and [end] inclusive.
*/

View File

@@ -141,6 +141,10 @@ fun MessageRecord.isTextOnly(context: Context): Boolean {
)
}
fun MessageRecord.isScheduled(): Boolean {
return (this as? MediaMmsMessageRecord)?.scheduledDate?.let { it != -1L } ?: false
}
/**
* Returns the QuoteType for this record, as if it was being quoted.
*/