diff --git a/app/schemas/com.todoroo.astrid.dao.Database/58.json b/app/schemas/com.todoroo.astrid.dao.Database/58.json
new file mode 100644
index 000000000..edc00eb49
--- /dev/null
+++ b/app/schemas/com.todoroo.astrid.dao.Database/58.json
@@ -0,0 +1,877 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 58,
+ "identityHash": "119477b5c2bd3389e53fef40e4844e01",
+ "entities": [
+ {
+ "tableName": "notification",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `type` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "taskId",
+ "columnName": "task",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timestamp",
+ "columnName": "timestamp",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "uid"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_notification_task",
+ "unique": true,
+ "columnNames": [
+ "task"
+ ],
+ "createSql": "CREATE UNIQUE INDEX `index_notification_task` ON `${TABLE_NAME}` (`task`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "tagdata",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `name` TEXT, `color` INTEGER, `tagOrdering` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "remoteId",
+ "columnName": "remoteId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "color",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "tagOrdering",
+ "columnName": "tagOrdering",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "userActivity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `message` TEXT, `picture` TEXT, `target_id` TEXT, `created_at` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "remoteId",
+ "columnName": "remoteId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "message",
+ "columnName": "message",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "picture",
+ "columnName": "picture",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetId",
+ "columnName": "target_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created_at",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "task_attachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `task_id` TEXT, `name` TEXT, `path` TEXT, `content_type` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "remoteId",
+ "columnName": "remoteId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "taskId",
+ "columnName": "task_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentType",
+ "columnName": "content_type",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "task_list_metadata",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `tag_uuid` TEXT, `filter` TEXT, `task_ids` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "remoteId",
+ "columnName": "remoteId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "tagUuid",
+ "columnName": "tag_uuid",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "filter",
+ "columnName": "filter",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "taskIds",
+ "columnName": "task_ids",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "tasks",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT, `importance` INTEGER, `dueDate` INTEGER, `hideUntil` INTEGER, `created` INTEGER, `modified` INTEGER, `completed` INTEGER, `deleted` INTEGER, `notes` TEXT, `estimatedSeconds` INTEGER, `elapsedSeconds` INTEGER, `timerStart` INTEGER, `notificationFlags` INTEGER, `notifications` INTEGER, `lastNotified` INTEGER, `snoozeTime` INTEGER, `recurrence` TEXT, `repeatUntil` INTEGER, `calendarUri` TEXT, `remoteId` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "importance",
+ "columnName": "importance",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "dueDate",
+ "columnName": "dueDate",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "hideUntil",
+ "columnName": "hideUntil",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "created",
+ "columnName": "created",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "modified",
+ "columnName": "modified",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "completed",
+ "columnName": "completed",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "deleted",
+ "columnName": "deleted",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "notes",
+ "columnName": "notes",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "estimatedSeconds",
+ "columnName": "estimatedSeconds",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "elapsedSeconds",
+ "columnName": "elapsedSeconds",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timerStart",
+ "columnName": "timerStart",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "notificationFlags",
+ "columnName": "notificationFlags",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "notifications",
+ "columnName": "notifications",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lastNotified",
+ "columnName": "lastNotified",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "snoozeTime",
+ "columnName": "snoozeTime",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "recurrence",
+ "columnName": "recurrence",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "repeatUntil",
+ "columnName": "repeatUntil",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "calendarUri",
+ "columnName": "calendarUri",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "remoteId",
+ "columnName": "remoteId",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "t_rid",
+ "unique": true,
+ "columnNames": [
+ "remoteId"
+ ],
+ "createSql": "CREATE UNIQUE INDEX `t_rid` ON `${TABLE_NAME}` (`remoteId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "alarms",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `time` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "task",
+ "columnName": "task",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "time",
+ "columnName": "time",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "locations",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `radius` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "task",
+ "columnName": "task",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "latitude",
+ "columnName": "latitude",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "longitude",
+ "columnName": "longitude",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "radius",
+ "columnName": "radius",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "tags",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `tag_uid` TEXT, `task_uid` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "task",
+ "columnName": "task",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "tagUid",
+ "columnName": "tag_uid",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "taskUid",
+ "columnName": "task_uid",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "google_tasks",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `remote_id` TEXT, `list_id` TEXT, `parent` INTEGER NOT NULL, `indent` INTEGER NOT NULL, `order` INTEGER NOT NULL, `remote_order` INTEGER NOT NULL, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "task",
+ "columnName": "task",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "remoteId",
+ "columnName": "remote_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "listId",
+ "columnName": "list_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parent",
+ "columnName": "parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "indent",
+ "columnName": "indent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "order",
+ "columnName": "order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "remoteOrder",
+ "columnName": "remote_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastSync",
+ "columnName": "last_sync",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deleted",
+ "columnName": "deleted",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "filters",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `sql` TEXT, `values` TEXT, `criterion` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "sql",
+ "columnName": "sql",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "values",
+ "columnName": "values",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "criterion",
+ "columnName": "criterion",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "google_task_lists",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `remote_id` TEXT, `title` TEXT, `remote_order` INTEGER NOT NULL, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `color` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "remoteId",
+ "columnName": "remote_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "remoteOrder",
+ "columnName": "remote_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastSync",
+ "columnName": "last_sync",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deleted",
+ "columnName": "deleted",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "color",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "caldav_calendar",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `uuid` TEXT, `name` TEXT, `color` INTEGER NOT NULL, `ctag` TEXT, `url` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "account",
+ "columnName": "account",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "color",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ctag",
+ "columnName": "ctag",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "caldav_tasks",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `calendar` TEXT, `object` TEXT, `remote_id` TEXT, `etag` TEXT, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `vtodo` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "task",
+ "columnName": "task",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "account",
+ "columnName": "calendar",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "object",
+ "columnName": "object",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "remoteId",
+ "columnName": "remote_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "etag",
+ "columnName": "etag",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lastSync",
+ "columnName": "last_sync",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deleted",
+ "columnName": "deleted",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "vtodo",
+ "columnName": "vtodo",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "caldav_account",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uuid` TEXT, `name` TEXT, `url` TEXT, `username` TEXT, `password` TEXT, `iv` BLOB)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "iv",
+ "columnName": "iv",
+ "affinity": "BLOB",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"119477b5c2bd3389e53fef40e4844e01\")"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 41303ebb2..9e8185184 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -300,7 +300,13 @@
-
+
+
+
{
item.icon = R.drawable.ic_cloud_off_black_24dp;
add(item);
- String title = preferences.getStringValue(GtasksPreferenceService.PREF_USER_NAME);
- if (Strings.isNullOrEmpty(title)) {
- title = activity.getResources().getString(R.string.gtasks_GPr_header);
+ String googleTaskTitle = preferences.getStringValue(GtasksPreferenceService.PREF_USER_NAME);
+ if (Strings.isNullOrEmpty(googleTaskTitle)) {
+ googleTaskTitle = activity.getResources().getString(R.string.gtasks_GPr_header);
}
- addSubMenu(title, filterProvider.getGoogleTaskFilters(), true);
+ addSubMenu(googleTaskTitle, filterProvider.getGoogleTaskFilters(), true);
- addSubMenu(R.string.CalDAV, filterProvider.getCaldavFilters(), true);
+ for (Pair> account : filterProvider.getCaldavFilters()) {
+ String caldavTitle = account.first;
+ if (TextUtils.isEmpty(caldavTitle)) {
+ caldavTitle = activity.getString(R.string.CalDAV);
+ }
+ addSubMenu(caldavTitle, account.second, true);
+ }
notifyDataSetChanged();
}
@@ -314,17 +321,12 @@ public class FilterAdapter extends ArrayAdapter {
}
}
- if (preferences.getBoolean(R.string.p_sync_caldav, false)) {
- addSubMenu(R.string.CalDAV, filterProvider.getCaldavFilters(), false);
-
- if (navigationDrawer) {
- add(
- new NavigationDrawerAction(
- activity.getResources().getString(R.string.add_account),
- R.drawable.ic_add_24dp,
- new Intent(activity, CaldavSettingsActivity.class),
- NavigationDrawerFragment.REQUEST_NEW_CALDAV_ACCOUNT));
+ for (Pair> account : filterProvider.getCaldavFilters()) {
+ String title = account.first;
+ if (TextUtils.isEmpty(title)) {
+ title = activity.getString(R.string.CalDAV);
}
+ addSubMenu(title, account.second, true);
}
if (navigationDrawer) {
diff --git a/app/src/main/java/com/todoroo/astrid/api/CaldavFilter.java b/app/src/main/java/com/todoroo/astrid/api/CaldavFilter.java
index 9eb86f799..c1bf5bfb0 100644
--- a/app/src/main/java/com/todoroo/astrid/api/CaldavFilter.java
+++ b/app/src/main/java/com/todoroo/astrid/api/CaldavFilter.java
@@ -10,7 +10,7 @@ import com.todoroo.astrid.data.Task;
import java.util.HashMap;
import java.util.Map;
import org.tasks.R;
-import org.tasks.data.CaldavAccount;
+import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavTask;
public class CaldavFilter extends Filter {
@@ -35,49 +35,49 @@ public class CaldavFilter extends Filter {
};
private static final int TAG = R.drawable.ic_cloud_black_24dp;
- private CaldavAccount account;
+ private CaldavCalendar calendar;
private CaldavFilter() {
super();
}
- public CaldavFilter(CaldavAccount account) {
- super(account.getName(), queryTemplate(account), getValuesForNewTask(account));
- this.account = account;
- tint = account.getColor();
+ public CaldavFilter(CaldavCalendar calendar) {
+ super(calendar.getName(), queryTemplate(calendar), getValuesForNewTask(calendar));
+ this.calendar = calendar;
+ tint = calendar.getColor();
icon = TAG;
}
- private static QueryTemplate queryTemplate(CaldavAccount caldavAccount) {
+ private static QueryTemplate queryTemplate(CaldavCalendar caldavCalendar) {
return new QueryTemplate()
.join(Join.left(CaldavTask.TABLE, Task.ID.eq(Field.field("caldav_tasks.task"))))
.where(
Criterion.and(
TaskDao.TaskCriteria.activeAndVisible(),
- Field.field("account").eq(caldavAccount.getUuid())));
+ Field.field("calendar").eq(caldavCalendar.getUuid())));
}
- private static Map getValuesForNewTask(CaldavAccount caldavAccount) {
+ private static Map getValuesForNewTask(CaldavCalendar caldavCalendar) {
Map result = new HashMap<>();
- result.put(CaldavTask.KEY, caldavAccount.getUuid());
+ result.put(CaldavTask.KEY, caldavCalendar.getUuid());
return result;
}
public String getUuid() {
- return account.getUuid();
+ return calendar.getUuid();
}
/** {@inheritDoc} */
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- dest.writeParcelable(account, 0);
+ dest.writeParcelable(calendar, 0);
}
@Override
protected void readFromParcel(Parcel source) {
super.readFromParcel(source);
- account = source.readParcelable(getClass().getClassLoader());
+ calendar = source.readParcelable(getClass().getClassLoader());
}
@Override
diff --git a/app/src/main/java/com/todoroo/astrid/dao/Database.java b/app/src/main/java/com/todoroo/astrid/dao/Database.java
index c77187dac..2a88f7a77 100644
--- a/app/src/main/java/com/todoroo/astrid/dao/Database.java
+++ b/app/src/main/java/com/todoroo/astrid/dao/Database.java
@@ -15,6 +15,7 @@ import org.tasks.analytics.Tracking;
import org.tasks.data.Alarm;
import org.tasks.data.AlarmDao;
import org.tasks.data.CaldavAccount;
+import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavDao;
import org.tasks.data.CaldavTask;
import org.tasks.data.Filter;
@@ -39,11 +40,6 @@ import org.tasks.notifications.Notification;
import org.tasks.notifications.NotificationDao;
import timber.log.Timber;
-/**
- * Database wrapper
- *
- * @author Tim Su
- */
@android.arch.persistence.room.Database(
entities = {
Notification.class,
@@ -58,10 +54,11 @@ import timber.log.Timber;
GoogleTask.class,
Filter.class,
GoogleTaskList.class,
- CaldavAccount.class,
- CaldavTask.class
+ CaldavCalendar.class,
+ CaldavTask.class,
+ CaldavAccount.class
},
- version = 57
+ version = 58
)
public abstract class Database extends RoomDatabase {
diff --git a/app/src/main/java/com/todoroo/astrid/dao/TaskDao.java b/app/src/main/java/com/todoroo/astrid/dao/TaskDao.java
index de6ca6720..cd8058937 100644
--- a/app/src/main/java/com/todoroo/astrid/dao/TaskDao.java
+++ b/app/src/main/java/com/todoroo/astrid/dao/TaskDao.java
@@ -27,11 +27,6 @@ import org.tasks.data.LimitOffsetDataSource;
import org.tasks.jobs.AfterSaveIntentService;
import timber.log.Timber;
-/**
- * Data Access layer for {@link Task}-related operations.
- *
- * @author Tim Su
- */
@Dao
public abstract class TaskDao {
@@ -103,9 +98,9 @@ public abstract class TaskDao {
@android.arch.persistence.room.Query(
"SELECT tasks.* FROM tasks "
+ "LEFT JOIN caldav_tasks ON tasks._id = caldav_tasks.task "
- + "WHERE caldav_tasks.account = :uid "
+ + "WHERE caldav_tasks.calendar = :calendar "
+ "AND tasks.modified > caldav_tasks.last_sync")
- public abstract List getCaldavTasksToPush(String uid);
+ public abstract List getCaldavTasksToPush(String calendar);
@android.arch.persistence.room.Query(
"SELECT * FROM TASKS "
@@ -146,8 +141,8 @@ public abstract class TaskDao {
public abstract List getGoogleTasks(String googleTaskList);
@android.arch.persistence.room.Query(
- "SELECT tasks.* FROM tasks INNER JOIN caldav_tasks ON caldav_tasks.task = tasks._id WHERE caldav_tasks.account = :caldavAccount")
- public abstract List getCaldavTasks(String caldavAccount);
+ "SELECT tasks.* FROM tasks INNER JOIN caldav_tasks ON caldav_tasks.task = tasks._id WHERE caldav_tasks.calendar = :calendar")
+ public abstract List getCaldavTasks(String calendar);
/**
* Saves the given task to the database.getDatabase(). Task must already exist. Returns true on
diff --git a/app/src/main/java/com/todoroo/astrid/gtasks/GtasksPreferenceService.java b/app/src/main/java/com/todoroo/astrid/gtasks/GtasksPreferenceService.java
index 4d6f4a22b..e094984d6 100644
--- a/app/src/main/java/com/todoroo/astrid/gtasks/GtasksPreferenceService.java
+++ b/app/src/main/java/com/todoroo/astrid/gtasks/GtasksPreferenceService.java
@@ -19,7 +19,6 @@ public class GtasksPreferenceService {
private static final String IDENTIFIER = "gtasks"; // $NON-NLS-1$
public static final String PREF_USER_NAME = IDENTIFIER + "_user"; // $NON-NLS-1$
private static final String PREF_LAST_SYNC = "_last_sync"; // $NON-NLS-1$
- private static final String PREF_ONGOING = "_ongoing"; // $NON-NLS-1$
private final Preferences preferences;
@Inject
@@ -40,28 +39,17 @@ public class GtasksPreferenceService {
return preferences.getLong(IDENTIFIER + PREF_LAST_SYNC, 0);
}
- /** @return Last Error, or null if no last error */
- public boolean isOngoing() {
- return preferences.getBoolean(IDENTIFIER + PREF_ONGOING, false);
- }
-
/** Deletes Last Successful Sync Date */
public void clearLastSyncDate() {
preferences.clear(IDENTIFIER + PREF_LAST_SYNC);
}
- /** Set Ongoing */
- public void stopOngoing() {
- preferences.setBoolean(IDENTIFIER + PREF_ONGOING, false);
- }
-
/** Set Last Successful Sync Date */
public void recordSuccessfulSync() {
preferences.setLong(IDENTIFIER + PREF_LAST_SYNC, DateUtilities.now() + 1000);
}
- /** Set Last Attempted Sync Date */
- public void recordSyncStart() {
- preferences.setBoolean(IDENTIFIER + PREF_ONGOING, true);
+ public boolean isOngoing() {
+ return preferences.isSyncOngoing();
}
}
diff --git a/app/src/main/java/org/tasks/Tasks.java b/app/src/main/java/org/tasks/Tasks.java
index 1c484e8c1..3c657104a 100644
--- a/app/src/main/java/org/tasks/Tasks.java
+++ b/app/src/main/java/org/tasks/Tasks.java
@@ -38,6 +38,8 @@ public class Tasks extends InjectingApplication {
AndroidThreeTen.init(this);
+ preferences.setSyncOngoing(false);
+
jobManager.addJobCreator(jobCreator);
flavorSetup.setup();
@@ -48,8 +50,6 @@ public class Tasks extends InjectingApplication {
startupService.onStartupApplication();
- gtasksPreferenceService.stopOngoing(); // if sync ongoing flag was set, clear it
-
jobManager.updateBackgroundSync();
jobManager.scheduleMidnightRefresh();
jobManager.scheduleBackup();
diff --git a/app/src/main/java/org/tasks/activities/ColorPickerActivity.java b/app/src/main/java/org/tasks/activities/ColorPickerActivity.java
index 32742ede8..e3c38b009 100644
--- a/app/src/main/java/org/tasks/activities/ColorPickerActivity.java
+++ b/app/src/main/java/org/tasks/activities/ColorPickerActivity.java
@@ -74,7 +74,7 @@ public class ColorPickerActivity extends ThemedInjectingAppCompatActivity
public void themePicked(ColorPickerDialog.Pickable picked) {
Intent data = new Intent();
data.putExtra(EXTRA_PALETTE, palette);
- data.putExtra(EXTRA_THEME_INDEX, picked.getIndex());
+ data.putExtra(EXTRA_THEME_INDEX, picked == null ? -1 : picked.getIndex());
setResult(RESULT_OK, data);
finish();
}
diff --git a/app/src/main/java/org/tasks/backup/BackupContainer.java b/app/src/main/java/org/tasks/backup/BackupContainer.java
index 4ef5c345a..8dff3646b 100644
--- a/app/src/main/java/org/tasks/backup/BackupContainer.java
+++ b/app/src/main/java/org/tasks/backup/BackupContainer.java
@@ -5,7 +5,7 @@ import static java.util.Collections.emptyList;
import com.todoroo.astrid.data.Task;
import java.util.List;
import org.tasks.data.Alarm;
-import org.tasks.data.CaldavAccount;
+import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavTask;
import org.tasks.data.Filter;
import org.tasks.data.GoogleTask;
@@ -22,19 +22,23 @@ class BackupContainer {
final List tags;
final List filters;
final List googleTaskLists;
- final List caldavAccounts;
+ private final List caldavCalendars;
BackupContainer(
List tasks,
List tags,
List filters,
List googleTaskLists,
- List caldavAccounts) {
+ List caldavCalendars) {
this.tasks = tasks;
this.tags = tags;
this.filters = filters;
this.googleTaskLists = googleTaskLists;
- this.caldavAccounts = caldavAccounts;
+ this.caldavCalendars = caldavCalendars;
+ }
+
+ public List getCaldavCalendars() {
+ return caldavCalendars == null ? emptyList() : caldavCalendars;
}
static class TaskBackup {
diff --git a/app/src/main/java/org/tasks/backup/TasksJsonExporter.java b/app/src/main/java/org/tasks/backup/TasksJsonExporter.java
index 23fa151df..2b3dc846a 100755
--- a/app/src/main/java/org/tasks/backup/TasksJsonExporter.java
+++ b/app/src/main/java/org/tasks/backup/TasksJsonExporter.java
@@ -179,7 +179,7 @@ public class TasksJsonExporter {
tagDataDao.getAll(),
filterDao.getAll(),
googleTaskListDao.getAll(),
- caldavDao.getAccounts()));
+ caldavDao.getCalendars()));
File file = new File(output);
file.createNewFile();
diff --git a/app/src/main/java/org/tasks/backup/TasksJsonImporter.java b/app/src/main/java/org/tasks/backup/TasksJsonImporter.java
index c92cb3029..27a34db30 100644
--- a/app/src/main/java/org/tasks/backup/TasksJsonImporter.java
+++ b/app/src/main/java/org/tasks/backup/TasksJsonImporter.java
@@ -18,7 +18,7 @@ import org.tasks.LocalBroadcastManager;
import org.tasks.R;
import org.tasks.data.Alarm;
import org.tasks.data.AlarmDao;
-import org.tasks.data.CaldavAccount;
+import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavDao;
import org.tasks.data.CaldavTask;
import org.tasks.data.Filter;
@@ -141,9 +141,10 @@ public class TasksJsonImporter {
filterDao.insert(filter);
}
}
- for (CaldavAccount account : backupContainer.caldavAccounts) {
- if (caldavDao.getByUuid(account.getUuid()) == null) {
- caldavDao.insert(account);
+ // TODO: Add caldav accounts to backup
+ for (CaldavCalendar calendar : backupContainer.getCaldavCalendars()) {
+ if (caldavDao.getCalendarByUuid(calendar.getUuid()) == null) {
+ caldavDao.insert(calendar);
}
}
for (BackupContainer.TaskBackup backup : backupContainer.tasks) {
diff --git a/app/src/main/java/org/tasks/caldav/CaldavSettingsActivity.java b/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java
similarity index 67%
rename from app/src/main/java/org/tasks/caldav/CaldavSettingsActivity.java
rename to app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java
index fd13cb52b..60503dbe0 100644
--- a/app/src/main/java/org/tasks/caldav/CaldavSettingsActivity.java
+++ b/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java
@@ -4,7 +4,6 @@ import static android.text.TextUtils.isEmpty;
import android.app.ProgressDialog;
import android.content.Context;
-import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
@@ -12,57 +11,49 @@ import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.Toolbar;
-import android.text.InputType;
import android.view.MenuItem;
import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
+import at.bitfire.dav4android.DavResource;
+import at.bitfire.dav4android.PropertyCollection;
import at.bitfire.dav4android.exception.HttpException;
+import at.bitfire.dav4android.property.DisplayName;
import butterknife.BindView;
import butterknife.ButterKnife;
-import butterknife.OnClick;
import butterknife.OnFocusChange;
import butterknife.OnTextChanged;
-import com.todoroo.astrid.activity.TaskListActivity;
-import com.todoroo.astrid.api.CaldavFilter;
import com.todoroo.astrid.helper.UUIDHelper;
+import com.todoroo.astrid.service.TaskDeleter;
import java.net.ConnectException;
import java.net.IDN;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.List;
import javax.inject.Inject;
import org.tasks.R;
-import org.tasks.activities.ColorPickerActivity;
import org.tasks.analytics.Tracker;
-import org.tasks.analytics.Tracking;
import org.tasks.data.CaldavAccount;
+import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavDao;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.injection.ActivityComponent;
import org.tasks.injection.ThemedInjectingAppCompatActivity;
import org.tasks.preferences.Preferences;
import org.tasks.sync.SyncAdapters;
-import org.tasks.themes.ThemeCache;
-import org.tasks.themes.ThemeColor;
import org.tasks.ui.DisplayableException;
import timber.log.Timber;
-public class CaldavSettingsActivity extends ThemedInjectingAppCompatActivity
+public class CaldavAccountSettingsActivity extends ThemedInjectingAppCompatActivity
implements Toolbar.OnMenuItemClickListener {
public static final String EXTRA_CALDAV_DATA = "caldavData"; // $NON-NLS-1$
- public static final String ACTION_RELOAD = "accountRenamed";
- public static final String ACTION_DELETED = "accountDeleted";
- private static final String EXTRA_CALDAV_UUID = "uuid"; // $NON-NLS-1$
- private static final String EXTRA_SELECTED_THEME = "extra_selected_theme";
private static final String PASSWORD_MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
- private static final int REQUEST_COLOR_PICKER = 10109;
@Inject DialogBuilder dialogBuilder;
@Inject Preferences preferences;
- @Inject ThemeCache themeCache;
- @Inject ThemeColor themeColor;
@Inject Tracker tracker;
@Inject CaldavDao caldavDao;
@Inject SyncAdapters syncAdapters;
+ @Inject TaskDeleter taskDeleter;
@BindView(R.id.root_layout)
LinearLayout root;
@@ -85,38 +76,29 @@ public class CaldavSettingsActivity extends ThemedInjectingAppCompatActivity
@BindView(R.id.password_layout)
TextInputLayout passwordLayout;
- @BindView(R.id.color)
- TextInputEditText color;
-
@BindView(R.id.toolbar)
Toolbar toolbar;
private CaldavAccount caldavAccount;
- private int selectedTheme;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_caldav_settings);
+ setContentView(R.layout.activity_caldav_account_settings);
ButterKnife.bind(this);
caldavAccount = getIntent().getParcelableExtra(EXTRA_CALDAV_DATA);
if (savedInstanceState == null) {
- if (caldavAccount == null) {
- selectedTheme = -1;
- } else {
- selectedTheme = caldavAccount.getColor();
+ if (caldavAccount != null) {
url.setText(caldavAccount.getUrl());
user.setText(caldavAccount.getUsername());
if (!isEmpty(caldavAccount.getPassword())) {
password.setText(PASSWORD_MASK);
}
}
- } else {
- selectedTheme = savedInstanceState.getInt(EXTRA_SELECTED_THEME);
}
final boolean backButtonSavesTask = preferences.backButtonSavesTask();
@@ -133,20 +115,16 @@ public class CaldavSettingsActivity extends ThemedInjectingAppCompatActivity
save();
}
});
- toolbar.inflateMenu(R.menu.menu_tag_settings);
+ toolbar.inflateMenu(R.menu.menu_caldav_account_settings);
toolbar.setOnMenuItemClickListener(this);
toolbar.showOverflowMenu();
- color.setInputType(InputType.TYPE_NULL);
-
if (caldavAccount == null) {
- toolbar.getMenu().findItem(R.id.delete).setVisible(false);
+ toolbar.getMenu().findItem(R.id.remove).setVisible(false);
url.requestFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(url, InputMethodManager.SHOW_IMPLICIT);
}
-
- updateTheme();
}
@OnTextChanged(R.id.url)
@@ -177,29 +155,6 @@ public class CaldavSettingsActivity extends ThemedInjectingAppCompatActivity
}
}
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- outState.putInt(EXTRA_SELECTED_THEME, selectedTheme);
- }
-
- @OnFocusChange(R.id.color)
- void onFocusChange(boolean focused) {
- if (focused) {
- color.clearFocus();
- showThemePicker();
- }
- }
-
- @OnClick(R.id.color)
- protected void showThemePicker() {
- Intent intent = new Intent(CaldavSettingsActivity.this, ColorPickerActivity.class);
- intent.putExtra(ColorPickerActivity.EXTRA_PALETTE, ColorPickerActivity.ColorPalette.COLORS);
- intent.putExtra(ColorPickerActivity.EXTRA_SHOW_NONE, true);
- startActivityForResult(intent, REQUEST_COLOR_PICKER);
- }
-
@Override
public void inject(ActivityComponent component) {
component.inject(this);
@@ -276,51 +231,49 @@ public class CaldavSettingsActivity extends ThemedInjectingAppCompatActivity
ProgressDialog dialog = dialogBuilder.newProgressDialog(R.string.contacting_server);
dialog.show();
client
- .getDisplayName()
+ .getHomeSet()
.doAfterTerminate(dialog::dismiss)
- .subscribe(this::addAccount, this::getDisplayNameFailed);
+ .subscribe(this::addAccount, this::requestFailed);
} else if (needsValidation()) {
CaldavClient client = new CaldavClient(url, username, password);
ProgressDialog dialog = dialogBuilder.newProgressDialog(R.string.contacting_server);
dialog.show();
client
- .getDisplayName()
+ .getHomeSet()
.doAfterTerminate(dialog::dismiss)
- .subscribe(this::updateAccount, this::getDisplayNameFailed);
+ .subscribe(this::updateAccount, this::requestFailed);
} else if (hasChanges()) {
- updateAccount(caldavAccount.getName());
+ updateAccount(caldavAccount.getUrl());
} else {
finish();
}
}
- private void addAccount(String name) {
- CaldavAccount newAccount = new CaldavAccount(name, UUIDHelper.newUUID());
- newAccount.setColor(selectedTheme);
- newAccount.setUrl(getNewURL());
+ private void addAccount(String principal) {
+ Timber.d("Found principal: %s", principal);
+
+ CaldavAccount newAccount = new CaldavAccount();
+ newAccount.setUrl(principal);
newAccount.setUsername(getNewUsername());
newAccount.setPassword(getNewPassword());
+ newAccount.setUuid(UUIDHelper.newUUID());
newAccount.setId(caldavDao.insert(newAccount));
- setResult(
- RESULT_OK,
- new Intent().putExtra(TaskListActivity.OPEN_FILTER, new CaldavFilter(newAccount)));
+
+ setResult(RESULT_OK);
finish();
}
- private void updateAccount(String name) {
- caldavAccount.setName(name);
- caldavAccount.setUrl(getNewURL());
+ private void updateAccount(String principal) {
+ caldavAccount.setUrl(principal);
caldavAccount.setUsername(getNewUsername());
- caldavAccount.setColor(selectedTheme);
caldavAccount.setPassword(getNewPassword());
caldavDao.update(caldavAccount);
- setResult(
- RESULT_OK,
- new Intent().putExtra(TaskListActivity.OPEN_FILTER, new CaldavFilter(caldavAccount)));
+
+ setResult(RESULT_OK);
finish();
}
- private void getDisplayNameFailed(Throwable t) {
+ private void requestFailed(Throwable t) {
if (t instanceof HttpException) {
showSnackbar(t.getMessage());
} else if (t instanceof DisplayableException) {
@@ -352,12 +305,9 @@ public class CaldavSettingsActivity extends ThemedInjectingAppCompatActivity
private boolean hasChanges() {
if (caldavAccount == null) {
- return selectedTheme >= 0
- || !isEmpty(getNewPassword())
- || !isEmpty(getNewURL())
- || !isEmpty(getNewUsername());
+ return !isEmpty(getNewPassword()) || !isEmpty(getNewURL()) || !isEmpty(getNewUsername());
}
- return selectedTheme != caldavAccount.getColor() || needsValidation();
+ return needsValidation();
}
private boolean needsValidation() {
@@ -382,33 +332,20 @@ public class CaldavSettingsActivity extends ThemedInjectingAppCompatActivity
}
}
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == REQUEST_COLOR_PICKER) {
- if (resultCode == RESULT_OK) {
- int index = data.getIntExtra(ColorPickerActivity.EXTRA_THEME_INDEX, 0);
- tracker.reportEvent(Tracking.Events.SET_TAG_COLOR, Integer.toString(index));
- selectedTheme = index;
- updateTheme();
- }
- } else {
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
-
- private void deleteAccount() {
+ private void removeAccount() {
dialogBuilder
- .newMessageDialog(R.string.delete_tag_confirmation, caldavAccount.getName())
+ .newMessageDialog(R.string.remove_caldav_account_confirmation, caldavAccount.getName())
.setPositiveButton(
- R.string.delete,
+ R.string.remove,
(dialog, which) -> {
- if (caldavAccount != null) {
- caldavDao.delete(caldavAccount);
- setResult(
- RESULT_OK,
- new Intent(ACTION_DELETED)
- .putExtra(EXTRA_CALDAV_UUID, caldavAccount.getUuid()));
+ for (CaldavCalendar calendar :
+ caldavDao.getCalendarsByAccount(caldavAccount.getUuid())) {
+ taskDeleter.markDeleted(caldavDao.getTasksByCalendar(calendar.getUuid()));
+ caldavDao.deleteTasksForCalendar(calendar.getUuid());
}
+ caldavDao.deleteCalendarsForAccount(caldavAccount.getUuid());
+ caldavDao.delete(caldavAccount);
+ setResult(RESULT_OK);
finish();
})
.setNegativeButton(android.R.string.cancel, null)
@@ -427,26 +364,13 @@ public class CaldavSettingsActivity extends ThemedInjectingAppCompatActivity
}
}
- private void updateTheme() {
- ThemeColor themeColor;
- if (selectedTheme < 0) {
- themeColor = this.themeColor;
- color.setText(R.string.none);
- } else {
- themeColor = themeCache.getThemeColor(selectedTheme);
- color.setText(themeColor.getName());
- }
- themeColor.apply(toolbar);
- themeColor.applyToStatusBar(this);
- }
-
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
- case R.id.delete:
- deleteAccount();
+ case R.id.remove:
+ removeAccount();
break;
}
- return super.onOptionsItemSelected(item);
+ return onOptionsItemSelected(item);
}
}
diff --git a/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.java b/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.java
new file mode 100644
index 000000000..5767d20b5
--- /dev/null
+++ b/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.java
@@ -0,0 +1,191 @@
+package org.tasks.caldav;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.design.widget.TextInputEditText;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.Toolbar;
+import android.text.InputType;
+import android.widget.LinearLayout;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+import butterknife.OnFocusChange;
+import com.todoroo.astrid.activity.TaskListActivity;
+import com.todoroo.astrid.api.CaldavFilter;
+import javax.inject.Inject;
+import org.tasks.R;
+import org.tasks.activities.ColorPickerActivity;
+import org.tasks.analytics.Tracker;
+import org.tasks.analytics.Tracking;
+import org.tasks.data.CaldavCalendar;
+import org.tasks.data.CaldavDao;
+import org.tasks.dialogs.DialogBuilder;
+import org.tasks.injection.ActivityComponent;
+import org.tasks.injection.ThemedInjectingAppCompatActivity;
+import org.tasks.preferences.Preferences;
+import org.tasks.sync.SyncAdapters;
+import org.tasks.themes.ThemeCache;
+import org.tasks.themes.ThemeColor;
+
+public class CaldavCalendarSettingsActivity extends ThemedInjectingAppCompatActivity {
+
+ public static final String EXTRA_CALDAV_DATA = "caldavData"; // $NON-NLS-1$
+ public static final String ACTION_RELOAD = "accountRenamed";
+ public static final String ACTION_DELETED = "accountDeleted";
+ private static final String EXTRA_SELECTED_THEME = "extra_selected_theme";
+ private static final int REQUEST_COLOR_PICKER = 10109;
+ @Inject DialogBuilder dialogBuilder;
+ @Inject Preferences preferences;
+ @Inject ThemeCache themeCache;
+ @Inject ThemeColor themeColor;
+ @Inject Tracker tracker;
+ @Inject CaldavDao caldavDao;
+ @Inject SyncAdapters syncAdapters;
+
+ @BindView(R.id.root_layout)
+ LinearLayout root;
+
+ @BindView(R.id.color)
+ TextInputEditText color;
+
+ @BindView(R.id.toolbar)
+ Toolbar toolbar;
+
+ private CaldavCalendar caldavCalendar;
+ private int selectedTheme;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_caldav_calendar_settings);
+
+ ButterKnife.bind(this);
+
+ caldavCalendar = getIntent().getParcelableExtra(EXTRA_CALDAV_DATA);
+
+ if (savedInstanceState == null) {
+ selectedTheme = caldavCalendar.getColor();
+ } else {
+ selectedTheme = savedInstanceState.getInt(EXTRA_SELECTED_THEME);
+ }
+
+ final boolean backButtonSavesTask = preferences.backButtonSavesTask();
+ toolbar.setTitle(caldavCalendar.getName());
+ toolbar.setNavigationIcon(
+ ContextCompat.getDrawable(
+ this, backButtonSavesTask ? R.drawable.ic_close_24dp : R.drawable.ic_save_24dp));
+ toolbar.setNavigationOnClickListener(
+ v -> {
+ if (backButtonSavesTask) {
+ discard();
+ } else {
+ save();
+ }
+ });
+
+ color.setInputType(InputType.TYPE_NULL);
+
+ updateTheme();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putInt(EXTRA_SELECTED_THEME, selectedTheme);
+ }
+
+ @OnFocusChange(R.id.color)
+ void onFocusChange(boolean focused) {
+ if (focused) {
+ color.clearFocus();
+ showThemePicker();
+ }
+ }
+
+ @OnClick(R.id.color)
+ protected void showThemePicker() {
+ Intent intent = new Intent(CaldavCalendarSettingsActivity.this, ColorPickerActivity.class);
+ intent.putExtra(ColorPickerActivity.EXTRA_PALETTE, ColorPickerActivity.ColorPalette.COLORS);
+ intent.putExtra(ColorPickerActivity.EXTRA_SHOW_NONE, true);
+ intent.putExtra(ColorPickerActivity.EXTRA_THEME_INDEX, selectedTheme);
+ startActivityForResult(intent, REQUEST_COLOR_PICKER);
+ }
+
+ @Override
+ public void inject(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ private void save() {
+ if (hasChanges()) {
+ updateAccount();
+ } else {
+ finish();
+ }
+ }
+
+ private void updateAccount() {
+ caldavCalendar.setColor(selectedTheme);
+ caldavDao.update(caldavCalendar);
+ setResult(
+ RESULT_OK,
+ new Intent(ACTION_RELOAD)
+ .putExtra(TaskListActivity.OPEN_FILTER, new CaldavFilter(caldavCalendar)));
+ finish();
+ }
+
+ private boolean hasChanges() {
+ return selectedTheme != caldavCalendar.getColor();
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (preferences.backButtonSavesTask()) {
+ save();
+ } else {
+ discard();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_COLOR_PICKER) {
+ if (resultCode == RESULT_OK) {
+ int index = data.getIntExtra(ColorPickerActivity.EXTRA_THEME_INDEX, 0);
+ tracker.reportEvent(Tracking.Events.SET_TAG_COLOR, Integer.toString(index));
+ selectedTheme = index;
+ updateTheme();
+ }
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ private void discard() {
+ if (!hasChanges()) {
+ finish();
+ } else {
+ dialogBuilder
+ .newMessageDialog(R.string.discard_changes)
+ .setPositiveButton(R.string.discard, (dialog, which) -> finish())
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
+ }
+
+ private void updateTheme() {
+ ThemeColor themeColor;
+ if (selectedTheme < 0) {
+ themeColor = this.themeColor;
+ color.setText(R.string.none);
+ } else {
+ themeColor = themeCache.getThemeColor(selectedTheme);
+ color.setText(themeColor.getName());
+ }
+ themeColor.apply(toolbar);
+ themeColor.applyToStatusBar(this);
+ }
+}
diff --git a/app/src/main/java/org/tasks/caldav/CaldavClient.java b/app/src/main/java/org/tasks/caldav/CaldavClient.java
index 15bfaa690..3cf04a968 100644
--- a/app/src/main/java/org/tasks/caldav/CaldavClient.java
+++ b/app/src/main/java/org/tasks/caldav/CaldavClient.java
@@ -1,21 +1,39 @@
package org.tasks.caldav;
+import static android.text.TextUtils.isEmpty;
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+
import at.bitfire.dav4android.BasicDigestAuthHandler;
-import at.bitfire.dav4android.DavCalendar;
+import at.bitfire.dav4android.DavResource;
+import at.bitfire.dav4android.PropertyCollection;
+import at.bitfire.dav4android.exception.DavException;
+import at.bitfire.dav4android.exception.HttpException;
+import at.bitfire.dav4android.property.CalendarHomeSet;
+import at.bitfire.dav4android.property.CurrentUserPrincipal;
import at.bitfire.dav4android.property.DisplayName;
+import at.bitfire.dav4android.property.GetCTag;
+import at.bitfire.dav4android.property.ResourceType;
+import at.bitfire.dav4android.property.SupportedCalendarComponentSet;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
+import java.io.IOException;
import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import org.tasks.R;
import org.tasks.ui.DisplayableException;
+import timber.log.Timber;
class CaldavClient {
- private final DavCalendar davCalendar;
+ private final DavResource davResource;
+ private HttpUrl httpUrl;
public CaldavClient(String url, String username, String password) {
BasicDigestAuthHandler basicDigestAuthHandler =
@@ -27,18 +45,110 @@ class CaldavClient {
.authenticator(basicDigestAuthHandler)
.cookieJar(new MemoryCookieStore())
.followRedirects(false)
- .followSslRedirects(false)
+ .followSslRedirects(true)
+ .readTimeout(30, TimeUnit.SECONDS)
.build();
URI uri = URI.create(url);
- HttpUrl httpUrl = HttpUrl.get(uri);
- davCalendar = new DavCalendar(httpClient, httpUrl);
+ httpUrl = HttpUrl.get(uri);
+ davResource = new DavResource(httpClient, httpUrl);
+ }
+
+ private String tryFindPrincipal() throws DavException, IOException, HttpException {
+ for (String link : asList("", "/.well-known/caldav")) {
+ HttpUrl url = httpUrl.resolve(link);
+ Timber.d("Checking for principal: %s", url);
+ davResource.setLocation(url);
+ try {
+ davResource.propfind(0, CurrentUserPrincipal.NAME);
+ } catch (HttpException e) {
+ switch (e.getStatus()) {
+ case 405:
+ Timber.w(e);
+ break;
+ default:
+ throw e;
+ }
+ }
+ PropertyCollection properties = davResource.getProperties();
+ CurrentUserPrincipal currentUserPrincipal = properties.get(CurrentUserPrincipal.class);
+ if (currentUserPrincipal != null) {
+ String href = currentUserPrincipal.getHref();
+ if (!isEmpty(href)) {
+ return href;
+ }
+ }
+ }
+ return null;
+ }
+
+ private String findHomeset() throws DavException, IOException, HttpException {
+ davResource.propfind(0, CalendarHomeSet.NAME);
+ PropertyCollection properties = davResource.getProperties();
+ CalendarHomeSet calendarHomeSet = properties.get(CalendarHomeSet.class);
+ if (calendarHomeSet == null) {
+ throw new DisplayableException(R.string.caldav_home_set_not_found);
+ }
+ List hrefs = calendarHomeSet.getHrefs();
+ if (hrefs.size() != 1) {
+ throw new DisplayableException(R.string.caldav_home_set_not_found);
+ }
+ String homeSet = hrefs.get(0);
+ if (isEmpty(homeSet)) {
+ throw new DisplayableException(R.string.caldav_home_set_not_found);
+ }
+ return davResource.getLocation().resolve(homeSet).toString();
+ }
+
+ public Single getHomeSet() {
+ return Single.fromCallable(
+ () -> {
+ String principal = tryFindPrincipal();
+ if (!isEmpty(principal)) {
+ davResource.setLocation(httpUrl.resolve(principal));
+ }
+ return findHomeset();
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread());
+ }
+
+ public List getCalendars() {
+ try {
+ davResource.propfind(
+ 1, ResourceType.NAME, DisplayName.NAME, SupportedCalendarComponentSet.NAME, GetCTag.NAME);
+ } catch (IOException | HttpException | DavException e) {
+ Timber.e(e);
+ return emptyList();
+ }
+ List urls = new ArrayList<>();
+ for (DavResource member : davResource.getMembers()) {
+ PropertyCollection properties = member.getProperties();
+ ResourceType resourceType = properties.get(ResourceType.class);
+ if (resourceType == null || !resourceType.getTypes().contains(ResourceType.CALENDAR)) {
+ Timber.d("%s is not a calendar", member);
+ continue;
+ }
+ SupportedCalendarComponentSet supportedCalendarComponentSet =
+ properties.get(SupportedCalendarComponentSet.class);
+ if (supportedCalendarComponentSet == null
+ || !supportedCalendarComponentSet.getSupportsTasks()) {
+ Timber.d("%s does not support tasks", member);
+ continue;
+ }
+ Timber.d("Found %s", member);
+ urls.add(member);
+ }
+ if (urls.isEmpty()) {
+ throw new DisplayableException(R.string.caldav_no_supported_calendars);
+ }
+ return urls;
}
public Single getDisplayName() {
Callable callable =
() -> {
- davCalendar.propfind(0, DisplayName.NAME);
- DisplayName displayName = davCalendar.getProperties().get(DisplayName.class);
+ davResource.propfind(0, DisplayName.NAME);
+ DisplayName displayName = davResource.getProperties().get(DisplayName.class);
if (displayName == null) {
throw new DisplayableException(R.string.calendar_not_found);
}
diff --git a/app/src/main/java/org/tasks/caldav/CaldavFilterExposer.java b/app/src/main/java/org/tasks/caldav/CaldavFilterExposer.java
index 3221d02fb..05ed8a276 100644
--- a/app/src/main/java/org/tasks/caldav/CaldavFilterExposer.java
+++ b/app/src/main/java/org/tasks/caldav/CaldavFilterExposer.java
@@ -1,12 +1,15 @@
package org.tasks.caldav;
+import static com.google.common.collect.Lists.transform;
+
+import android.support.v4.util.Pair;
import com.todoroo.astrid.api.CaldavFilter;
import com.todoroo.astrid.api.Filter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import org.tasks.data.CaldavAccount;
+import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavDao;
import org.tasks.sync.SyncAdapters;
@@ -21,23 +24,20 @@ public class CaldavFilterExposer {
this.syncAdapters = syncAdapters;
}
- public List getFilters() {
- if (!syncAdapters.isCaldavSyncEnabled()) {
- return Collections.emptyList();
- }
- List allOrderedByName = caldavDao.getAllOrderedByName();
- List result = new ArrayList<>();
- for (CaldavAccount account : allOrderedByName) {
- result.add(new CaldavFilter(account));
+ public List>> getFilters() {
+ List>> filters = new ArrayList<>();
+ for (CaldavAccount account : caldavDao.getAccounts()) {
+ List calendars = caldavDao.getCalendarsByAccount(account.getUuid());
+ filters.add(new Pair<>(account.getName(), transform(calendars, CaldavFilter::new)));
}
- return result;
+ return filters;
}
public Filter getFilterByUuid(String uuid) {
if (syncAdapters.isCaldavSyncEnabled()) {
- CaldavAccount caldavAccount = caldavDao.getByUuid(uuid);
- if (caldavAccount != null) {
- return new CaldavFilter(caldavAccount);
+ CaldavCalendar caldavCalendar = caldavDao.getCalendarByUuid(uuid);
+ if (caldavCalendar != null) {
+ return new CaldavFilter(caldavCalendar);
}
}
return null;
diff --git a/app/src/main/java/org/tasks/caldav/CaldavListFragment.java b/app/src/main/java/org/tasks/caldav/CaldavListFragment.java
index dd29d7b66..290a4ff1b 100644
--- a/app/src/main/java/org/tasks/caldav/CaldavListFragment.java
+++ b/app/src/main/java/org/tasks/caldav/CaldavListFragment.java
@@ -1,7 +1,7 @@
package org.tasks.caldav;
import static android.app.Activity.RESULT_OK;
-import static org.tasks.caldav.CaldavSettingsActivity.EXTRA_CALDAV_DATA;
+import static org.tasks.caldav.CaldavCalendarSettingsActivity.EXTRA_CALDAV_DATA;
import android.content.Intent;
import android.os.Bundle;
@@ -12,19 +12,19 @@ import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.api.CaldavFilter;
import com.todoroo.astrid.api.Filter;
import org.tasks.R;
-import org.tasks.data.CaldavAccount;
+import org.tasks.data.CaldavCalendar;
import org.tasks.injection.FragmentComponent;
public class CaldavListFragment extends TaskListFragment {
- private static final String EXTRA_CALDAV_ACCOUNT = "extra_caldav_account";
+ private static final String EXTRA_CALDAV_CALENDAR = "extra_caldav_calendar";
private static final int REQUEST_ACCOUNT_SETTINGS = 10101;
- private CaldavAccount account;
+ private CaldavCalendar calendar;
- public static TaskListFragment newCaldavListFragment(CaldavFilter filter, CaldavAccount account) {
+ public static TaskListFragment newCaldavListFragment(CaldavFilter filter, CaldavCalendar calendar) {
CaldavListFragment fragment = new CaldavListFragment();
fragment.filter = filter;
- fragment.account = account;
+ fragment.calendar = calendar;
return fragment;
}
@@ -33,7 +33,7 @@ public class CaldavListFragment extends TaskListFragment {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
- this.account = savedInstanceState.getParcelable(EXTRA_CALDAV_ACCOUNT);
+ this.calendar = savedInstanceState.getParcelable(EXTRA_CALDAV_CALENDAR);
}
}
@@ -47,8 +47,8 @@ public class CaldavListFragment extends TaskListFragment {
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_caldav_list_fragment:
- Intent intent = new Intent(getActivity(), CaldavSettingsActivity.class);
- intent.putExtra(EXTRA_CALDAV_DATA, account);
+ Intent intent = new Intent(getActivity(), CaldavCalendarSettingsActivity.class);
+ intent.putExtra(EXTRA_CALDAV_DATA, calendar);
startActivityForResult(intent, REQUEST_ACCOUNT_SETTINGS);
return true;
default:
@@ -62,9 +62,9 @@ public class CaldavListFragment extends TaskListFragment {
if (resultCode == RESULT_OK) {
TaskListActivity activity = (TaskListActivity) getActivity();
String action = data.getAction();
- if (CaldavSettingsActivity.ACTION_DELETED.equals(action)) {
+ if (CaldavCalendarSettingsActivity.ACTION_DELETED.equals(action)) {
activity.onFilterItemClicked(null);
- } else if (CaldavSettingsActivity.ACTION_RELOAD.equals(action)) {
+ } else if (CaldavCalendarSettingsActivity.ACTION_RELOAD.equals(action)) {
activity
.getIntent()
.putExtra(
@@ -81,7 +81,7 @@ public class CaldavListFragment extends TaskListFragment {
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putParcelable(EXTRA_CALDAV_ACCOUNT, account);
+ outState.putParcelable(EXTRA_CALDAV_CALENDAR, calendar);
}
@Override
diff --git a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java
index aac3e8af8..c21b59ec6 100644
--- a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java
+++ b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java
@@ -5,6 +5,7 @@ import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.partition;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Lists.transform;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.newHashSet;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
@@ -23,6 +24,7 @@ import at.bitfire.dav4android.property.GetETag;
import at.bitfire.ical4android.InvalidCalendarException;
import at.bitfire.ical4android.iCalendar;
import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
import com.google.common.io.CharStreams;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.TaskDao;
@@ -49,6 +51,7 @@ import okhttp3.ResponseBody;
import org.tasks.BuildConfig;
import org.tasks.LocalBroadcastManager;
import org.tasks.data.CaldavAccount;
+import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavDao;
import org.tasks.data.CaldavTask;
import org.tasks.injection.ForApplication;
@@ -88,18 +91,46 @@ public class CaldavSynchronizer {
// required for dav4android (ServiceLoader)
Thread.currentThread().setContextClassLoader(context.getClassLoader());
for (CaldavAccount account : caldavDao.getAccounts()) {
- sync(account);
+ CaldavClient caldavClient =
+ new CaldavClient(account.getUrl(), account.getUsername(), account.getPassword());
+ List resources = caldavClient.getCalendars();
+ Set urls = newHashSet(transform(resources, c -> c.getLocation().toString()));
+ Timber.d("Found calendars: %s", urls);
+ for (CaldavCalendar deleted : caldavDao.findDeletedCalendars(account.getUuid(), newArrayList(urls))) {
+ taskDeleter.markDeleted(caldavDao.getTasksByCalendar(deleted.getUuid()));
+ caldavDao.deleteTasksForCalendar(deleted.getUuid());
+ caldavDao.delete(deleted);
+ localBroadcastManager.broadcastRefreshList();
+ }
+ for (DavResource resource : resources) {
+ String url = resource.getLocation().toString();
+ PropertyCollection properties = resource.getProperties();
+ CaldavCalendar calendar = caldavDao.getCalendarByUrl(account.getUuid(), url);
+ if (calendar == null) {
+ calendar = new CaldavCalendar();
+ calendar.setName(properties.get(DisplayName.class).getDisplayName());
+ calendar.setAccount(account.getUuid());
+ calendar.setUrl(url);
+ calendar.setUuid(UUIDHelper.newUUID());
+ calendar.setId(caldavDao.insert(calendar));
+ localBroadcastManager.broadcastRefreshList();
+ }
+ String ctag = properties.get(GetCTag.class).getCTag();
+ if (calendar.getCtag() == null || !calendar.getCtag().equals(ctag)) {
+ sync(account, calendar);
+ }
+ }
}
}
- private void sync(CaldavAccount caldavAccount) {
- if (isNullOrEmpty(caldavAccount.getPassword())) {
- Timber.e("Missing password for %s", caldavAccount);
+ private void sync(CaldavAccount account, CaldavCalendar caldavCalendar) {
+ if (isNullOrEmpty(account.getPassword())) {
+ Timber.e("Missing password for %s", caldavCalendar);
return;
}
- Timber.d("sync(%s)", caldavAccount);
+ Timber.d("sync(%s)", caldavCalendar);
BasicDigestAuthHandler basicDigestAuthHandler =
- new BasicDigestAuthHandler(null, caldavAccount.getUsername(), caldavAccount.getPassword());
+ new BasicDigestAuthHandler(null, account.getUsername(), account.getPassword());
OkHttpClient httpClient =
new OkHttpClient()
.newBuilder()
@@ -110,28 +141,28 @@ public class CaldavSynchronizer {
.followSslRedirects(false)
.readTimeout(30, TimeUnit.SECONDS)
.build();
- URI uri = URI.create(caldavAccount.getUrl());
+ URI uri = URI.create(caldavCalendar.getUrl());
HttpUrl httpUrl = HttpUrl.get(uri);
DavCalendar davCalendar = new DavCalendar(httpClient, httpUrl);
try {
- pushLocalChanges(caldavAccount, httpClient, httpUrl);
+ pushLocalChanges(caldavCalendar, httpClient, httpUrl);
davCalendar.propfind(0, GetCTag.NAME, DisplayName.NAME);
PropertyCollection properties = davCalendar.getProperties();
String remoteName = properties.get(DisplayName.class).getDisplayName();
- if (!caldavAccount.getName().equals(remoteName)) {
- Timber.d("%s -> %s", caldavAccount.getName(), remoteName);
- caldavAccount.setName(remoteName);
- caldavDao.update(caldavAccount);
+ if (!caldavCalendar.getName().equals(remoteName)) {
+ Timber.d("%s -> %s", caldavCalendar.getName(), remoteName);
+ caldavCalendar.setName(remoteName);
+ caldavDao.update(caldavCalendar);
localBroadcastManager.broadcastRefreshList();
}
String remoteCtag = properties.get(GetCTag.class).getCTag();
- String localCtag = caldavAccount.getCtag();
+ String localCtag = caldavCalendar.getCtag();
if (localCtag != null && localCtag.equals(remoteCtag)) {
- Timber.d("%s up to date", caldavAccount.getName());
+ Timber.d("%s up to date", caldavCalendar.getName());
return;
}
@@ -149,7 +180,7 @@ public class CaldavSynchronizer {
return false;
}
CaldavTask caldavTask =
- caldavDao.getTask(caldavAccount.getUuid(), vCard.fileName());
+ caldavDao.getTask(caldavCalendar.getUuid(), vCard.fileName());
return caldavTask == null || !eTag.getETag().equals(caldavTask.getEtag());
});
@@ -168,14 +199,15 @@ public class CaldavSynchronizer {
try {
reader = responseBody.charStream();
processVTodo(
- vCard.fileName(), caldavAccount, eTag.getETag(), CharStreams.toString(reader));
+ vCard.fileName(), caldavCalendar, eTag.getETag(), CharStreams.toString(reader));
} finally {
if (reader != null) {
reader.close();
}
}
} else {
- ArrayList urls = newArrayList(transform(items, DavResource::getLocation));
+ ArrayList urls =
+ newArrayList(Iterables.transform(items, DavResource::getLocation));
davCalendar.multiget(urls);
Timber.d("MULTI %s", urls);
@@ -195,7 +227,7 @@ public class CaldavSynchronizer {
}
processVTodo(
- vCard.fileName(), caldavAccount, eTag.getETag(), calendarData.getICalendar());
+ vCard.fileName(), caldavCalendar, eTag.getETag(), calendarData.getICalendar());
}
}
}
@@ -203,17 +235,17 @@ public class CaldavSynchronizer {
List deleted =
newArrayList(
difference(
- newHashSet(caldavDao.getObjects(caldavAccount.getUuid())),
+ newHashSet(caldavDao.getObjects(caldavCalendar.getUuid())),
newHashSet(remoteObjects)));
if (deleted.size() > 0) {
Timber.d("DELETED %s", deleted);
- taskDeleter.markDeleted(caldavDao.getTasks(caldavAccount.getUuid(), deleted));
- caldavDao.deleteObjects(caldavAccount.getUuid(), deleted);
+ taskDeleter.markDeleted(caldavDao.getTasks(caldavCalendar.getUuid(), deleted));
+ caldavDao.deleteObjects(caldavCalendar.getUuid(), deleted);
}
- caldavAccount.setCtag(remoteCtag);
- Timber.d("UPDATE %s", caldavAccount);
- caldavDao.update(caldavAccount);
+ caldavCalendar.setCtag(remoteCtag);
+ Timber.d("UPDATE %s", caldavCalendar);
+ caldavDao.update(caldavCalendar);
} catch (IOException | HttpException | DavException e) {
Timber.e(e);
} catch (Exception e) {
@@ -224,11 +256,11 @@ public class CaldavSynchronizer {
}
private void pushLocalChanges(
- CaldavAccount caldavAccount, OkHttpClient httpClient, HttpUrl httpUrl) {
- List tasks = taskDao.getCaldavTasksToPush(caldavAccount.getUuid());
+ CaldavCalendar caldavCalendar, OkHttpClient httpClient, HttpUrl httpUrl) {
+ List tasks = taskDao.getCaldavTasksToPush(caldavCalendar.getUuid());
for (com.todoroo.astrid.data.Task task : tasks) {
try {
- pushTask(task, caldavAccount, httpClient, httpUrl);
+ pushTask(task, caldavCalendar, httpClient, httpUrl);
} catch (IOException e) {
Timber.e(e);
}
@@ -258,10 +290,10 @@ public class CaldavSynchronizer {
}
private void pushTask(
- Task task, CaldavAccount caldavAccount, OkHttpClient httpClient, HttpUrl httpUrl)
+ Task task, CaldavCalendar caldavCalendar, OkHttpClient httpClient, HttpUrl httpUrl)
throws IOException {
Timber.d("pushing %s", task);
- List deleted = getDeleted(task.getId(), caldavAccount);
+ List deleted = getDeleted(task.getId(), caldavCalendar);
if (!deleted.isEmpty()) {
for (CaldavTask entry : deleted) {
deleteRemoteResource(httpClient, httpUrl, entry);
@@ -317,11 +349,12 @@ public class CaldavSynchronizer {
Timber.d("SENT %s", caldavTask);
}
- private List getDeleted(long taskId, CaldavAccount caldavAccount) {
- return caldavDao.getDeleted(taskId, caldavAccount.getUuid());
+ private List getDeleted(long taskId, CaldavCalendar caldavCalendar) {
+ return caldavDao.getDeleted(taskId, caldavCalendar.getUuid());
}
- private void processVTodo(String fileName, CaldavAccount caldavAccount, String eTag, String vtodo)
+ private void processVTodo(
+ String fileName, CaldavCalendar caldavCalendar, String eTag, String vtodo)
throws IOException {
List tasks;
try {
@@ -334,12 +367,12 @@ public class CaldavSynchronizer {
if (tasks.size() == 1) {
at.bitfire.ical4android.Task remote = tasks.get(0);
Task task;
- CaldavTask caldavTask = caldavDao.getTask(caldavAccount.getUuid(), fileName);
+ CaldavTask caldavTask = caldavDao.getTask(caldavCalendar.getUuid(), fileName);
if (caldavTask == null) {
task = taskCreator.createWithValues(null, "");
taskDao.createNew(task);
caldavTask =
- new CaldavTask(task.getId(), caldavAccount.getUuid(), remote.getUid(), fileName);
+ new CaldavTask(task.getId(), caldavCalendar.getUuid(), remote.getUid(), fileName);
} else {
task = taskDao.fetch(caldavTask.getTask());
}
diff --git a/app/src/main/java/org/tasks/data/CaldavAccount.java b/app/src/main/java/org/tasks/data/CaldavAccount.java
index 93aa908e2..ea05de25d 100644
--- a/app/src/main/java/org/tasks/data/CaldavAccount.java
+++ b/app/src/main/java/org/tasks/data/CaldavAccount.java
@@ -8,13 +8,14 @@ import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.PrimaryKey;
import android.os.Parcel;
import android.os.Parcelable;
-import android.text.TextUtils;
+import java.util.Arrays;
@Entity(tableName = "caldav_account")
-public final class CaldavAccount implements Parcelable {
+public class CaldavAccount implements Parcelable {
public static Parcelable.Creator CREATOR =
new Parcelable.Creator() {
+
@Override
public CaldavAccount createFromParcel(Parcel source) {
return new CaldavAccount(source);
@@ -36,12 +37,6 @@ public final class CaldavAccount implements Parcelable {
@ColumnInfo(name = "name")
private String name = "";
- @ColumnInfo(name = "color")
- private int color = -1;
-
- @ColumnInfo(name = "ctag")
- private String ctag;
-
@ColumnInfo(name = "url")
private String url = "";
@@ -51,24 +46,20 @@ public final class CaldavAccount implements Parcelable {
@ColumnInfo(name = "password")
private transient String password = "";
- public CaldavAccount() {}
+ @ColumnInfo(name = "iv")
+ private transient byte[] iv = null;
- @Ignore
- public CaldavAccount(String name, String uuid) {
- this.name = name;
- this.uuid = uuid;
- }
+ public CaldavAccount() {}
@Ignore
public CaldavAccount(Parcel source) {
id = source.readLong();
uuid = source.readString();
name = source.readString();
- color = source.readInt();
- ctag = source.readString();
url = source.readString();
username = source.readString();
password = source.readString();
+ iv = source.createByteArray();
}
public long getId() {
@@ -95,22 +86,6 @@ public final class CaldavAccount implements Parcelable {
this.name = name;
}
- public int getColor() {
- return color;
- }
-
- public void setColor(int color) {
- this.color = color;
- }
-
- public String getCtag() {
- return ctag;
- }
-
- public void setCtag(String ctag) {
- this.ctag = ctag;
- }
-
public String getUrl() {
return url;
}
@@ -135,49 +110,25 @@ public final class CaldavAccount implements Parcelable {
this.password = password;
}
- @Override
- public int describeContents() {
- return 0;
+ public byte[] getIv() {
+ return iv;
}
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(id);
- dest.writeString(uuid);
- dest.writeString(name);
- dest.writeInt(color);
- dest.writeString(ctag);
- dest.writeString(url);
- dest.writeString(username);
- dest.writeString(password);
+ public void setIv(byte[] iv) {
+ this.iv = iv;
}
@Override
public String toString() {
- return "CaldavAccount{"
- + "id="
- + id
- + ", uuid='"
- + uuid
- + '\''
- + ", name='"
- + name
- + '\''
- + ", color="
- + color
- + ", ctag='"
- + ctag
- + '\''
- + ", url='"
- + url
- + '\''
- + ", username='"
- + username
- + '\''
- + ", password='"
- + (TextUtils.isEmpty(password) ? "null" : "******")
- + '\''
- + '}';
+ return "CaldavAccount{" +
+ "id=" + id +
+ ", uuid='" + uuid + '\'' +
+ ", name='" + name + '\'' +
+ ", url='" + url + '\'' +
+ ", username='" + username + '\'' +
+ ", password='" + password + '\'' +
+ ", iv=" + Arrays.toString(iv) +
+ '}';
}
@Override
@@ -194,25 +145,22 @@ public final class CaldavAccount implements Parcelable {
if (id != that.id) {
return false;
}
- if (color != that.color) {
- return false;
- }
if (uuid != null ? !uuid.equals(that.uuid) : that.uuid != null) {
return false;
}
if (name != null ? !name.equals(that.name) : that.name != null) {
return false;
}
- if (ctag != null ? !ctag.equals(that.ctag) : that.ctag != null) {
- return false;
- }
if (url != null ? !url.equals(that.url) : that.url != null) {
return false;
}
if (username != null ? !username.equals(that.username) : that.username != null) {
return false;
}
- return password != null ? password.equals(that.password) : that.password == null;
+ if (password != null ? !password.equals(that.password) : that.password != null) {
+ return false;
+ }
+ return Arrays.equals(iv, that.iv);
}
@Override
@@ -220,11 +168,26 @@ public final class CaldavAccount implements Parcelable {
int result = (int) (id ^ (id >>> 32));
result = 31 * result + (uuid != null ? uuid.hashCode() : 0);
result = 31 * result + (name != null ? name.hashCode() : 0);
- result = 31 * result + color;
- result = 31 * result + (ctag != null ? ctag.hashCode() : 0);
result = 31 * result + (url != null ? url.hashCode() : 0);
result = 31 * result + (username != null ? username.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
+ result = 31 * result + Arrays.hashCode(iv);
return result;
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(id);
+ dest.writeString(uuid);
+ dest.writeString(name);
+ dest.writeString(url);
+ dest.writeString(username);
+ dest.writeString(password);
+ dest.writeByteArray(iv);
+ }
}
diff --git a/app/src/main/java/org/tasks/data/CaldavCalendar.java b/app/src/main/java/org/tasks/data/CaldavCalendar.java
new file mode 100644
index 000000000..2d741c577
--- /dev/null
+++ b/app/src/main/java/org/tasks/data/CaldavCalendar.java
@@ -0,0 +1,197 @@
+package org.tasks.data;
+
+import static com.todoroo.astrid.data.Task.NO_UUID;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.Ignore;
+import android.arch.persistence.room.PrimaryKey;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+@Entity(tableName = "caldav_calendar")
+public final class CaldavCalendar implements Parcelable {
+
+ public static Parcelable.Creator CREATOR =
+ new Parcelable.Creator() {
+ @Override
+ public CaldavCalendar createFromParcel(Parcel source) {
+ return new CaldavCalendar(source);
+ }
+
+ @Override
+ public CaldavCalendar[] newArray(int size) {
+ return new CaldavCalendar[size];
+ }
+ };
+
+ @PrimaryKey(autoGenerate = true)
+ @ColumnInfo(name = "_id")
+ private long id;
+
+ @ColumnInfo(name = "account")
+ private String account = NO_UUID;
+
+ @ColumnInfo(name = "uuid")
+ private String uuid = NO_UUID;
+
+ @ColumnInfo(name = "name")
+ private String name = "";
+
+ @ColumnInfo(name = "color")
+ private int color = -1;
+
+ @ColumnInfo(name = "ctag")
+ private String ctag;
+
+ @ColumnInfo(name = "url")
+ private String url = "";
+
+ public CaldavCalendar() {}
+
+ @Ignore
+ public CaldavCalendar(String name, String uuid) {
+ this.name = name;
+ this.uuid = uuid;
+ }
+
+ @Ignore
+ public CaldavCalendar(Parcel source) {
+ id = source.readLong();
+ account = source.readString();
+ uuid = source.readString();
+ name = source.readString();
+ color = source.readInt();
+ ctag = source.readString();
+ url = source.readString();
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getAccount() {
+ return account;
+ }
+
+ public void setAccount(String account) {
+ this.account = account;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getColor() {
+ return color;
+ }
+
+ public void setColor(int color) {
+ this.color = color;
+ }
+
+ public String getCtag() {
+ return ctag;
+ }
+
+ public void setCtag(String ctag) {
+ this.ctag = ctag;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(id);
+ dest.writeString(account);
+ dest.writeString(uuid);
+ dest.writeString(name);
+ dest.writeInt(color);
+ dest.writeString(ctag);
+ dest.writeString(url);
+ }
+
+ @Override
+ public String toString() {
+ return "CaldavCalendar{" +
+ "id=" + id +
+ ", account='" + account + '\'' +
+ ", uuid='" + uuid + '\'' +
+ ", name='" + name + '\'' +
+ ", color=" + color +
+ ", ctag='" + ctag + '\'' +
+ ", url='" + url + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CaldavCalendar)) {
+ return false;
+ }
+
+ CaldavCalendar that = (CaldavCalendar) o;
+
+ if (id != that.id) {
+ return false;
+ }
+ if (color != that.color) {
+ return false;
+ }
+ if (account != null ? !account.equals(that.account) : that.account != null) {
+ return false;
+ }
+ if (uuid != null ? !uuid.equals(that.uuid) : that.uuid != null) {
+ return false;
+ }
+ if (name != null ? !name.equals(that.name) : that.name != null) {
+ return false;
+ }
+ if (ctag != null ? !ctag.equals(that.ctag) : that.ctag != null) {
+ return false;
+ }
+ return url != null ? url.equals(that.url) : that.url == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (id ^ (id >>> 32));
+ result = 31 * result + (account != null ? account.hashCode() : 0);
+ result = 31 * result + (uuid != null ? uuid.hashCode() : 0);
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ result = 31 * result + color;
+ result = 31 * result + (ctag != null ? ctag.hashCode() : 0);
+ result = 31 * result + (url != null ? url.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/app/src/main/java/org/tasks/data/CaldavDao.java b/app/src/main/java/org/tasks/data/CaldavDao.java
index a6c55bbf1..3825b063c 100644
--- a/app/src/main/java/org/tasks/data/CaldavDao.java
+++ b/app/src/main/java/org/tasks/data/CaldavDao.java
@@ -10,11 +10,11 @@ import java.util.List;
@Dao
public interface CaldavDao {
- @Query("SELECT * FROM caldav_account WHERE uuid = :uuid LIMIT 1")
- CaldavAccount getByUuid(String uuid);
+ @Query("SELECT * FROM caldav_calendar WHERE uuid = :uuid LIMIT 1")
+ CaldavCalendar getCalendarByUuid(String uuid);
@Query("SELECT * FROM caldav_account ORDER BY UPPER(name) ASC")
- List getAllOrderedByName();
+ List getAccounts();
@Insert
long insert(CaldavAccount caldavAccount);
@@ -22,6 +22,18 @@ public interface CaldavDao {
@Update
void update(CaldavAccount caldavAccount);
+ @Delete
+ void delete(CaldavAccount caldavAccount);
+
+ @Insert
+ long insert(CaldavCalendar caldavCalendar);
+
+ @Update
+ void update(CaldavCalendar caldavCalendar);
+
+ @Delete
+ void delete(CaldavCalendar caldavCalendar);
+
@Insert
long insert(CaldavTask caldavTask);
@@ -31,14 +43,14 @@ public interface CaldavDao {
@Delete
void delete(CaldavTask caldavTask);
- @Query("SELECT * FROM caldav_tasks WHERE task = :taskId AND deleted > 0 AND account = :account")
- List getDeleted(long taskId, String account);
+ @Query("SELECT * FROM caldav_tasks WHERE task = :taskId AND deleted > 0 AND calendar = :calendar")
+ List getDeleted(long taskId, String calendar);
@Query("SELECT * FROM caldav_tasks WHERE task = :taskId AND deleted = 0 LIMIT 1")
CaldavTask getTask(long taskId);
- @Query("SELECT * FROM caldav_tasks WHERE account = :account AND object = :object LIMIT 1")
- CaldavTask getTask(String account, String object);
+ @Query("SELECT * FROM caldav_tasks WHERE calendar = :calendar AND object = :object LIMIT 1")
+ CaldavTask getTask(String calendar, String object);
@Query("DELETE FROM caldav_tasks WHERE task = :taskId")
void deleteById(long taskId);
@@ -46,24 +58,36 @@ public interface CaldavDao {
@Query("SELECT * FROM caldav_tasks WHERE task = :taskId")
List getTasks(long taskId);
- @Query("SELECT * FROM caldav_account")
- List getAccounts();
+ @Query("SELECT task FROM caldav_tasks WHERE calendar = :calendar")
+ List getTasksByCalendar(String calendar);
- @Delete
- void delete(CaldavAccount caldavAccount);
+ @Query("SELECT * FROM caldav_calendar")
+ List getCalendars();
+
+ @Query("SELECT * FROM caldav_calendar WHERE account = :account")
+ List getCalendarsByAccount(String account);
+
+ @Query("SELECT * FROM caldav_calendar WHERE uuid = :uuid LIMIT 1")
+ CaldavCalendar getCalendar(String uuid);
+
+ @Query("DELETE FROM caldav_calendar WHERE account = :account")
+ void deleteCalendarsForAccount(String account);
+
+ @Query("DELETE FROM caldav_tasks WHERE calendar = :calendar")
+ void deleteTasksForCalendar(String calendar);
- @Query("SELECT * FROM caldav_account WHERE uuid = :uuid LIMIT 1")
- CaldavAccount getAccount(String uuid);
+ @Query("SELECT object FROM caldav_tasks WHERE calendar = :calendar")
+ List getObjects(String calendar);
- @Query("DELETE FROM caldav_tasks WHERE account = :uuid")
- void deleteTasksForAccount(String uuid);
+ @Query("SELECT task FROM caldav_tasks WHERE calendar = :calendar AND object IN (:objects)")
+ List getTasks(String calendar, List objects);
- @Query("SELECT object FROM caldav_tasks WHERE account = :account")
- List getObjects(String account);
+ @Query("DELETE FROM caldav_tasks WHERE calendar = :calendar AND object IN (:objects)")
+ void deleteObjects(String calendar, List objects);
- @Query("SELECT task FROM caldav_tasks WHERE account = :account AND object IN (:objects)")
- List getTasks(String account, List objects);
+ @Query("SELECT * FROM caldav_calendar WHERE account = :account AND url NOT IN (:urls)")
+ List findDeletedCalendars(String account, List urls);
- @Query("DELETE FROM caldav_tasks WHERE account = :account AND object IN (:objects)")
- void deleteObjects(String account, List objects);
+ @Query("SELECT * FROM caldav_calendar WHERE account = :account AND url = :url LIMIT 1")
+ CaldavCalendar getCalendarByUrl(String account, String url);
}
diff --git a/app/src/main/java/org/tasks/data/CaldavTask.java b/app/src/main/java/org/tasks/data/CaldavTask.java
index 26e6363e1..afafe5f65 100644
--- a/app/src/main/java/org/tasks/data/CaldavTask.java
+++ b/app/src/main/java/org/tasks/data/CaldavTask.java
@@ -20,8 +20,8 @@ public class CaldavTask {
@ColumnInfo(name = "task")
private long task;
- @ColumnInfo(name = "account")
- private String account;
+ @ColumnInfo(name = "calendar")
+ private String calendar;
@ColumnInfo(name = "object")
private String object;
@@ -49,9 +49,9 @@ public class CaldavTask {
}
@Ignore
- public CaldavTask(long task, String account, String remoteId, String object) {
+ public CaldavTask(long task, String calendar, String remoteId, String object) {
this.task = task;
- this.account = account;
+ this.calendar = calendar;
this.remoteId = remoteId;
this.object = object;
}
@@ -72,12 +72,12 @@ public class CaldavTask {
this.task = task;
}
- public String getAccount() {
- return account;
+ public String getCalendar() {
+ return calendar;
}
- public void setAccount(String account) {
- this.account = account;
+ public void setCalendar(String calendar) {
+ this.calendar = calendar;
}
public String getObject() {
@@ -135,8 +135,8 @@ public class CaldavTask {
+ id
+ ", task="
+ task
- + ", account='"
- + account
+ + ", calendar='"
+ + calendar
+ '\''
+ ", object='"
+ object
diff --git a/app/src/main/java/org/tasks/filters/FilterProvider.java b/app/src/main/java/org/tasks/filters/FilterProvider.java
index c58861c5f..c29f57999 100644
--- a/app/src/main/java/org/tasks/filters/FilterProvider.java
+++ b/app/src/main/java/org/tasks/filters/FilterProvider.java
@@ -1,5 +1,6 @@
package org.tasks.filters;
+import android.support.v4.util.Pair;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.BuiltInFilterExposer;
import com.todoroo.astrid.core.CustomFilterExposer;
@@ -8,6 +9,7 @@ import com.todoroo.astrid.tags.TagFilterExposer;
import com.todoroo.astrid.timers.TimerFilterExposer;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import javax.inject.Inject;
import org.tasks.caldav.CaldavFilterExposer;
@@ -56,7 +58,7 @@ public class FilterProvider {
return gtasksFilterExposer.getFilters();
}
- public List getCaldavFilters() {
+ public List>> getCaldavFilters() {
return caldavFilterExposer.getFilters();
}
}
diff --git a/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java b/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java
index d76478a97..7727213c2 100644
--- a/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java
+++ b/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java
@@ -32,7 +32,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
-import org.tasks.LocalBroadcastManager;
import org.tasks.R;
import org.tasks.analytics.Tracker;
import org.tasks.data.GoogleTask;
@@ -43,7 +42,6 @@ import org.tasks.injection.ForApplication;
import org.tasks.notifications.NotificationManager;
import org.tasks.preferences.DefaultFilterProvider;
import org.tasks.preferences.Preferences;
-import org.tasks.sync.RecordSyncStatusCallback;
import org.tasks.time.DateTime;
import timber.log.Timber;
@@ -53,7 +51,6 @@ public class GoogleTaskSynchronizer {
private final Context context;
private final GtasksPreferenceService gtasksPreferenceService;
- private final LocalBroadcastManager localBroadcastManager;
private final GoogleTaskListDao googleTaskListDao;
private final GtasksSyncService gtasksSyncService;
private final GtasksListService gtasksListService;
@@ -71,7 +68,6 @@ public class GoogleTaskSynchronizer {
public GoogleTaskSynchronizer(
@ForApplication Context context,
GtasksPreferenceService gtasksPreferenceService,
- LocalBroadcastManager localBroadcastManager,
GoogleTaskListDao googleTaskListDao,
GtasksSyncService gtasksSyncService,
GtasksListService gtasksListService,
@@ -86,7 +82,6 @@ public class GoogleTaskSynchronizer {
DefaultFilterProvider defaultFilterProvider) {
this.context = context;
this.gtasksPreferenceService = gtasksPreferenceService;
- this.localBroadcastManager = localBroadcastManager;
this.googleTaskListDao = googleTaskListDao;
this.gtasksSyncService = gtasksSyncService;
this.gtasksListService = gtasksListService;
@@ -122,10 +117,7 @@ public class GoogleTaskSynchronizer {
return;
}
Timber.d("%s: start sync", account);
- RecordSyncStatusCallback callback =
- new RecordSyncStatusCallback(gtasksPreferenceService, localBroadcastManager);
try {
- callback.started();
synchronize();
gtasksPreferenceService.recordSuccessfulSync();
} catch (UserRecoverableAuthIOException e) {
@@ -136,7 +128,6 @@ public class GoogleTaskSynchronizer {
} catch (Exception e) {
tracker.reportException(e);
} finally {
- callback.finished();
Timber.d("%s: end sync", account);
}
}
diff --git a/app/src/main/java/org/tasks/injection/ActivityComponent.java b/app/src/main/java/org/tasks/injection/ActivityComponent.java
index 1479ff49a..5a871e661 100644
--- a/app/src/main/java/org/tasks/injection/ActivityComponent.java
+++ b/app/src/main/java/org/tasks/injection/ActivityComponent.java
@@ -23,7 +23,8 @@ import org.tasks.activities.GoogleTaskListSettingsActivity;
import org.tasks.activities.TagSettingsActivity;
import org.tasks.activities.TimePickerActivity;
import org.tasks.billing.PurchaseActivity;
-import org.tasks.caldav.CaldavSettingsActivity;
+import org.tasks.caldav.CaldavAccountSettingsActivity;
+import org.tasks.caldav.CaldavCalendarSettingsActivity;
import org.tasks.dashclock.DashClockSettings;
import org.tasks.files.FileExplore;
import org.tasks.files.MyFilePickerActivity;
@@ -127,11 +128,13 @@ public interface ActivityComponent {
void inject(GoogleTaskListSettingsActivity googleTaskListSettingsActivity);
- void inject(CaldavSettingsActivity caldavSettingsActivity);
+ void inject(CaldavCalendarSettingsActivity caldavCalendarSettingsActivity);
void inject(TaskerCreateTaskActivity taskerCreateTaskActivity);
void inject(TaskListViewModel taskListViewModel);
void inject(PurchaseActivity purchaseActivity);
+
+ void inject(CaldavAccountSettingsActivity caldavAccountSettingsActivity);
}
diff --git a/app/src/main/java/org/tasks/jobs/JobCreator.java b/app/src/main/java/org/tasks/jobs/JobCreator.java
index 590f3c1e0..aacd9a674 100644
--- a/app/src/main/java/org/tasks/jobs/JobCreator.java
+++ b/app/src/main/java/org/tasks/jobs/JobCreator.java
@@ -65,7 +65,8 @@ public class JobCreator implements com.evernote.android.job.JobCreator {
return new NotificationJob(preferences, notifier, notificationQueue);
case TAG_SYNC:
case TAG_BACKGROUND_SYNC:
- return new SyncJob(caldavSynchronizer, googleTaskSynchronizer);
+ return new SyncJob(
+ caldavSynchronizer, googleTaskSynchronizer, localBroadcastManager, preferences);
case TAG_BACKUP:
return new BackupJob(context, tasksJsonExporter, preferences);
case TAG_MIDNIGHT_REFRESH:
diff --git a/app/src/main/java/org/tasks/jobs/JobManager.java b/app/src/main/java/org/tasks/jobs/JobManager.java
index d0a12b058..bb02d567c 100644
--- a/app/src/main/java/org/tasks/jobs/JobManager.java
+++ b/app/src/main/java/org/tasks/jobs/JobManager.java
@@ -11,6 +11,7 @@ import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import javax.inject.Inject;
import org.tasks.R;
+import org.tasks.data.CaldavDao;
import org.tasks.injection.ApplicationScope;
import org.tasks.preferences.Preferences;
import timber.log.Timber;
@@ -28,11 +29,16 @@ public class JobManager {
private final com.evernote.android.job.JobManager jobManager;
private final Preferences preferences;
+ private final CaldavDao caldavDao;
@Inject
- public JobManager(com.evernote.android.job.JobManager jobManager, Preferences preferences) {
+ public JobManager(
+ com.evernote.android.job.JobManager jobManager,
+ Preferences preferences,
+ CaldavDao caldavDao) {
this.jobManager = jobManager;
this.preferences = preferences;
+ this.caldavDao = caldavDao;
}
public void scheduleNotification(long time) {
@@ -76,7 +82,7 @@ public class JobManager {
boolean accountsPresent =
forceAccountPresent == null
? (preferences.getBoolean(R.string.sync_gtasks, false)
- || preferences.getBoolean(R.string.p_sync_caldav, false))
+ || caldavDao.getAccounts().size() > 0)
: forceAccountPresent;
boolean onlyOnWifi =
forceOnlyOnUnmetered == null
diff --git a/app/src/main/java/org/tasks/jobs/SyncJob.java b/app/src/main/java/org/tasks/jobs/SyncJob.java
index 3d0abf9f6..7fd7abab0 100644
--- a/app/src/main/java/org/tasks/jobs/SyncJob.java
+++ b/app/src/main/java/org/tasks/jobs/SyncJob.java
@@ -2,24 +2,52 @@ package org.tasks.jobs;
import android.support.annotation.NonNull;
import com.evernote.android.job.Job;
+import org.tasks.LocalBroadcastManager;
import org.tasks.caldav.CaldavSynchronizer;
import org.tasks.gtasks.GoogleTaskSynchronizer;
+import org.tasks.preferences.Preferences;
+import timber.log.Timber;
public class SyncJob extends Job {
+ private static final Object LOCK = new Object();
+
private final CaldavSynchronizer caldavSynchronizer;
private final GoogleTaskSynchronizer googleTaskSynchronizer;
+ private final LocalBroadcastManager localBroadcastManager;
+ private final Preferences preferences;
- SyncJob(CaldavSynchronizer caldavSynchronizer, GoogleTaskSynchronizer googleTaskSynchronizer) {
+ SyncJob(
+ CaldavSynchronizer caldavSynchronizer,
+ GoogleTaskSynchronizer googleTaskSynchronizer,
+ LocalBroadcastManager localBroadcastManager,
+ Preferences preferences) {
this.caldavSynchronizer = caldavSynchronizer;
this.googleTaskSynchronizer = googleTaskSynchronizer;
+ this.localBroadcastManager = localBroadcastManager;
+ this.preferences = preferences;
}
@NonNull
@Override
protected Result onRunJob(@NonNull Params params) {
- caldavSynchronizer.sync();
- googleTaskSynchronizer.sync();
+ synchronized (LOCK) {
+ if (preferences.isSyncOngoing()) {
+ return Result.SUCCESS;
+ }
+ }
+
+ preferences.setSyncOngoing(true);
+ localBroadcastManager.broadcastRefresh();
+ try {
+ caldavSynchronizer.sync();
+ googleTaskSynchronizer.sync();
+ } catch (Exception e) {
+ Timber.e(e);
+ } finally {
+ preferences.setSyncOngoing(false);
+ localBroadcastManager.broadcastRefresh();
+ }
return Result.SUCCESS;
}
}
diff --git a/app/src/main/java/org/tasks/preferences/Preferences.java b/app/src/main/java/org/tasks/preferences/Preferences.java
index 2a49d4432..a4b0dd156 100644
--- a/app/src/main/java/org/tasks/preferences/Preferences.java
+++ b/app/src/main/java/org/tasks/preferences/Preferences.java
@@ -445,4 +445,12 @@ public class Preferences {
public boolean usePersistentReminders() {
return getBoolean(R.string.p_rmd_persistent, true);
}
+
+ public void setSyncOngoing(boolean value) {
+ setBoolean(R.string.p_sync_ongoing, value);
+ }
+
+ public boolean isSyncOngoing() {
+ return getBoolean(R.string.p_sync_ongoing, false);
+ }
}
diff --git a/app/src/main/java/org/tasks/sync/RecordSyncStatusCallback.java b/app/src/main/java/org/tasks/sync/RecordSyncStatusCallback.java
deleted file mode 100644
index 4348ce9f2..000000000
--- a/app/src/main/java/org/tasks/sync/RecordSyncStatusCallback.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.tasks.sync;
-
-import com.todoroo.astrid.gtasks.GtasksPreferenceService;
-import com.todoroo.astrid.sync.SyncResultCallback;
-import org.tasks.LocalBroadcastManager;
-
-public class RecordSyncStatusCallback implements SyncResultCallback {
-
- private final GtasksPreferenceService gtasksPreferenceService;
- private final LocalBroadcastManager localBroadcastManager;
-
- public RecordSyncStatusCallback(
- GtasksPreferenceService gtasksPreferenceService,
- LocalBroadcastManager localBroadcastManager) {
- this.gtasksPreferenceService = gtasksPreferenceService;
- this.localBroadcastManager = localBroadcastManager;
- }
-
- @Override
- public void started() {
- gtasksPreferenceService.recordSyncStart();
- localBroadcastManager.broadcastRefresh();
- }
-
- @Override
- public void finished() {
- gtasksPreferenceService.stopOngoing();
- localBroadcastManager.broadcastRefresh();
- }
-}
diff --git a/app/src/main/java/org/tasks/sync/SyncAdapters.java b/app/src/main/java/org/tasks/sync/SyncAdapters.java
index 737e7d751..76ab6ce8e 100644
--- a/app/src/main/java/org/tasks/sync/SyncAdapters.java
+++ b/app/src/main/java/org/tasks/sync/SyncAdapters.java
@@ -2,25 +2,22 @@ package org.tasks.sync;
import android.app.Activity;
import javax.inject.Inject;
-import org.tasks.R;
+import org.tasks.data.CaldavDao;
import org.tasks.gtasks.GtaskSyncAdapterHelper;
import org.tasks.jobs.JobManager;
-import org.tasks.preferences.Preferences;
public class SyncAdapters {
private final GtaskSyncAdapterHelper gtaskSyncAdapterHelper;
- private final Preferences preferences;
private final JobManager jobManager;
+ private final CaldavDao caldavDao;
@Inject
public SyncAdapters(
- GtaskSyncAdapterHelper gtaskSyncAdapterHelper,
- Preferences preferences,
- JobManager jobManager) {
+ GtaskSyncAdapterHelper gtaskSyncAdapterHelper, JobManager jobManager, CaldavDao caldavDao) {
this.gtaskSyncAdapterHelper = gtaskSyncAdapterHelper;
- this.preferences = preferences;
this.jobManager = jobManager;
+ this.caldavDao = caldavDao;
}
public boolean syncNow() {
@@ -40,7 +37,7 @@ public class SyncAdapters {
}
public boolean isCaldavSyncEnabled() {
- return preferences.getBoolean(R.string.p_sync_caldav, false);
+ return caldavDao.getAccounts().size() > 0;
}
public void checkPlayServices(Activity activity) {
diff --git a/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java b/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java
index 7e6b3a62d..4164146fa 100644
--- a/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java
+++ b/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java
@@ -10,6 +10,8 @@ import static org.tasks.PermissionUtil.verifyPermissions;
import android.content.Intent;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceCategory;
import android.support.annotation.NonNull;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
@@ -18,6 +20,9 @@ import javax.inject.Inject;
import org.tasks.R;
import org.tasks.analytics.Tracker;
import org.tasks.analytics.Tracking;
+import org.tasks.caldav.CaldavAccountSettingsActivity;
+import org.tasks.data.CaldavAccount;
+import org.tasks.data.CaldavDao;
import org.tasks.data.GoogleTaskDao;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.gtasks.GoogleAccountManager;
@@ -34,6 +39,7 @@ import org.tasks.preferences.Preferences;
public class SynchronizationPreferences extends InjectingPreferenceActivity {
private static final int REQUEST_LOGIN = 0;
+ private static final int REQUEST_CALDAV_SETTINGS = 101;
@Inject GtasksPreferenceService gtasksPreferenceService;
@Inject ActivityPermissionRequestor permissionRequestor;
@@ -47,6 +53,7 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
@Inject GoogleAccountManager googleAccountManager;
@Inject Preferences preferences;
@Inject JobManager jobManager;
+ @Inject CaldavDao caldavDao;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -54,14 +61,25 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
addPreferencesFromResource(R.xml.preferences_synchronization);
- CheckBoxPreference caldavEnabled =
- (CheckBoxPreference) findPreference(getString(R.string.p_sync_caldav));
- caldavEnabled.setChecked(syncAdapters.isCaldavSyncEnabled());
- caldavEnabled.setOnPreferenceChangeListener(
- (preference, newValue) -> {
- jobManager.updateBackgroundSync(((boolean) newValue), null, null);
- return true;
+ PreferenceCategory caldavPreferences = (PreferenceCategory) findPreference(getString(R.string.CalDAV));
+ for (CaldavAccount caldavAccount : caldavDao.getAccounts()) {
+ Preference accountPreferences = new Preference(this);
+ accountPreferences.setTitle(caldavAccount.getName());
+ accountPreferences.setOnPreferenceClickListener(preference -> {
+ Intent intent = new Intent(this, CaldavAccountSettingsActivity.class);
+ intent.putExtra(CaldavAccountSettingsActivity.EXTRA_CALDAV_DATA, caldavAccount);
+ startActivityForResult(intent, REQUEST_CALDAV_SETTINGS);
+ return false;
+ });
+ caldavPreferences.addPreference(accountPreferences);
+ }
+ findPreference(getString(R.string.add_account)).setOnPreferenceClickListener(
+ preference -> {
+ startActivityForResult(new Intent(this, CaldavAccountSettingsActivity.class),
+ REQUEST_CALDAV_SETTINGS);
+ return false;
});
+
final CheckBoxPreference gtaskPreference =
(CheckBoxPreference) findPreference(getString(R.string.sync_gtasks));
gtaskPreference.setChecked(syncAdapters.isGoogleTaskSyncEnabled());
@@ -77,7 +95,6 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
} else {
jobManager.updateBackgroundSync();
tracker.reportEvent(Tracking.Events.GTASK_DISABLED);
- gtasksPreferenceService.stopOngoing();
return true;
}
});
@@ -143,6 +160,13 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
jobManager.updateBackgroundSync();
}
((CheckBoxPreference) findPreference(getString(R.string.sync_gtasks))).setChecked(enabled);
+ } else if (requestCode == REQUEST_CALDAV_SETTINGS) {
+ if (resultCode == RESULT_OK) {
+ jobManager.updateBackgroundSync();
+ Intent intent = getIntent();
+ finish();
+ startActivity(intent);
+ }
} else {
super.onActivityResult(requestCode, resultCode, data);
}
diff --git a/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.java b/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.java
index 8167ea710..0fe6e52b8 100644
--- a/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.java
+++ b/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.java
@@ -33,7 +33,6 @@ public class NavigationDrawerFragment extends InjectingFragment {
public static final int REQUEST_NEW_LIST = 4;
public static final int ACTIVITY_REQUEST_NEW_FILTER = 5;
public static final int REQUEST_NEW_GTASK_LIST = 6;
- public static final int REQUEST_NEW_CALDAV_ACCOUNT = 7;
private static final String TOKEN_LAST_SELECTED = "lastSelected"; // $NON-NLS-1$
private final RefreshReceiver refreshReceiver = new RefreshReceiver();
@Inject LocalBroadcastManager localBroadcastManager;
@@ -77,8 +76,7 @@ public class NavigationDrawerFragment extends InjectingFragment {
}
} else if (requestCode == REQUEST_NEW_LIST
|| requestCode == ACTIVITY_REQUEST_NEW_FILTER
- || requestCode == REQUEST_NEW_GTASK_LIST
- || requestCode == REQUEST_NEW_CALDAV_ACCOUNT) {
+ || requestCode == REQUEST_NEW_GTASK_LIST) {
if (resultCode == RESULT_OK && data != null) {
Filter newList = data.getParcelableExtra(TaskListActivity.OPEN_FILTER);
if (newList != null) {
diff --git a/app/src/main/java/org/tasks/ui/RemoteListFragment.java b/app/src/main/java/org/tasks/ui/RemoteListFragment.java
index 4053d5a36..dc190055d 100644
--- a/app/src/main/java/org/tasks/ui/RemoteListFragment.java
+++ b/app/src/main/java/org/tasks/ui/RemoteListFragment.java
@@ -20,6 +20,7 @@ import com.todoroo.astrid.gtasks.GtasksListService;
import com.todoroo.astrid.helper.UUIDHelper;
import javax.inject.Inject;
import org.tasks.R;
+import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavDao;
import org.tasks.data.CaldavTask;
import org.tasks.data.GoogleTask;
@@ -60,7 +61,7 @@ public class RemoteListFragment extends TaskEditControlFragment {
originalList =
new GtasksFilter(gtasksListService.getList(task.getTransitory(GoogleTask.KEY)));
} else if (task.hasTransitory(CaldavTask.KEY)) {
- originalList = new CaldavFilter(caldavDao.getByUuid(task.getTransitory(CaldavTask.KEY)));
+ originalList = new CaldavFilter(caldavDao.getCalendarByUuid(task.getTransitory(CaldavTask.KEY)));
} else {
originalList = defaultFilterProvider.getDefaultRemoteList();
}
@@ -70,7 +71,10 @@ public class RemoteListFragment extends TaskEditControlFragment {
if (googleTask != null) {
originalList = new GtasksFilter(gtasksListService.getList(googleTask.getListId()));
} else if (caldavTask != null) {
- originalList = new CaldavFilter(caldavDao.getByUuid(caldavTask.getAccount()));
+ CaldavCalendar calendarByUuid = caldavDao.getCalendarByUuid(caldavTask.getCalendar());
+ if (calendarByUuid != null) {
+ originalList = new CaldavFilter(calendarByUuid);
+ }
}
}
@@ -125,7 +129,7 @@ public class RemoteListFragment extends TaskEditControlFragment {
CaldavTask caldavTask = caldavDao.getTask(task.getId());
if (caldavTask != null
&& selectedList instanceof CaldavFilter
- && caldavTask.getAccount().equals(((CaldavFilter) selectedList).getUuid())) {
+ && caldavTask.getCalendar().equals(((CaldavFilter) selectedList).getUuid())) {
return;
}
task.putTransitory(SyncFlags.FORCE_SYNC, true);
diff --git a/app/src/main/res/layout/activity_caldav_settings.xml b/app/src/main/res/layout/activity_caldav_account_settings.xml
similarity index 85%
rename from app/src/main/res/layout/activity_caldav_settings.xml
rename to app/src/main/res/layout/activity_caldav_account_settings.xml
index b30ac0106..fff8470ba 100644
--- a/app/src/main/res/layout/activity_caldav_settings.xml
+++ b/app/src/main/res/layout/activity_caldav_account_settings.xml
@@ -64,16 +64,6 @@
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_caldav_calendar_settings.xml b/app/src/main/res/layout/activity_caldav_calendar_settings.xml
new file mode 100644
index 000000000..591a56925
--- /dev/null
+++ b/app/src/main/res/layout/activity_caldav_calendar_settings.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/menu_caldav_account_settings.xml b/app/src/main/res/menu/menu_caldav_account_settings.xml
new file mode 100644
index 000000000..2f39c8125
--- /dev/null
+++ b/app/src/main/res/menu/menu_caldav_account_settings.xml
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_caldav_list_fragment.xml b/app/src/main/res/menu/menu_caldav_list_fragment.xml
index a36bef66a..e9473661c 100644
--- a/app/src/main/res/menu/menu_caldav_list_fragment.xml
+++ b/app/src/main/res/menu/menu_caldav_list_fragment.xml
@@ -3,6 +3,6 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
\ No newline at end of file
diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml
index 44d3ae7f5..d3d294eb6 100644
--- a/app/src/main/res/values-bg-rBG/strings.xml
+++ b/app/src/main/res/values-bg-rBG/strings.xml
@@ -446,7 +446,6 @@
Потребител
Парола
Грешка при добавяне на акаунт
- Настройки на акаунта
Управление на уведомления
Управление на оптимизациите на батерията
Оптимизациите на батерията могат да забавят уведомленията
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index fc9c93c85..4afe1a5d2 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -432,7 +432,6 @@
Benutzer
Passwort
Fehler beim Hinzufügen des Accounts
- Account Einstellungen
Verwalte Benachrichtigungen
Energiespar-Optionen verwalten
Energiesparfunktionen verwalten
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index d63dca642..e1e9865d9 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -440,7 +440,6 @@
Usuario
Contraseña
Error añadiendo cuenta
- Configuración de cuenta
Gestionar notificaciones
Optimizaciones en gestión de batería
Optimizaciones de batería pueden retrasar notificaciones
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 7d517f134..92f18dcca 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -427,7 +427,6 @@
Mot de passe
Adresse URL
Erreur dans l\'ajout du compte
- Paramètres du compte
Gérer les notifications
Gérer les optimisations de la batterie
Optimisations de la batterie avec des notifications par délais
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 95970263f..5a62e3117 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -447,7 +447,6 @@
Jelszó
Link
Felhasználói fiók hozzáadása sikertelen
- Felhasználói fiók beállítások
Értesítések kezelése
Akkumulátor optimalizálások kezelése
Az akkumulátor optimalizálási funkciók késleltethetik az értesítéseket
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index d52d06d51..462900d78 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -442,7 +442,6 @@
Aggiungi account
Utente
Aggiunta account fallita
- Impostazioni account
Gestione notifiche
Ottimizza consumo batteria
L\'ottimizzazione consumi può ritardare le notifiche
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index 71374522a..682f59ace 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -395,7 +395,6 @@
סיסמא
כתובת URL
שגיאה בהוספת חשבון
- הגדרות חשבון
נהל התראות
נהל חיסכון בסוללה
חיסכון בסוללה עלול לעכב התראות
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index c591adfd0..f07c54a08 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -444,7 +444,6 @@
ユーザー
パスワード
アカウントの追加時にエラーが発生しました
- アカウント設定
通知を管理
バッテリー最適化を管理
バッテリー最適化をすると通知が遅れることがあります
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 247c03663..8e8c01092 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -447,7 +447,6 @@
사용자
비밀번호
계정 추가 오류
- 계정 설정
알림 관리
배터리 최적화 관리
배터리 최적화 시 알림이 지연될 수 있습니다.
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index 6413d5eac..601e9cd0d 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -443,7 +443,6 @@
Vartotojas
Slaptažodis
Klaida kuriant paskyrą
- Paskyros nustatymai
Valdyti pranešimus
Valdyti baterijos optimizacijas
Baterijos optimizacijos gali uždelsti pranešimus
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index c63642e02..3fa69351b 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -433,7 +433,6 @@
Gebruiker
Wachtwoord
Fout bij account toevoegen
- Account instellingen
Beheer notificaties
Beheer batterij optimalisatie
Batterij optimalisatie kan meldingen vertragen
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index de0025586..41a1245ff 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -420,7 +420,6 @@
Użytkownik
Hasło
Błąd przy dodawaniu konta
- Ustawienia konta
Zarządzaj powiadamieniami
Zarządzaj zużyciem energii
Optymalizacja zużycia energii może opóźniać powiadomienia
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 1b1cbba04..5c8c81506 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -444,7 +444,6 @@
Пользователь
Пароль
Ошибка при добавлении учётной записи
- Настройки учётной записи
Управление уведомлениями
Управление расходом батареи
Оптимизация расхода может задерживать уведомления
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 829add14f..c496379b5 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -443,7 +443,6 @@
Užívateľ
Heslo
Chyba pridania účtu
- Nastavenie účtu
Spravovať upozornenia
Zníženie spotreby energie
Zníženie spotreby môže spôsobiť oneskorenie upozornení
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 3854cc410..ca510b1a6 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -449,7 +449,6 @@
Kullanıcı
Parola
Hesap eklenirken hata
- Hesap ayarları
Bildirimleri yönet
Pil optimizasyonlarını yönet
Pil optimizasyonları bildirimleri geciktirebilir
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 03c096f00..bef455cc2 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -444,7 +444,6 @@
密码
网址
添加帐号错误
- 帐号设置
管理通知
电池优化管理
电池优化可能会延迟通知
diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml
index 0696cb818..91f029eea 100644
--- a/app/src/main/res/values/keys.xml
+++ b/app/src/main/res/values/keys.xml
@@ -289,8 +289,7 @@
bundle_notifications
strict_mode
warned_play_services
- sync_caldav
background_sync_unmetered_only
purchases
-
+ sync_ongoing
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d3ff13fd5..343f2be3c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -603,7 +603,7 @@ File %1$s contained %2$s.\n\n
Uncategorized
Delete %s?
-
+ Remove %s?
Timers Active for %s!
@@ -830,7 +830,7 @@ File %1$s contained %2$s.\n\n
Password
URL
Error adding account
- Account settings
+ Calendar settings
Manage notifications
Manage battery optimizations
Battery optimizations may delay notifications
@@ -875,6 +875,9 @@ File %1$s contained %2$s.\n\n
Create task
List notification
Help
+ Principal not found
+ Home set not found
+ Could not find any calendars with VTODO support
Calendar not found
Connection failed
Only on unmetered connections
diff --git a/app/src/main/res/xml/preferences_synchronization.xml b/app/src/main/res/xml/preferences_synchronization.xml
index 7d098b1c0..615e37d49 100644
--- a/app/src/main/res/xml/preferences_synchronization.xml
+++ b/app/src/main/res/xml/preferences_synchronization.xml
@@ -22,11 +22,12 @@
-
-
+
+
diff --git a/app/src/release/res/xml/google_analytics.xml b/app/src/release/res/xml/google_analytics.xml
index 12c090e76..b891e7aa9 100644
--- a/app/src/release/res/xml/google_analytics.xml
+++ b/app/src/release/res/xml/google_analytics.xml
@@ -52,5 +52,5 @@
MiscellaneousPreferences
WidgetConfigActivity
- CaldavSettingsActivity
+ CaldavSettingsActivity
\ No newline at end of file