15442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff/*
25442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff * Copyright (C) 2006 The Android Open Source Project
35442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff *
45442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff * Licensed under the Apache License, Version 2.0 (the "License");
55442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff * you may not use this file except in compliance with the License.
65442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff * You may obtain a copy of the License at
75442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff *
85442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff *      http://www.apache.org/licenses/LICENSE-2.0
95442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff *
105442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff * Unless required by applicable law or agreed to in writing, software
115442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff * distributed under the License is distributed on an "AS IS" BASIS,
125442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff * See the License for the specific language governing permissions and
145442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff * limitations under the License.
155442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff */
165442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff
175442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriffpackage com.android.calendar;
185442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff
19b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chanimport android.content.Context;
205442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriffimport android.content.res.Resources;
215442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriffimport android.text.format.DateUtils;
22b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chanimport android.text.format.Time;
23b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chanimport android.util.TimeFormatException;
24b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan
25b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chanimport com.android.calendarcommon2.EventRecurrence;
265442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff
275442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriffimport java.util.Calendar;
285442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff
295442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriffpublic class EventRecurrenceFormatter
305442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff{
3186693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein
3286693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein    private static int[] mMonthRepeatByDayOfWeekIds;
3386693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein    private static String[][] mMonthRepeatByDayOfWeekStrs;
3486693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein
35b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan    public static String getRepeatString(Context context, Resources r, EventRecurrence recurrence,
36b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan            boolean includeEndString) {
37b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan        String endString = "";
38b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan        if (includeEndString) {
39b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan            StringBuilder sb = new StringBuilder();
40b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan            if (recurrence.until != null) {
41b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                try {
42b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    Time t = new Time();
43b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    t.parse(recurrence.until);
44b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    final String dateStr = DateUtils.formatDateTime(context,
45b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                            t.toMillis(false), DateUtils.FORMAT_NUMERIC_DATE);
46b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    sb.append(r.getString(R.string.endByDate, dateStr));
47b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                } catch (TimeFormatException e) {
48b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                }
49b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan            }
50b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan
51b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan            if (recurrence.count > 0) {
52b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                sb.append(r.getQuantityString(R.plurals.endByCount, recurrence.count,
53b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        recurrence.count));
54b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan            }
55b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan            endString = sb.toString();
56b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan        }
57b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan
585442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff        // TODO Implement "Until" portion of string, as well as custom settings
59b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan        int interval = recurrence.interval <= 1 ? 1 : recurrence.interval;
605442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff        switch (recurrence.freq) {
615442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff            case EventRecurrence.DAILY:
62b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                return r.getQuantityString(R.plurals.daily, interval, interval) + endString;
635442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff            case EventRecurrence.WEEKLY: {
645442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff                if (recurrence.repeatsOnEveryWeekDay()) {
65b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    return r.getString(R.string.every_weekday) + endString;
665442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff                } else {
67b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    String string;
68b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan
69b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    int dayOfWeekLength = DateUtils.LENGTH_MEDIUM;
70b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    if (recurrence.bydayCount == 1) {
71b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        dayOfWeekLength = DateUtils.LENGTH_LONG;
72b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    }
73b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan
745442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff                    StringBuilder days = new StringBuilder();
755442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff
765442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff                    // Do one less iteration in the loop so the last element is added out of the
775442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff                    // loop. This is done so the comma is not placed after the last item.
78b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan
79b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    if (recurrence.bydayCount > 0) {
80b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        int count = recurrence.bydayCount - 1;
815442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff                        for (int i = 0 ; i < count ; i++) {
82b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                            days.append(dayToString(recurrence.byday[i], dayOfWeekLength));
83b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                            days.append(", ");
845442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff                        }
85b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        days.append(dayToString(recurrence.byday[count], dayOfWeekLength));
865442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff
87b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        string = days.toString();
88b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    } else {
89b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        // There is no "BYDAY" specifier, so use the day of the
90b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        // first event.  For this to work, the setStartDate()
91b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        // method must have been used by the caller to set the
92b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        // date of the first event in the recurrence.
93b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        if (recurrence.startDate == null) {
94b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                            return null;
95b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        }
965442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff
97b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        int day = EventRecurrence.timeDay2Day(recurrence.startDate.weekDay);
98b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                        string = dayToString(day, DateUtils.LENGTH_LONG);
995442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff                    }
100b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    return r.getQuantityString(R.plurals.weekly, interval, interval, string)
101b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                            + endString;
1025442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff                }
1035442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff            }
1045442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff            case EventRecurrence.MONTHLY: {
105b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                if (recurrence.bydayCount == 1) {
10686693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein                    int weekday = recurrence.startDate.weekDay;
10786693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein                    // Cache this stuff so we won't have to redo work again later.
10886693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein                    cacheMonthRepeatStrings(r, weekday);
109b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                    int dayNumber = (recurrence.startDate.monthDay - 1) / 7;
11086693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein                    StringBuilder sb = new StringBuilder();
11186693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein                    sb.append(r.getString(R.string.monthly));
11286693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein                    sb.append(" (");
11386693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein                    sb.append(mMonthRepeatByDayOfWeekStrs[weekday][dayNumber]);
11486693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein                    sb.append(")");
11586693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein                    sb.append(endString);
11686693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein                    return sb.toString();
117b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                }
118b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                return r.getString(R.string.monthly) + endString;
1195442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff            }
1205442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff            case EventRecurrence.YEARLY:
121b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan                return r.getString(R.string.yearly_plain) + endString;
1225442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff        }
1235442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff
1245442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff        return null;
1255442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff    }
1265442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff
12786693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein    private static void cacheMonthRepeatStrings(Resources r, int weekday) {
12886693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein        if (mMonthRepeatByDayOfWeekIds == null) {
12986693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein            mMonthRepeatByDayOfWeekIds = new int[7];
13086693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein            mMonthRepeatByDayOfWeekIds[0] = R.array.repeat_by_nth_sun;
13186693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein            mMonthRepeatByDayOfWeekIds[1] = R.array.repeat_by_nth_mon;
13286693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein            mMonthRepeatByDayOfWeekIds[2] = R.array.repeat_by_nth_tues;
13386693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein            mMonthRepeatByDayOfWeekIds[3] = R.array.repeat_by_nth_wed;
13486693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein            mMonthRepeatByDayOfWeekIds[4] = R.array.repeat_by_nth_thurs;
13586693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein            mMonthRepeatByDayOfWeekIds[5] = R.array.repeat_by_nth_fri;
13686693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein            mMonthRepeatByDayOfWeekIds[6] = R.array.repeat_by_nth_sat;
13786693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein        }
13886693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein        if (mMonthRepeatByDayOfWeekStrs == null) {
13986693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein            mMonthRepeatByDayOfWeekStrs = new String[7][];
14086693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein        }
14186693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein        if (mMonthRepeatByDayOfWeekStrs[weekday] == null) {
14286693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein            mMonthRepeatByDayOfWeekStrs[weekday] =
14386693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein                    r.getStringArray(mMonthRepeatByDayOfWeekIds[weekday]);
14486693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein        }
14586693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein    }
14686693b2f19243b64f5bb7a363bd756453f2b7d6cSam Blitzstein
1475442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff    /**
1485442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff     * Converts day of week to a String.
1495442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff     * @param day a EventRecurrence constant
1505442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff     * @return day of week as a string
1515442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff     */
152b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan    private static String dayToString(int day, int dayOfWeekLength) {
153b21c638ca11d9be3a3d9e7d28223bb4a3dab5f15Michael Chan        return DateUtils.getDayOfWeekString(dayToUtilDay(day), dayOfWeekLength);
1545442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff    }
1555442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff
1565442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff    /**
1575442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff     * Converts EventRecurrence's day of week to DateUtil's day of week.
1585442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff     * @param day of week as an EventRecurrence value
1595442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff     * @return day of week as a DateUtil value.
1605442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff     */
1615442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff    private static int dayToUtilDay(int day) {
1625442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff        switch (day) {
163fed57ecf8784c761b61d40e87c88a2628142d1ddErik        case EventRecurrence.SU: return Calendar.SUNDAY;
164fed57ecf8784c761b61d40e87c88a2628142d1ddErik        case EventRecurrence.MO: return Calendar.MONDAY;
165fed57ecf8784c761b61d40e87c88a2628142d1ddErik        case EventRecurrence.TU: return Calendar.TUESDAY;
166fed57ecf8784c761b61d40e87c88a2628142d1ddErik        case EventRecurrence.WE: return Calendar.WEDNESDAY;
167fed57ecf8784c761b61d40e87c88a2628142d1ddErik        case EventRecurrence.TH: return Calendar.THURSDAY;
168fed57ecf8784c761b61d40e87c88a2628142d1ddErik        case EventRecurrence.FR: return Calendar.FRIDAY;
169fed57ecf8784c761b61d40e87c88a2628142d1ddErik        case EventRecurrence.SA: return Calendar.SATURDAY;
1705442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff        default: throw new IllegalArgumentException("bad day argument: " + day);
1715442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff        }
1725442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff    }
1735442a16f740a9c98d1d53627cf7ed00098b1bd24Ken Shirriff}
174