Services: Move to a requery model instead of raw SQL and improve models.

Having it in raw sql was slowing down development, and was error-prone.
It's much cleaner now, easier to handle, and enables us to develop
faster.

In this change I also fixed the fetching of journals to be by service
and id, not just id, because just id is not guaranteed to be unique.
pull/14/head
Tom Hacohen 7 years ago
parent 8b79529a94
commit e2f206e02e

@ -13,17 +13,12 @@ import android.accounts.AccountManager;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Service; import android.app.Service;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor;
import android.database.DatabaseUtils; import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.etesync.syncadapter.model.JournalEntity; import com.etesync.syncadapter.model.ServiceEntity;
import com.etesync.syncadapter.model.ServiceDB.OpenHelper;
import com.etesync.syncadapter.model.ServiceDB.Services;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.HashSet; import java.util.HashSet;
@ -105,31 +100,17 @@ public class AccountUpdateService extends Service {
void cleanupAccounts() { void cleanupAccounts() {
App.log.info("Cleaning up orphaned accounts"); App.log.info("Cleaning up orphaned accounts");
final OpenHelper dbHelper = new OpenHelper(this);
try {
SQLiteDatabase db = dbHelper.getWritableDatabase();
List<String> sqlAccountNames = new LinkedList<>(); List<String> sqlAccountNames = new LinkedList<>();
AccountManager am = AccountManager.get(this); AccountManager am = AccountManager.get(this);
for (Account account : am.getAccountsByType(Constants.ACCOUNT_TYPE)) for (Account account : am.getAccountsByType(Constants.ACCOUNT_TYPE))
sqlAccountNames.add(DatabaseUtils.sqlEscapeString(account.name)); sqlAccountNames.add(account.name);
EntityDataStore<Persistable> data = ((App) getApplication()).getData(); EntityDataStore<Persistable> data = ((App) getApplication()).getData();
if (sqlAccountNames.isEmpty()) { if (sqlAccountNames.isEmpty()) {
data.delete(JournalEntity.class).get().value(); data.delete(ServiceEntity.class).get().value();
db.delete(Services._TABLE, null, null);
} else { } else {
Cursor cur = db.query(Services._TABLE, new String[]{Services.ID}, Services.ACCOUNT_NAME + " NOT IN (" + TextUtils.join(",", sqlAccountNames) + ")", null, null, null, null); data.delete(ServiceEntity.class).where(ServiceEntity.ACCOUNT.notIn(sqlAccountNames)).get().value();
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();
} }
} }
} }

@ -40,8 +40,10 @@ import com.etesync.syncadapter.log.LogcatHandler;
import com.etesync.syncadapter.log.PlainTextFormatter; import com.etesync.syncadapter.log.PlainTextFormatter;
import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.JournalEntity; import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.JournalModel;
import com.etesync.syncadapter.model.Models; import com.etesync.syncadapter.model.Models;
import com.etesync.syncadapter.model.ServiceDB; import com.etesync.syncadapter.model.ServiceDB;
import com.etesync.syncadapter.model.ServiceEntity;
import com.etesync.syncadapter.model.Settings; import com.etesync.syncadapter.model.Settings;
import com.etesync.syncadapter.resource.LocalAddressBook; import com.etesync.syncadapter.resource.LocalAddressBook;
import com.etesync.syncadapter.resource.LocalCalendar; import com.etesync.syncadapter.resource.LocalCalendar;
@ -227,7 +229,7 @@ public class App extends Application {
public EntityDataStore<Persistable> getData() { public EntityDataStore<Persistable> getData() {
if (dataStore == null) { if (dataStore == null) {
// override onUpgrade to handle migrating to a new version // 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(); Configuration configuration = source.getConfiguration();
dataStore = new EntityDataStore<>(configuration); dataStore = new EntityDataStore<>(configuration);
} }
@ -257,7 +259,7 @@ public class App extends Application {
List<CollectionInfo> collections = readCollections(dbHelper); List<CollectionInfo> collections = readCollections(dbHelper);
for (CollectionInfo info : collections) { for (CollectionInfo info : collections) {
JournalEntity journalEntity = new JournalEntity(info); JournalEntity journalEntity = new JournalEntity(data, info);
data.insert(journalEntity); data.insert(journalEntity);
} }
@ -291,6 +293,12 @@ public class App extends Application {
if (fromVersion < 10) { if (fromVersion < 10) {
HintManager.setHintSeen(this, AccountsActivity.HINT_ACCOUNT_ADD, true); 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 { public static class AppUpdatedReceiver extends BroadcastReceiver {
@ -321,4 +329,25 @@ public class App extends Application {
} }
return collections; return collections;
} }
public void migrateServices(ServiceDB.OpenHelper dbHelper) {
@Cleanup SQLiteDatabase db = dbHelper.getReadableDatabase();
EntityDataStore<Persistable> 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);
}
} }

@ -8,22 +8,13 @@
package com.etesync.syncadapter; package com.etesync.syncadapter;
import android.accounts.Account;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.annotation.NonNull; 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 com.etesync.syncadapter.resource.LocalTaskList;
import at.bitfire.ical4android.TaskProvider;
import lombok.Cleanup;
public class PackageChangedReceiver extends BroadcastReceiver { 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); App.log.info("Package (un)installed; OpenTasks provider now available = " + tasksInstalled);
// check all accounts and (de)activate OpenTasks if a CalDAV service is defined // check all accounts and (de)activate OpenTasks if a CalDAV service is defined
@Cleanup ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(context); // FIXME: Do something if we ever bring back tasks.
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);
}
} }
} }

