1
0
mirror of https://github.com/etesync/android synced 2025-02-16 17:42:03 +00:00

Check for migrations only when package is replaced, DB fixes

* AccountSettings$AppUpdatedReceiver: check for migrations only when package is replaced
* SyncAdapter: move DB helper from service to SyncAdapter to prevent databases from being closed too early
* Manual sync button: run sync immediately (without queueing)
This commit is contained in:
Ricki Hirner 2016-04-05 16:52:43 +02:00
parent 25c54cce62
commit 7ab13d648e
7 changed files with 150 additions and 130 deletions

View File

@ -60,6 +60,14 @@
<action android:name="at.bitfire.davdroid.REINIT_LOGGER"/>
</intent-filter>
</receiver>
<receiver
android:name=".AccountSettings$AppUpdatedReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" android:path="at.bitfire.davdroid" />
</intent-filter>
</receiver>
<service
android:name=".syncadapter.AccountAuthenticatorService"

View File

@ -11,28 +11,23 @@ import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.PeriodicSync;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Calendars;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.v7.app.NotificationCompat;
import android.text.TextUtils;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -362,4 +357,24 @@ public class AccountSettings {
accountManager.setUserData(account, KEY_SETTINGS_VERSION, "3");
}
public static class AppUpdatedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
App.log.info("DAVdroid was updated, checking for AccountSettings version");
// peek into AccountSettings to initiate a possible migration
AccountManager accountManager = AccountManager.get(context);
for (Account account : accountManager.getAccountsByType(Constants.ACCOUNT_TYPE))
try {
App.log.info("Checking account " + account.name);
new AccountSettings(context, account);
} catch (InvalidAccountException e) {
App.log.log(Level.SEVERE, "Couldn't check for updated account settings", e);
}
}
}
}

View File

