1899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulospackage com.android.contacts.interactions; 2899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 3899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulosimport com.android.contacts.R; 4899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 5899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulosimport android.content.Context; 6899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulosimport android.content.res.Resources; 7899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulosimport android.text.format.DateFormat; 8899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulosimport android.text.format.DateUtils; 9899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulosimport android.text.format.Time; 10899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 11899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulosimport java.util.Formatter; 12899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulosimport java.util.Locale; 13899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 14899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos/** 15899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * The following methods were pulled from 16899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * {@link com.android.calendar.EventInfoFragment.updateEvent(View view)} 17899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * TODO: Move this to frameworks/opt 18899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos */ 19899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulospublic class CalendarInteractionUtils { 20899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 21899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // Using int constants as a return value instead of an enum to minimize resources. 22899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos private static final int TODAY = 1; 23899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos private static final int TOMORROW = 2; 24899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos private static final int NONE = 0; 25899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 26899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos /** 27899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * Returns a string description of the specified time interval. 28899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos */ 29899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos public static String getDisplayedDatetime(long startMillis, long endMillis, long currentMillis, 30899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos String localTimezone, boolean allDay, Context context) { 31899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // Configure date/time formatting. 32899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos int flagsDate = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; 33899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos int flagsTime = DateUtils.FORMAT_SHOW_TIME; 34899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (DateFormat.is24HourFormat(context)) { 35899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos flagsTime |= DateUtils.FORMAT_24HOUR; 36899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 37899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 38899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos Time currentTime = new Time(localTimezone); 39899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos currentTime.set(currentMillis); 40899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos Resources resources = context.getResources(); 41899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos String datetimeString = null; 42899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (allDay) { 43899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // All day events require special timezone adjustment. 44899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos long localStartMillis = convertAlldayUtcToLocal(null, startMillis, localTimezone); 45899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos long localEndMillis = convertAlldayUtcToLocal(null, endMillis, localTimezone); 46899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (singleDayEvent(localStartMillis, localEndMillis, currentTime.gmtoff)) { 47899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // If possible, use "Today" or "Tomorrow" instead of a full date string. 48899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), 49899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos localStartMillis, currentMillis, currentTime.gmtoff); 50899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (TODAY == todayOrTomorrow) { 51899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos datetimeString = resources.getString(R.string.today); 52899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } else if (TOMORROW == todayOrTomorrow) { 53899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos datetimeString = resources.getString(R.string.tomorrow); 54899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 55899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 56899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (datetimeString == null) { 57899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // For multi-day allday events or single-day all-day events that are not 58899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // today or tomorrow, use framework formatter. 59899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault()); 60899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos datetimeString = DateUtils.formatDateRange(context, f, startMillis, 61899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos endMillis, flagsDate, Time.TIMEZONE_UTC).toString(); 62899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 63899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } else { 64899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (singleDayEvent(startMillis, endMillis, currentTime.gmtoff)) { 65899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // Format the time. 66899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos String timeString = formatDateRange(context, startMillis, endMillis, 67899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos flagsTime); 68899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 69899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // If possible, use "Today" or "Tomorrow" instead of a full date string. 70899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), startMillis, 71899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos currentMillis, currentTime.gmtoff); 72899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (TODAY == todayOrTomorrow) { 73899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // Example: "Today at 1:00pm - 2:00 pm" 74899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos datetimeString = resources.getString(R.string.today_at_time_fmt, 75899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos timeString); 76899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } else if (TOMORROW == todayOrTomorrow) { 77899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // Example: "Tomorrow at 1:00pm - 2:00 pm" 78899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos datetimeString = resources.getString(R.string.tomorrow_at_time_fmt, 79899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos timeString); 80899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } else { 81899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // Format the full date. Example: "Thursday, April 12, 1:00pm - 2:00pm" 82899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos String dateString = formatDateRange(context, startMillis, endMillis, 83899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos flagsDate); 84899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos datetimeString = resources.getString(R.string.date_time_fmt, dateString, 85899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos timeString); 86899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 87899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } else { 88899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // For multiday events, shorten day/month names. 89899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // Example format: "Fri Apr 6, 5:00pm - Sun, Apr 8, 6:00pm" 90899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos int flagsDatetime = flagsDate | flagsTime | DateUtils.FORMAT_ABBREV_MONTH | 91899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos DateUtils.FORMAT_ABBREV_WEEKDAY; 92899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos datetimeString = formatDateRange(context, startMillis, endMillis, 93899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos flagsDatetime); 94899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 95899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 96899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos return datetimeString; 97899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 98899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 99899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos /** 100899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * Convert given UTC time into current local time. This assumes it is for an 101899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * allday event and will adjust the time to be on a midnight boundary. 102899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * 103899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * @param recycle Time object to recycle, otherwise null. 104899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * @param utcTime Time to convert, in UTC. 105899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * @param tz The time zone to convert this time to. 106899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos */ 107899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos private static long convertAlldayUtcToLocal(Time recycle, long utcTime, String tz) { 108899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (recycle == null) { 109899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos recycle = new Time(); 110899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 111899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos recycle.timezone = Time.TIMEZONE_UTC; 112899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos recycle.set(utcTime); 113899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos recycle.timezone = tz; 114899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos return recycle.normalize(true); 115899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 116899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 117899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos public static long convertAlldayLocalToUTC(Time recycle, long localTime, String tz) { 118899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (recycle == null) { 119899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos recycle = new Time(); 120899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 121899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos recycle.timezone = tz; 122899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos recycle.set(localTime); 123899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos recycle.timezone = Time.TIMEZONE_UTC; 124899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos return recycle.normalize(true); 125899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 126899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 127899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos /** 128899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * Returns whether the specified time interval is in a single day. 129899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos */ 130899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos private static boolean singleDayEvent(long startMillis, long endMillis, long localGmtOffset) { 131899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (startMillis == endMillis) { 132899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos return true; 133899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 134899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 135899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // An event ending at midnight should still be a single-day event, so check 136899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos // time end-1. 137899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos int startDay = Time.getJulianDay(startMillis, localGmtOffset); 138899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos int endDay = Time.getJulianDay(endMillis - 1, localGmtOffset); 139899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos return startDay == endDay; 140899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 141899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 142899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos /** 143899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * Returns TODAY or TOMORROW if applicable. Otherwise returns NONE. 144899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos */ 145899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos private static int isTodayOrTomorrow(Resources r, long dayMillis, 146899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos long currentMillis, long localGmtOffset) { 147899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos int startDay = Time.getJulianDay(dayMillis, localGmtOffset); 148899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos int currentDay = Time.getJulianDay(currentMillis, localGmtOffset); 149899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 150899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos int days = startDay - currentDay; 151899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (days == 1) { 152899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos return TOMORROW; 153899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } else if (days == 0) { 154899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos return TODAY; 155899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } else { 156899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos return NONE; 157899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 158899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 159899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos 160899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos /** 161899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * Formats a date or a time range according to the local conventions. 162899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * 163899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * This formats a date/time range using Calendar's time zone and the 164899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * local conventions for the region of the device. 165899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * 166899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * If the {@link DateUtils#FORMAT_UTC} flag is used it will pass in 167899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * the UTC time zone instead. 168899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * 169899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * @param context the context is required only if the time is shown 170899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * @param startMillis the start time in UTC milliseconds 171899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * @param endMillis the end time in UTC milliseconds 172899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * @param flags a bit mask of options See 173899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * {@link DateUtils#formatDateRange(Context, Formatter, long, long, int, String) formatDateRange} 174899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos * @return a string containing the formatted date/time range. 175899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos */ 176899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos private static String formatDateRange(Context context, long startMillis, 177899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos long endMillis, int flags) { 178899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos String date; 179899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos String tz; 180899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if ((flags & DateUtils.FORMAT_UTC) != 0) { 181899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos tz = Time.TIMEZONE_UTC; 182899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } else { 183899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos tz = Time.getCurrentTimezone(); 184899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 185899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos StringBuilder sb = new StringBuilder(50); 186899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos Formatter f = new Formatter(sb, Locale.getDefault()); 187899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos sb.setLength(0); 188899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos date = DateUtils.formatDateRange(context, f, startMillis, endMillis, flags, 189899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos tz).toString(); 190899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos return date; 191899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 192899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos} 193