Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 65c15258 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Process recurring events, exceptions etc.

parent de7ca7b9
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -17,7 +17,5 @@ public class Constants {
		WEB_URL_HELP = "https://davdroid.bitfire.at/configuration?pk_campaign=davdroid-app",
		WEB_URL_VIEW_LOGS = "https://github.com/bitfireAT/davdroid/wiki/How-to-view-the-logs";

	//public static final ProdId ICAL_PRODID = new ProdId("-//bitfire web engineering//DAVdroid " + BuildConfig.VERSION_CODE + " (ical4j 2.0-beta1)//EN");

    public static final Logger log = LoggerFactory.getLogger("davdroid");
}
+0 −3
Original line number Diff line number Diff line
@@ -118,9 +118,6 @@ public class HttpClient extends OkHttpClient {
            }
        }

        // don't follow redirects automatically because this may rewrite DAV methods to GET
        setFollowRedirects(false);

        // set timeouts
        setConnectTimeout(30, TimeUnit.SECONDS);
        setWriteTimeout(15, TimeUnit.SECONDS);
+114 −9
Original line number Diff line number Diff line
@@ -10,10 +10,13 @@ package at.bitfire.davdroid.resource;

import android.accounts.Account;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.RemoteException;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Calendars;
@@ -22,8 +25,14 @@ import android.provider.CalendarContract.Reminders;

import com.google.common.base.Joiner;

import java.io.FileNotFoundException;
import java.util.LinkedList;
import java.util.List;

