1
0
mirror of https://github.com/etesync/android synced 2025-05-11 03:18:48 +00:00
etesync-android/src/at/bitfire/davdroid/syncadapter/SyncManager.java

176 lines
5.9 KiB
Java

/*******************************************************************************
* Copyright (c) 2013 Richard Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
******************************************************************************/
package at.bitfire.davdroid.syncadapter;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import net.fortuna.ical4j.model.ValidationException;
import org.apache.http.HttpException;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.OperationApplicationException;
import android.content.SyncResult;
import android.os.RemoteException;
import android.util.Log;
import at.bitfire.davdroid.resource.LocalCollection;
import at.bitfire.davdroid.resource.RemoteCollection;
import at.bitfire.davdroid.resource.Resource;
import at.bitfire.davdroid.webdav.PreconditionFailedException;
public class SyncManager {
private static final String TAG = "davdroid.SyncManager";
private static final int MAX_UPDATES_BEFORE_COMMIT = 100;
protected Account account;
protected AccountManager accountManager;
public SyncManager(Account account, AccountManager accountManager) {
this.account = account;
this.accountManager = accountManager;
}
public void synchronize(LocalCollection local, RemoteCollection dav, boolean manualSync, SyncResult syncResult) throws RemoteException, OperationApplicationException, IOException, HttpException {
boolean fetchCollection = false;
// PHASE 1: UPLOAD LOCALLY-CHANGED RESOURCES
// remove deleted resources from remote
Resource[] deletedResources = local.findDeleted();
if (deletedResources != null) {
Log.i(TAG, "Remotely removing " + deletedResources.length + " deleted resource(s) (if not changed)");
for (Resource res : deletedResources) {
try {
dav.delete(res);
} catch(PreconditionFailedException e) {
Log.i(TAG, "Locally-deleted resource has been changed on the server in the meanwhile");
}
fetchCollection = true;
local.delete(res);
}
local.commit();
}
// upload new resources
Resource[] newResources = local.findNew();
if (newResources != null) {
Log.i(TAG, "Uploading " + newResources.length + " new resource(s) (if not existing)");
for (Resource res : newResources) {
try {
dav.add(res);
} catch(PreconditionFailedException e) {
Log.i(TAG, "Didn't overwrite existing resource with other content");
} catch (ValidationException e) {
Log.e(TAG, "Couldn't create entity for adding: " + e.toString());
}
fetchCollection = true;
local.clearDirty(res);
}
local.commit();
}
// upload modified resources
Resource[] dirtyResources = local.findDirty();
if (dirtyResources != null) {
Log.i(TAG, "Uploading " + dirtyResources.length + " modified resource(s) (if not changed)");
for (Resource res : dirtyResources) {
try {
dav.update(res);
} catch(PreconditionFailedException e) {
Log.i(TAG, "Locally changed resource has been changed on the server in the meanwhile");
} catch (ValidationException e) {
Log.e(TAG, "Couldn't create entity for updating: " + e.toString());
}
fetchCollection = true;
local.clearDirty(res);
}
local.commit();
}
// PHASE 2A: FETCH REMOTE COLLECTION STATUS
// has collection changed -> fetch resources?
if (manualSync) {
Log.i(TAG, "Synchronization forced");
fetchCollection = true;
}
if (!fetchCollection) {
String currentCTag = dav.getCTag(),
lastCTag = local.getCTag();
if (currentCTag == null || !currentCTag.equals(lastCTag))
fetchCollection = true;
}
if (!fetchCollection)
return;
// PHASE 2B: FETCH REMOTE COLLECTION SUMMARY
// fetch remote resources -> add/overwrite local resources
Log.i(TAG, "Fetching remote resource list");
Set<Resource> resourcesToAdd = new HashSet<Resource>(),
resourcesToUpdate = new HashSet<Resource>();
Resource[] remoteResources = dav.getMemberETags();
if (remoteResources == null) // failure
return;
for (Resource remoteResource : remoteResources) {
Resource localResource = local.findByRemoteName(remoteResource.getName());
if (localResource == null)
resourcesToAdd.add(remoteResource);
else if (localResource.getETag() == null || !localResource.getETag().equals(remoteResource.getETag()))
resourcesToUpdate.add(remoteResource);
}
// PHASE 3: DOWNLOAD NEW/REMOTELY-CHANGED RESOURCES
Log.i(TAG, "Adding " + resourcesToAdd.size() + " remote resource(s)");
if (!resourcesToAdd.isEmpty()) {
for (Resource res : dav.multiGet(resourcesToAdd.toArray(new Resource[0]))) {
Log.i(TAG, "Adding " + res.getName());
try {
local.add(res);
} catch (ValidationException ex) {
Log.w(TAG, "Ignoring invalid remote resource: " + res.getName(), ex);
}
if (++syncResult.stats.numInserts % MAX_UPDATES_BEFORE_COMMIT == 0) // avoid TransactionTooLargeException
local.commit();
}
local.commit();
}
Log.i(TAG, "Updating " + resourcesToUpdate.size() + " remote resource(s)");
if (!resourcesToUpdate.isEmpty())
for (Resource res : dav.multiGet(resourcesToUpdate.toArray(new Resource[0]))) {
Log.i(TAG, "Updating " + res.getName());
try {
local.updateByRemoteName(res);
} catch (ValidationException ex) {
Log.e(TAG, "Ignoring invalid remote resource: " + res.getName(), ex);
}
if (++syncResult.stats.numUpdates % MAX_UPDATES_BEFORE_COMMIT == 0) // avoid TransactionTooLargeException
local.commit();
}
local.commit();
// delete remotely removed resources
Log.i(TAG, "Removing resources that are missing remotely");
local.deleteAllExceptRemoteNames(remoteResources);
local.commit();
// update collection CTag
local.setCTag(dav.getCTag());
local.commit();
}
}