/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.pim; import android.content.res.Resources; import android.text.TextUtils; import android.text.format.Time; import java.util.Calendar; public class EventRecurrence { /** * Thrown when a recurrence string provided can not be parsed according * to RFC2445. */ public static class InvalidFormatException extends RuntimeException { InvalidFormatException(String s) { super(s); } } public EventRecurrence() { wkst = MO; } /** * Parse an iCalendar/RFC2445 recur type according to Section 4.3.10. */ public native void parse(String recur); public void setStartDate(Time date) { startDate = date; } public static final int SECONDLY = 1; public static final int MINUTELY = 2; public static final int HOURLY = 3; public static final int DAILY = 4; public static final int WEEKLY = 5; public static final int MONTHLY = 6; public static final int YEARLY = 7; public static final int SU = 0x00010000; public static final int MO = 0x00020000; public static final int TU = 0x00040000; public static final int WE = 0x00080000; public static final int TH = 0x00100000; public static final int FR = 0x00200000; public static final int SA = 0x00400000; public Time startDate; public int freq; public String until; public int count; public int interval; public int wkst; public int[] bysecond; public int bysecondCount; public int[] byminute; public int byminuteCount; public int[] byhour; public int byhourCount; public int[] byday; public int[] bydayNum; public int bydayCount; public int[] bymonthday; public int bymonthdayCount; public int[] byyearday; public int byyeardayCount; public int[] byweekno; public int byweeknoCount; public int[] bymonth; public int bymonthCount; public int[] bysetpos; public int bysetposCount; /** * Converts one of the Calendar.SUNDAY constants to the SU, MO, etc. * constants. btw, I think we should switch to those here too, to * get rid of this function, if possible. */ public static int calendarDay2Day(int day) { switch (day) { case Calendar.SUNDAY: return SU; case Calendar.MONDAY: return MO; case Calendar.TUESDAY: return TU; case Calendar.WEDNESDAY: return WE; case Calendar.THURSDAY: return TH; case Calendar.FRIDAY: return FR; case Calendar.SATURDAY: return SA; default: throw new RuntimeException("bad day of week: " + day); } } public static int timeDay2Day(int day) { switch (day) { case Time.SUNDAY: return SU; case Time.MONDAY: return MO; case Time.TUESDAY: return TU; case Time.WEDNESDAY: return WE; case Time.THURSDAY: return TH; case Time.FRIDAY: return FR; case Time.SATURDAY: return SA; default: throw new RuntimeException("bad day of week: " + day); } } public static int day2TimeDay(int day) { switch (day) { case SU: return Time.SUNDAY; case MO: return Time.MONDAY; case TU: return Time.TUESDAY; case WE: return Time.WEDNESDAY; case TH: return Time.THURSDAY; case FR: return Time.FRIDAY; case SA: return Time.SATURDAY; default: throw new RuntimeException("bad day of week: " + day); } } /** * Converts one of the SU, MO, etc. constants to the Calendar.SUNDAY * constants. btw, I think we should switch to those here too, to * get rid of this function, if possible. */ public static int day2CalendarDay(int day) { switch (day) { case SU: return Calendar.SUNDAY; case MO: return Calendar.MONDAY; case TU: return Calendar.TUESDAY; case WE: return Calendar.WEDNESDAY; case TH: return Calendar.THURSDAY; case FR: return Calendar.FRIDAY; case SA: return Calendar.SATURDAY; default: throw new RuntimeException("bad day of week: " + day); } } /** * Converts one of the internal day constants (SU, MO, etc.) to the * two-letter string representing that constant. * * @throws IllegalArgumentException Thrown if the day argument is not one of * the defined day constants. * * @param day one the internal constants SU, MO, etc. * @return the two-letter string for the day ("SU", "MO", etc.) */ private static String day2String(int day) { switch (day) { case SU: return "SU"; case MO: return "MO"; case TU: return "TU"; case WE: return "WE"; case TH: return "TH"; case FR: return "FR"; case SA: return "SA"; default: throw new IllegalArgumentException("bad day argument: " + day); } } private static void appendNumbers(StringBuilder s, String label, int count, int[] values) { if (count > 0) { s.append(label); count--; for (int i=0; i<count; i++) { s.append(values[i]); s.append(","); } s.append(values[count]); } } private void appendByDay(StringBuilder s, int i) { int n = this.bydayNum[i]; if (n != 0) { s.append(n); } String str = day2String(this.byday[i]); s.append(str); } @Override public String toString() { StringBuilder s = new StringBuilder(); s.append("FREQ="); switch (this.freq) { case SECONDLY: s.append("SECONDLY"); break; case MINUTELY: s.append("MINUTELY"); break; case HOURLY: s.append("HOURLY"); break; case DAILY: s.append("DAILY"); break; case WEEKLY: s.append("WEEKLY"); break; case MONTHLY: s.append("MONTHLY"); break; case YEARLY: s.append("YEARLY"); break; } if (!TextUtils.isEmpty(this.until)) { s.append(";UNTIL="); s.append(until); } if (this.count != 0) { s.append(";COUNT="); s.append(this.count); } if (this.interval != 0) { s.append(";INTERVAL="); s.append(this.interval); } if (this.wkst != 0) { s.append(";WKST="); s.append(day2String(this.wkst)); } appendNumbers(s, ";BYSECOND=", this.bysecondCount, this.bysecond); appendNumbers(s, ";BYMINUTE=", this.byminuteCount, this.byminute); appendNumbers(s, ";BYSECOND=", this.byhourCount, this.byhour); // day int count = this.bydayCount; if (count > 0) { s.append(";BYDAY="); count--; for (int i=0; i<count; i++) { appendByDay(s, i); s.append(","); } appendByDay(s, count); } appendNumbers(s, ";BYMONTHDAY=", this.bymonthdayCount, this.bymonthday); appendNumbers(s, ";BYYEARDAY=", this.byyeardayCount, this.byyearday); appendNumbers(s, ";BYWEEKNO=", this.byweeknoCount, this.byweekno); appendNumbers(s, ";BYMONTH=", this.bymonthCount, this.bymonth); appendNumbers(s, ";BYSETPOS=", this.bysetposCount, this.bysetpos); return s.toString(); } public String getRepeatString() { Resources r = Resources.getSystem(); // TODO Implement "Until" portion of string, as well as custom settings switch (this.freq) { case DAILY: return r.getString(com.android.internal.R.string.daily); case WEEKLY: { if (repeatsOnEveryWeekDay()) { return r.getString(com.android.internal.R.string.every_weekday); } else { String format = r.getString(com.android.internal.R.string.weekly); StringBuilder days = new StringBuilder(); // Do one less iteration in the loop so the last element is added out of the // loop. This is done so the comma is not placed after the last item. int count = this.bydayCount - 1; if (count >= 0) { for (int i = 0 ; i < count ; i++) { days.append(dayToString(r, this.byday[i])); days.append(","); } days.append(dayToString(r, this.byday[count])); return String.format(format, days.toString()); } // There is no "BYDAY" specifier, so use the day of the // first event. For this to work, the setStartDate() // method must have been used by the caller to set the // date of the first event in the recurrence. if (startDate == null) { return null; } int day = timeDay2Day(startDate.weekDay); return String.format(format, dayToString(r, day)); } } case MONTHLY: { return r.getString(com.android.internal.R.string.monthly); } case YEARLY: return r.getString(com.android.internal.R.string.yearly); } return null; } public boolean repeatsOnEveryWeekDay() { if (this.freq != WEEKLY) { return false; } int count = this.bydayCount; if (count != 5) { return false; } for (int i = 0 ; i < count ; i++) { int day = byday[i]; if (day == SU || day == SA) { return false; } } return true; } public boolean repeatsMonthlyOnDayCount() { if (this.freq != MONTHLY) { return false; } if (bydayCount != 1 || bymonthdayCount != 0) { return false; } return true; } private String dayToString(Resources r, int day) { switch (day) { case SU: return r.getString(com.android.internal.R.string.sunday); case MO: return r.getString(com.android.internal.R.string.monday); case TU: return r.getString(com.android.internal.R.string.tuesday); case WE: return r.getString(com.android.internal.R.string.wednesday); case TH: return r.getString(com.android.internal.R.string.thursday); case FR: return r.getString(com.android.internal.R.string.friday); case SA: return r.getString(com.android.internal.R.string.saturday); default: throw new IllegalArgumentException("bad day argument: " + day); } } }