diff --git a/app/src/main/java/com/etesync/syncadapter/AccountUpdateService.java b/app/src/main/java/com/etesync/syncadapter/AccountUpdateService.java index af5d6a31..4f6fb65d 100644 --- a/app/src/main/java/com/etesync/syncadapter/AccountUpdateService.java +++ b/app/src/main/java/com/etesync/syncadapter/AccountUpdateService.java @@ -13,17 +13,12 @@ import android.accounts.AccountManager; import android.annotation.SuppressLint; import android.app.Service; import android.content.Intent; -import android.database.Cursor; import android.database.DatabaseUtils; -import android.database.sqlite.SQLiteDatabase; import android.os.Binder; import android.os.IBinder; import android.support.annotation.NonNull; -import android.text.TextUtils; -import com.etesync.syncadapter.model.JournalEntity; -import com.etesync.syncadapter.model.ServiceDB.OpenHelper; -import com.etesync.syncadapter.model.ServiceDB.Services; +import com.etesync.syncadapter.model.ServiceEntity; import java.lang.ref.WeakReference; import java.util.HashSet; @@ -105,31 +100,17 @@ public class AccountUpdateService extends Service { void cleanupAccounts() { App.log.info("Cleaning up orphaned accounts"); - final OpenHelper dbHelper = new OpenHelper(this); - try { - SQLiteDatabase db = dbHelper.getWritableDatabase(); - - List sqlAccountNames = new LinkedList<>(); - AccountManager am = AccountManager.get(this); - for (Account account : am.getAccountsByType(Constants.ACCOUNT_TYPE)) - sqlAccountNames.add(DatabaseUtils.sqlEscapeString(account.name)); - - EntityDataStore data = ((App) getApplication()).getData(); - - if (sqlAccountNames.isEmpty()) { - data.delete(JournalEntity.class).get().value(); - db.delete(Services._TABLE, null, null); - } else { - Cursor cur = db.query(Services._TABLE, new String[]{Services.ID}, Services.ACCOUNT_NAME + " NOT IN (" + TextUtils.join(",", sqlAccountNames) + ")", null, null, null, null); - cur.moveToFirst(); - while(!cur.isAfterLast()) { - data.delete(JournalEntity.class).where(JournalEntity.SERVICE.eq(cur.getLong(0))).get().value(); - cur.moveToNext(); - } - db.delete(Services._TABLE, Services.ACCOUNT_NAME + " NOT IN (" + TextUtils.join(",", sqlAccountNames) + ")", null); - } - } finally { - dbHelper.close(); + List sqlAccountNames = new LinkedList<>(); + AccountManager am = AccountManager.get(this); + for (Account account : am.getAccountsByType(Constants.ACCOUNT_TYPE)) + sqlAccountNames.add(account.name); + + EntityDataStore data = ((App) getApplication()).getData(); + + if (sqlAccountNames.isEmpty()) { + data.delete(ServiceEntity.class).get().value(); + } else { + data.delete(ServiceEntity.class).where(ServiceEntity.ACCOUNT.notIn(sqlAccountNames)).get().value(); } } } diff --git a/app/src/main/java/com/etesync/syncadapter/App.java b/app/src/main/java/com/etesync/syncadapter/App.java index 3b956d7b..b0433329 100644 --- a/app/src/main/java/com/etesync/syncadapter/App.java +++ b/app/src/main/java/com/etesync/syncadapter/App.java @@ -40,8 +40,10 @@ import com.etesync.syncadapter.log.LogcatHandler; import com.etesync.syncadapter.log.PlainTextFormatter; import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.JournalEntity; +import com.etesync.syncadapter.model.JournalModel; import com.etesync.syncadapter.model.Models; import com.etesync.syncadapter.model.ServiceDB; +import com.etesync.syncadapter.model.ServiceEntity; import com.etesync.syncadapter.model.Settings; import com.etesync.syncadapter.resource.LocalAddressBook; import com.etesync.syncadapter.resource.LocalCalendar; @@ -227,7 +229,7 @@ public class App extends Application { public EntityDataStore getData() { if (dataStore == null) { // override onUpgrade to handle migrating to a new version - DatabaseSource source = new DatabaseSource(this, Models.DEFAULT, 2); + DatabaseSource source = new DatabaseSource(this, Models.DEFAULT, 3); Configuration configuration = source.getConfiguration(); dataStore = new EntityDataStore<>(configuration); } @@ -257,7 +259,7 @@ public class App extends Application { List collections = readCollections(dbHelper); for (CollectionInfo info : collections) { - JournalEntity journalEntity = new JournalEntity(info); + JournalEntity journalEntity = new JournalEntity(data, info); data.insert(journalEntity); } @@ -291,6 +293,12 @@ public class App extends Application { if (fromVersion < 10) { HintManager.setHintSeen(this, AccountsActivity.HINT_ACCOUNT_ADD, true); } + + if (fromVersion < 11) { + ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(this); + + migrateServices(dbHelper); + } } public static class AppUpdatedReceiver extends BroadcastReceiver { @@ -321,4 +329,25 @@ public class App extends Application { } return collections; } + + public void migrateServices(ServiceDB.OpenHelper dbHelper) { + @Cleanup SQLiteDatabase db = dbHelper.getReadableDatabase(); + EntityDataStore data = this.getData(); + @Cleanup Cursor cursor = db.query(ServiceDB.Services._TABLE, null, null, null, null, null, null); + while (cursor.moveToNext()) { + ContentValues values = new ContentValues(); + DatabaseUtils.cursorRowToContentValues(cursor, values); + ServiceEntity service = new ServiceEntity(); + service.setAccount(values.getAsString(ServiceDB.Services.ACCOUNT_NAME)); + service.setType(CollectionInfo.Type.valueOf(values.getAsString(ServiceDB.Services.SERVICE))); + data.insert(service); + + for (JournalEntity journalEntity : data.select(JournalEntity.class).where(JournalEntity.SERVICE.eq(values.getAsLong(ServiceDB.Services.ID))).get()) { + journalEntity.setServiceModel(service); + data.update(journalEntity); + } + } + + db.delete(ServiceDB.Services._TABLE, null, null); + } } diff --git a/app/src/main/java/com/etesync/syncadapter/PackageChangedReceiver.java b/app/src/main/java/com/etesync/syncadapter/PackageChangedReceiver.java index f0ffce08..4eac385d 100644 --- a/app/src/main/java/com/etesync/syncadapter/PackageChangedReceiver.java +++ b/app/src/main/java/com/etesync/syncadapter/PackageChangedReceiver.java @@ -8,22 +8,13 @@ package com.etesync.syncadapter; -import android.accounts.Account; import android.annotation.SuppressLint; import android.content.BroadcastReceiver; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.os.Bundle; import android.support.annotation.NonNull; -import com.etesync.syncadapter.model.ServiceDB; -import com.etesync.syncadapter.model.ServiceDB.Services; import com.etesync.syncadapter.resource.LocalTaskList; -import at.bitfire.ical4android.TaskProvider; -import lombok.Cleanup; public class PackageChangedReceiver extends BroadcastReceiver { @@ -40,24 +31,7 @@ public class PackageChangedReceiver extends BroadcastReceiver { App.log.info("Package (un)installed; OpenTasks provider now available = " + tasksInstalled); // check all accounts and (de)activate OpenTasks if a CalDAV service is defined - @Cleanup ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(context); - SQLiteDatabase db = dbHelper.getReadableDatabase(); - - @Cleanup Cursor cursor = db.query(Services._TABLE, new String[] { Services.ACCOUNT_NAME }, - Services.SERVICE + "=?", new String[] { Services.SERVICE_CALDAV }, null, null, null); - while (cursor.moveToNext()) { - Account account = new Account(cursor.getString(0), Constants.ACCOUNT_TYPE); - - if (tasksInstalled) { - if (ContentResolver.getIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority) <= 0) { - ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1); - ContentResolver.setSyncAutomatically(account, TaskProvider.ProviderName.OpenTasks.authority, true); - ContentResolver.addPeriodicSync(account, TaskProvider.ProviderName.OpenTasks.authority, new Bundle(), Constants.DEFAULT_SYNC_INTERVAL); - } - } else - ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 0); - - } + // FIXME: Do something if we ever bring back tasks. } } diff --git a/app/src/main/java/com/etesync/syncadapter/model/CollectionInfo.java b/app/src/main/java/com/etesync/syncadapter/model/CollectionInfo.java index 7b3729da..1e7b0abb 100644 --- a/app/src/main/java/com/etesync/syncadapter/model/CollectionInfo.java +++ b/app/src/main/java/com/etesync/syncadapter/model/CollectionInfo.java @@ -18,6 +18,8 @@ import com.google.gson.annotations.Expose; import java.io.Serializable; +import io.requery.Persistable; +import io.requery.sql.EntityDataStore; import lombok.ToString; @ToString(exclude = {"id"}) @@ -25,7 +27,7 @@ public class CollectionInfo implements Serializable { @Deprecated public long id; - public Long serviceID; + public int serviceID; public enum Type { ADDRESS_BOOK, @@ -80,7 +82,7 @@ public class CollectionInfo implements Serializable { public static CollectionInfo fromDB(ContentValues values) { CollectionInfo info = new CollectionInfo(); info.id = values.getAsLong(Collections.ID); - info.serviceID = values.getAsLong(Collections.SERVICE_ID); + info.serviceID = values.getAsInteger(Collections.SERVICE_ID); info.uid = values.getAsString(Collections.URL); info.readOnly = values.getAsInteger(Collections.READ_ONLY) != 0; @@ -95,6 +97,10 @@ public class CollectionInfo implements Serializable { return info; } + public ServiceEntity getServiceEntity(EntityDataStore data) { + return data.findByKey(ServiceEntity.class, serviceID); + } + public static CollectionInfo fromJson(String json) { return new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create().fromJson(json, CollectionInfo.class); } diff --git a/app/src/main/java/com/etesync/syncadapter/model/JournalModel.java b/app/src/main/java/com/etesync/syncadapter/model/JournalModel.java index 1e1aaca2..ad383ff1 100644 --- a/app/src/main/java/com/etesync/syncadapter/model/JournalModel.java +++ b/app/src/main/java/com/etesync/syncadapter/model/JournalModel.java @@ -37,14 +37,19 @@ public class JournalModel { byte[] encryptedKey; - @Index(value = "uid_unique") + + @Deprecated long service; + @ForeignKey(update = ReferentialAction.CASCADE) + @ManyToOne + Service serviceModel; + boolean deleted; @PostLoad void afterLoad() { - this.info.serviceID = service; + this.info.serviceID = this.serviceModel.id; this.info.uid = uid; } @@ -52,21 +57,21 @@ public class JournalModel { this.deleted = false; } - public Journal(CollectionInfo info) { + public Journal(EntityDataStore data, CollectionInfo info) { this(); this.info = info; this.uid = info.uid; - this.service = info.serviceID; + this.serviceModel = info.getServiceEntity(data); } - public static List getJournals(EntityDataStore data, long service) { - return data.select(JournalEntity.class).where(JournalEntity.SERVICE.eq(service).and(JournalEntity.DELETED.eq(false))).get().toList(); + public static List getJournals(EntityDataStore data, ServiceEntity serviceEntity) { + return data.select(JournalEntity.class).where(JournalEntity.SERVICE_MODEL.eq(serviceEntity).and(JournalEntity.DELETED.eq(false))).get().toList(); } - public static List getCollections(EntityDataStore data, long service) { + public static List getCollections(EntityDataStore data, ServiceEntity serviceEntity) { List ret = new LinkedList<>(); - List journals = getJournals(data, service); + List journals = getJournals(data, serviceEntity); for (JournalEntity journal : journals) { // FIXME: For some reason this isn't always being called, manually do it here. journal.afterLoad(); @@ -76,8 +81,8 @@ public class JournalModel { return ret; } - public static JournalEntity fetch(EntityDataStore data, String url) { - JournalEntity ret = data.select(JournalEntity.class).where(JournalEntity.UID.eq(url)).limit(1).get().firstOrNull(); + public static JournalEntity fetch(EntityDataStore data, ServiceEntity serviceEntity, String uid) { + JournalEntity ret = data.select(JournalEntity.class).where(JournalEntity.SERVICE_MODEL.eq(serviceEntity).and(JournalEntity.UID.eq(uid))).limit(1).get().firstOrNull(); if (ret != null) { // FIXME: For some reason this isn't always being called, manually do it here. ret.afterLoad(); @@ -86,9 +91,9 @@ public class JournalModel { } public static JournalEntity fetchOrCreate(EntityDataStore data, CollectionInfo collection) { - JournalEntity journalEntity = fetch(data, collection.uid); + JournalEntity journalEntity = fetch(data, collection.getServiceEntity(data), collection.uid); if (journalEntity == null) { - journalEntity = new JournalEntity(collection); + journalEntity = new JournalEntity(data, collection); } else { journalEntity.setInfo(collection); } @@ -125,6 +130,26 @@ public class JournalModel { Journal journal; } + + @Entity + @Table(name = "Service", uniqueIndexes = "service_unique_together") + public static abstract class Service { + @Key + @Generated + int id; + + @Index(value = "service_unique_together") + @Column(nullable = false) + String account; + + @Index(value = "service_unique_together") + CollectionInfo.Type type; + + public static ServiceEntity fetch(EntityDataStore data, String account, CollectionInfo.Type type) { + return data.select(ServiceEntity.class).where(ServiceEntity.ACCOUNT.eq(account).and(ServiceEntity.TYPE.eq(type))).limit(1).get().firstOrNull(); + } + } + static class CollectionInfoConverter implements Converter { @Override public Class getMappedType() { diff --git a/app/src/main/java/com/etesync/syncadapter/model/ServiceDB.java b/app/src/main/java/com/etesync/syncadapter/model/ServiceDB.java index 83e95b50..196b35ae 100644 --- a/app/src/main/java/com/etesync/syncadapter/model/ServiceDB.java +++ b/app/src/main/java/com/etesync/syncadapter/model/ServiceDB.java @@ -33,17 +33,13 @@ public class ServiceDB { VALUE = "value"; } + @Deprecated public static class Services { public static final String _TABLE = "services", ID = "_id", ACCOUNT_NAME = "accountName", SERVICE = "service"; - - // allowed values for SERVICE column - public static final String - SERVICE_CALDAV = CollectionInfo.Type.CALENDAR.toString(), - SERVICE_CARDDAV = CollectionInfo.Type.ADDRESS_BOOK.toString(); } @Deprecated @@ -90,15 +86,8 @@ public class ServiceDB { db.execSQL("CREATE TABLE " + Settings._TABLE + "(" + Settings.NAME + " TEXT NOT NULL," + Settings.VALUE + " TEXT NOT NULL" + - ")"); + ")"); db.execSQL("CREATE UNIQUE INDEX settings_name ON " + Settings._TABLE + " (" + Settings.NAME + ")"); - - db.execSQL("CREATE TABLE " + Services._TABLE + "(" + - Services.ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Services.ACCOUNT_NAME + " TEXT NOT NULL," + - Services.SERVICE + " TEXT NOT NULL" + - ")"); - db.execSQL("CREATE UNIQUE INDEX services_account ON " + Services._TABLE + " (" + Services.ACCOUNT_NAME + "," + Services.SERVICE + ")"); } @Override @@ -112,7 +101,7 @@ public class ServiceDB { db.beginTransactionNonExclusive(); // iterate through all tables - @Cleanup Cursor cursorTables = db.query("sqlite_master", new String[] { "name" }, "type='table'", null, null, null, null); + @Cleanup Cursor cursorTables = db.query("sqlite_master", new String[]{"name"}, "type='table'", null, null, null, null); while (cursorTables.moveToNext()) { String table = cursorTables.getString(0); sb.append(table).append("\n"); @@ -153,47 +142,5 @@ public class ServiceDB { } db.endTransaction(); } - - @NonNull - public Account getServiceAccount(SQLiteDatabase db, long service) { - @Cleanup Cursor cursor = db.query(Services._TABLE, new String[]{Services.ACCOUNT_NAME}, Services.ID + "=?", new String[]{String.valueOf(service)}, null, null, null); - if (cursor.moveToNext()) { - return new Account(cursor.getString(0), Constants.ACCOUNT_TYPE); - } else - throw new IllegalArgumentException("Service not found"); - } - - @NonNull - public String getServiceType(SQLiteDatabase db, long service) { - @Cleanup Cursor cursor = db.query(Services._TABLE, new String[]{Services.SERVICE}, Services.ID + "=?", new String[]{String.valueOf(service)}, null, null, null); - if (cursor.moveToNext()) - return cursor.getString(0); - else - throw new IllegalArgumentException("Service not found"); - } - - @Nullable - public Long getService(@NonNull SQLiteDatabase db, @NonNull Account account, String service) { - @Cleanup Cursor c = db.query(Services._TABLE, new String[]{Services.ID}, - Services.ACCOUNT_NAME + "=? AND " + Services.SERVICE + "=?", new String[]{account.name, service}, null, null, null); - if (c.moveToNext()) - return c.getLong(0); - else - return null; - } - - @Nullable - public Long getService(@NonNull Account account, String service) { - @Cleanup SQLiteDatabase db = getReadableDatabase(); - return getService(db, account, service); - } } - - - public static void onRenameAccount(@NonNull SQLiteDatabase db, @NonNull String oldName, @NonNull String newName) { - ContentValues values = new ContentValues(1); - values.put(Services.ACCOUNT_NAME, newName); - db.update(Services._TABLE, values, Services.ACCOUNT_NAME + "=?", new String[] { oldName }); - } - } diff --git a/app/src/main/java/com/etesync/syncadapter/syncadapter/CalendarsSyncAdapterService.java b/app/src/main/java/com/etesync/syncadapter/syncadapter/CalendarsSyncAdapterService.java index 1d9126ed..cd4ba3a7 100644 --- a/app/src/main/java/com/etesync/syncadapter/syncadapter/CalendarsSyncAdapterService.java +++ b/app/src/main/java/com/etesync/syncadapter/syncadapter/CalendarsSyncAdapterService.java @@ -14,7 +14,6 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SyncResult; -import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.os.Bundle; import android.provider.CalendarContract; @@ -27,8 +26,8 @@ import com.etesync.syncadapter.R; import com.etesync.syncadapter.journalmanager.Exceptions; import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.JournalEntity; -import com.etesync.syncadapter.model.ServiceDB; -import com.etesync.syncadapter.model.ServiceDB.Services; +import com.etesync.syncadapter.model.JournalModel; +import com.etesync.syncadapter.model.ServiceEntity; import com.etesync.syncadapter.resource.LocalCalendar; import com.etesync.syncadapter.ui.DebugInfoActivity; @@ -109,47 +108,40 @@ public class CalendarsSyncAdapterService extends SyncAdapterService { } private void updateLocalCalendars(ContentProviderClient provider, Account account, AccountSettings settings) throws CalendarStorageException { - ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(getContext()); - try { - // enumerate remote and local calendars - SQLiteDatabase db = dbHelper.getReadableDatabase(); - Long service = dbHelper.getService(db, account, Services.SERVICE_CALDAV); - - EntityDataStore data = ((App) getContext().getApplicationContext()).getData(); - Map remote = new HashMap<>(); - List remoteCollections = JournalEntity.getCollections(data, service); - for (CollectionInfo info : remoteCollections) { - remote.put(info.uid, info); - } + EntityDataStore data = ((App) getContext().getApplicationContext()).getData(); + ServiceEntity service = JournalModel.Service.fetch(data, account.name, CollectionInfo.Type.CALENDAR); - LocalCalendar[] local = (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, null, null); - - boolean updateColors = settings.getManageCalendarColors(); - - // delete obsolete local calendar - for (LocalCalendar calendar : local) { - String url = calendar.getName(); - if (!remote.containsKey(url)) { - App.log.fine("Deleting obsolete local calendar " + url); - calendar.delete(); - } else { - // remote CollectionInfo found for this local collection, update data - CollectionInfo info = remote.get(url); - App.log.fine("Updating local calendar " + url + " with " + info); - calendar.update(info, updateColors); - // we already have a local calendar for this remote collection, don't take into consideration anymore - remote.remove(url); - } - } + Map remote = new HashMap<>(); + List remoteCollections = JournalEntity.getCollections(data, service); + for (CollectionInfo info : remoteCollections) { + remote.put(info.uid, info); + } - // create new local calendars - for (String url : remote.keySet()) { + LocalCalendar[] local = (LocalCalendar[]) LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, null, null); + + boolean updateColors = settings.getManageCalendarColors(); + + // delete obsolete local calendar + for (LocalCalendar calendar : local) { + String url = calendar.getName(); + if (!remote.containsKey(url)) { + App.log.fine("Deleting obsolete local calendar " + url); + calendar.delete(); + } else { + // remote CollectionInfo found for this local collection, update data CollectionInfo info = remote.get(url); - App.log.info("Adding local calendar list " + info); - LocalCalendar.create(account, provider, info); + App.log.fine("Updating local calendar " + url + " with " + info); + calendar.update(info, updateColors); + // we already have a local calendar for this remote collection, don't take into consideration anymore + remote.remove(url); } - } finally { - dbHelper.close(); + } + + // create new local calendars + for (String url : remote.keySet()) { + CollectionInfo info = remote.get(url); + App.log.info("Adding local calendar list " + info); + LocalCalendar.create(account, provider, info); } } } diff --git a/app/src/main/java/com/etesync/syncadapter/syncadapter/ContactsSyncAdapterService.java b/app/src/main/java/com/etesync/syncadapter/syncadapter/ContactsSyncAdapterService.java index fc2f55cf..74d8ead0 100644 --- a/app/src/main/java/com/etesync/syncadapter/syncadapter/ContactsSyncAdapterService.java +++ b/app/src/main/java/com/etesync/syncadapter/syncadapter/ContactsSyncAdapterService.java @@ -14,7 +14,6 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SyncResult; -import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import com.etesync.syncadapter.AccountSettings; @@ -26,7 +25,9 @@ import com.etesync.syncadapter.R; import com.etesync.syncadapter.journalmanager.Exceptions; import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.JournalEntity; +import com.etesync.syncadapter.model.JournalModel; import com.etesync.syncadapter.model.ServiceDB; +import com.etesync.syncadapter.model.ServiceEntity; import com.etesync.syncadapter.ui.DebugInfoActivity; import java.util.logging.Level; @@ -65,11 +66,12 @@ public class ContactsSyncAdapterService extends SyncAdapterService { new RefreshCollections(account, CollectionInfo.Type.ADDRESS_BOOK).run(); - SQLiteDatabase db = dbHelper.getReadableDatabase(); - Long service = dbHelper.getService(db, account, ServiceDB.Services.SERVICE_CARDDAV); + EntityDataStore data = ((App) getContext().getApplicationContext()).getData(); + + ServiceEntity service = JournalModel.Service.fetch(data, account.name, CollectionInfo.Type.ADDRESS_BOOK); + if (service != null) { HttpUrl principal = HttpUrl.get(settings.getUri()); - EntityDataStore data = ((App) getContext().getApplicationContext()).getData(); CollectionInfo info = JournalEntity.getCollections(data, service).get(0); try { ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, settings, extras, authority, provider, syncResult, principal, info); diff --git a/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncAdapterService.java b/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncAdapterService.java index f639615e..25bd22bb 100644 --- a/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncAdapterService.java +++ b/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncAdapterService.java @@ -47,7 +47,9 @@ import com.etesync.syncadapter.journalmanager.Exceptions; import com.etesync.syncadapter.journalmanager.JournalManager; import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.JournalEntity; +import com.etesync.syncadapter.model.JournalModel; import com.etesync.syncadapter.model.ServiceDB; +import com.etesync.syncadapter.model.ServiceEntity; import com.etesync.syncadapter.ui.PermissionsActivity; import io.requery.Persistable; @@ -175,22 +177,16 @@ public abstract class SyncAdapterService extends Service { journals.add(new Pair<>(journal, info)); } - db.beginTransactionNonExclusive(); - try { - saveCollections(db, journals); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } + saveCollections(journals); } finally { dbHelper.close(); } } - private void saveCollections(SQLiteDatabase db, Iterable> journals) { - Long service = dbHelper.getService(db, account, serviceType.toString()); - + private void saveCollections(Iterable> journals) { EntityDataStore data = ((App) context.getApplicationContext()).getData(); + ServiceEntity service = JournalModel.Service.fetch(data, account.name, serviceType); + Map existing = new HashMap<>(); for (JournalEntity journalEntity : JournalEntity.getJournals(data, service)) { existing.put(journalEntity.getUid(), journalEntity); @@ -201,7 +197,7 @@ public abstract class SyncAdapterService extends Service { CollectionInfo collection = pair.second; App.log.log(Level.FINE, "Saving collection", journal.getUid()); - collection.serviceID = service; + collection.serviceID = service.getId(); JournalEntity journalEntity = JournalEntity.fetchOrCreate(data, collection); journalEntity.setOwner(journal.getOwner()); journalEntity.setEncryptedKey(journal.getKey()); diff --git a/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncManager.java b/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncManager.java index cf07d4a0..4db3a398 100644 --- a/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncManager.java +++ b/app/src/main/java/com/etesync/syncadapter/syncadapter/SyncManager.java @@ -27,6 +27,8 @@ import com.etesync.syncadapter.journalmanager.JournalEntryManager; import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.EntryEntity; import com.etesync.syncadapter.model.JournalEntity; +import com.etesync.syncadapter.model.JournalModel; +import com.etesync.syncadapter.model.ServiceEntity; import com.etesync.syncadapter.model.SyncEntry; import com.etesync.syncadapter.resource.LocalCollection; import com.etesync.syncadapter.resource.LocalResource; @@ -108,7 +110,8 @@ abstract public class SyncManager { httpClient = HttpClient.create(context, account); data = ((App) context.getApplicationContext()).getData(); - info = JournalEntity.fetch(data, journalUid).getInfo(); + ServiceEntity serviceEntity = JournalModel.Service.fetch(data, account.name, serviceType); + info = JournalEntity.fetch(data, serviceEntity, journalUid).getInfo(); // dismiss previous error notifications notificationManager = new NotificationHelper(context, journalUid, notificationId()); @@ -232,7 +235,7 @@ abstract public class SyncManager { private JournalEntity getJournalEntity() { if (_journalEntity == null) - _journalEntity = data.select(JournalEntity.class).where(JournalEntity.UID.eq(journal.getUid())).limit(1).get().first(); + _journalEntity = JournalModel.Journal.fetch(data, info.getServiceEntity(data), journal.getUid()); return _journalEntity; } diff --git a/app/src/main/java/com/etesync/syncadapter/ui/AccountActivity.java b/app/src/main/java/com/etesync/syncadapter/ui/AccountActivity.java index cba38ac2..4ccfc867 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/AccountActivity.java +++ b/app/src/main/java/com/etesync/syncadapter/ui/AccountActivity.java @@ -24,8 +24,6 @@ import android.content.Intent; import android.content.Loader; import android.content.ServiceConnection; import android.content.SyncStatusObserver; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; @@ -57,8 +55,7 @@ import com.etesync.syncadapter.Constants; import com.etesync.syncadapter.R; import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.JournalEntity; -import com.etesync.syncadapter.model.ServiceDB.OpenHelper; -import com.etesync.syncadapter.model.ServiceDB.Services; +import com.etesync.syncadapter.model.ServiceEntity; import com.etesync.syncadapter.resource.LocalCalendar; import com.etesync.syncadapter.utils.HintManager; import com.etesync.syncadapter.utils.ShowcaseBuilder; @@ -71,7 +68,6 @@ import at.bitfire.cert4android.CustomCertManager; import at.bitfire.ical4android.TaskProvider; import io.requery.Persistable; import io.requery.sql.EntityDataStore; -import lombok.Cleanup; import tourguide.tourguide.ToolTip; import static android.content.ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE; @@ -318,31 +314,23 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu public AccountInfo loadInBackground() { AccountInfo info = new AccountInfo(); - @Cleanup OpenHelper dbHelper = new OpenHelper(getContext()); - SQLiteDatabase db = dbHelper.getReadableDatabase(); EntityDataStore data = ((App) getContext().getApplicationContext()).getData(); - @Cleanup Cursor cursor = db.query( - Services._TABLE, - new String[] { Services.ID, Services.SERVICE }, - Services.ACCOUNT_NAME + "=?", new String[] { account.name }, - null, null, null); - - while (cursor.moveToNext()) { - long id = cursor.getLong(0); - String service = cursor.getString(1); - if (Services.SERVICE_CARDDAV.equals(service)) { + for (ServiceEntity serviceEntity : data.select(ServiceEntity.class).where(ServiceEntity.ACCOUNT.eq(account.name)).get()) { + long id = serviceEntity.getId(); + CollectionInfo.Type service = serviceEntity.getType(); + if (service.equals(CollectionInfo.Type.ADDRESS_BOOK)) { info.carddav = new AccountInfo.ServiceInfo(); info.carddav.id = id; info.carddav.refreshing = (davService != null && davService.isRefreshing(id)) || ContentResolver.isSyncActive(account, ContactsContract.AUTHORITY); - info.carddav.collections = JournalEntity.getCollections(data, id); - } else if (Services.SERVICE_CALDAV.equals(service)) { + info.carddav.collections = JournalEntity.getCollections(data, serviceEntity); + } else if (service.equals(CollectionInfo.Type.CALENDAR)) { info.caldav = new AccountInfo.ServiceInfo(); info.caldav.id = id; info.caldav.refreshing = (davService != null && davService.isRefreshing(id)) || ContentResolver.isSyncActive(account, CalendarContract.AUTHORITY) || ContentResolver.isSyncActive(account, TaskProvider.ProviderName.OpenTasks.authority); - info.caldav.collections = JournalEntity.getCollections(data, id); + info.caldav.collections = JournalEntity.getCollections(data, serviceEntity); } } return info; diff --git a/app/src/main/java/com/etesync/syncadapter/ui/CreateCollectionFragment.java b/app/src/main/java/com/etesync/syncadapter/ui/CreateCollectionFragment.java index 4e86c044..fdaa671e 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/CreateCollectionFragment.java +++ b/app/src/main/java/com/etesync/syncadapter/ui/CreateCollectionFragment.java @@ -35,7 +35,9 @@ import com.etesync.syncadapter.journalmanager.Exceptions; import com.etesync.syncadapter.journalmanager.JournalManager; import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.JournalEntity; +import com.etesync.syncadapter.model.JournalModel; import com.etesync.syncadapter.model.ServiceDB; +import com.etesync.syncadapter.model.ServiceEntity; import io.requery.Persistable; import io.requery.sql.EntityDataStore; @@ -125,31 +127,21 @@ public class CreateCollectionFragment extends DialogFragment implements LoaderMa @Override public Exception loadInBackground() { - ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(getContext()); - try { String authority = null; - // now insert collection into database: - SQLiteDatabase db = dbHelper.getWritableDatabase(); + + EntityDataStore data = ((App) getContext().getApplicationContext()).getData(); // 1. find service ID - String serviceType; if (info.type == CollectionInfo.Type.ADDRESS_BOOK) { - serviceType = ServiceDB.Services.SERVICE_CARDDAV; authority = ContactsContract.AUTHORITY; } else if (info.type == CollectionInfo.Type.CALENDAR) { - serviceType = ServiceDB.Services.SERVICE_CALDAV; authority = CalendarContract.AUTHORITY; } else { throw new IllegalArgumentException("Collection must be an address book or calendar"); } - @Cleanup Cursor c = db.query(ServiceDB.Services._TABLE, new String[]{ServiceDB.Services.ID}, - ServiceDB.Services.ACCOUNT_NAME + "=? AND " + ServiceDB.Services.SERVICE + "=?", - new String[]{account.name, serviceType}, null, null, null - ); - if (!c.moveToNext()) - throw new IllegalStateException(); - long serviceID = c.getLong(0); + + ServiceEntity serviceEntity = JournalModel.Service.fetch(data, account.name, info.type); AccountSettings settings = new AccountSettings(getContext(), account); HttpUrl principal = HttpUrl.get(settings.getUri()); @@ -167,8 +159,7 @@ public class CreateCollectionFragment extends DialogFragment implements LoaderMa } // 2. add collection to service - EntityDataStore data = ((App) getContext().getApplicationContext()).getData(); - info.serviceID = serviceID; + info.serviceID = serviceEntity.getId(); JournalEntity journalEntity = JournalEntity.fetchOrCreate(data, info); data.upsert(journalEntity); @@ -180,8 +171,6 @@ public class CreateCollectionFragment extends DialogFragment implements LoaderMa return e; } catch (Exceptions.IntegrityException|Exceptions.GenericCryptoException e) { return e; - } finally { - dbHelper.close(); } return null; diff --git a/app/src/main/java/com/etesync/syncadapter/ui/DebugInfoActivity.java b/app/src/main/java/com/etesync/syncadapter/ui/DebugInfoActivity.java index 25178b54..f26eaeba 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/DebugInfoActivity.java +++ b/app/src/main/java/com/etesync/syncadapter/ui/DebugInfoActivity.java @@ -49,6 +49,7 @@ import com.etesync.syncadapter.journalmanager.Exceptions.HttpException; import com.etesync.syncadapter.model.EntryEntity; import com.etesync.syncadapter.model.JournalEntity; import com.etesync.syncadapter.model.ServiceDB; +import com.etesync.syncadapter.model.ServiceEntity; import io.requery.Persistable; import io.requery.sql.EntityDataStore; @@ -246,8 +247,14 @@ public class DebugInfoActivity extends AppCompatActivity implements LoaderManage dbHelper.dump(report); report.append("\n"); - report.append("JOURNALS DUMP\n"); + report.append("SERVICES DUMP\n"); EntityDataStore data = ((App) getContext().getApplicationContext()).getData(); + for (ServiceEntity serviceEntity : data.select(ServiceEntity.class).get()) { + report.append(serviceEntity.toString() + "\n"); + } + report.append("\n"); + + report.append("JOURNALS DUMP\n"); List journals = data.select(JournalEntity.class).where(JournalEntity.DELETED.eq(false)).get().toList(); for (JournalEntity journal : journals) { report.append(journal.toString() + "\n"); diff --git a/app/src/main/java/com/etesync/syncadapter/ui/DeleteCollectionFragment.java b/app/src/main/java/com/etesync/syncadapter/ui/DeleteCollectionFragment.java index fcf93e60..88cc560f 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/DeleteCollectionFragment.java +++ b/app/src/main/java/com/etesync/syncadapter/ui/DeleteCollectionFragment.java @@ -123,7 +123,7 @@ public class DeleteCollectionFragment extends DialogFragment implements LoaderMa Crypto.CryptoManager crypto = new Crypto.CryptoManager(collectionInfo.version, settings.password(), collectionInfo.uid); journalManager.deleteJournal(new JournalManager.Journal(crypto, collectionInfo.toJson(), collectionInfo.uid)); - JournalEntity journalEntity = JournalEntity.fetch(data, collectionInfo.uid); + JournalEntity journalEntity = JournalEntity.fetch(data, collectionInfo.getServiceEntity(data), collectionInfo.uid); journalEntity.setDeleted(true); data.update(journalEntity); diff --git a/app/src/main/java/com/etesync/syncadapter/ui/EditCollectionActivity.java b/app/src/main/java/com/etesync/syncadapter/ui/EditCollectionActivity.java index ac96e87e..20908bb7 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/EditCollectionActivity.java +++ b/app/src/main/java/com/etesync/syncadapter/ui/EditCollectionActivity.java @@ -78,7 +78,7 @@ public class EditCollectionActivity extends CreateCollectionActivity { public void onDeleteCollection(MenuItem item) { EntityDataStore data = ((App) getApplication()).getData(); - int journalCount = data.count(JournalEntity.class).where(JournalEntity.SERVICE.eq(info.serviceID)).get().value(); + int journalCount = data.count(JournalEntity.class).where(JournalEntity.SERVICE_MODEL.eq(info.getServiceEntity(data))).get().value(); if (journalCount < 2) { new AlertDialog.Builder(this) diff --git a/app/src/main/java/com/etesync/syncadapter/ui/ViewCollectionActivity.java b/app/src/main/java/com/etesync/syncadapter/ui/ViewCollectionActivity.java index 29c9206f..a859a9ca 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/ViewCollectionActivity.java +++ b/app/src/main/java/com/etesync/syncadapter/ui/ViewCollectionActivity.java @@ -27,6 +27,8 @@ import com.etesync.syncadapter.R; import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.EntryEntity; import com.etesync.syncadapter.model.JournalEntity; +import com.etesync.syncadapter.model.JournalModel; +import com.etesync.syncadapter.model.ServiceEntity; import com.etesync.syncadapter.resource.LocalAddressBook; import com.etesync.syncadapter.resource.LocalCalendar; import com.etesync.syncadapter.ui.importlocal.ImportActivity; @@ -63,7 +65,7 @@ public class ViewCollectionActivity extends AppCompatActivity implements Refresh public void refresh() { EntityDataStore data = ((App) getApplicationContext()).getData(); - final JournalEntity journalEntity = JournalEntity.fetch(data, info.uid); + final JournalEntity journalEntity = JournalEntity.fetch(data, info.getServiceEntity(data), info.uid); if ((journalEntity == null) || journalEntity.isDeleted()) { finish(); return; @@ -173,7 +175,7 @@ public class ViewCollectionActivity extends AppCompatActivity implements Refresh protected Long doInBackground(Void... aVoids) { EntityDataStore data = ((App) getApplicationContext()).getData(); - final JournalEntity journalEntity = JournalEntity.fetch(data, info.uid); + final JournalEntity journalEntity = JournalEntity.fetch(data, info.getServiceEntity(data), info.uid); entryCount = data.count(EntryEntity.class).where(EntryEntity.JOURNAL.eq(journalEntity)).get().value(); long count; diff --git a/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListEntriesFragment.java b/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListEntriesFragment.java index c525245a..87feec28 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListEntriesFragment.java +++ b/app/src/main/java/com/etesync/syncadapter/ui/journalviewer/ListEntriesFragment.java @@ -137,7 +137,7 @@ public class ListEntriesFragment extends ListFragment implements AdapterView.OnI @Override protected List doInBackground(Void... voids) { - journalEntity = JournalModel.Journal.fetch(data, info.uid); + journalEntity = JournalModel.Journal.fetch(data, info.getServiceEntity(data), info.uid); return data.select(EntryEntity.class).where(EntryEntity.JOURNAL.eq(journalEntity)).orderBy(EntryEntity.ID.desc()).get().toList(); } diff --git a/app/src/main/java/com/etesync/syncadapter/ui/setup/SetupEncryptionFragment.java b/app/src/main/java/com/etesync/syncadapter/ui/setup/SetupEncryptionFragment.java index cf1cafa8..1b4d920f 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/setup/SetupEncryptionFragment.java +++ b/app/src/main/java/com/etesync/syncadapter/ui/setup/SetupEncryptionFragment.java @@ -14,7 +14,6 @@ import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; import android.content.ContentResolver; -import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; @@ -26,8 +25,6 @@ import android.support.v4.app.LoaderManager; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; -import java.util.logging.Level; - import com.etesync.syncadapter.AccountSettings; import com.etesync.syncadapter.App; import com.etesync.syncadapter.Constants; @@ -37,8 +34,12 @@ import com.etesync.syncadapter.journalmanager.Crypto; import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.JournalEntity; import com.etesync.syncadapter.model.ServiceDB; +import com.etesync.syncadapter.model.ServiceEntity; import com.etesync.syncadapter.resource.LocalTaskList; import com.etesync.syncadapter.ui.setup.BaseConfigurationFinder.Configuration; + +import java.util.logging.Level; + import at.bitfire.ical4android.TaskProvider; import io.requery.Persistable; import io.requery.sql.EntityDataStore; @@ -140,7 +141,7 @@ public class SetupEncryptionFragment extends DialogFragment implements LoaderMan if (config.cardDAV != null) { // insert CardDAV service - insertService(db, accountName, ServiceDB.Services.SERVICE_CARDDAV, config.cardDAV); + insertService(db, accountName, CollectionInfo.Type.ADDRESS_BOOK, config.cardDAV); // contact sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_contacts.xml settings.setSyncInterval(ContactsContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL); @@ -150,7 +151,7 @@ public class SetupEncryptionFragment extends DialogFragment implements LoaderMan if (config.calDAV != null) { // insert CalDAV service - insertService(db, accountName, ServiceDB.Services.SERVICE_CALDAV, config.calDAV); + insertService(db, accountName, CollectionInfo.Type.CALENDAR, config.calDAV); // calendar sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_contacts.xml settings.setSyncInterval(CalendarContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL); @@ -172,22 +173,20 @@ public class SetupEncryptionFragment extends DialogFragment implements LoaderMan return true; } - protected long insertService(SQLiteDatabase db, String accountName, String service, BaseConfigurationFinder.Configuration.ServiceInfo info) { - ContentValues values = new ContentValues(); + protected void insertService(SQLiteDatabase db, String accountName, CollectionInfo.Type serviceType, BaseConfigurationFinder.Configuration.ServiceInfo info) { + EntityDataStore data = ((App) getContext().getApplicationContext()).getData(); // insert service - values.put(ServiceDB.Services.ACCOUNT_NAME, accountName); - values.put(ServiceDB.Services.SERVICE, service); - long serviceID = db.insertWithOnConflict(ServiceDB.Services._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE); - EntityDataStore data = ((App) getContext().getApplicationContext()).getData(); + ServiceEntity serviceEntity = new ServiceEntity(); + serviceEntity.setAccount(accountName); + serviceEntity.setType(serviceType); + data.upsert(serviceEntity); // insert collections for (CollectionInfo collection : info.collections.values()) { - collection.serviceID = serviceID; - JournalEntity journalEntity = new JournalEntity(collection); + collection.serviceID = serviceEntity.getId(); + JournalEntity journalEntity = new JournalEntity(data, collection); data.insert(journalEntity); } - - return serviceID; } }