You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
etesync-android/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.java

178 lines
7.1 KiB

/*
* Copyright © 2013 2015 Ricki 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 android.accounts.Account;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
import android.os.Build;
import android.os.Bundle;
import java.io.IOException;
import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.HttpClient;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.ui.DebugInfoActivity;
import at.bitfire.vcard4android.ContactsStorageException;
abstract public class SyncManager {
protected final int SYNC_PHASE_PREPARE = 0,
SYNC_PHASE_QUERY_CAPABILITIES = 1,
SYNC_PHASE_PROCESS_LOCALLY_DELETED = 2,
SYNC_PHASE_PREPARE_LOCALLY_CREATED = 3,
SYNC_PHASE_UPLOAD_DIRTY = 4,
SYNC_PHASE_CHECK_SYNC_STATE = 5,
SYNC_PHASE_LIST_LOCAL = 6,
SYNC_PHASE_LIST_REMOTE = 7,
SYNC_PHASE_COMPARE_ENTRIES = 8,
SYNC_PHASE_DOWNLOAD_REMOTE = 9,
SYNC_PHASE_SAVE_SYNC_STATE = 10;
final NotificationManager notificationManager;
final int notificationId;
final Context context;
final Account account;
final Bundle extras;
final ContentProviderClient provider;
final SyncResult syncResult;
final AccountSettings settings;
final HttpClient httpClient;
public SyncManager(int notificationId, Context context, Account account, Bundle extras, ContentProviderClient provider, SyncResult syncResult) {
this.context = context;
this.account = account;
this.extras = extras;
this.provider = provider;
this.syncResult = syncResult;
// get account settings and generate httpClient
settings = new AccountSettings(context, account);
httpClient = new HttpClient(context, settings.getUserName(), settings.getPassword(), settings.getPreemptiveAuth());
// dismiss previous error notifications
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(account.name, this.notificationId = notificationId);
}
public void performSync() {
int syncPhase = SYNC_PHASE_PREPARE;
try {
prepare();
syncPhase = SYNC_PHASE_QUERY_CAPABILITIES;
queryCapabilities();
syncPhase = SYNC_PHASE_PROCESS_LOCALLY_DELETED;
processLocallyDeleted();
syncPhase = SYNC_PHASE_PREPARE_LOCALLY_CREATED;
processLocallyCreated();
syncPhase = SYNC_PHASE_UPLOAD_DIRTY;
uploadDirty();
syncPhase = SYNC_PHASE_CHECK_SYNC_STATE;
if (checkSyncState()) {
syncPhase = SYNC_PHASE_LIST_LOCAL;
listLocal();
syncPhase = SYNC_PHASE_LIST_REMOTE;
listRemote();
syncPhase = SYNC_PHASE_COMPARE_ENTRIES;
compareEntries();
syncPhase = SYNC_PHASE_DOWNLOAD_REMOTE;
downloadRemote();
syncPhase = SYNC_PHASE_SAVE_SYNC_STATE;
saveSyncState();
} else
Constants.log.info("Remote collection didn't change, skipping remote sync");
} catch (IOException e) {
Constants.log.error("I/O exception during sync, trying again later", e);
syncResult.stats.numIoExceptions++;
} catch(HttpException e) {
Constants.log.error("HTTP Exception during sync", e);
syncResult.stats.numParseExceptions++;
Intent detailsIntent = new Intent(context, DebugInfoActivity.class);
detailsIntent.putExtra(DebugInfoActivity.KEY_EXCEPTION, e);
detailsIntent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account);
detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase);
Notification.Builder builder = new Notification.Builder(context);
Notification notification;
builder .setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(context.getString(R.string.sync_error_title, account.name))
.setContentIntent(PendingIntent.getActivity(context, 0, detailsIntent, PendingIntent.FLAG_UPDATE_CURRENT));
String[] phases = context.getResources().getStringArray(R.array.sync_error_phases);
if (phases.length > syncPhase)
builder.setContentText(context.getString(R.string.sync_error_http, phases[syncPhase]));
if (Build.VERSION.SDK_INT >= 16) {
if (Build.VERSION.SDK_INT >= 21)
builder.setCategory(Notification.CATEGORY_ERROR);
notification = builder.build();
} else {
notification = builder.getNotification();
}
notificationManager.notify(account.name, notificationId, notification);
} catch(DavException e) {
// TODO
} catch(ContactsStorageException e) {
syncResult.databaseError = true;
}
}
abstract protected void prepare();
abstract protected void queryCapabilities() throws IOException, HttpException, DavException, ContactsStorageException;
abstract protected void processLocallyDeleted() throws IOException, HttpException, DavException, ContactsStorageException;
abstract protected void processLocallyCreated() throws IOException, HttpException, DavException, ContactsStorageException;
abstract protected void uploadDirty() throws IOException, HttpException, DavException, ContactsStorageException;
/**
* Checks the current sync state (e.g. CTag) and whether synchronization from remote is required.
* @return true if the remote collection has changed, i.e. synchronization from remote is required
* false if the remote collection hasn't changed
*/
abstract protected boolean checkSyncState() throws IOException, HttpException, DavException, ContactsStorageException;
abstract protected void listLocal() throws IOException, HttpException, DavException, ContactsStorageException;
abstract protected void listRemote() throws IOException, HttpException, DavException, ContactsStorageException;
abstract protected void compareEntries() throws IOException, HttpException, DavException, ContactsStorageException;
abstract protected void downloadRemote() throws IOException, HttpException, DavException, ContactsStorageException;
abstract protected void saveSyncState() throws IOException, HttpException, DavException, ContactsStorageException;
}