Paul Soulos | 899aa21 | 2014-06-11 12:04:43 -0700 | [diff] [blame] | 1 | package com.android.contacts.interactions; |
| 2 | |
Paul Soulos | 899aa21 | 2014-06-11 12:04:43 -0700 | [diff] [blame] | 3 | import android.content.Context; |
| 4 | import android.content.res.Resources; |
| 5 | import android.text.format.DateFormat; |
| 6 | import android.text.format.DateUtils; |
| 7 | import android.text.format.Time; |
| 8 | |
Gary Mai | 0a49afa | 2016-12-05 15:53:58 -0800 | [diff] [blame] | 9 | import com.android.contacts.R; |
| 10 | |
Paul Soulos | 899aa21 | 2014-06-11 12:04:43 -0700 | [diff] [blame] | 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 | */ |
| 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 | */ |
| 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 | |
| 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 | */ |
| 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 | */ |
| 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 | */ |
| 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 | } |