1 package com.android.contacts.interactions; 2 3 import com.android.contacts.R; 4 5 import android.content.Context; 6 import android.content.res.Resources; 7 import android.text.format.DateFormat; 8 import android.text.format.DateUtils; 9 import android.text.format.Time; 10 11 import java.util.Formatter; 12 import java.util.Locale; 13 14 /** 15 * The following methods were pulled from 16 * {@link com.android.calendar.EventInfoFragment.updateEvent(View view)} 17 * TODO: Move this to frameworks/opt 18 */ 19 public class CalendarInteractionUtils { 20 21 // Using int constants as a return value instead of an enum to minimize resources. 22 private static final int TODAY = 1; 23 private static final int TOMORROW = 2; 24 private static final int NONE = 0; 25 26 /** 27 * Returns a string description of the specified time interval. 28 */ getDisplayedDatetime(long startMillis, long endMillis, long currentMillis, String localTimezone, boolean allDay, Context context)29 public static String getDisplayedDatetime(long startMillis, long endMillis, long currentMillis, 30 String localTimezone, boolean allDay, Context context) { 31 // Configure date/time formatting. 32 int flagsDate = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; 33 int flagsTime = DateUtils.FORMAT_SHOW_TIME; 34 if (DateFormat.is24HourFormat(context)) { 35 flagsTime |= DateUtils.FORMAT_24HOUR; 36 } 37 38 Time currentTime = new Time(localTimezone); 39 currentTime.set(currentMillis); 40 Resources resources = context.getResources(); 41 String datetimeString = null; 42 if (allDay) { 43 // All day events require special timezone adjustment. 44 long localStartMillis = convertAlldayUtcToLocal(null, startMillis, localTimezone); 45 long localEndMillis = convertAlldayUtcToLocal(null, endMillis, localTimezone); 46 if (singleDayEvent(localStartMillis, localEndMillis, currentTime.gmtoff)) { 47 // If possible, use "Today" or "Tomorrow" instead of a full date string. 48 int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), 49 localStartMillis, currentMillis, currentTime.gmtoff); 50 if (TODAY == todayOrTomorrow) { 51 datetimeString = resources.getString(R.string.today); 52 } else if (TOMORROW == todayOrTomorrow) { 53 datetimeString = resources.getString(R.string.tomorrow); 54 } 55 } 56 if (datetimeString == null) { 57 // For multi-day allday events or single-day all-day events that are not 58 // today or tomorrow, use framework formatter. 59 Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault()); 60 datetimeString = DateUtils.formatDateRange(context, f, startMillis, 61 endMillis, flagsDate, Time.TIMEZONE_UTC).toString(); 62 } 63 } else { 64 if (singleDayEvent(startMillis, endMillis, currentTime.gmtoff)) { 65 // Format the time. 66 String timeString = formatDateRange(context, startMillis, endMillis, 67 flagsTime); 68 69 // If possible, use "Today" or "Tomorrow" instead of a full date string. 70 int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), startMillis, 71 currentMillis, currentTime.gmtoff); 72 if (TODAY == todayOrTomorrow) { 73 // Example: "Today at 1:00pm - 2:00 pm" 74 datetimeString = resources.getString(R.string.today_at_time_fmt, 75 timeString); 76 } else if (TOMORROW == todayOrTomorrow) { 77 // Example: "Tomorrow at 1:00pm - 2:00 pm" 78 datetimeString = resources.getString(R.string.tomorrow_at_time_fmt, 79 timeString); 80 } else { 81 // Format the full date. Example: "Thursday, April 12, 1:00pm - 2:00pm" 82 String dateString = formatDateRange(context, startMillis, endMillis, 83 flagsDate); 84 datetimeString = resources.getString(R.string.date_time_fmt, dateString, 85 timeString); 86 } 87 } else { 88 // For multiday events, shorten day/month names. 89 // Example format: "Fri Apr 6, 5:00pm - Sun, Apr 8, 6:00pm" 90 int flagsDatetime = flagsDate | flagsTime | DateUtils.FORMAT_ABBREV_MONTH | 91 DateUtils.FORMAT_ABBREV_WEEKDAY; 92 datetimeString = formatDateRange(context, startMillis, endMillis, 93 flagsDatetime); 94 } 95 } 96 return datetimeString; 97 } 98 99 /** 100 * Convert given UTC time into current local time. This assumes it is for an 101 * allday event and will adjust the time to be on a midnight boundary. 102 * 103 * @param recycle Time object to recycle, otherwise null. 104 * @param utcTime Time to convert, in UTC. 105 * @param tz The time zone to convert this time to. 106 */ convertAlldayUtcToLocal(Time recycle, long utcTime, String tz)107 private static long convertAlldayUtcToLocal(Time recycle, long utcTime, String tz) { 108 if (recycle == null) { 109 recycle = new Time(); 110 } 111 recycle.timezone = Time.TIMEZONE_UTC; 112 recycle.set(utcTime); 113 recycle.timezone = tz; 114 return recycle.normalize(true); 115 } 116 convertAlldayLocalToUTC(Time recycle, long localTime, String tz)117 public static long convertAlldayLocalToUTC(Time recycle, long localTime, String tz) { 118 if (recycle == null) { 119 recycle = new Time(); 120 } 121 recycle.timezone = tz; 122 recycle.set(localTime); 123 recycle.timezone = Time.TIMEZONE_UTC; 124 return recycle.normalize(true); 125 } 126 127 /** 128 * Returns whether the specified time interval is in a single day. 129 */ singleDayEvent(long startMillis, long endMillis, long localGmtOffset)130 private static boolean singleDayEvent(long startMillis, long endMillis, long localGmtOffset) { 131 if (startMillis == endMillis) { 132 return true; 133 } 134 135 // An event ending at midnight should still be a single-day event, so check 136 // time end-1. 137 int startDay = Time.getJulianDay(startMillis, localGmtOffset); 138 int endDay = Time.getJulianDay(endMillis - 1, localGmtOffset); 139 return startDay == endDay; 140 } 141 142 /** 143 * Returns TODAY or TOMORROW if applicable. Otherwise returns NONE. 144 */ isTodayOrTomorrow(Resources r, long dayMillis, long currentMillis, long localGmtOffset)145 private static int isTodayOrTomorrow(Resources r, long dayMillis, 146 long currentMillis, long localGmtOffset) { 147 int startDay = Time.getJulianDay(dayMillis, localGmtOffset); 148 int currentDay = Time.getJulianDay(currentMillis, localGmtOffset); 149 150 int days = startDay - currentDay; 151 if (days == 1) { 152 return TOMORROW; 153 } else if (days == 0) { 154 return TODAY; 155 } else { 156 return NONE; 157 } 158 } 159 160 /** 161 * Formats a date or a time range according to the local conventions. 162 * 163 * This formats a date/time range using Calendar's time zone and the 164 * local conventions for the region of the device. 165 * 166 * If the {@link DateUtils#FORMAT_UTC} flag is used it will pass in 167 * the UTC time zone instead. 168 * 169 * @param context the context is required only if the time is shown 170 * @param startMillis the start time in UTC milliseconds 171 * @param endMillis the end time in UTC milliseconds 172 * @param flags a bit mask of options See 173 * {@link DateUtils#formatDateRange(Context, Formatter, long, long, int, String) formatDateRange} 174 * @return a string containing the formatted date/time range. 175 */ formatDateRange(Context context, long startMillis, long endMillis, int flags)176 private static String formatDateRange(Context context, long startMillis, 177 long endMillis, int flags) { 178 String date; 179 String tz; 180 if ((flags & DateUtils.FORMAT_UTC) != 0) { 181 tz = Time.TIMEZONE_UTC; 182 } else { 183 tz = Time.getCurrentTimezone(); 184 } 185 StringBuilder sb = new StringBuilder(50); 186 Formatter f = new Formatter(sb, Locale.getDefault()); 187 sb.setLength(0); 188 date = DateUtils.formatDateRange(context, f, startMillis, endMillis, flags, 189 tz).toString(); 190 return date; 191 } 192 } 193