android.text.format.Time is deprecated since 2014 and there are better alternatives. Bug: 16550209 Test: presubmit Change-Id: Ibbc4fb11e96b5179ff5dec906e74c9d582b6fb97
279 lines
11 KiB
Java
279 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.messaging.util;
|
|
|
|
import android.content.Context;
|
|
import android.text.format.DateUtils;
|
|
|
|
import com.android.messaging.Factory;
|
|
import com.android.messaging.R;
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
|
|
import java.text.SimpleDateFormat;
|
|
import java.time.Instant;
|
|
import java.time.LocalDateTime;
|
|
import java.time.temporal.ChronoUnit;
|
|
import java.time.ZoneId;
|
|
import java.util.Date;
|
|
import java.util.Locale;
|
|
|
|
/**
|
|
* Collection of date utilities.
|
|
*/
|
|
public class Dates {
|
|
public static final long SECOND_IN_MILLIS = 1000;
|
|
public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
|
|
public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
|
|
public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
|
|
public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
|
|
|
|
// Flags to specify whether or not to use 12 or 24 hour mode.
|
|
// Callers of methods in this class should never have to specify these; this is really
|
|
// intended only for unit tests.
|
|
@SuppressWarnings("deprecation")
|
|
@VisibleForTesting public static final int FORCE_12_HOUR = DateUtils.FORMAT_12HOUR;
|
|
@SuppressWarnings("deprecation")
|
|
@VisibleForTesting public static final int FORCE_24_HOUR = DateUtils.FORMAT_24HOUR;
|
|
|
|
/**
|
|
* Private default constructor
|
|
*/
|
|
private Dates() {
|
|
}
|
|
|
|
private static Context getContext() {
|
|
return Factory.get().getApplicationContext();
|
|
}
|
|
/**
|
|
* Get the relative time as a string
|
|
*
|
|
* @param time The time
|
|
*
|
|
* @return The relative time
|
|
*/
|
|
public static CharSequence getRelativeTimeSpanString(final long time) {
|
|
final long now = System.currentTimeMillis();
|
|
if (now - time < DateUtils.MINUTE_IN_MILLIS) {
|
|
// Also fixes bug where posts appear in the future
|
|
return getContext().getResources().getText(R.string.posted_just_now);
|
|
}
|
|
|
|
// Workaround for b/5657035. The platform method {@link DateUtils#getRelativeTimeSpan()}
|
|
// passes a null context to other platform methods. However, on some devices, this
|
|
// context is dereferenced when it shouldn't be and an NPE is thrown. We catch that
|
|
// here and use a slightly less precise time.
|
|
try {
|
|
return DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS,
|
|
DateUtils.FORMAT_ABBREV_RELATIVE).toString();
|
|
} catch (final NullPointerException npe) {
|
|
return getShortRelativeTimeSpanString(time);
|
|
}
|
|
}
|
|
|
|
public static CharSequence getConversationTimeString(final long time) {
|
|
return getTimeString(time, true /*abbreviated*/, false /*minPeriodToday*/);
|
|
}
|
|
|
|
public static CharSequence getMessageTimeString(final long time) {
|
|
return getTimeString(time, false /*abbreviated*/, false /*minPeriodToday*/);
|
|
}
|
|
|
|
public static CharSequence getWidgetTimeString(final long time, final boolean abbreviated) {
|
|
return getTimeString(time, abbreviated, true /*minPeriodToday*/);
|
|
}
|
|
|
|
public static CharSequence getFastScrollPreviewTimeString(final long time) {
|
|
return getTimeString(time, true /* abbreviated */, true /* minPeriodToday */);
|
|
}
|
|
|
|
public static CharSequence getMessageDetailsTimeString(final long time) {
|
|
final Context context = getContext();
|
|
int flags;
|
|
if (android.text.format.DateFormat.is24HourFormat(context)) {
|
|
flags = FORCE_24_HOUR;
|
|
} else {
|
|
flags = FORCE_12_HOUR;
|
|
}
|
|
return getOlderThanAYearTimestamp(time,
|
|
context.getResources().getConfiguration().locale, false /*abbreviated*/,
|
|
flags);
|
|
}
|
|
|
|
private static CharSequence getTimeString(final long time, final boolean abbreviated,
|
|
final boolean minPeriodToday) {
|
|
final Context context = getContext();
|
|
int flags;
|
|
if (android.text.format.DateFormat.is24HourFormat(context)) {
|
|
flags = FORCE_24_HOUR;
|
|
} else {
|
|
flags = FORCE_12_HOUR;
|
|
}
|
|
return getTimestamp(time, System.currentTimeMillis(), abbreviated,
|
|
context.getResources().getConfiguration().locale, flags, minPeriodToday);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public static CharSequence getTimestamp(final long time, final long now,
|
|
final boolean abbreviated, final Locale locale, final int flags,
|
|
final boolean minPeriodToday) {
|
|
final long timeDiff = now - time;
|
|
|
|
if (!minPeriodToday && timeDiff < DateUtils.MINUTE_IN_MILLIS) {
|
|
return getLessThanAMinuteOldTimeString(abbreviated);
|
|
} else if (!minPeriodToday && timeDiff < DateUtils.HOUR_IN_MILLIS) {
|
|
return getLessThanAnHourOldTimeString(timeDiff, flags);
|
|
} else if (getNumberOfDaysPassed(time, now) == 0) {
|
|
return getTodayTimeStamp(time, flags);
|
|
} else if (timeDiff < DateUtils.WEEK_IN_MILLIS) {
|
|
return getThisWeekTimestamp(time, locale, abbreviated, flags);
|
|
} else if (timeDiff < DateUtils.YEAR_IN_MILLIS) {
|
|
return getThisYearTimestamp(time, locale, abbreviated, flags);
|
|
} else {
|
|
return getOlderThanAYearTimestamp(time, locale, abbreviated, flags);
|
|
}
|
|
}
|
|
|
|
private static CharSequence getLessThanAMinuteOldTimeString(
|
|
final boolean abbreviated) {
|
|
return getContext().getResources().getText(
|
|
abbreviated ? R.string.posted_just_now : R.string.posted_now);
|
|
}
|
|
|
|
private static CharSequence getLessThanAnHourOldTimeString(final long timeDiff,
|
|
final int flags) {
|
|
final long count = (timeDiff / MINUTE_IN_MILLIS);
|
|
final String format = getContext().getResources().getQuantityString(
|
|
R.plurals.num_minutes_ago, (int) count);
|
|
return String.format(format, count);
|
|
}
|
|
|
|
private static CharSequence getTodayTimeStamp(final long time, final int flags) {
|
|
return DateUtils.formatDateTime(getContext(), time,
|
|
DateUtils.FORMAT_SHOW_TIME | flags);
|
|
}
|
|
|
|
private static CharSequence getExplicitFormattedTime(final long time, final int flags,
|
|
final String format24, final String format12) {
|
|
SimpleDateFormat formatter;
|
|
if ((flags & FORCE_24_HOUR) == FORCE_24_HOUR) {
|
|
formatter = new SimpleDateFormat(format24);
|
|
} else {
|
|
formatter = new SimpleDateFormat(format12);
|
|
}
|
|
return formatter.format(new Date(time));
|
|
}
|
|
|
|
private static CharSequence getThisWeekTimestamp(final long time,
|
|
final Locale locale, final boolean abbreviated, final int flags) {
|
|
final Context context = getContext();
|
|
if (abbreviated) {
|
|
return DateUtils.formatDateTime(context, time,
|
|
DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_WEEKDAY | flags);
|
|
} else {
|
|
if (locale.equals(Locale.US)) {
|
|
return getExplicitFormattedTime(time, flags, "EEE HH:mm", "EEE h:mmaa");
|
|
} else {
|
|
return DateUtils.formatDateTime(context, time,
|
|
DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_TIME
|
|
| DateUtils.FORMAT_ABBREV_WEEKDAY
|
|
| flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static CharSequence getThisYearTimestamp(final long time, final Locale locale,
|
|
final boolean abbreviated, final int flags) {
|
|
final Context context = getContext();
|
|
if (abbreviated) {
|
|
return DateUtils.formatDateTime(context, time,
|
|
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH
|
|
| DateUtils.FORMAT_NO_YEAR | flags);
|
|
} else {
|
|
if (locale.equals(Locale.US)) {
|
|
return getExplicitFormattedTime(time, flags, "MMM d, HH:mm", "MMM d, h:mmaa");
|
|
} else {
|
|
return DateUtils.formatDateTime(context, time,
|
|
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME
|
|
| DateUtils.FORMAT_ABBREV_MONTH
|
|
| DateUtils.FORMAT_NO_YEAR
|
|
| flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static CharSequence getOlderThanAYearTimestamp(final long time,
|
|
final Locale locale, final boolean abbreviated, final int flags) {
|
|
final Context context = getContext();
|
|
if (abbreviated) {
|
|
if (locale.equals(Locale.US)) {
|
|
return getExplicitFormattedTime(time, flags, "M/d/yy", "M/d/yy");
|
|
} else {
|
|
return DateUtils.formatDateTime(context, time,
|
|
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
|
|
| DateUtils.FORMAT_NUMERIC_DATE);
|
|
}
|
|
} else {
|
|
if (locale.equals(Locale.US)) {
|
|
return getExplicitFormattedTime(time, flags, "M/d/yy, HH:mm", "M/d/yy, h:mmaa");
|
|
} else {
|
|
return DateUtils.formatDateTime(context, time,
|
|
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME
|
|
| DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_YEAR
|
|
| flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static CharSequence getShortRelativeTimeSpanString(final long time) {
|
|
final long now = System.currentTimeMillis();
|
|
final long duration = Math.abs(now - time);
|
|
|
|
int resId;
|
|
long count;
|
|
|
|
final Context context = getContext();
|
|
|
|
if (duration < HOUR_IN_MILLIS) {
|
|
count = duration / MINUTE_IN_MILLIS;
|
|
resId = R.plurals.num_minutes_ago;
|
|
} else if (duration < DAY_IN_MILLIS) {
|
|
count = duration / HOUR_IN_MILLIS;
|
|
resId = R.plurals.num_hours_ago;
|
|
} else if (duration < WEEK_IN_MILLIS) {
|
|
count = getNumberOfDaysPassed(time, now);
|
|
resId = R.plurals.num_days_ago;
|
|
} else {
|
|
// Although we won't be showing a time, there is a bug on some devices that use
|
|
// the passed in context. On these devices, passing in a {@code null} context
|
|
// here will generate an NPE. See b/5657035.
|
|
return DateUtils.formatDateRange(context, time, time,
|
|
DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_ABBREV_RELATIVE);
|
|
}
|
|
|
|
final String format = context.getResources().getQuantityString(resId, (int) count);
|
|
return String.format(format, count);
|
|
}
|
|
|
|
private static long getNumberOfDaysPassed(final long date1, final long date2) {
|
|
LocalDateTime dateTime1 = LocalDateTime.ofInstant(Instant.ofEpochMilli(date1),
|
|
ZoneId.systemDefault());
|
|
LocalDateTime dateTime2 = LocalDateTime.ofInstant(Instant.ofEpochMilli(date2),
|
|
ZoneId.systemDefault());
|
|
return Math.abs(ChronoUnit.DAYS.between(dateTime2, dateTime1));
|
|
}
|
|
}
|