import at.bitfire.davdroid.Constants;
import at.bitfire.ical4android.AndroidCalendar;
import at.bitfire.ical4android.AndroidCalendarFactory;
import at.bitfire.ical4android.BatchOperation;
import at.bitfire.ical4android.CalendarStorageException;
import at.bitfire.vcard4android.ContactsStorageException;
import lombok.Cleanup;
@@ -34,9 +43,13 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {

    public static final String COLUMN_CTAG = Calendars.CAL_SYNC1;

    protected static final int
            DIRTY_INCREASE_SEQUENCE = 1,
            DIRTY_DONT_INCREASE_SEQUENCE = 2;

    static String[] BASE_INFO_COLUMNS = new String[] {
            Events._ID,
            LocalEvent.COLUMN_FILENAME,
            Events._SYNC_ID,
            LocalEvent.COLUMN_ETAG
    };

@@ -59,38 +72,61 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
        values.put(Calendars.NAME, info.getURL());
        values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle());
        values.put(Calendars.CALENDAR_COLOR, info.color != null ? info.color : defaultColor);
        values.put(Calendars.CALENDAR_ACCESS_LEVEL, info.readOnly ? Calendars.CAL_ACCESS_READ : Calendars.CAL_ACCESS_OWNER);

        if (info.isReadOnly())
            values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_READ);
        else {
            values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER);
            values.put(Calendars.CAN_MODIFY_TIME_ZONE, 1);
            values.put(Calendars.CAN_ORGANIZER_RESPOND, 1);
        }

        values.put(Calendars.OWNER_ACCOUNT, account.name);
        values.put(Calendars.SYNC_EVENTS, 1);
        values.put(Calendars.VISIBLE, 1);
        if (info.timezone != null) {
            // TODO parse VTIMEZONE
            // values.put(Calendars.CALENDAR_TIME_ZONE, DateUtils.findAndroidTimezoneID(info.timezone));
        }
        values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT);
        if (Build.VERSION.SDK_INT >= 15) {
            values.put(Calendars.ALLOWED_AVAILABILITY, Joiner.on(",").join(Reminders.AVAILABILITY_TENTATIVE, Reminders.AVAILABILITY_FREE, Reminders.AVAILABILITY_BUSY));
            values.put(Calendars.ALLOWED_ATTENDEE_TYPES, Joiner.on(",").join(CalendarContract.Attendees.TYPE_OPTIONAL, CalendarContract.Attendees.TYPE_REQUIRED, CalendarContract.Attendees.TYPE_RESOURCE));
        }
        return create(account, provider, values);
    }


    @Override
    public LocalResource[] getAll() throws CalendarStorageException, ContactsStorageException {
        return (LocalEvent[])queryEvents(null, null);
        return (LocalEvent[])queryEvents(Events.ORIGINAL_ID + " IS NULL", null);
    }

    @Override
    public LocalEvent[] getDeleted() throws CalendarStorageException {
        return (LocalEvent[])queryEvents(Events.DELETED + "!=0", null);
        return (LocalEvent[])queryEvents(Events.DELETED + "!=0 AND " + Events.ORIGINAL_ID + " IS NULL", null);
    }

    @Override
    public LocalEvent[] getWithoutFileName() throws CalendarStorageException {
        return (LocalEvent[])queryEvents(LocalEvent.COLUMN_FILENAME + " IS NULL", null);
        return (LocalEvent[])queryEvents(Events._SYNC_ID + " IS NULL AND " + Events.ORIGINAL_ID + " IS NULL", null);
    }

    @Override
    public LocalResource[] getDirty() throws CalendarStorageException {
        return (LocalEvent[])queryEvents(Events.DIRTY + "!=0", null);
    public LocalResource[] getDirty() throws CalendarStorageException, FileNotFoundException {
        List<LocalResource> dirty = new LinkedList<>();

        // get dirty events which are not required to have an increased SEQUENCE value
        for (LocalEvent event : (LocalEvent[])queryEvents(Events.DIRTY + "=" + DIRTY_DONT_INCREASE_SEQUENCE + " AND " + Events.ORIGINAL_ID + " IS NULL", null))
            dirty.add(event);

        // get dirty events which are required to have an increased SEQUENCE value
        for (LocalEvent event : (LocalEvent[])queryEvents(Events.DIRTY + "=" + DIRTY_INCREASE_SEQUENCE + " AND " + Events.ORIGINAL_ID + " IS NULL", null)) {
            event.getEvent().sequence++;
            dirty.add(event);
        }

        return dirty.toArray(new LocalResource[dirty.size()]);
    }


@@ -117,6 +153,75 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
        }
    }

    public void processDirtyExceptions() throws CalendarStorageException {
        // process deleted exceptions
        Constants.log.info("Processing deleted exceptions");
        try {
            @Cleanup Cursor cursor = provider.query(
                    syncAdapterURI(Events.CONTENT_URI),
                    new String[] { Events._ID, Events.ORIGINAL_ID, LocalEvent.COLUMN_SEQUENCE },
                    Events.DELETED + "!=0 AND " + Events.ORIGINAL_ID + " IS NOT NULL", null, null);
            while (cursor != null && cursor.moveToNext()) {
                Constants.log.debug("Found deleted exception, removing; then re-schuling original event");
                long    id = cursor.getLong(0),             // can't be null (by definition)
                        originalID = cursor.getLong(1);     // can't be null (by query)
                int sequence = cursor.isNull(2) ? 0 : cursor.getInt(2);

                // get original event's SEQUENCE
                @Cleanup Cursor cursor2 = provider.query(
                        syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, originalID)),
                        new String[] { LocalEvent.COLUMN_SEQUENCE },
                        null, null, null);
                int originalSequence = cursor.isNull(0) ? 0 : cursor.getInt(0);

                BatchOperation batch = new BatchOperation(provider);
                // re-schedule original event and set it to DIRTY
                batch.enqueue(ContentProviderOperation.newUpdate(
                        syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, originalID)))
                        .withValue(LocalEvent.COLUMN_SEQUENCE, originalSequence)
                        .withValue(Events.DIRTY, DIRTY_INCREASE_SEQUENCE)
                        .build());
                // remove exception
                batch.enqueue(ContentProviderOperation.newDelete(
                        syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, id))).build());
                batch.commit();
            }
        } catch (RemoteException e) {
            throw new CalendarStorageException("Couldn't process locally modified exception", e);
        }

        // process dirty exceptions
        Constants.log.info("Processing dirty exceptions");
        try {
            @Cleanup Cursor cursor = provider.query(
                    syncAdapterURI(Events.CONTENT_URI),
                    new String[] { Events._ID, Events.ORIGINAL_ID, LocalEvent.COLUMN_SEQUENCE },
                    Events.DIRTY + "!=0 AND " + Events.ORIGINAL_ID + " IS NOT NULL", null, null);
            while (cursor != null && cursor.moveToNext()) {
                Constants.log.debug("Found dirty exception, increasing SEQUENCE to re-schedule");
                long    id = cursor.getLong(0),             // can't be null (by definition)
                        originalID = cursor.getLong(1);     // can't be null (by query)
                int sequence = cursor.isNull(2) ? 0 : cursor.getInt(2);

                BatchOperation batch = new BatchOperation(provider);
                // original event to DIRTY
                batch.enqueue(ContentProviderOperation.newUpdate(
                        syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, originalID)))
                        .withValue(Events.DIRTY, DIRTY_DONT_INCREASE_SEQUENCE)
                        .build());
                // increase SEQUENCE and set DIRTY to 0
                batch.enqueue(ContentProviderOperation.newUpdate(
                                syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, id)))
                                .withValue(LocalEvent.COLUMN_SEQUENCE, sequence + 1)
                                .withValue(Events.DIRTY, 0)
                                .build());
                batch.commit();
            }
        } catch (RemoteException e) {
            throw new CalendarStorageException("Couldn't process locally modified exception", e);
        }
    }


    public static class Factory implements AndroidCalendarFactory {
        public static final Factory INSTANCE = new Factory();
+3 −1
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@ package at.bitfire.davdroid.resource;

import android.provider.ContactsContract;

import java.io.FileNotFoundException;

import at.bitfire.ical4android.CalendarStorageException;
import at.bitfire.vcard4android.ContactsStorageException;

@@ -17,7 +19,7 @@ public interface LocalCollection {

    LocalResource[] getDeleted() throws CalendarStorageException, ContactsStorageException;
    LocalResource[] getWithoutFileName()  throws CalendarStorageException, ContactsStorageException;
    LocalResource[] getDirty() throws CalendarStorageException, ContactsStorageException;
    LocalResource[] getDirty() throws CalendarStorageException, ContactsStorageException, FileNotFoundException;

    LocalResource[] getAll() throws CalendarStorageException, ContactsStorageException;

+1 −1
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@ import ezvcard.Ezvcard;

public class LocalContact extends AndroidContact implements LocalResource {
    static {
        Contact.productID = "+//IDN bitfire.at//DAVdroid/" + BuildConfig.VERSION_NAME + " ez-vcard/" + Ezvcard.VERSION;
        Contact.productID = "+//IDN bitfire.at//DAVdroid/" + BuildConfig.VERSION_NAME + "vcard4android ez-vcard/" + Ezvcard.VERSION;
    }

    protected LocalContact(AndroidAddressBook addressBook, long id, String fileName, String eTag) {
Loading