All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.calendarcommon2.RecurrenceSet Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * Copyright (C) 2007 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 com.android.calendarcommon2;

import android.content.ContentValues;
import android.database.Cursor;
import android.provider.CalendarContract;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.Log;
import android.util.TimeFormatException;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * Basic information about a recurrence, following RFC 2445 Section 4.8.5.
 * Contains the RRULEs, RDATE, EXRULEs, and EXDATE properties.
 */
public class RecurrenceSet {

    private final static String TAG = "RecurrenceSet";

    private final static String RULE_SEPARATOR = "\n";
    private final static String FOLDING_SEPARATOR = "\n ";

    // TODO: make these final?
    public EventRecurrence[] rrules = null;
    public long[] rdates = null;
    public EventRecurrence[] exrules = null;
    public long[] exdates = null;

    /**
     * Creates a new RecurrenceSet from information stored in the
     * events table in the CalendarProvider.
     * @param values The values retrieved from the Events table.
     */
    public RecurrenceSet(ContentValues values)
            throws EventRecurrence.InvalidFormatException {
        String rruleStr = values.getAsString(CalendarContract.Events.RRULE);
        String rdateStr = values.getAsString(CalendarContract.Events.RDATE);
        String exruleStr = values.getAsString(CalendarContract.Events.EXRULE);
        String exdateStr = values.getAsString(CalendarContract.Events.EXDATE);
        init(rruleStr, rdateStr, exruleStr, exdateStr);
    }

    /**
     * Creates a new RecurrenceSet from information stored in a database
     * {@link Cursor} pointing to the events table in the
     * CalendarProvider.  The cursor must contain the RRULE, RDATE, EXRULE,
     * and EXDATE columns.
     *
     * @param cursor The cursor containing the RRULE, RDATE, EXRULE, and EXDATE
     * columns.
     */
    public RecurrenceSet(Cursor cursor)
            throws EventRecurrence.InvalidFormatException {
        int rruleColumn = cursor.getColumnIndex(CalendarContract.Events.RRULE);
        int rdateColumn = cursor.getColumnIndex(CalendarContract.Events.RDATE);
        int exruleColumn = cursor.getColumnIndex(CalendarContract.Events.EXRULE);
        int exdateColumn = cursor.getColumnIndex(CalendarContract.Events.EXDATE);
        String rruleStr = cursor.getString(rruleColumn);
        String rdateStr = cursor.getString(rdateColumn);
        String exruleStr = cursor.getString(exruleColumn);
        String exdateStr = cursor.getString(exdateColumn);
        init(rruleStr, rdateStr, exruleStr, exdateStr);
    }

    public RecurrenceSet(String rruleStr, String rdateStr,
                  String exruleStr, String exdateStr)
            throws EventRecurrence.InvalidFormatException {
        init(rruleStr, rdateStr, exruleStr, exdateStr);
    }

    private void init(String rruleStr, String rdateStr,
                      String exruleStr, String exdateStr)
            throws EventRecurrence.InvalidFormatException {
        if (!TextUtils.isEmpty(rruleStr) || !TextUtils.isEmpty(rdateStr)) {
            rrules = parseMultiLineRecurrenceRules(rruleStr);
            rdates = parseMultiLineRecurrenceDates(rdateStr);
            exrules = parseMultiLineRecurrenceRules(exruleStr);
            exdates = parseMultiLineRecurrenceDates(exdateStr);
        }
    }

    private EventRecurrence[] parseMultiLineRecurrenceRules(String ruleStr) {
        if (TextUtils.isEmpty(ruleStr)) {
            return null;
        }
        String[] ruleStrs = ruleStr.split(RULE_SEPARATOR);
        final EventRecurrence[] rules = new EventRecurrence[ruleStrs.length];
        for (int i = 0; i < ruleStrs.length; ++i) {
            EventRecurrence rule = new EventRecurrence();
            rule.parse(ruleStrs[i]);
            rules[i] = rule;
        }
        return rules;
    }

    private long[] parseMultiLineRecurrenceDates(String dateStr) {
        if (TextUtils.isEmpty(dateStr)) {
            return null;
        }
        final List list = new ArrayList<>();
        for (String date : dateStr.split(RULE_SEPARATOR)) {
            final long[] parsedDates = parseRecurrenceDates(date);
            for (long parsedDate : parsedDates) {
                list.add(parsedDate);
            }
        }
        final long[] result = new long[list.size()];
        for (int i = 0, n = list.size(); i < n; i++) {
            result[i] = list.get(i);
        }
        return result;
    }