@ -8,6 +8,7 @@
package at.bitfire.davdroid.syncadapter;
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.content.Context;
@ -15,6 +16,7 @@ import android.content.SyncResult;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.support.annotation.NonNull;
@ -28,6 +30,7 @@ import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.InvalidAccountException;
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.Services;
import at.bitfire.davdroid.resource.LocalCalendar;
@ -37,16 +40,15 @@ import lombok.Cleanup;
public class CalendarsSyncAdapterService extends SyncAdapterService {
@Override
public void onCreate() {
super.onCreate();
syncAdapter = new SyncAdapter(this, dbHelper.getReadableDatabase());
protected AbstractThreadedSyncAdapter syncAdapter() {
return new SyncAdapter(this);
}
private static class SyncAdapter extends SyncAdapterService.SyncAdapter {
public SyncAdapter(Context context, SQLiteDatabase db) {
super(context, db);
public SyncAdapter(Context context) {
super(context);
}
@Override
@ -72,40 +74,47 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
}
private void updateLocalCalendars(ContentProviderClient provider, Account account) throws CalendarStorageException, InvalidAccountException {
// enumerate remote and local calendars
Long service = getService(account);
Map<String, CollectionInfo> remote = remoteCalendars(service);
LocalCalendar[] local = (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, null, null);
SQLiteOpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
try {
// enumerate remote and local calendars
SQLiteDatabase db = dbHelper.getReadableDatabase();
Long service = getService(db, account);
Map<String, CollectionInfo> remote = remoteCalendars(db, service);
AccountSettings settings = new AccountSettings(getContext(), account);
boolean updateColors = settings.getManageCalendarColors();
LocalCalendar[] local = (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, null, null);
// 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);
AccountSettings settings = new AccountSettings(getContext(), account);
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);
}
}
}
// 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);
// 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);
}
} finally {
dbHelper.close();
}
}
@Nullable
Long getService(Account account) {
Long getService(@NonNull SQLiteDatabase db, @NonNull Account account) {
@Cleanup Cursor c = db.query(Services._TABLE, new String[] { Services.ID },
Services.ACCOUNT_NAME + "=? AND " + Services.SERVICE + "=?", new String[] { account.name, Services.SERVICE_CALDAV }, null, null, null);
if (c.moveToNext())
@ -115,7 +124,7 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
}
@NonNull
private Map<String, CollectionInfo> remoteCalendars(Long service) {
private Map<String, CollectionInfo> remoteCalendars(@NonNull SQLiteDatabase db, Long service) {
Map<String, CollectionInfo> collections = new LinkedHashMap<>();
if (service != null) {
@Cleanup Cursor cursor = db.query(Collections._TABLE, null,

View File

@ -8,6 +8,7 @@
package at.bitfire.davdroid.syncadapter;
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.content.Context;
@ -15,6 +16,7 @@ import android.content.SyncResult;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -30,43 +32,48 @@ import lombok.Cleanup;
public class ContactsSyncAdapterService extends SyncAdapterService {
@Override
public void onCreate() {
super.onCreate();
syncAdapter = new ContactsSyncAdapter(this, dbHelper.getReadableDatabase());
}
@Override
protected AbstractThreadedSyncAdapter syncAdapter() {
return new ContactsSyncAdapter(this);
}
private static class ContactsSyncAdapter extends SyncAdapter {
public ContactsSyncAdapter(Context context, SQLiteDatabase db) {
super(context, db);
public ContactsSyncAdapter(Context context) {
super(context);
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
super.onPerformSync(account, extras, authority, provider, syncResult);
Long service = getService(account);
if (service != null) {
CollectionInfo remote = remoteAddressBook(service);
if (remote != null)
try {
ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, extras, authority, provider, syncResult, remote);
syncManager.performSync();
} catch (InvalidAccountException e) {
App.log.log(Level.SEVERE, "Couldn't get account settings", e);
}
else
App.log.info("No address book collection selected for synchronization");
} else
App.log.info("No CardDAV service found in DB");
SQLiteOpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
try {
SQLiteDatabase db = dbHelper.getReadableDatabase();
Long service = getService(db, account);
if (service != null) {
CollectionInfo remote = remoteAddressBook(db, service);
if (remote != null)
try {
ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, extras, authority, provider, syncResult, remote);
syncManager.performSync();
} catch(InvalidAccountException e) {
App.log.log(Level.SEVERE, "Couldn't get account settings", e);
}
else
App.log.info("No address book collection selected for synchronization");
} else
App.log.info("No CardDAV service found in DB");
} finally {
dbHelper.close();
}
App.log.info("Address book sync complete");
}
@Nullable
private Long getService(@NonNull Account account) {
private Long getService(@NonNull SQLiteDatabase db, @NonNull Account account) {
@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, ServiceDB.Services.SERVICE_CARDDAV }, null, null, null);
if (c.moveToNext())
@ -76,7 +83,7 @@ public class ContactsSyncAdapterService extends SyncAdapterService {
}
@Nullable
private CollectionInfo remoteAddressBook(long service) {
private CollectionInfo remoteAddressBook(@NonNull SQLiteDatabase db, long service) {
@Cleanup Cursor c = db.query(Collections._TABLE, null,
Collections.SERVICE_ID + "=? AND " + Collections.SYNC, new String[] { String.valueOf(service) }, null, null, null);
if (c.moveToNext()) {

View File

@ -29,33 +29,19 @@ import at.bitfire.davdroid.model.ServiceDB;
public abstract class SyncAdapterService extends Service {
ServiceDB.OpenHelper dbHelper;
AbstractThreadedSyncAdapter syncAdapter;
@Override
public void onCreate() {
dbHelper = new ServiceDB.OpenHelper(this);
}
@Override
public void onDestroy() {
dbHelper.close();
}
abstract protected AbstractThreadedSyncAdapter syncAdapter();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return syncAdapter.getSyncAdapterBinder();
return syncAdapter().getSyncAdapterBinder();
}
public static abstract class SyncAdapter extends AbstractThreadedSyncAdapter {
protected final SQLiteDatabase db; // will be closed in SyncAdapterService::onDestroy(), don't close manually!
public SyncAdapter(Context context, SQLiteDatabase db) {
public SyncAdapter(Context context) {
super(context, false);
this.db = db;
}
@Override
@ -64,13 +50,6 @@ public abstract class SyncAdapterService extends Service {
// required for dav4android (ServiceLoader)
Thread.currentThread().setContextClassLoader(getContext().getClassLoader());
// peek into AccountSettings to cause possible migration (v0.9 -> v1.0)
try {
new AccountSettings(getContext(), account);
} catch (InvalidAccountException e) {
App.log.log(Level.SEVERE, "Couldn't check for updated account settings", e);
}
}
}

View File

@ -8,6 +8,7 @@
package at.bitfire.davdroid.syncadapter;
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.content.Context;
@ -15,6 +16,7 @@ import android.content.SyncResult;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -26,6 +28,7 @@ import java.util.logging.Level;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.InvalidAccountException;
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.Services;
import at.bitfire.davdroid.resource.LocalTaskList;
@ -35,17 +38,16 @@ import lombok.Cleanup;
public class TasksSyncAdapterService extends SyncAdapterService {
@Override
public void onCreate() {
super.onCreate();
syncAdapter = new SyncAdapter(this, dbHelper.getReadableDatabase());
}
@Override
protected AbstractThreadedSyncAdapter syncAdapter() {
return new SyncAdapter(this);
}
private static class SyncAdapter extends SyncAdapterService.SyncAdapter {
public SyncAdapter(Context context, SQLiteDatabase db) {
super(context, db);
public SyncAdapter(Context context) {
super(context);
}
@Override
@ -74,37 +76,43 @@ public class TasksSyncAdapterService extends SyncAdapterService {
}
private void updateLocalTaskLists(TaskProvider provider, Account account) throws CalendarStorageException {
// enumerate remote and local task lists
Long service = getService(account);
Map<String, CollectionInfo> remote = remoteTaskLists(service);
LocalTaskList[] local = (LocalTaskList[])LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, null, null);
SQLiteOpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
try {
// enumerate remote and local task lists
SQLiteDatabase db = dbHelper.getReadableDatabase();
Long service = getService(db, account);
Map<String, CollectionInfo> remote = remoteTaskLists(db, service);
LocalTaskList[] local = (LocalTaskList[])LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, null, null);
// delete obsolete local task lists
for (LocalTaskList list : local) {
String url = list.getSyncId();
if (!remote.containsKey(url)) {
App.log.fine("Deleting obsolete local task list" + url);
list.delete();
} else {
// remote CollectionInfo found for this local collection, update data
CollectionInfo info = remote.get(url);
App.log.fine("Updating local task list " + url + " with " + info);
list.update(info);
// we already have a local task list for this remote collection, don't take into consideration anymore
remote.remove(url);
// delete obsolete local task lists
for (LocalTaskList list : local) {
String url = list.getSyncId();
if (!remote.containsKey(url)) {
App.log.fine("Deleting obsolete local task list" + url);
list.delete();
} else {
// remote CollectionInfo found for this local collection, update data
CollectionInfo info = remote.get(url);
App.log.fine("Updating local task list " + url + " with " + info);
list.update(info);
// we already have a local task list for this remote collection, don't take into consideration anymore
remote.remove(url);
}
}
}
// create new local task lists
for (String url : remote.keySet()) {
CollectionInfo info = remote.get(url);
App.log.info("Adding local task list " + info);
LocalTaskList.create(account, provider, info);
// create new local task lists
for (String url : remote.keySet()) {
CollectionInfo info = remote.get(url);
App.log.info("Adding local task list " + info);
LocalTaskList.create(account, provider, info);
}
} finally {
dbHelper.close();
}
}
@Nullable
Long getService(Account account) {
Long getService(@NonNull SQLiteDatabase db, @NonNull Account account) {
@Cleanup Cursor c = db.query(Services._TABLE, new String[] { Services.ID },
Services.ACCOUNT_NAME + "=? AND " + Services.SERVICE + "=?", new String[] { account.name, Services.SERVICE_CALDAV }, null, null, null);
if (c.moveToNext())
@ -114,7 +122,7 @@ public class TasksSyncAdapterService extends SyncAdapterService {
}
@NonNull
private Map<String, CollectionInfo> remoteTaskLists(Long service) {
private Map<String, CollectionInfo> remoteTaskLists(@NonNull SQLiteDatabase db, Long service) {
Map<String, CollectionInfo> collections = new LinkedHashMap<>();
if (service != null) {
@Cleanup Cursor cursor = db.query(Collections._TABLE, null,

View File

@ -62,11 +62,8 @@ import java.util.LinkedList;
import java.util.List;
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.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB;
@ -373,15 +370,6 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
@Override
public AccountInfo loadInBackground() {
// peek into AccountSettings to call possible 0.9 -> 1.0 migration
// The next line can be removed as soon as migration from 0.9 is not required anymore!
try {
new AccountSettings(getContext(), new Account(accountName, Constants.ACCOUNT_TYPE));
} catch (InvalidAccountException e) {
App.log.log(Level.INFO, "Account doesn't exist (anymore)", e);
return null;
}
AccountInfo info = new AccountInfo();
try {
SQLiteDatabase db = dbHelper.getReadableDatabase();
@ -391,6 +379,11 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
new String[] { Services.ID, Services.SERVICE },
Services.ACCOUNT_NAME + "=?", new String[] { accountName },
null, null, null);
if (cursor.getCount() == 0)
// no services, account not useable
return null;
while (cursor.moveToNext()) {
long id = cursor.getLong(0);
String service = cursor.getString(1);
@ -557,7 +550,8 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
for (String authority : authorities) {
Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); // manual sync
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); // run immediately (don't queue)
ContentResolver.requestSync(account, authority, extras);
}