| package com.android.contacts.interactions; |
| |
| import com.android.contacts.R; |
| |
| import android.content.Context; |
| import android.content.res.Resources; |
| import android.text.format.DateFormat; |
| import android.text.format.DateUtils; |
| import android.text.format.Time; |
| |
| import java.util.Formatter; |
| import java.util.Locale; |
| |
| /** |
| * The following methods were pulled from |
| * {@link com.android.calendar.EventInfoFragment.updateEvent(View view)} |
| * TODO: Move this to frameworks/opt |
| */ |
| public class CalendarInteractionUtils { |
| |
| // Using int constants as a return value instead of an enum to minimize resources. |
| private static final int TODAY = 1; |
| private static final int TOMORROW = 2; |
| private static final int NONE = 0; |
| |
| /** |
| * Returns a string description of the specified time interval. |
| */ |
| public static String getDisplayedDatetime(long startMillis, long endMillis, long currentMillis, |
| String localTimezone, boolean allDay, Context context) { |
| // Configure date/time formatting. |
| int flagsDate = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; |
| int flagsTime = DateUtils.FORMAT_SHOW_TIME; |
| if (DateFormat.is24HourFormat(context)) { |
| flagsTime |= DateUtils.FORMAT_24HOUR; |
| } |
| |
| Time currentTime = new Time(localTimezone); |
| currentTime.set(currentMillis); |
| Resources resources = context.getResources(); |
| String datetimeString = null; |
| if (allDay) { |
| // All day events require special timezone adjustment. |
| long localStartMillis = convertAlldayUtcToLocal(null, startMillis, localTimezone); |
| long localEndMillis = convertAlldayUtcToLocal(null, endMillis, localTimezone); |
| if (singleDayEvent(localStartMillis, localEndMillis, currentTime.gmtoff)) { |
| // If possible, use "Today" or "Tomorrow" instead of a full date string. |
| int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), |
| localStartMillis, currentMillis, currentTime.gmtoff); |
| if (TODAY == todayOrTomorrow) { |
| datetimeString = resources.getString(R.string.today); |
| } else if (TOMORROW == todayOrTomorrow) { |
| datetimeString = resources.getString(R.string.tomorrow); |
| } |
| } |
| if (datetimeString == null) { |
| // For multi-day allday events or single-day all-day events that are not |
| // today or tomorrow, use framework formatter. |
| Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault()); |
| datetimeString = DateUtils.formatDateRange(context, f, startMillis, |
| endMillis, flagsDate, Time.TIMEZONE_UTC).toString(); |
| } |
| } else { |
| if (singleDayEvent(startMillis, endMillis, currentTime.gmtoff)) { |
| // Format the time. |
| String timeString = formatDateRange(context, startMillis, endMillis, |
| flagsTime); |
| |
| // If possible, use "Today" or "Tomorrow" instead of a full date string. |
| int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), startMillis, |
| currentMillis, currentTime.gmtoff); |
| if (TODAY == todayOrTomorrow) { |
| // Example: "Today at 1:00pm - 2:00 pm" |
| datetimeString = resources.getString(R.string.today_at_time_fmt, |
| timeString); |
| } else if (TOMORROW == todayOrTomorrow) { |
| // Example: "Tomorrow at 1:00pm - 2:00 pm" |
| datetimeString = resources.getString(R.string.tomorrow_at_time_fmt, |
| timeString); |
| } else { |
| // Format the full date. Example: "Thursday, April 12, 1:00pm - 2:00pm" |
| String dateString = formatDateRange(context, startMillis, endMillis, |
| flagsDate); |
| datetimeString = resources.getString(R.string.date_time_fmt, dateString, |
| timeString); |
| } |
| } else { |
| // For multiday events, shorten day/month names. |
| // Example format: "Fri Apr 6, 5:00pm - Sun, Apr 8, 6:00pm" |
| int flagsDatetime = flagsDate | flagsTime | DateUtils.FORMAT_ABBREV_MONTH | |
| DateUtils.FORMAT_ABBREV_WEEKDAY; |
| datetimeString = formatDateRange(context, startMillis, endMillis, |
| flagsDatetime); |
| } |
| } |
| return datetimeString; |
| } |
| |
| /** |
| * Convert given UTC time into current local time. This assumes it is for an |
| * allday event and will adjust the time to be on a midnight boundary. |
| * |
| * @param recycle Time object to recycle, otherwise null. |
| * @param utcTime Time to convert, in UTC. |
| * @param tz The time zone to convert this time to. |
| */ |
| private static long convertAlldayUtcToLocal(Time recycle, long utcTime, String tz) { |
| if (recycle == null) { |
| recycle = new Time(); |
| } |
| recycle.timezone = Time.TIMEZONE_UTC; |
| recycle.set(utcTime); |
| recycle.timezone = tz; |
| return recycle.normalize(true); |
| } |
| |
| public static long convertAlldayLocalToUTC(Time recycle, long localTime, String tz) { |
| if (recycle == null) { |
| recycle = new Time(); |
| } |
| recycle.timezone = tz; |
| recycle.set(localTime); |
| recycle.timezone = Time.TIMEZONE_UTC; |
| return recycle.normalize(true); |
| } |
| |
| /** |
| * Returns whether the specified time interval is in a single day. |
| */ |
| private static boolean singleDayEvent(long startMillis, long endMillis, long localGmtOffset) { |
| if (startMillis == endMillis) { |
| return true; |
| } |
| |
| // An event ending at midnight should still be a single-day event, so check |
| // time end-1. |
| int startDay = Time.getJulianDay(startMillis, localGmtOffset); |
| int endDay = Time.getJulianDay(endMillis - 1, localGmtOffset); |
| return startDay == endDay; |
| } |
| |
| /** |
| * Returns TODAY or TOMORROW if applicable. Otherwise returns NONE. |
| */ |
| private static int isTodayOrTomorrow(Resources r, long dayMillis, |
| long currentMillis, long localGmtOffset) { |
| int startDay = Time.getJulianDay(dayMillis, localGmtOffset); |
| int currentDay = Time.getJulianDay(currentMillis, localGmtOffset); |
| |
| int days = startDay - currentDay; |
| if (days == 1) { |
| return TOMORROW; |
| } else if (days == 0) { |
| return TODAY; |
| } else { |
| return NONE; |
| } |
| } |
| |
| /** |
| * Formats a date or a time range according to the local conventions. |
| * |
| * This formats a date/time range using Calendar's time zone and the |
| * local conventions for the region of the device. |
| * |
| * If the {@link DateUtils#FORMAT_UTC} flag is used it will pass in |
| * the UTC time zone instead. |
| * |
| * @param context the context is required only if the time is shown |
| * @param startMillis the start time in UTC milliseconds |
| * @param endMillis the end time in UTC milliseconds |
| * @param flags a bit mask of options See |
| * {@link DateUtils#formatDateRange(Context, Formatter, long, long, int, String) formatDateRange} |
| * @return a string containing the formatted date/time range. |
| */ |
| private static String formatDateRange(Context context, long startMillis, |
| long endMillis, int flags) { |
| String date; |
| String tz; |
| if ((flags & DateUtils.FORMAT_UTC) != 0) { |
| tz = Time.TIMEZONE_UTC; |
| } else { |
| tz = Time.getCurrentTimezone(); |
| } |
| StringBuilder sb = new StringBuilder(50); |
| Formatter f = new Formatter(sb, Locale.getDefault()); |
| sb.setLength(0); |
| date = DateUtils.formatDateRange(context, f, startMillis, endMillis, flags, |
| tz).toString(); |
| return date; |
| } |
| } |