    /**
     * Returns whether or not a recurrence is defined in this RecurrenceSet.
     * @return Whether or not a recurrence is defined in this RecurrenceSet.
     */
    public boolean hasRecurrence() {
        return (rrules != null || rdates != null);
    }

    /**
     * Parses the provided RDATE or EXDATE string into an array of longs
     * representing each date/time in the recurrence.
     * @param recurrence The recurrence to be parsed.
     * @return The list of date/times.
     */
    public static long[] parseRecurrenceDates(String recurrence)
            throws EventRecurrence.InvalidFormatException{
        // TODO: use "local" time as the default.  will need to handle times
        // that end in "z" (UTC time) explicitly at that point.
        String tz = Time.TIMEZONE_UTC;
        int tzidx = recurrence.indexOf(";");
        if (tzidx != -1) {
            tz = recurrence.substring(0, tzidx);
            recurrence = recurrence.substring(tzidx + 1);
        }
        Time time = new Time(tz);
        String[] rawDates = recurrence.split(",");
        int n = rawDates.length;
        long[] dates = new long[n];
        for (int i = 0; i4.1 Content Lines
    *
    * 

The iCalendar object is organized into individual lines of text, called * content lines. Content lines are delimited by a line break, which is a CRLF * sequence (US-ASCII decimal 13, followed by US-ASCII decimal 10). * *

Lines of text SHOULD NOT be longer than 75 octets, excluding the line * break. Long content lines SHOULD be split into a multiple line * representations using a line "folding" technique. That is, a long line can * be split between any two characters by inserting a CRLF immediately * followed by a single linear white space character (i.e., SPACE, US-ASCII * decimal 32 or HTAB, US-ASCII decimal 9). Any sequence of CRLF followed * immediately by a single linear white space character is ignored (i.e., * removed) when processing the content type. */ public static String fold(String unfoldedIcalContent) { return FOLD_RE.matcher(unfoldedIcalContent).replaceAll("$0\r\n "); } public static String unfold(String foldedIcalContent) { return IGNORABLE_ICAL_WHITESPACE_RE.matcher( foldedIcalContent).replaceAll(""); } public static void addPropertyForDateStr(ICalendar.Component component, String propertyName, String dateStr) { if (TextUtils.isEmpty(dateStr)) { return; } ICalendar.Property prop = new ICalendar.Property(propertyName); String tz = null; int tzidx = dateStr.indexOf(";"); if (tzidx != -1) { tz = dateStr.substring(0, tzidx); dateStr = dateStr.substring(tzidx + 1); } if (!TextUtils.isEmpty(tz)) { prop.addParameter(new ICalendar.Parameter("TZID", tz)); } prop.setValue(dateStr); component.addProperty(prop); } private static String computeDuration(Time start, ICalendar.Component component) { // see if a duration is defined ICalendar.Property durationProperty = component.getFirstProperty("DURATION"); if (durationProperty != null) { // just return the duration return durationProperty.getValue(); } // must compute a duration from the DTEND ICalendar.Property dtendProperty = component.getFirstProperty("DTEND"); if (dtendProperty == null) { // no DURATION, no DTEND: 0 second duration return "+P0S"; } ICalendar.Parameter endTzidParameter = dtendProperty.getFirstParameter("TZID"); String endTzid = (endTzidParameter == null) ? start.timezone : endTzidParameter.value; Time end = new Time(endTzid); end.parse(dtendProperty.getValue()); long durationMillis = end.toMillis(false /* use isDst */) - start.toMillis(false /* use isDst */); long durationSeconds = (durationMillis / 1000); if (start.allDay && (durationSeconds % 86400) == 0) { return "P" + (durationSeconds / 86400) + "D"; // Server wants this instead of P86400S } else { return "P" + durationSeconds + "S"; } } private static String flattenProperties(ICalendar.Component component, String name) { List properties = component.getProperties(name); if (properties == null || properties.isEmpty()) { return null; } if (properties.size() == 1) { return properties.get(0).getValue(); } StringBuilder sb = new StringBuilder(); boolean first = true; for (ICalendar.Property property : component.getProperties(name)) { if (first) { first = false; } else { // TODO: use commas. our RECUR parsing should handle that // anyway. sb.append(RULE_SEPARATOR); } sb.append(property.getValue()); } return sb.toString(); } private static String extractDates(ICalendar.Property recurrence) { if (recurrence == null) { return null; } ICalendar.Parameter tzidParam = recurrence.getFirstParameter("TZID"); if (tzidParam != null) { return tzidParam.value + ";" + recurrence.getValue(); } return recurrence.getValue(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy