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