Sync journals when syncing everything else.

pull/2/head
Tom Hacohen 8 years ago
parent 0b67be2e89
commit 44a240fd75

@ -12,9 +12,7 @@ import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
@ -25,29 +23,17 @@ import android.text.TextUtils;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import at.bitfire.davdroid.journalmanager.Exceptions;
import at.bitfire.davdroid.journalmanager.JournalManager;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB.Collections;
import at.bitfire.davdroid.model.ServiceDB.HomeSets;
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
import at.bitfire.davdroid.model.ServiceDB.Services;
import lombok.Cleanup;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
public class DavService extends Service {
public static final String
ACTION_ACCOUNTS_UPDATED = "accountsUpdated",
ACTION_REFRESH_COLLECTIONS = "refreshCollections",
EXTRA_DAV_SERVICE_ID = "davServiceID";
private final IBinder binder = new InfoBinder();
@ -66,16 +52,6 @@ public class DavService extends Service {
case ACTION_ACCOUNTS_UPDATED:
cleanupAccounts();
break;
case ACTION_REFRESH_COLLECTIONS:
if (runningRefresh.add(id)) {
new Thread(new RefreshCollections(id)).start();
for (WeakReference<RefreshingStatusListener> ref : refreshingStatusListeners) {
RefreshingStatusListener listener = ref.get();
if (listener != null)
listener.onDavRefreshStatusChanged(id, true);
}
}
break;
}
}
@ -143,104 +119,4 @@ public class DavService extends Service {
dbHelper.close();
}
}
private class RefreshCollections implements Runnable {
final long service;
final OpenHelper dbHelper;
RefreshCollections(long davServiceId) {
this.service = davServiceId;
dbHelper = new OpenHelper(DavService.this);
}
@Override
public void run() {
Account account = null;
try {
@Cleanup SQLiteDatabase db = dbHelper.getWritableDatabase();
String serviceType = dbHelper.getServiceType(db, service);
App.log.info("Refreshing " + serviceType + " collections of service #" + service);
// get account
account = dbHelper.getServiceAccount(db, service);
OkHttpClient httpClient = HttpClient.create(DavService.this, account);
AccountSettings settings = new AccountSettings(DavService.this, account);
JournalManager journalsManager = new JournalManager(httpClient, HttpUrl.get(settings.getUri()));
List<CollectionInfo> collections = new LinkedList<>();
for (JournalManager.Journal journal : journalsManager.getJournals(settings.password())) {
CollectionInfo info = CollectionInfo.fromJson(journal.getContent(settings.password()));
info.url = journal.getUuid();
if (info.isOfTypeService(serviceType)) {
collections.add(info);
}
}
// FIXME: handle deletion from server
if (collections.isEmpty()) {
CollectionInfo info = CollectionInfo.defaultForService(serviceType);
JournalManager.Journal journal = new JournalManager.Journal(settings.password(), info.toJson());
journalsManager.putJournal(journal);
info.url = journal.getUuid();
collections.add(info);
}
db.beginTransactionNonExclusive();
try {
saveCollections(db, collections);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
} catch (InvalidAccountException e) {
// FIXME: Do something
e.printStackTrace();
} catch (Exceptions.HttpException e) {
// FIXME: do something
e.printStackTrace();
} catch (Exceptions.IntegrityException e) {
// FIXME: do something
e.printStackTrace();
} finally {
dbHelper.close();
runningRefresh.remove(service);
for (WeakReference<RefreshingStatusListener> ref : refreshingStatusListeners) {
RefreshingStatusListener listener = ref.get();
if (listener != null)
listener.onDavRefreshStatusChanged(service, false);
}
}
}
@NonNull
private Map<String, CollectionInfo> readCollections(SQLiteDatabase db) {
Map<String, CollectionInfo> collections = new LinkedHashMap<>();
@Cleanup Cursor cursor = db.query(Collections._TABLE, null, Collections.SERVICE_ID + "=?", new String[]{String.valueOf(service)}, null, null, null);
while (cursor.moveToNext()) {
ContentValues values = new ContentValues();
DatabaseUtils.cursorRowToContentValues(cursor, values);
collections.put(values.getAsString(Collections.URL), CollectionInfo.fromDB(values));
}
return collections;
}
private void saveCollections(SQLiteDatabase db, Iterable<CollectionInfo> collections) {
db.delete(Collections._TABLE, HomeSets.SERVICE_ID + "=?", new String[]{String.valueOf(service)});
for (CollectionInfo collection : collections) {
ContentValues values = collection.toDB();
App.log.log(Level.FINE, "Saving collection", values);
values.put(Collections.SERVICE_ID, service);
db.insertWithOnConflict(Collections._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
}
}
}
}

@ -43,8 +43,7 @@ public class CollectionInfo implements Serializable {
public CollectionInfo() {
}
public static CollectionInfo defaultForService(String sService) {
Type service = Type.valueOf(sService);
public static CollectionInfo defaultForServiceType(Type service) {
CollectionInfo info = new CollectionInfo();
info.displayName = "Default";
info.selected = true;

@ -26,6 +26,7 @@ import at.bitfire.davdroid.InvalidAccountException;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.journalmanager.Exceptions;
import at.bitfire.davdroid.journalmanager.JournalEntryManager;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.resource.LocalCalendar;
import at.bitfire.davdroid.resource.LocalEvent;
import at.bitfire.davdroid.resource.LocalResource;
@ -44,7 +45,7 @@ public class CalendarSyncManager extends SyncManager {
final private HttpUrl remote;
public CalendarSyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, SyncResult result, LocalCalendar calendar, HttpUrl remote) throws InvalidAccountException {
super(context, account, settings, extras, authority, result, "calendar/" + calendar.getId());
super(context, account, settings, extras, authority, result, "calendar/" + calendar.getId(), CollectionInfo.Type.CALENDAR);
localCollection = calendar;
this.remote = remote;
}

@ -29,6 +29,7 @@ import java.util.logging.Level;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.InvalidAccountException;
import at.bitfire.davdroid.journalmanager.Exceptions;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB;
import at.bitfire.davdroid.model.ServiceDB.Collections;
@ -61,6 +62,8 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return;
new RefreshCollections(account, CollectionInfo.Type.CALENDAR).run();
HttpUrl principal = updateLocalCalendars(provider, account, settings);
for (LocalCalendar calendar : (LocalCalendar[]) LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, CalendarContract.Calendars.SYNC_EVENTS + "!=0", null)) {
@ -73,6 +76,10 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
syncResult.databaseError = true;
} catch (InvalidAccountException e) {
App.log.log(Level.SEVERE, "Couldn't get account settings", e);
} catch (Exceptions.HttpException e) {
e.printStackTrace();
} catch (Exceptions.IntegrityException e) {
e.printStackTrace();
}
App.log.info("Calendar sync complete");

@ -26,6 +26,7 @@ import java.util.logging.Level;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.InvalidAccountException;
import at.bitfire.davdroid.journalmanager.Exceptions;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB;
import at.bitfire.davdroid.model.ServiceDB.Collections;
@ -56,6 +57,8 @@ public class ContactsSyncAdapterService extends SyncAdapterService {
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return;
new RefreshCollections(account, CollectionInfo.Type.ADDRESS_BOOK).run();
SQLiteDatabase db = dbHelper.getReadableDatabase();
Long service = dbHelper.getService(db, account, ServiceDB.Services.SERVICE_CARDDAV);
if (service != null) {
@ -71,6 +74,10 @@ public class ContactsSyncAdapterService extends SyncAdapterService {
App.log.info("No CardDAV service found in DB");
} catch (InvalidAccountException e) {
App.log.log(Level.SEVERE, "Couldn't get account settings", e);
} catch (Exceptions.HttpException e) {
e.printStackTrace();
} catch (Exceptions.IntegrityException e) {
e.printStackTrace();
} finally {
dbHelper.close();
}

@ -58,7 +58,7 @@ public class ContactsSyncManager extends SyncManager {
final private CollectionInfo info;
public ContactsSyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, ContentProviderClient provider, SyncResult result, HttpUrl principal, CollectionInfo info) throws InvalidAccountException {
super(context, account, settings, extras, authority, result, "addressBook");
super(context, account, settings, extras, authority, result, "addressBook", CollectionInfo.Type.ADDRESS_BOOK);
this.provider = provider;
this.remote = principal;
this.info = info;

@ -14,9 +14,13 @@ import android.app.PendingIntent;
import android.app.Service;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
@ -28,13 +32,26 @@ import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.HttpClient;
import at.bitfire.davdroid.InvalidAccountException;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.journalmanager.Exceptions;
import at.bitfire.davdroid.journalmanager.JournalManager;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB;
import at.bitfire.davdroid.ui.PermissionsActivity;
import lombok.Cleanup;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
//import com.android.vending.billing.IInAppBillingService;
@ -109,6 +126,90 @@ public abstract class SyncAdapterService extends Service {
}
return true;
}
}
protected class RefreshCollections {
final private ServiceDB.OpenHelper dbHelper;
final private Account account;
final private Context context;
final private CollectionInfo.Type serviceType;
RefreshCollections(Account account, CollectionInfo.Type serviceType) {
this.account = account;
this.serviceType = serviceType;
context = getContext();
dbHelper = new ServiceDB.OpenHelper(context);
}
void run() throws Exceptions.HttpException, Exceptions.IntegrityException {
try {
@Cleanup SQLiteDatabase db = dbHelper.getWritableDatabase();
App.log.info("Refreshing " + serviceType + " collections of service #" + serviceType.toString());
OkHttpClient httpClient = HttpClient.create(context, account);
AccountSettings settings = new AccountSettings(context, account);
JournalManager journalsManager = new JournalManager(httpClient, HttpUrl.get(settings.getUri()));
List<CollectionInfo> collections = new LinkedList<>();
for (JournalManager.Journal journal : journalsManager.getJournals(settings.password())) {
CollectionInfo info = CollectionInfo.fromJson(journal.getContent(settings.password()));
info.url = journal.getUuid();
if (info.type.equals(serviceType)) {
collections.add(info);
}
}
// FIXME: handle deletion from server
if (collections.isEmpty()) {
CollectionInfo info = CollectionInfo.defaultForServiceType(serviceType);
JournalManager.Journal journal = new JournalManager.Journal(settings.password(), info.toJson());
journalsManager.putJournal(journal);
info.url = journal.getUuid();
collections.add(info);
}
db.beginTransactionNonExclusive();
try {
saveCollections(db, collections);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
} catch (InvalidAccountException e) {
// FIXME: Do something
e.printStackTrace();
} finally {
dbHelper.close();
}
}
@NonNull
private Map<String, CollectionInfo> readCollections(SQLiteDatabase db) {
Long service = dbHelper.getService(db, account, serviceType.toString());
Map<String, CollectionInfo> collections = new LinkedHashMap<>();
@Cleanup Cursor cursor = db.query(ServiceDB.Collections._TABLE, null, ServiceDB.Collections.SERVICE_ID + "=?", new String[]{String.valueOf(service)}, null, null, null);
while (cursor.moveToNext()) {
ContentValues values = new ContentValues();
DatabaseUtils.cursorRowToContentValues(cursor, values);
collections.put(values.getAsString(ServiceDB.Collections.URL), CollectionInfo.fromDB(values));
}
return collections;
}
private void saveCollections(SQLiteDatabase db, Iterable<CollectionInfo> collections) {
Long service = dbHelper.getService(db, account, serviceType.toString());
db.delete(ServiceDB.Collections._TABLE, ServiceDB.Collections.SERVICE_ID + "=?", new String[]{String.valueOf(service)});
for (CollectionInfo collection : collections) {
ContentValues values = collection.toDB();
App.log.log(Level.FINE, "Saving collection", values);
values.put(ServiceDB.Collections.SERVICE_ID, service);
db.insertWithOnConflict(ServiceDB.Collections._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
}
}
}
}
}

@ -37,6 +37,7 @@ import at.bitfire.davdroid.InvalidAccountException;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.journalmanager.Exceptions;
import at.bitfire.davdroid.journalmanager.JournalEntryManager;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.resource.LocalCollection;
import at.bitfire.davdroid.resource.LocalResource;
import at.bitfire.davdroid.ui.AccountSettingsActivity;
@ -69,6 +70,7 @@ abstract public class SyncManager {
protected final Bundle extras;
protected final String authority;
protected final SyncResult syncResult;
protected final CollectionInfo.Type serviceType;
protected final AccountSettings settings;
protected LocalCollection localCollection;
@ -97,13 +99,14 @@ abstract public class SyncManager {
*/
protected Map<String, LocalResource> localResources;
public SyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, SyncResult syncResult, String uniqueCollectionId) throws InvalidAccountException {
public SyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, SyncResult syncResult, String uniqueCollectionId, CollectionInfo.Type serviceType) throws InvalidAccountException {
this.context = context;
this.account = account;
this.settings = settings;
this.extras = extras;
this.authority = authority;
this.syncResult = syncResult;
this.serviceType = serviceType;
// create HttpClient with given logger
httpClient = HttpClient.create(context, account);

@ -14,7 +14,6 @@ import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Dialog;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.ComponentName;
@ -33,12 +32,10 @@ import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.CardView;
@ -53,7 +50,6 @@ import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
@ -69,12 +65,9 @@ import at.bitfire.davdroid.App;
import at.bitfire.davdroid.DavService;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB;
import at.bitfire.davdroid.model.ServiceDB.Collections;
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
import at.bitfire.davdroid.model.ServiceDB.Services;
import at.bitfire.davdroid.resource.LocalAddressBook;
import at.bitfire.davdroid.resource.LocalTaskList;
import at.bitfire.ical4android.TaskProvider;
import lombok.Cleanup;

@ -30,7 +30,6 @@ import at.bitfire.davdroid.App;
import at.bitfire.davdroid.InvalidAccountException;
import at.bitfire.davdroid.R;
import at.bitfire.ical4android.TaskProvider;
import at.bitfire.vcard4android.GroupMethod;
public class AccountSettingsActivity extends AppCompatActivity {
public final static String EXTRA_ACCOUNT = "account";

@ -19,7 +19,6 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.Spinner;
import net.fortuna.ical4j.model.Calendar;

@ -19,14 +19,8 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import org.apache.commons.lang3.StringUtils;
import java.net.IDN;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.logging.Level;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.Constants;

@ -16,7 +16,6 @@ import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.provider.CalendarContract;
@ -32,7 +31,6 @@ import java.util.logging.Level;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.DavService;
import at.bitfire.davdroid.InvalidAccountException;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.journalmanager.Helpers;
@ -135,18 +133,11 @@ public class SetupEncryptionFragment extends DialogFragment implements LoaderMan
try {
AccountSettings settings = new AccountSettings(getContext(), account);
Intent refreshIntent = new Intent(getActivity(), DavService.class);
refreshIntent.setAction(DavService.ACTION_REFRESH_COLLECTIONS);
settings.setAuthToken(config.authtoken);
if (config.cardDAV != null) {
// insert CardDAV service
long id = insertService(db, accountName, ServiceDB.Services.SERVICE_CARDDAV, config.cardDAV);
// start CardDAV service detection (refresh collections)
refreshIntent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, id);
getActivity().startService(refreshIntent);
insertService(db, accountName, ServiceDB.Services.SERVICE_CARDDAV, config.cardDAV);
// enable contact sync
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
@ -155,11 +146,7 @@ public class SetupEncryptionFragment extends DialogFragment implements LoaderMan
if (config.calDAV != null) {
// insert CalDAV service
long id = insertService(db, accountName, ServiceDB.Services.SERVICE_CALDAV, config.calDAV);
// start CalDAV service detection (refresh collections)
refreshIntent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, id);
getActivity().startService(refreshIntent);
insertService(db, accountName, ServiceDB.Services.SERVICE_CALDAV, config.calDAV);
// enable calendar sync
ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 1);

Loading…
Cancel
Save