@ -18,6 +18,8 @@ import com.google.gson.annotations.Expose;
import java.io.Serializable; import java.io.Serializable;
import io.requery.Persistable;
import io.requery.sql.EntityDataStore;
import lombok.ToString; import lombok.ToString;
@ToString(exclude = {"id"}) @ToString(exclude = {"id"})
@ -25,7 +27,7 @@ public class CollectionInfo implements Serializable {
@Deprecated @Deprecated
public long id; public long id;
public Long serviceID; public int serviceID;
public enum Type { public enum Type {
ADDRESS_BOOK, ADDRESS_BOOK,
@ -80,7 +82,7 @@ public class CollectionInfo implements Serializable {
public static CollectionInfo fromDB(ContentValues values) { public static CollectionInfo fromDB(ContentValues values) {
CollectionInfo info = new CollectionInfo(); CollectionInfo info = new CollectionInfo();
info.id = values.getAsLong(Collections.ID); 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.uid = values.getAsString(Collections.URL);
info.readOnly = values.getAsInteger(Collections.READ_ONLY) != 0; info.readOnly = values.getAsInteger(Collections.READ_ONLY) != 0;
@ -95,6 +97,10 @@ public class CollectionInfo implements Serializable {
return info; return info;
} }
public ServiceEntity getServiceEntity(EntityDataStore<Persistable> data) {
return data.findByKey(ServiceEntity.class, serviceID);
}
public static CollectionInfo fromJson(String json) { public static CollectionInfo fromJson(String json) {
return new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create().fromJson(json, CollectionInfo.class); return new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create().fromJson(json, CollectionInfo.class);
} }

@ -37,14 +37,19 @@ public class JournalModel {
byte[] encryptedKey; byte[] encryptedKey;
@Index(value = "uid_unique")
@Deprecated
long service; long service;
@ForeignKey(update = ReferentialAction.CASCADE)
@ManyToOne
Service serviceModel;
boolean deleted; boolean deleted;
@PostLoad @PostLoad
void afterLoad() { void afterLoad() {
this.info.serviceID = service; this.info.serviceID = this.serviceModel.id;
this.info.uid = uid; this.info.uid = uid;
} }
@ -52,21 +57,21 @@ public class JournalModel {
this.deleted = false; this.deleted = false;
} }
public Journal(CollectionInfo info) { public Journal(EntityDataStore<Persistable> data, CollectionInfo info) {
this(); this();
this.info = info; this.info = info;
this.uid = info.uid; this.uid = info.uid;
this.service = info.serviceID; this.serviceModel = info.getServiceEntity(data);
} }
public static List<JournalEntity> getJournals(EntityDataStore<Persistable> data, long service) { public static List<JournalEntity> getJournals(EntityDataStore<Persistable> data, ServiceEntity serviceEntity) {
return data.select(JournalEntity.class).where(JournalEntity.SERVICE.eq(service).and(JournalEntity.DELETED.eq(false))).get().toList(); return data.select(JournalEntity.class).where(JournalEntity.SERVICE_MODEL.eq(serviceEntity).and(JournalEntity.DELETED.eq(false))).get().toList();
} }
public static List<CollectionInfo> getCollections(EntityDataStore<Persistable> data, long service) { public static List<CollectionInfo> getCollections(EntityDataStore<Persistable> data, ServiceEntity serviceEntity) {
List<CollectionInfo> ret = new LinkedList<>(); List<CollectionInfo> ret = new LinkedList<>();
List<JournalEntity> journals = getJournals(data, service); List<JournalEntity> journals = getJournals(data, serviceEntity);
for (JournalEntity journal : journals) { for (JournalEntity journal : journals) {
// FIXME: For some reason this isn't always being called, manually do it here. // FIXME: For some reason this isn't always being called, manually do it here.
journal.afterLoad(); journal.afterLoad();
@ -76,8 +81,8 @@ public class JournalModel {
return ret; return ret;
} }
public static JournalEntity fetch(EntityDataStore<Persistable> data, String url) { public static JournalEntity fetch(EntityDataStore<Persistable> data, ServiceEntity serviceEntity, String uid) {
JournalEntity ret = data.select(JournalEntity.class).where(JournalEntity.UID.eq(url)).limit(1).get().firstOrNull(); JournalEntity ret = data.select(JournalEntity.class).where(JournalEntity.SERVICE_MODEL.eq(serviceEntity).and(JournalEntity.UID.eq(uid))).limit(1).get().firstOrNull();
if (ret != null) { if (ret != null) {
// FIXME: For some reason this isn't always being called, manually do it here. // FIXME: For some reason this isn't always being called, manually do it here.
ret.afterLoad(); ret.afterLoad();
@ -86,9 +91,9 @@ public class JournalModel {
} }
public static JournalEntity fetchOrCreate(EntityDataStore<Persistable> data, CollectionInfo collection) { public static JournalEntity fetchOrCreate(EntityDataStore<Persistable> data, CollectionInfo collection) {
JournalEntity journalEntity = fetch(data, collection.uid); JournalEntity journalEntity = fetch(data, collection.getServiceEntity(data), collection.uid);
if (journalEntity == null) { if (journalEntity == null) {
journalEntity = new JournalEntity(collection); journalEntity = new JournalEntity(data, collection);
} else { } else {
journalEntity.setInfo(collection); journalEntity.setInfo(collection);
} }
@ -125,6 +130,26 @@ public class JournalModel {
Journal journal; 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<Persistable> 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<CollectionInfo, String> { static class CollectionInfoConverter implements Converter<CollectionInfo, String> {
@Override @Override
public Class<CollectionInfo> getMappedType() { public Class<CollectionInfo> getMappedType() {

@ -33,17 +33,13 @@ public class ServiceDB {
VALUE = "value"; VALUE = "value";
} }
@Deprecated
public static class Services { public static class Services {
public static final String public static final String
_TABLE = "services", _TABLE = "services",
ID = "_id", ID = "_id",
ACCOUNT_NAME = "accountName", ACCOUNT_NAME = "accountName",
SERVICE = "service"; 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 @Deprecated
@ -92,13 +88,6 @@ public class ServiceDB {
Settings.VALUE + " TEXT NOT NULL" + Settings.VALUE + " TEXT NOT NULL" +
")"); ")");
db.execSQL("CREATE UNIQUE INDEX settings_name ON " + Settings._TABLE + " (" + Settings.NAME + ")"); 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 @Override
@ -153,47 +142,5 @@ public class ServiceDB {
} }
db.endTransaction(); 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 });
}
} }

@ -14,7 +14,6 @@ import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SyncResult; import android.content.SyncResult;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.os.Bundle; import android.os.Bundle;
import android.provider.CalendarContract; import android.provider.CalendarContract;
@ -27,8 +26,8 @@ import com.etesync.syncadapter.R;
import com.etesync.syncadapter.journalmanager.Exceptions; import com.etesync.syncadapter.journalmanager.Exceptions;
import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.JournalEntity; import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.ServiceDB; import com.etesync.syncadapter.model.JournalModel;
import com.etesync.syncadapter.model.ServiceDB.Services; import com.etesync.syncadapter.model.ServiceEntity;
import com.etesync.syncadapter.resource.LocalCalendar; import com.etesync.syncadapter.resource.LocalCalendar;
import com.etesync.syncadapter.ui.DebugInfoActivity; import com.etesync.syncadapter.ui.DebugInfoActivity;
@ -109,13 +108,9 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
} }
private void updateLocalCalendars(ContentProviderClient provider, Account account, AccountSettings settings) throws CalendarStorageException { 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<Persistable> data = ((App) getContext().getApplicationContext()).getData(); EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData();
ServiceEntity service = JournalModel.Service.fetch(data, account.name, CollectionInfo.Type.CALENDAR);
Map<String, CollectionInfo> remote = new HashMap<>(); Map<String, CollectionInfo> remote = new HashMap<>();
List<CollectionInfo> remoteCollections = JournalEntity.getCollections(data, service); List<CollectionInfo> remoteCollections = JournalEntity.getCollections(data, service);
for (CollectionInfo info : remoteCollections) { for (CollectionInfo info : remoteCollections) {
@ -148,9 +143,6 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
App.log.info("Adding local calendar list " + info); App.log.info("Adding local calendar list " + info);
LocalCalendar.create(account, provider, info); LocalCalendar.create(account, provider, info);
} }
} finally {
dbHelper.close();
}
} }
} }

@ -14,7 +14,6 @@ import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SyncResult; import android.content.SyncResult;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle; import android.os.Bundle;
import com.etesync.syncadapter.AccountSettings; import com.etesync.syncadapter.AccountSettings;
@ -26,7 +25,9 @@ import com.etesync.syncadapter.R;
import com.etesync.syncadapter.journalmanager.Exceptions; import com.etesync.syncadapter.journalmanager.Exceptions;
import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.JournalEntity; import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.JournalModel;
import com.etesync.syncadapter.model.ServiceDB; import com.etesync.syncadapter.model.ServiceDB;
import com.etesync.syncadapter.model.ServiceEntity;
import com.etesync.syncadapter.ui.DebugInfoActivity; import com.etesync.syncadapter.ui.DebugInfoActivity;
import java.util.logging.Level; import java.util.logging.Level;
@ -65,11 +66,12 @@ public class ContactsSyncAdapterService extends SyncAdapterService {
new RefreshCollections(account, CollectionInfo.Type.ADDRESS_BOOK).run(); new RefreshCollections(account, CollectionInfo.Type.ADDRESS_BOOK).run();
SQLiteDatabase db = dbHelper.getReadableDatabase(); EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData();
Long service = dbHelper.getService(db, account, ServiceDB.Services.SERVICE_CARDDAV);
ServiceEntity service = JournalModel.Service.fetch(data, account.name, CollectionInfo.Type.ADDRESS_BOOK);
if (service != null) { if (service != null) {
HttpUrl principal = HttpUrl.get(settings.getUri()); HttpUrl principal = HttpUrl.get(settings.getUri());
EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData();
CollectionInfo info = JournalEntity.getCollections(data, service).get(0); CollectionInfo info = JournalEntity.getCollections(data, service).get(0);
try { try {
ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, settings, extras, authority, provider, syncResult, principal, info); ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, settings, extras, authority, provider, syncResult, principal, info);

@ -47,7 +47,9 @@ import com.etesync.syncadapter.journalmanager.Exceptions;
import com.etesync.syncadapter.journalmanager.JournalManager; import com.etesync.syncadapter.journalmanager.JournalManager;
import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.JournalEntity; import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.JournalModel;
import com.etesync.syncadapter.model.ServiceDB; import com.etesync.syncadapter.model.ServiceDB;
import com.etesync.syncadapter.model.ServiceEntity;
import com.etesync.syncadapter.ui.PermissionsActivity; import com.etesync.syncadapter.ui.PermissionsActivity;
import io.requery.Persistable; import io.requery.Persistable;
@ -175,22 +177,16 @@ public abstract class SyncAdapterService extends Service {
journals.add(new Pair<>(journal, info)); journals.add(new Pair<>(journal, info));
} }
db.beginTransactionNonExclusive(); saveCollections(journals);
try {
saveCollections(db, journals);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
} finally { } finally {
dbHelper.close(); dbHelper.close();
} }
} }
private void saveCollections(SQLiteDatabase db, Iterable<Pair<JournalManager.Journal, CollectionInfo>> journals) { private void saveCollections(Iterable<Pair<JournalManager.Journal, CollectionInfo>> journals) {
Long service = dbHelper.getService(db, account, serviceType.toString());
EntityDataStore<Persistable> data = ((App) context.getApplicationContext()).getData(); EntityDataStore<Persistable> data = ((App) context.getApplicationContext()).getData();
ServiceEntity service = JournalModel.Service.fetch(data, account.name, serviceType);
Map<String, JournalEntity> existing = new HashMap<>(); Map<String, JournalEntity> existing = new HashMap<>();
for (JournalEntity journalEntity : JournalEntity.getJournals(data, service)) { for (JournalEntity journalEntity : JournalEntity.getJournals(data, service)) {
existing.put(journalEntity.getUid(), journalEntity); existing.put(journalEntity.getUid(), journalEntity);
@ -201,7 +197,7 @@ public abstract class SyncAdapterService extends Service {
CollectionInfo collection = pair.second; CollectionInfo collection = pair.second;
App.log.log(Level.FINE, "Saving collection", journal.getUid()); App.log.log(Level.FINE, "Saving collection", journal.getUid());
collection.serviceID = service; collection.serviceID = service.getId();
JournalEntity journalEntity = JournalEntity.fetchOrCreate(data, collection); JournalEntity journalEntity = JournalEntity.fetchOrCreate(data, collection);
journalEntity.setOwner(journal.getOwner()); journalEntity.setOwner(journal.getOwner());
journalEntity.setEncryptedKey(journal.getKey()); journalEntity.setEncryptedKey(journal.getKey());

@ -27,6 +27,8 @@ import com.etesync.syncadapter.journalmanager.JournalEntryManager;
import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.EntryEntity; import com.etesync.syncadapter.model.EntryEntity;
import com.etesync.syncadapter.model.JournalEntity; 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.model.SyncEntry;
import com.etesync.syncadapter.resource.LocalCollection; import com.etesync.syncadapter.resource.LocalCollection;
import com.etesync.syncadapter.resource.LocalResource; import com.etesync.syncadapter.resource.LocalResource;
@ -108,7 +110,8 @@ abstract public class SyncManager {
httpClient = HttpClient.create(context, account); httpClient = HttpClient.create(context, account);
data = ((App) context.getApplicationContext()).getData(); 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 // dismiss previous error notifications
notificationManager = new NotificationHelper(context, journalUid, notificationId()); notificationManager = new NotificationHelper(context, journalUid, notificationId());
@ -232,7 +235,7 @@ abstract public class SyncManager {
private JournalEntity getJournalEntity() { private JournalEntity getJournalEntity() {
if (_journalEntity == null) 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; return _journalEntity;
} }

@ -24,8 +24,6 @@ import android.content.Intent;
import android.content.Loader; import android.content.Loader;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SyncStatusObserver; import android.content.SyncStatusObserver;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -57,8 +55,7 @@ import com.etesync.syncadapter.Constants;
import com.etesync.syncadapter.R; import com.etesync.syncadapter.R;
import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.JournalEntity; import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.ServiceDB.OpenHelper; import com.etesync.syncadapter.model.ServiceEntity;
import com.etesync.syncadapter.model.ServiceDB.Services;
import com.etesync.syncadapter.resource.LocalCalendar; import com.etesync.syncadapter.resource.LocalCalendar;
import com.etesync.syncadapter.utils.HintManager; import com.etesync.syncadapter.utils.HintManager;
import com.etesync.syncadapter.utils.ShowcaseBuilder; import com.etesync.syncadapter.utils.ShowcaseBuilder;
@ -71,7 +68,6 @@ import at.bitfire.cert4android.CustomCertManager;
import at.bitfire.ical4android.TaskProvider; import at.bitfire.ical4android.TaskProvider;
import io.requery.Persistable; import io.requery.Persistable;
import io.requery.sql.EntityDataStore; import io.requery.sql.EntityDataStore;
import lombok.Cleanup;
import tourguide.tourguide.ToolTip; import tourguide.tourguide.ToolTip;
import static android.content.ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE; import static android.content.ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE;
@ -318,31 +314,23 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
public AccountInfo loadInBackground() { public AccountInfo loadInBackground() {
AccountInfo info = new AccountInfo(); AccountInfo info = new AccountInfo();
@Cleanup OpenHelper dbHelper = new OpenHelper(getContext());
SQLiteDatabase db = dbHelper.getReadableDatabase();
EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData(); EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData();
@Cleanup Cursor cursor = db.query( for (ServiceEntity serviceEntity : data.select(ServiceEntity.class).where(ServiceEntity.ACCOUNT.eq(account.name)).get()) {
Services._TABLE, long id = serviceEntity.getId();
new String[] { Services.ID, Services.SERVICE }, CollectionInfo.Type service = serviceEntity.getType();
Services.ACCOUNT_NAME + "=?", new String[] { account.name }, if (service.equals(CollectionInfo.Type.ADDRESS_BOOK)) {
null, null, null);
while (cursor.moveToNext()) {
long id = cursor.getLong(0);
String service = cursor.getString(1);
if (Services.SERVICE_CARDDAV.equals(service)) {
info.carddav = new AccountInfo.ServiceInfo(); info.carddav = new AccountInfo.ServiceInfo();
info.carddav.id = id; info.carddav.id = id;
info.carddav.refreshing = (davService != null && davService.isRefreshing(id)) || ContentResolver.isSyncActive(account, ContactsContract.AUTHORITY); info.carddav.refreshing = (davService != null && davService.isRefreshing(id)) || ContentResolver.isSyncActive(account, ContactsContract.AUTHORITY);
info.carddav.collections = JournalEntity.getCollections(data, id); info.carddav.collections = JournalEntity.getCollections(data, serviceEntity);
} else if (Services.SERVICE_CALDAV.equals(service)) { } else if (service.equals(CollectionInfo.Type.CALENDAR)) {
info.caldav = new AccountInfo.ServiceInfo(); info.caldav = new AccountInfo.ServiceInfo();
info.caldav.id = id; info.caldav.id = id;
info.caldav.refreshing = (davService != null && davService.isRefreshing(id)) || info.caldav.refreshing = (davService != null && davService.isRefreshing(id)) ||
ContentResolver.isSyncActive(account, CalendarContract.AUTHORITY) || ContentResolver.isSyncActive(account, CalendarContract.AUTHORITY) ||
ContentResolver.isSyncActive(account, TaskProvider.ProviderName.OpenTasks.authority); ContentResolver.isSyncActive(account, TaskProvider.ProviderName.OpenTasks.authority);
info.caldav.collections = JournalEntity.getCollections(data, id); info.caldav.collections = JournalEntity.getCollections(data, serviceEntity);
} }
} }
return info; return info;

@ -35,7 +35,9 @@ import com.etesync.syncadapter.journalmanager.Exceptions;
import com.etesync.syncadapter.journalmanager.JournalManager; import com.etesync.syncadapter.journalmanager.JournalManager;
import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.JournalEntity; import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.JournalModel;
import com.etesync.syncadapter.model.ServiceDB; import com.etesync.syncadapter.model.ServiceDB;
import com.etesync.syncadapter.model.ServiceEntity;
import io.requery.Persistable; import io.requery.Persistable;
import io.requery.sql.EntityDataStore; import io.requery.sql.EntityDataStore;
@ -125,31 +127,21 @@ public class CreateCollectionFragment extends DialogFragment implements LoaderMa
@Override @Override
public Exception loadInBackground() { public Exception loadInBackground() {
ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
try { try {
String authority = null; String authority = null;
// now insert collection into database:
SQLiteDatabase db = dbHelper.getWritableDatabase(); EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData();
// 1. find service ID // 1. find service ID
String serviceType;
if (info.type == CollectionInfo.Type.ADDRESS_BOOK) { if (info.type == CollectionInfo.Type.ADDRESS_BOOK) {
serviceType = ServiceDB.Services.SERVICE_CARDDAV;
authority = ContactsContract.AUTHORITY; authority = ContactsContract.AUTHORITY;
} else if (info.type == CollectionInfo.Type.CALENDAR) { } else if (info.type == CollectionInfo.Type.CALENDAR) {
serviceType = ServiceDB.Services.SERVICE_CALDAV;
authority = CalendarContract.AUTHORITY; authority = CalendarContract.AUTHORITY;
} else { } else {
throw new IllegalArgumentException("Collection must be an address book or calendar"); 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 + "=?", ServiceEntity serviceEntity = JournalModel.Service.fetch(data, account.name, info.type);
new String[]{account.name, serviceType}, null, null, null
);
if (!c.moveToNext())
throw new IllegalStateException();
long serviceID = c.getLong(0);
AccountSettings settings = new AccountSettings(getContext(), account); AccountSettings settings = new AccountSettings(getContext(), account);
HttpUrl principal = HttpUrl.get(settings.getUri()); HttpUrl principal = HttpUrl.get(settings.getUri());
@ -167,8 +159,7 @@ public class CreateCollectionFragment extends DialogFragment implements LoaderMa
} }
// 2. add collection to service // 2. add collection to service
EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData(); info.serviceID = serviceEntity.getId();
info.serviceID = serviceID;
JournalEntity journalEntity = JournalEntity.fetchOrCreate(data, info); JournalEntity journalEntity = JournalEntity.fetchOrCreate(data, info);
data.upsert(journalEntity); data.upsert(journalEntity);
@ -180,8 +171,6 @@ public class CreateCollectionFragment extends DialogFragment implements LoaderMa
return e; return e;
} catch (Exceptions.IntegrityException|Exceptions.GenericCryptoException e) { } catch (Exceptions.IntegrityException|Exceptions.GenericCryptoException e) {
return e; return e;
} finally {
dbHelper.close();
} }
return null; return null;

@ -49,6 +49,7 @@ import com.etesync.syncadapter.journalmanager.Exceptions.HttpException;
import com.etesync.syncadapter.model.EntryEntity; import com.etesync.syncadapter.model.EntryEntity;
import com.etesync.syncadapter.model.JournalEntity; import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.ServiceDB; import com.etesync.syncadapter.model.ServiceDB;
import com.etesync.syncadapter.model.ServiceEntity;
import io.requery.Persistable; import io.requery.Persistable;
import io.requery.sql.EntityDataStore; import io.requery.sql.EntityDataStore;
@ -246,8 +247,14 @@ public class DebugInfoActivity extends AppCompatActivity implements LoaderManage
dbHelper.dump(report); dbHelper.dump(report);
report.append("\n"); report.append("\n");
report.append("JOURNALS DUMP\n"); report.append("SERVICES DUMP\n");
EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData(); EntityDataStore<Persistable> 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<JournalEntity> journals = data.select(JournalEntity.class).where(JournalEntity.DELETED.eq(false)).get().toList(); List<JournalEntity> journals = data.select(JournalEntity.class).where(JournalEntity.DELETED.eq(false)).get().toList();
for (JournalEntity journal : journals) { for (JournalEntity journal : journals) {
report.append(journal.toString() + "\n"); report.append(journal.toString() + "\n");

@ -123,7 +123,7 @@ public class DeleteCollectionFragment extends DialogFragment implements LoaderMa
Crypto.CryptoManager crypto = new Crypto.CryptoManager(collectionInfo.version, settings.password(), collectionInfo.uid); Crypto.CryptoManager crypto = new Crypto.CryptoManager(collectionInfo.version, settings.password(), collectionInfo.uid);
journalManager.deleteJournal(new JournalManager.Journal(crypto, collectionInfo.toJson(), 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); journalEntity.setDeleted(true);
data.update(journalEntity); data.update(journalEntity);

@ -78,7 +78,7 @@ public class EditCollectionActivity extends CreateCollectionActivity {
public void onDeleteCollection(MenuItem item) { public void onDeleteCollection(MenuItem item) {
EntityDataStore<Persistable> data = ((App) getApplication()).getData(); EntityDataStore<Persistable> 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) { if (journalCount < 2) {
new AlertDialog.Builder(this) new AlertDialog.Builder(this)

@ -27,6 +27,8 @@ import com.etesync.syncadapter.R;
import com.etesync.syncadapter.model.CollectionInfo; import com.etesync.syncadapter.model.CollectionInfo;
import com.etesync.syncadapter.model.EntryEntity; import com.etesync.syncadapter.model.EntryEntity;
import com.etesync.syncadapter.model.JournalEntity; 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.LocalAddressBook;
import com.etesync.syncadapter.resource.LocalCalendar; import com.etesync.syncadapter.resource.LocalCalendar;
import com.etesync.syncadapter.ui.importlocal.ImportActivity; import com.etesync.syncadapter.ui.importlocal.ImportActivity;
@ -63,7 +65,7 @@ public class ViewCollectionActivity extends AppCompatActivity implements Refresh
public void refresh() { public void refresh() {
EntityDataStore<Persistable> data = ((App) getApplicationContext()).getData(); EntityDataStore<Persistable> 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()) { if ((journalEntity == null) || journalEntity.isDeleted()) {
finish(); finish();
return; return;
@ -173,7 +175,7 @@ public class ViewCollectionActivity extends AppCompatActivity implements Refresh
protected Long doInBackground(Void... aVoids) { protected Long doInBackground(Void... aVoids) {
EntityDataStore<Persistable> data = ((App) getApplicationContext()).getData(); EntityDataStore<Persistable> 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(); entryCount = data.count(EntryEntity.class).where(EntryEntity.JOURNAL.eq(journalEntity)).get().value();
long count; long count;

@ -137,7 +137,7 @@ public class ListEntriesFragment extends ListFragment implements AdapterView.OnI
@Override @Override
protected List<EntryEntity> doInBackground(Void... voids) { protected List<EntryEntity> 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(); return data.select(EntryEntity.class).where(EntryEntity.JOURNAL.eq(journalEntity)).orderBy(EntryEntity.ID.desc()).get().toList();
} }

@ -14,7 +14,6 @@ import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle; 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.AsyncTaskLoader;
import android.support.v4.content.Loader; import android.support.v4.content.Loader;
import java.util.logging.Level;
import com.etesync.syncadapter.AccountSettings; import com.etesync.syncadapter.AccountSettings;
import com.etesync.syncadapter.App; import com.etesync.syncadapter.App;
import com.etesync.syncadapter.Constants; 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.CollectionInfo;
import com.etesync.syncadapter.model.JournalEntity; import com.etesync.syncadapter.model.JournalEntity;
import com.etesync.syncadapter.model.ServiceDB; import com.etesync.syncadapter.model.ServiceDB;
import com.etesync.syncadapter.model.ServiceEntity;
import com.etesync.syncadapter.resource.LocalTaskList; import com.etesync.syncadapter.resource.LocalTaskList;
import com.etesync.syncadapter.ui.setup.BaseConfigurationFinder.Configuration; import com.etesync.syncadapter.ui.setup.BaseConfigurationFinder.Configuration;
import java.util.logging.Level;
import at.bitfire.ical4android.TaskProvider; import at.bitfire.ical4android.TaskProvider;
import io.requery.Persistable; import io.requery.Persistable;
import io.requery.sql.EntityDataStore; import io.requery.sql.EntityDataStore;
@ -140,7 +141,7 @@ public class SetupEncryptionFragment extends DialogFragment implements LoaderMan
if (config.cardDAV != null) { if (config.cardDAV != null) {
// insert CardDAV service // 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 // contact sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_contacts.xml
settings.setSyncInterval(ContactsContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL); settings.setSyncInterval(ContactsContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL);
@ -150,7 +151,7 @@ public class SetupEncryptionFragment extends DialogFragment implements LoaderMan
if (config.calDAV != null) { if (config.calDAV != null) {
// insert CalDAV service // 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 // calendar sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_contacts.xml
settings.setSyncInterval(CalendarContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL); settings.setSyncInterval(CalendarContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL);
@ -172,22 +173,20 @@ public class SetupEncryptionFragment extends DialogFragment implements LoaderMan
return true; return true;
} }
protected long insertService(SQLiteDatabase db, String accountName, String service, BaseConfigurationFinder.Configuration.ServiceInfo info) { protected void insertService(SQLiteDatabase db, String accountName, CollectionInfo.Type serviceType, BaseConfigurationFinder.Configuration.ServiceInfo info) {
ContentValues values = new ContentValues(); EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData();
// insert service // insert service
values.put(ServiceDB.Services.ACCOUNT_NAME, accountName); ServiceEntity serviceEntity = new ServiceEntity();
values.put(ServiceDB.Services.SERVICE, service); serviceEntity.setAccount(accountName);
long serviceID = db.insertWithOnConflict(ServiceDB.Services._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE); serviceEntity.setType(serviceType);
EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData(); data.upsert(serviceEntity);
// insert collections // insert collections
for (CollectionInfo collection : info.collections.values()) { for (CollectionInfo collection : info.collections.values()) {
collection.serviceID = serviceID; collection.serviceID = serviceEntity.getId();
JournalEntity journalEntity = new JournalEntity(collection); JournalEntity journalEntity = new JournalEntity(data, collection);
data.insert(journalEntity); data.insert(journalEntity);
} }
return serviceID;
} }
} }

Loading…
Cancel
Save