mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-19 16:19:33 +01:00
Add support for scheduled message sends.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user