diff --git a/app/src/main/java/at/bitfire/davdroid/NotificationHelper.java b/app/src/main/java/at/bitfire/davdroid/NotificationHelper.java
new file mode 100644
index 00000000..ee07d2ca
--- /dev/null
+++ b/app/src/main/java/at/bitfire/davdroid/NotificationHelper.java
@@ -0,0 +1,84 @@
+package at.bitfire.davdroid;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.database.sqlite.SQLiteException;
+import android.net.Uri;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v7.app.NotificationCompat;
+
+import java.util.logging.Level;
+
+import at.bitfire.davdroid.journalmanager.Exceptions;
+import at.bitfire.davdroid.ui.AccountSettingsActivity;
+import at.bitfire.davdroid.ui.DebugInfoActivity;
+import at.bitfire.ical4android.CalendarStorageException;
+import at.bitfire.vcard4android.ContactsStorageException;
+import lombok.Getter;
+
+public class NotificationHelper {
+ final NotificationManagerCompat notificationManager;
+ final Context context;
+ final String notificationTag;
+ final int notificationId;
+ @Getter
+ Intent detailsIntent;
+ int messageString;
+
+ public NotificationHelper(Context context, String notificationTag, int notificationId) {
+ this.notificationManager = NotificationManagerCompat.from(context);
+ this.context = context;
+ this.notificationTag = notificationTag;
+ this.notificationId = notificationId;
+ }
+
+ public void setThrowable(Throwable e) {
+ if (e instanceof Exceptions.UnauthorizedException) {
+ App.log.log(Level.SEVERE, "Not authorized anymore", e);
+ messageString = R.string.sync_error_unauthorized;
+ } else if (e instanceof Exceptions.ServiceUnavailableException) {
+ App.log.log(Level.SEVERE, "Service unavailable");
+ messageString = R.string.sync_error_unavailable;
+ } else if (e instanceof Exceptions.HttpException) {
+ App.log.log(Level.SEVERE, "HTTP Exception during sync", e);
+ messageString = R.string.sync_error_http_dav;
+ } else if (e instanceof CalendarStorageException || e instanceof ContactsStorageException || e instanceof SQLiteException) {
+ App.log.log(Level.SEVERE, "Couldn't access local storage", e);
+ messageString = R.string.sync_error_local_storage;
+ } else if (e instanceof Exceptions.IntegrityException) {
+ App.log.log(Level.SEVERE, "Integrity error", e);
+ messageString = R.string.sync_error_integrity;
+ } else {
+ App.log.log(Level.SEVERE, "Unknown sync error", e);
+ messageString = R.string.sync_error;
+ }
+
+ if (e instanceof Exceptions.UnauthorizedException) {
+ detailsIntent = new Intent(context, AccountSettingsActivity.class);
+ } else {
+ detailsIntent = new Intent(context, DebugInfoActivity.class);
+ detailsIntent.putExtra(DebugInfoActivity.KEY_THROWABLE, e);
+ }
+
+ detailsIntent.setData(Uri.parse("uri://" + getClass().getName() + "/" + notificationTag));
+ }
+
+ public void notify(String title, String state) {
+ String message = context.getString(messageString, state);
+
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+ builder.setSmallIcon(R.drawable.ic_error_light)
+ .setLargeIcon(App.getLauncherBitmap(context))
+ .setContentTitle(title)
+ .setContentIntent(PendingIntent.getActivity(context, 0, detailsIntent, PendingIntent.FLAG_CANCEL_CURRENT))
+ .setCategory(NotificationCompat.CATEGORY_ERROR)
+ .setContentText(message);
+
+ notificationManager.notify(notificationTag, notificationId, builder.build());
+ }
+
+ public void cancel() {
+ notificationManager.cancel(notificationTag, notificationId);
+ }
+}
diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java
index 187b7ce4..5fa7a7bd 100644
--- a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java
+++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java
@@ -13,6 +13,7 @@ import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import android.content.SyncResult;
import android.database.Cursor;
import android.database.DatabaseUtils;
@@ -21,6 +22,7 @@ import android.database.sqlite.SQLiteException;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.support.annotation.NonNull;
+import android.support.v4.app.NotificationManagerCompat;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -28,13 +30,17 @@ import java.util.logging.Level;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
-import at.bitfire.davdroid.InvalidAccountException;
+import at.bitfire.davdroid.Constants;
+import at.bitfire.davdroid.NotificationHelper;
+import at.bitfire.davdroid.R;
import at.bitfire.davdroid.journalmanager.Exceptions;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB;
import at.bitfire.davdroid.model.ServiceDB.Collections;
import at.bitfire.davdroid.model.ServiceDB.Services;
import at.bitfire.davdroid.resource.LocalCalendar;
+import at.bitfire.davdroid.ui.AccountSettingsActivity;
+import at.bitfire.davdroid.ui.DebugInfoActivity;
import at.bitfire.ical4android.CalendarStorageException;
import lombok.Cleanup;
import okhttp3.HttpUrl;
@@ -57,6 +63,9 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
super.onPerformSync(account, extras, authority, provider, syncResult);
+ NotificationHelper notificationManager = new NotificationHelper(getContext(), "journals", Constants.NOTIFICATION_CALENDAR_SYNC);
+ notificationManager.cancel();
+
try {
AccountSettings settings = new AccountSettings(getContext(), account);
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
@@ -71,15 +80,27 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
CalendarSyncManager syncManager = new CalendarSyncManager(getContext(), account, settings, extras, authority, syncResult, calendar, principal);
syncManager.performSync();
}
- } catch (CalendarStorageException | SQLiteException e) {
- App.log.log(Level.SEVERE, "Couldn't prepare local calendars", e);
- syncResult.databaseError = true;
- } catch (InvalidAccountException e) {
- App.log.log(Level.SEVERE, "Couldn't get account settings", e);
- } catch (Exceptions.HttpException e) {
- e.printStackTrace();
- } catch (Exceptions.IntegrityException e) {
- e.printStackTrace();
+ } catch (Exception | OutOfMemoryError e) {
+ if (e instanceof CalendarStorageException || e instanceof SQLiteException) {
+ App.log.log(Level.SEVERE, "Couldn't prepare local calendars", e);
+ syncResult.databaseError = true;
+ }
+
+ String syncPhase = SyncManager.SYNC_PHASE_JOURNALS;
+ String title = getContext().getString(R.string.sync_error_calendar, account.name);
+
+ notificationManager.setThrowable(e);
+
+ final Intent detailsIntent = notificationManager.getDetailsIntent();
+ if (e instanceof Exceptions.UnauthorizedException) {
+ detailsIntent.putExtra(AccountSettingsActivity.EXTRA_ACCOUNT, account);
+ } else {
+ detailsIntent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account);
+ detailsIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority);
+ detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase);
+ }
+
+ notificationManager.notify(title, syncPhase);
}
App.log.info("Calendar sync complete");
diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java
index cfb4e2de..5ca04737 100644
--- a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java
+++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java
@@ -13,6 +13,7 @@ import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import android.content.SyncResult;
import android.database.Cursor;
import android.database.DatabaseUtils;
@@ -20,16 +21,22 @@ import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.v4.app.NotificationManagerCompat;
import java.util.logging.Level;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
+import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.InvalidAccountException;
+import at.bitfire.davdroid.NotificationHelper;
+import at.bitfire.davdroid.R;
import at.bitfire.davdroid.journalmanager.Exceptions;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB;
import at.bitfire.davdroid.model.ServiceDB.Collections;
+import at.bitfire.davdroid.ui.AccountSettingsActivity;
+import at.bitfire.davdroid.ui.DebugInfoActivity;
import lombok.Cleanup;
import okhttp3.HttpUrl;
@@ -50,6 +57,8 @@ public class ContactsSyncAdapterService extends SyncAdapterService {
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
super.onPerformSync(account, extras, authority, provider, syncResult);
+ NotificationHelper notificationManager = new NotificationHelper(getContext(), "journals", Constants.NOTIFICATION_CONTACTS_SYNC);
+ notificationManager.cancel();
ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
try {
@@ -72,12 +81,21 @@ public class ContactsSyncAdapterService extends SyncAdapterService {
}
} else
App.log.info("No CardDAV service found in DB");
- } catch (InvalidAccountException e) {
- App.log.log(Level.SEVERE, "Couldn't get account settings", e);
- } catch (Exceptions.HttpException e) {
- e.printStackTrace();
- } catch (Exceptions.IntegrityException e) {
- e.printStackTrace();
+ } catch (Exception | OutOfMemoryError e) {
+ String syncPhase = SyncManager.SYNC_PHASE_JOURNALS;
+ String title = getContext().getString(R.string.sync_error_contacts, account.name);
+
+ notificationManager.setThrowable(e);
+
+ final Intent detailsIntent = notificationManager.getDetailsIntent();
+ if (e instanceof Exceptions.UnauthorizedException) {
+ detailsIntent.putExtra(AccountSettingsActivity.EXTRA_ACCOUNT, account);
+ } else {
+ detailsIntent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account);
+ detailsIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority);
+ detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase);
+ }
+ notificationManager.notify(title, syncPhase);
} finally {
dbHelper.close();
}
diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.java b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.java
index ebf39f87..e82d1c46 100644
--- a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.java
+++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.java
@@ -9,14 +9,11 @@ package at.bitfire.davdroid.syncadapter;
import android.accounts.Account;
import android.annotation.TargetApi;
-import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
-import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationManagerCompat;
-import android.support.v7.app.NotificationCompat;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -34,7 +31,7 @@ import at.bitfire.davdroid.App;
import at.bitfire.davdroid.GsonHelper;
import at.bitfire.davdroid.HttpClient;
import at.bitfire.davdroid.InvalidAccountException;
-import at.bitfire.davdroid.R;
+import at.bitfire.davdroid.NotificationHelper;
import at.bitfire.davdroid.journalmanager.Exceptions;
import at.bitfire.davdroid.journalmanager.JournalEntryManager;
import at.bitfire.davdroid.model.CollectionInfo;
@@ -50,7 +47,8 @@ import okhttp3.OkHttpClient;
abstract public class SyncManager {
- protected final String SYNC_PHASE_PREPARE = "sync_phase_prepare",
+ final static String SYNC_PHASE_PREPARE = "sync_phase_prepare",
+ SYNC_PHASE_JOURNALS = "sync_phase_journals",
SYNC_PHASE_QUERY_CAPABILITIES = "sync_phase_query_capabilities",
SYNC_PHASE_PREPARE_LOCAL = "sync_phase_prepare_local",
SYNC_PHASE_CREATE_LOCAL_ENTRIES = "sync_phase_create_local_entries",
@@ -62,7 +60,7 @@ abstract public class SyncManager {
SYNC_PHASE_SAVE_SYNC_TAG = "sync_phase_save_sync_tag";
- protected final NotificationManagerCompat notificationManager;
+ protected final NotificationHelper notificationManager;
protected final String uniqueCollectionId;
protected final Context context;
@@ -113,8 +111,8 @@ abstract public class SyncManager {
// dismiss previous error notifications
this.uniqueCollectionId = uniqueCollectionId;
- notificationManager = NotificationManagerCompat.from(context);
- notificationManager.cancel(uniqueCollectionId, notificationId());
+ notificationManager = new NotificationHelper(context, uniqueCollectionId, notificationId());
+ notificationManager.cancel();
}
protected abstract int notificationId();
@@ -193,57 +191,30 @@ abstract public class SyncManager {
// syncResult.delayUntil = (retryAfter.getTime() - new Date().getTime()) / 1000;
}
} catch (Exception | OutOfMemoryError e) {
- final int messageString;
-
if (e instanceof Exceptions.UnauthorizedException) {
- App.log.log(Level.SEVERE, "Not authorized anymore", e);
- messageString = R.string.sync_error_unauthorized;
syncResult.stats.numAuthExceptions++;
} else if (e instanceof Exceptions.HttpException) {
- App.log.log(Level.SEVERE, "HTTP Exception during sync", e);
- messageString = R.string.sync_error_http_dav;
syncResult.stats.numParseExceptions++;
} else if (e instanceof CalendarStorageException || e instanceof ContactsStorageException) {
- App.log.log(Level.SEVERE, "Couldn't access local storage", e);
- messageString = R.string.sync_error_local_storage;
syncResult.databaseError = true;
} else if (e instanceof Exceptions.IntegrityException) {
- App.log.log(Level.SEVERE, "Integrity error", e);
- messageString = R.string.sync_error_integrity;
syncResult.stats.numParseExceptions++;
} else {
- App.log.log(Level.SEVERE, "Unknown sync error", e);
- messageString = R.string.sync_error;
syncResult.stats.numParseExceptions++;
}
- final Intent detailsIntent;
+ notificationManager.setThrowable(e);
+
+ final Intent detailsIntent = notificationManager.getDetailsIntent();
if (e instanceof Exceptions.UnauthorizedException) {
- detailsIntent = new Intent(context, AccountSettingsActivity.class);
detailsIntent.putExtra(AccountSettingsActivity.EXTRA_ACCOUNT, account);
} else {
- detailsIntent = new Intent(context, DebugInfoActivity.class);
- detailsIntent.putExtra(DebugInfoActivity.KEY_THROWABLE, e);
detailsIntent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account);
detailsIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority);
detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase);
}
- // to make the PendingIntent unique
- detailsIntent.setData(Uri.parse("uri://" + getClass().getName() + "/" + uniqueCollectionId));
-
- NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
- builder.setSmallIcon(R.drawable.ic_error_light)
- .setLargeIcon(App.getLauncherBitmap(context))
- .setContentTitle(getSyncErrorTitle())
- .setContentIntent(PendingIntent.getActivity(context, 0, detailsIntent, PendingIntent.FLAG_CANCEL_CURRENT))
- .setCategory(NotificationCompat.CATEGORY_ERROR);
-
- String message = context.getString(messageString, syncPhase);
- builder.setContentText(message);
-
-
- notificationManager.notify(uniqueCollectionId, notificationId(), builder.build());
+ notificationManager.notify(getSyncErrorTitle(), syncPhase);
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 20805d76..8112a688 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -264,6 +264,7 @@
Error while %s
Integrity error while %s
Server error while %s
+ Could not connect to server while %s
Database error while %s
- preparing synchronization