blob: 36a562183a79ce1230dee2f312f7508d8329c526 [file] [log] [blame]
Paul Soulos899aa212014-06-11 12:04:43 -07001package com.android.contacts.interactions;
2
Paul Soulos899aa212014-06-11 12:04:43 -07003import android.content.Context;
4import android.content.res.Resources;
5import android.text.format.DateFormat;
6import android.text.format.DateUtils;
7import android.text.format.Time;
8
Gary Mai0a49afa2016-12-05 15:53:58 -08009import com.android.contacts.R;
10
Paul Soulos899aa212014-06-11 12:04:43 -070011import java.util.Formatter;
12import 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 */
19public 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}