mirror of
https://github.com/etesync/android
synced 2024-12-27 00:48:12 +00:00
Process recurring events, exceptions etc.
This commit is contained in:
parent
0c819c842b
commit
419d732195
@ -17,7 +17,5 @@ public class Constants {
|
|||||||
WEB_URL_HELP = "https://davdroid.bitfire.at/configuration?pk_campaign=davdroid-app",
|
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";
|
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");
|
public static final Logger log = LoggerFactory.getLogger("davdroid");
|
||||||
}
|
}
|
||||||
|
@ -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
|
// set timeouts
|
||||||
setConnectTimeout(30, TimeUnit.SECONDS);
|
setConnectTimeout(30, TimeUnit.SECONDS);
|
||||||
setWriteTimeout(15, TimeUnit.SECONDS);
|
setWriteTimeout(15, TimeUnit.SECONDS);
|
||||||
|
@ -10,10 +10,13 @@ package at.bitfire.davdroid.resource;
|
|||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.ContentProviderOperation;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentUris;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.provider.CalendarContract;
|
import android.provider.CalendarContract;
|
||||||
import android.provider.CalendarContract.Calendars;
|
import android.provider.CalendarContract.Calendars;
|
||||||
@ -22,8 +25,14 @@ import android.provider.CalendarContract.Reminders;
|
|||||||
|
|
||||||
import com.google.common.base.Joiner;
|
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.AndroidCalendar;
|
||||||
import at.bitfire.ical4android.AndroidCalendarFactory;
|
import at.bitfire.ical4android.AndroidCalendarFactory;
|
||||||
|
import at.bitfire.ical4android.BatchOperation;
|
||||||
import at.bitfire.ical4android.CalendarStorageException;
|
import at.bitfire.ical4android.CalendarStorageException;
|
||||||
import at.bitfire.vcard4android.ContactsStorageException;
|
import at.bitfire.vcard4android.ContactsStorageException;
|
||||||
import lombok.Cleanup;
|
import lombok.Cleanup;
|
||||||
@ -34,9 +43,13 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
|
|||||||
|
|
||||||
public static final String COLUMN_CTAG = Calendars.CAL_SYNC1;
|
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[] {
|
static String[] BASE_INFO_COLUMNS = new String[] {
|
||||||
Events._ID,
|
Events._ID,
|
||||||
LocalEvent.COLUMN_FILENAME,
|
Events._SYNC_ID,
|
||||||
LocalEvent.COLUMN_ETAG
|
LocalEvent.COLUMN_ETAG
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,38 +72,61 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
|
|||||||
values.put(Calendars.NAME, info.getURL());
|
values.put(Calendars.NAME, info.getURL());
|
||||||
values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle());
|
values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle());
|
||||||
values.put(Calendars.CALENDAR_COLOR, info.color != null ? info.color : defaultColor);
|
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.OWNER_ACCOUNT, account.name);
|
||||||
values.put(Calendars.SYNC_EVENTS, 1);
|
values.put(Calendars.SYNC_EVENTS, 1);
|
||||||
|
values.put(Calendars.VISIBLE, 1);
|
||||||
if (info.timezone != null) {
|
if (info.timezone != null) {
|
||||||
// TODO parse VTIMEZONE
|
// TODO parse VTIMEZONE
|
||||||
// values.put(Calendars.CALENDAR_TIME_ZONE, DateUtils.findAndroidTimezoneID(info.timezone));
|
// values.put(Calendars.CALENDAR_TIME_ZONE, DateUtils.findAndroidTimezoneID(info.timezone));
|
||||||
}
|
}
|
||||||
values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT);
|
values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT);
|
||||||
values.put(Calendars.ALLOWED_AVAILABILITY, Joiner.on(",").join(Reminders.AVAILABILITY_TENTATIVE, Reminders.AVAILABILITY_FREE, Reminders.AVAILABILITY_BUSY));
|
if (Build.VERSION.SDK_INT >= 15) {
|
||||||
values.put(Calendars.ALLOWED_ATTENDEE_TYPES, Joiner.on(",").join(CalendarContract.Attendees.TYPE_OPTIONAL, CalendarContract.Attendees.TYPE_REQUIRED, CalendarContract.Attendees.TYPE_RESOURCE));
|
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);
|
return create(account, provider, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LocalResource[] getAll() throws CalendarStorageException, ContactsStorageException {
|
public LocalResource[] getAll() throws CalendarStorageException, ContactsStorageException {
|
||||||
return (LocalEvent[])queryEvents(null, null);
|
return (LocalEvent[])queryEvents(Events.ORIGINAL_ID + " IS NULL", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LocalEvent[] getDeleted() throws CalendarStorageException {
|
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
|
@Override
|
||||||
public LocalEvent[] getWithoutFileName() throws CalendarStorageException {
|
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
|
@Override
|
||||||
public LocalResource[] getDirty() throws CalendarStorageException {
|
public LocalResource[] getDirty() throws CalendarStorageException, FileNotFoundException {
|
||||||
return (LocalEvent[])queryEvents(Events.DIRTY + "!=0", null);
|
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 class Factory implements AndroidCalendarFactory {
|
||||||
public static final Factory INSTANCE = new Factory();
|
public static final Factory INSTANCE = new Factory();
|
||||||
|
@ -10,6 +10,8 @@ package at.bitfire.davdroid.resource;
|
|||||||
|
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
import at.bitfire.ical4android.CalendarStorageException;
|
import at.bitfire.ical4android.CalendarStorageException;
|
||||||
import at.bitfire.vcard4android.ContactsStorageException;
|
import at.bitfire.vcard4android.ContactsStorageException;
|
||||||
|
|
||||||
@ -17,7 +19,7 @@ public interface LocalCollection {
|
|||||||
|
|
||||||
LocalResource[] getDeleted() throws CalendarStorageException, ContactsStorageException;
|
LocalResource[] getDeleted() throws CalendarStorageException, ContactsStorageException;
|
||||||
LocalResource[] getWithoutFileName() throws CalendarStorageException, ContactsStorageException;
|
LocalResource[] getWithoutFileName() throws CalendarStorageException, ContactsStorageException;
|
||||||
LocalResource[] getDirty() throws CalendarStorageException, ContactsStorageException;
|
LocalResource[] getDirty() throws CalendarStorageException, ContactsStorageException, FileNotFoundException;
|
||||||
|
|
||||||
LocalResource[] getAll() throws CalendarStorageException, ContactsStorageException;
|
LocalResource[] getAll() throws CalendarStorageException, ContactsStorageException;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import ezvcard.Ezvcard;
|
|||||||
|
|
||||||
public class LocalContact extends AndroidContact implements LocalResource {
|
public class LocalContact extends AndroidContact implements LocalResource {
|
||||||
static {
|
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) {
|
protected LocalContact(AndroidAddressBook addressBook, long id, String fileName, String eTag) {
|
||||||
|
@ -10,37 +10,46 @@ package at.bitfire.davdroid.resource;
|
|||||||
|
|
||||||
import android.content.ContentProviderOperation;
|
import android.content.ContentProviderOperation;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.provider.CalendarContract;
|
import android.provider.CalendarContract;
|
||||||
|
import android.provider.CalendarContract.Events;
|
||||||
|
|
||||||
|
import net.fortuna.ical4j.model.property.ProdId;
|
||||||
|
|
||||||
|
import at.bitfire.davdroid.BuildConfig;
|
||||||
import at.bitfire.ical4android.AndroidCalendar;
|
import at.bitfire.ical4android.AndroidCalendar;
|
||||||
import at.bitfire.ical4android.AndroidEvent;
|
import at.bitfire.ical4android.AndroidEvent;
|
||||||
import at.bitfire.ical4android.AndroidEventFactory;
|
import at.bitfire.ical4android.AndroidEventFactory;
|
||||||
import at.bitfire.ical4android.CalendarStorageException;
|
import at.bitfire.ical4android.CalendarStorageException;
|
||||||
import at.bitfire.ical4android.Event;
|
import at.bitfire.ical4android.Event;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
public class LocalEvent extends AndroidEvent implements LocalResource {
|
public class LocalEvent extends AndroidEvent implements LocalResource {
|
||||||
|
static {
|
||||||
|
Event.prodId = new ProdId("+//IDN bitfire.at//DAVdroid/" + BuildConfig.VERSION_NAME + " ical4android ical4j/2.x");
|
||||||
|
}
|
||||||
|
|
||||||
static final String COLUMN_FILENAME = CalendarContract.Events.SYNC_DATA1,
|
static final String COLUMN_ETAG = CalendarContract.Events.SYNC_DATA1,
|
||||||
COLUMN_ETAG = CalendarContract.Events.SYNC_DATA2,
|
COLUMN_UID = CalendarContract.Events.UID_2445,
|
||||||
COLUMN_UID = CalendarContract.Events.UID_2445;
|
COLUMN_SEQUENCE = CalendarContract.Events.SYNC_DATA2;
|
||||||
|
|
||||||
@Getter protected String fileName;
|
@Getter protected String fileName;
|
||||||
@Getter @Setter protected String eTag;
|
@Getter @Setter protected String eTag;
|
||||||
|
|
||||||
public LocalEvent(AndroidCalendar calendar, Event event, String fileName, String eTag) {
|
public LocalEvent(@NonNull AndroidCalendar calendar, Event event, String fileName, String eTag) {
|
||||||
super(calendar, event);
|
super(calendar, event);
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
this.eTag = eTag;
|
this.eTag = eTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected LocalEvent(AndroidCalendar calendar, long id, ContentValues baseInfo) {
|
protected LocalEvent(@NonNull AndroidCalendar calendar, long id, ContentValues baseInfo) {
|
||||||
super(calendar, id, baseInfo);
|
super(calendar, id, baseInfo);
|
||||||
fileName = baseInfo.getAsString(COLUMN_FILENAME);
|
if (baseInfo != null) {
|
||||||
eTag = baseInfo.getAsString(COLUMN_ETAG);
|
fileName = baseInfo.getAsString(Events._SYNC_ID);
|
||||||
|
eTag = baseInfo.getAsString(COLUMN_ETAG);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -49,17 +58,31 @@ public class LocalEvent extends AndroidEvent implements LocalResource {
|
|||||||
@Override
|
@Override
|
||||||
protected void populateEvent(ContentValues values) {
|
protected void populateEvent(ContentValues values) {
|
||||||
super.populateEvent(values);
|
super.populateEvent(values);
|
||||||
fileName = values.getAsString(COLUMN_FILENAME);
|
fileName = values.getAsString(Events._SYNC_ID);
|
||||||
eTag = values.getAsString(COLUMN_ETAG);
|
eTag = values.getAsString(COLUMN_ETAG);
|
||||||
event.uid = values.getAsString(COLUMN_UID);
|
event.uid = values.getAsString(COLUMN_UID);
|
||||||
|
|
||||||
|
if (values.containsKey(COLUMN_SEQUENCE))
|
||||||
|
event.sequence = values.getAsInteger(COLUMN_SEQUENCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void buildEvent(Event recurrence, ContentProviderOperation.Builder builder) {
|
protected void buildEvent(Event recurrence, ContentProviderOperation.Builder builder) {
|
||||||
super.buildEvent(recurrence, builder);
|
super.buildEvent(recurrence, builder);
|
||||||
builder .withValue(COLUMN_FILENAME, fileName)
|
|
||||||
.withValue(COLUMN_ETAG, eTag)
|
boolean buildException = recurrence != null;
|
||||||
.withValue(COLUMN_UID, event.uid);
|
Event eventToBuild = buildException ? recurrence : event;
|
||||||
|
|
||||||
|
builder .withValue(COLUMN_UID, event.uid)
|
||||||
|
.withValue(COLUMN_SEQUENCE, eventToBuild.sequence)
|
||||||
|
.withValue(CalendarContract.Events.DIRTY, 0)
|
||||||
|
.withValue(CalendarContract.Events.DELETED, 0);
|
||||||
|
|
||||||
|
if (buildException)
|
||||||
|
builder.withValue(Events.ORIGINAL_SYNC_ID, fileName);
|
||||||
|
else
|
||||||
|
builder .withValue(Events._SYNC_ID, fileName)
|
||||||
|
.withValue(COLUMN_ETAG, eTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -70,7 +93,7 @@ public class LocalEvent extends AndroidEvent implements LocalResource {
|
|||||||
String newFileName = uid + ".ics";
|
String newFileName = uid + ".ics";
|
||||||
|
|
||||||
ContentValues values = new ContentValues(2);
|
ContentValues values = new ContentValues(2);
|
||||||
values.put(COLUMN_FILENAME, newFileName);
|
values.put(Events._SYNC_ID, newFileName);
|
||||||
values.put(COLUMN_UID, uid);
|
values.put(COLUMN_UID, uid);
|
||||||
calendar.provider.update(eventSyncURI(), values, null, null);
|
calendar.provider.update(eventSyncURI(), values, null, null);
|
||||||
|
|
||||||
@ -89,6 +112,8 @@ public class LocalEvent extends AndroidEvent implements LocalResource {
|
|||||||
ContentValues values = new ContentValues(2);
|
ContentValues values = new ContentValues(2);
|
||||||
values.put(CalendarContract.Events.DIRTY, 0);
|
values.put(CalendarContract.Events.DIRTY, 0);
|
||||||
values.put(COLUMN_ETAG, eTag);
|
values.put(COLUMN_ETAG, eTag);
|
||||||
|
if (event != null)
|
||||||
|
values.put(COLUMN_SEQUENCE, event.sequence);
|
||||||
calendar.provider.update(eventSyncURI(), values, null, null);
|
calendar.provider.update(eventSyncURI(), values, null, null);
|
||||||
|
|
||||||
this.eTag = eTag;
|
this.eTag = eTag;
|
||||||
|
@ -101,6 +101,13 @@ public class CalendarSyncManager extends SyncManager {
|
|||||||
localCalendar().update(values);
|
localCalendar().update(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void prepareDirty() throws CalendarStorageException, ContactsStorageException {
|
||||||
|
super.prepareDirty();
|
||||||
|
|
||||||
|
localCalendar().processDirtyExceptions();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RequestBody prepareUpload(LocalResource resource) throws IOException, CalendarStorageException {
|
protected RequestBody prepareUpload(LocalResource resource) throws IOException, CalendarStorageException {
|
||||||
LocalEvent local = (LocalEvent)resource;
|
LocalEvent local = (LocalEvent)resource;
|
||||||
|
@ -16,6 +16,7 @@ import android.content.Intent;
|
|||||||
import android.content.SyncResult;
|
import android.content.SyncResult;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.provider.CalendarContract;
|
||||||
|
|
||||||
import at.bitfire.davdroid.Constants;
|
import at.bitfire.davdroid.Constants;
|
||||||
import at.bitfire.davdroid.resource.LocalCalendar;
|
import at.bitfire.davdroid.resource.LocalCalendar;
|
||||||
@ -52,7 +53,7 @@ public class CalendarsSyncAdapterService extends Service {
|
|||||||
Constants.log.info("Starting calendar sync (" + authority + ")");
|
Constants.log.info("Starting calendar sync (" + authority + ")");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (LocalCalendar calendar : (LocalCalendar[])LocalCalendar.findAll(account, provider, LocalCalendar.Factory.INSTANCE)) {
|
for (LocalCalendar calendar : (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, CalendarContract.Calendars.SYNC_EVENTS + "!=0", null)) {
|
||||||
Constants.log.info("Synchronizing calendar #" + calendar.getId() + ", URL: " + calendar.getName());
|
Constants.log.info("Synchronizing calendar #" + calendar.getId() + ", URL: " + calendar.getName());
|
||||||
CalendarSyncManager syncManager = new CalendarSyncManager(getContext(), account, extras, provider, syncResult, calendar);
|
CalendarSyncManager syncManager = new CalendarSyncManager(getContext(), account, extras, provider, syncResult, calendar);
|
||||||
syncManager.performSync();
|
syncManager.performSync();
|
||||||
|
@ -24,6 +24,7 @@ import com.squareup.okhttp.HttpUrl;
|
|||||||
import com.squareup.okhttp.RequestBody;
|
import com.squareup.okhttp.RequestBody;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -34,6 +35,7 @@ import at.bitfire.dav4android.DavResource;
|
|||||||
import at.bitfire.dav4android.exception.DavException;
|
import at.bitfire.dav4android.exception.DavException;
|
||||||
import at.bitfire.dav4android.exception.HttpException;
|
import at.bitfire.dav4android.exception.HttpException;
|
||||||
import at.bitfire.dav4android.exception.PreconditionFailedException;
|
import at.bitfire.dav4android.exception.PreconditionFailedException;
|
||||||
|
import at.bitfire.dav4android.exception.ServiceUnavailableException;
|
||||||
import at.bitfire.dav4android.property.GetCTag;
|
import at.bitfire.dav4android.property.GetCTag;
|
||||||
import at.bitfire.dav4android.property.GetETag;
|
import at.bitfire.dav4android.property.GetETag;
|
||||||
import at.bitfire.davdroid.Constants;
|
import at.bitfire.davdroid.Constants;
|
||||||
@ -50,7 +52,7 @@ abstract public class SyncManager {
|
|||||||
protected final int SYNC_PHASE_PREPARE = 0,
|
protected final int SYNC_PHASE_PREPARE = 0,
|
||||||
SYNC_PHASE_QUERY_CAPABILITIES = 1,
|
SYNC_PHASE_QUERY_CAPABILITIES = 1,
|
||||||
SYNC_PHASE_PROCESS_LOCALLY_DELETED = 2,
|
SYNC_PHASE_PROCESS_LOCALLY_DELETED = 2,
|
||||||
SYNC_PHASE_PREPARE_LOCALLY_CREATED = 3,
|
SYNC_PHASE_PREPARE_DIRTY = 3,
|
||||||
SYNC_PHASE_UPLOAD_DIRTY = 4,
|
SYNC_PHASE_UPLOAD_DIRTY = 4,
|
||||||
SYNC_PHASE_CHECK_SYNC_STATE = 5,
|
SYNC_PHASE_CHECK_SYNC_STATE = 5,
|
||||||
SYNC_PHASE_LIST_LOCAL = 6,
|
SYNC_PHASE_LIST_LOCAL = 6,
|
||||||
@ -120,9 +122,9 @@ abstract public class SyncManager {
|
|||||||
Constants.log.info("Processing locally deleted entries");
|
Constants.log.info("Processing locally deleted entries");
|
||||||
processLocallyDeleted();
|
processLocallyDeleted();
|
||||||
|
|
||||||
syncPhase = SYNC_PHASE_PREPARE_LOCALLY_CREATED;
|
syncPhase = SYNC_PHASE_PREPARE_DIRTY;
|
||||||
Constants.log.info("Processing locally created entries");
|
Constants.log.info("Locally preparing dirty entries");
|
||||||
processLocallyCreated();
|
prepareDirty();
|
||||||
|
|
||||||
syncPhase = SYNC_PHASE_UPLOAD_DIRTY;
|
syncPhase = SYNC_PHASE_UPLOAD_DIRTY;
|
||||||
Constants.log.info("Uploading dirty entries");
|
Constants.log.info("Uploading dirty entries");
|
||||||
@ -153,10 +155,18 @@ abstract public class SyncManager {
|
|||||||
} else
|
} else
|
||||||
Constants.log.info("Remote collection didn't change, skipping remote sync");
|
Constants.log.info("Remote collection didn't change, skipping remote sync");
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException|ServiceUnavailableException e) {
|
||||||
Constants.log.error("I/O exception during sync, trying again later", e);
|
Constants.log.error("I/O exception during sync, trying again later", e);
|
||||||
syncResult.stats.numIoExceptions++;
|
syncResult.stats.numIoExceptions++;
|
||||||
|
|
||||||
|
if (e instanceof ServiceUnavailableException) {
|
||||||
|
Date retryAfter = ((ServiceUnavailableException) e).retryAfter;
|
||||||
|
if (retryAfter != null) {
|
||||||
|
// how many seconds to wait? getTime() returns ms, so divide by 1000
|
||||||
|
syncResult.delayUntil = (retryAfter.getTime() - new Date().getTime()) / 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch(HttpException|DavException e) {
|
} catch(HttpException|DavException e) {
|
||||||
Constants.log.error("HTTP/DAV Exception during sync", e);
|
Constants.log.error("HTTP/DAV Exception during sync", e);
|
||||||
syncResult.stats.numParseExceptions++;
|
syncResult.stats.numParseExceptions++;
|
||||||
@ -207,7 +217,7 @@ abstract public class SyncManager {
|
|||||||
try {
|
try {
|
||||||
new DavResource(httpClient, collectionURL.newBuilder().addPathSegment(fileName).build())
|
new DavResource(httpClient, collectionURL.newBuilder().addPathSegment(fileName).build())
|
||||||
.delete(local.getETag());
|
.delete(local.getETag());
|
||||||
} catch (IOException | HttpException e) {
|
} catch (IOException|HttpException e) {
|
||||||
Constants.log.warn("Couldn't delete " + fileName + " from server");
|
Constants.log.warn("Couldn't delete " + fileName + " from server");
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -217,7 +227,7 @@ abstract public class SyncManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processLocallyCreated() throws CalendarStorageException, ContactsStorageException {
|
protected void prepareDirty() throws CalendarStorageException, ContactsStorageException {
|
||||||
// assign file names and UIDs to new contacts so that we can use the file name as an index
|
// assign file names and UIDs to new contacts so that we can use the file name as an index
|
||||||
for (LocalResource local : localCollection.getWithoutFileName()) {
|
for (LocalResource local : localCollection.getWithoutFileName()) {
|
||||||
String uuid = UUID.randomUUID().toString();
|
String uuid = UUID.randomUUID().toString();
|
||||||
|
@ -38,8 +38,6 @@ public class DebugInfoActivity extends Activity {
|
|||||||
KEY_ACCOUNT = "account",
|
KEY_ACCOUNT = "account",
|
||||||
KEY_PHASE = "phase";
|
KEY_PHASE = "phase";
|
||||||
|
|
||||||
private static final String APP_ID = "at.bitfire.davdroid";
|
|
||||||
|
|
||||||
String report;
|
String report;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -49,7 +47,7 @@ public class DebugInfoActivity extends Activity {
|
|||||||
setContentView(R.layout.debug_info_activity);
|
setContentView(R.layout.debug_info_activity);
|
||||||
|
|
||||||
TextView tvReport = (TextView)findViewById(R.id.text_report);
|
TextView tvReport = (TextView)findViewById(R.id.text_report);
|
||||||
tvReport.setText(generateReport(getIntent().getExtras()));
|
tvReport.setText(report = generateReport(getIntent().getExtras()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -96,7 +94,7 @@ public class DebugInfoActivity extends Activity {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
PackageManager pm = getPackageManager();
|
PackageManager pm = getPackageManager();
|
||||||
String installedFrom = pm.getInstallerPackageName("at.bitfire.davdroid");
|
String installedFrom = pm.getInstallerPackageName(BuildConfig.APPLICATION_ID);
|
||||||
if (TextUtils.isEmpty(installedFrom))
|
if (TextUtils.isEmpty(installedFrom))
|
||||||
installedFrom = "APK (directly)";
|
installedFrom = "APK (directly)";
|
||||||
else {
|
else {
|
||||||
|
@ -183,8 +183,8 @@
|
|||||||
<item>Vorbereiten der Synchronisierung</item>
|
<item>Vorbereiten der Synchronisierung</item>
|
||||||
<item>Abfragen der Server-Fähigkeiten</item>
|
<item>Abfragen der Server-Fähigkeiten</item>
|
||||||
<item>Verarbeiten lokal gelöschter Einträge</item>
|
<item>Verarbeiten lokal gelöschter Einträge</item>
|
||||||
<item>Vorbereiten neuer lokaler Einträge</item>
|
<item>Vorbereiten neuer/geänderter Einträge</item>
|
||||||
<item>Hochladen neuer/geänderter lokaler Einträge</item>
|
<item>Hochladen neuer/geänderter Einträge</item>
|
||||||
<item>Abfragen des Synchronisierungs-Zustands</item>
|
<item>Abfragen des Synchronisierungs-Zustands</item>
|
||||||
<item>Auflisten lokaler Einträge</item>
|
<item>Auflisten lokaler Einträge</item>
|
||||||
<item>Auflisten der Server-Einträge</item>
|
<item>Auflisten der Server-Einträge</item>
|
||||||
|
@ -198,7 +198,7 @@
|
|||||||
<item>preparing synchronization</item>
|
<item>preparing synchronization</item>
|
||||||
<item>querying capabilities</item>
|
<item>querying capabilities</item>
|
||||||
<item>processing locally deleted entries</item>
|
<item>processing locally deleted entries</item>
|
||||||
<item>preparing locally created entries</item>
|
<item>preparing created/modified entries</item>
|
||||||
<item>uploading created/modified entries</item>
|
<item>uploading created/modified entries</item>
|
||||||
<item>checking sync state</item>
|
<item>checking sync state</item>
|
||||||
<item>listing local entries</item>
|
<item>listing local entries</item>
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 4e1131ae4607b4220e2d37632fd54a987b633849
|
Subproject commit ea504f2512ad5e9a85391797bdbdeb6c92871cdf
|
Loading…
Reference in New Issue
Block a user