1package com.android.contacts.interactions;
2
3import com.android.contacts.R;
4
5import android.content.Context;
6import android.content.res.Resources;
7import android.text.format.DateFormat;
8import android.text.format.DateUtils;
9import android.text.format.Time;
10
11import 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}
193