From bb0f99f35aa94dece3bce437e58e0dd2fbd2c89f Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Thu, 8 Mar 2018 15:46:48 -0600 Subject: [PATCH] Add CalDAVSettingsActivity --- .../com.todoroo.astrid.dao.Database/55.json | 12 +- .../org/tasks/gtasks/SyncAdapterHelper.java | 32 -- .../tasks/injection/ActivityComponent.java | 3 + .../injection/DialogFragmentComponent.java | 6 + .../java/org/tasks/sync/SyncAdapters.java | 42 ++ .../tasks/injection/ActivityComponent.java | 3 + .../injection/DialogFragmentComponent.java | 6 + .../java/org/tasks/sync/SyncAdapters.java | 42 ++ app/src/googleplay/AndroidManifest.xml | 11 +- .../tasks/injection/ActivityComponent.java | 3 + .../injection/DialogFragmentComponent.java | 6 + .../java/org/tasks/sync/SyncAdapters.java | 51 ++ .../res/xml/sync_adapter_gtask.xml} | 0 app/src/main/AndroidManifest.xml | 27 + .../astrid/activity/TaskListActivity.java | 8 +- .../astrid/activity/TaskListFragment.java | 8 +- .../todoroo/astrid/adapter/FilterAdapter.java | 9 + .../astrid/core/DefaultsPreferences.java | 8 +- .../java/com/todoroo/astrid/dao/TaskDao.java | 3 + .../main/java/org/tasks/caldav/Account.java | 65 +++ .../caldav/CalDAVAccountAuthenticator.java | 59 +++ .../CalDAVAccountAuthenticatorService.java | 25 + .../tasks/caldav/CalDAVSettingsActivity.java | 464 ++++++++++++++++++ .../tasks/caldav/CaldavAccountManager.java | 164 +++++++ .../org/tasks/caldav/CaldavListFragment.java | 42 +- .../org/tasks/caldav/DeleteAccountDialog.java | 93 ++++ .../org/tasks/caldav/RenameAccountDialog.java | 112 +++++ .../java/org/tasks/data/CaldavAccount.java | 28 +- .../main/java/org/tasks/data/CaldavDao.java | 16 +- .../tasks/filters/FilterCriteriaProvider.java | 10 +- .../TaskEditControlSetFragmentManager.java | 10 +- .../org/tasks/injection/ActivityModule.java | 6 +- .../tasks/ui/NavigationDrawerFragment.java | 4 +- .../res/layout/activity_caldav_settings.xml | 133 +++++ app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/keys.xml | 1 + app/src/main/res/values/strings.xml | 12 + .../main/res/xml/account_authenticator.xml | 6 + app/src/main/res/xml/sync_adapter_caldav.xml | 7 + 39 files changed, 1433 insertions(+), 105 deletions(-) delete mode 100644 app/src/amazon/java/org/tasks/gtasks/SyncAdapterHelper.java create mode 100644 app/src/amazon/java/org/tasks/sync/SyncAdapters.java create mode 100644 app/src/generic/java/org/tasks/sync/SyncAdapters.java create mode 100644 app/src/googleplay/java/org/tasks/sync/SyncAdapters.java rename app/src/{main/res/xml/syncadapter.xml => googleplay/res/xml/sync_adapter_gtask.xml} (100%) create mode 100644 app/src/main/java/org/tasks/caldav/Account.java create mode 100644 app/src/main/java/org/tasks/caldav/CalDAVAccountAuthenticator.java create mode 100644 app/src/main/java/org/tasks/caldav/CalDAVAccountAuthenticatorService.java create mode 100644 app/src/main/java/org/tasks/caldav/CalDAVSettingsActivity.java create mode 100644 app/src/main/java/org/tasks/caldav/CaldavAccountManager.java create mode 100644 app/src/main/java/org/tasks/caldav/DeleteAccountDialog.java create mode 100644 app/src/main/java/org/tasks/caldav/RenameAccountDialog.java create mode 100644 app/src/main/res/layout/activity_caldav_settings.xml create mode 100644 app/src/main/res/xml/account_authenticator.xml create mode 100644 app/src/main/res/xml/sync_adapter_caldav.xml diff --git a/app/schemas/com.todoroo.astrid.dao.Database/55.json b/app/schemas/com.todoroo.astrid.dao.Database/55.json index aa4af4b18..ff51ec570 100644 --- a/app/schemas/com.todoroo.astrid.dao.Database/55.json +++ b/app/schemas/com.todoroo.astrid.dao.Database/55.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 55, - "identityHash": "298da0475e0b181e5298ea1e05302000", + "identityHash": "13c156cbb4c26b323967670f04502f53", "entities": [ { "tableName": "notification", @@ -690,7 +690,7 @@ }, { "tableName": "caldav_account", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uuid` TEXT, `name` TEXT, `color` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `ctag` TEXT, `url` TEXT, `username` TEXT)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uuid` TEXT, `name` TEXT, `color` INTEGER NOT NULL, `ctag` TEXT, `url` TEXT, `username` TEXT)", "fields": [ { "fieldPath": "id", @@ -716,12 +716,6 @@ "affinity": "INTEGER", "notNull": true }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, { "fieldPath": "ctag", "columnName": "ctag", @@ -809,7 +803,7 @@ ], "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, \"298da0475e0b181e5298ea1e05302000\")" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"13c156cbb4c26b323967670f04502f53\")" ] } } \ No newline at end of file diff --git a/app/src/amazon/java/org/tasks/gtasks/SyncAdapterHelper.java b/app/src/amazon/java/org/tasks/gtasks/SyncAdapterHelper.java deleted file mode 100644 index 43af07826..000000000 --- a/app/src/amazon/java/org/tasks/gtasks/SyncAdapterHelper.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.tasks.gtasks; - -import com.todoroo.astrid.activity.TaskListFragment; - -import javax.inject.Inject; - -public class SyncAdapterHelper { - @Inject - public SyncAdapterHelper() { - - } - - public boolean shouldShowBackgroundSyncWarning() { - return false; - } - - public void checkPlayServices(TaskListFragment taskListFragment) { - - } - - public boolean initiateManualSync() { - return false; - } - - public boolean isEnabled() { - return false; - } - - public void requestSynchronization() { - - } -} diff --git a/app/src/amazon/java/org/tasks/injection/ActivityComponent.java b/app/src/amazon/java/org/tasks/injection/ActivityComponent.java index 5422257a0..f2b3af3e9 100644 --- a/app/src/amazon/java/org/tasks/injection/ActivityComponent.java +++ b/app/src/amazon/java/org/tasks/injection/ActivityComponent.java @@ -21,6 +21,7 @@ import org.tasks.activities.FilterSettingsActivity; import org.tasks.activities.GoogleTaskListSettingsActivity; import org.tasks.activities.TagSettingsActivity; import org.tasks.activities.TimePickerActivity; +import org.tasks.caldav.CalDAVSettingsActivity; import org.tasks.dashclock.DashClockSettings; import org.tasks.files.FileExplore; import org.tasks.files.MyFilePickerActivity; @@ -123,6 +124,8 @@ public interface ActivityComponent { void inject(GoogleTaskListSettingsActivity googleTaskListSettingsActivity); + void inject(CalDAVSettingsActivity calDAVSettingsActivity); + void inject(TaskerCreateTaskActivity taskerCreateTaskActivity); void inject(TaskListViewModel viewModel); diff --git a/app/src/amazon/java/org/tasks/injection/DialogFragmentComponent.java b/app/src/amazon/java/org/tasks/injection/DialogFragmentComponent.java index e1c2cd1ec..a321480ae 100644 --- a/app/src/amazon/java/org/tasks/injection/DialogFragmentComponent.java +++ b/app/src/amazon/java/org/tasks/injection/DialogFragmentComponent.java @@ -2,6 +2,8 @@ package org.tasks.injection; import org.tasks.activities.CalendarSelectionDialog; import org.tasks.activities.RemoteListSupportPicker; +import org.tasks.caldav.DeleteAccountDialog; +import org.tasks.caldav.RenameAccountDialog; import org.tasks.dialogs.AddAttachmentDialog; import org.tasks.dialogs.ColorPickerDialog; import org.tasks.dialogs.RecordAudioDialog; @@ -34,4 +36,8 @@ public interface DialogFragmentComponent { void inject(CustomRecurrenceDialog customRecurrenceDialog); void inject(RemoteListSupportPicker remoteListSupportPicker); + + void inject(DeleteAccountDialog deleteAccountDialog); + + void inject(RenameAccountDialog renameAccountDialog); } diff --git a/app/src/amazon/java/org/tasks/sync/SyncAdapters.java b/app/src/amazon/java/org/tasks/sync/SyncAdapters.java new file mode 100644 index 000000000..9f9ec03eb --- /dev/null +++ b/app/src/amazon/java/org/tasks/sync/SyncAdapters.java @@ -0,0 +1,42 @@ +package org.tasks.sync; + +import android.content.ContentResolver; + +import com.todoroo.astrid.activity.TaskListFragment; + +import org.tasks.caldav.CaldavAccountManager; + +import javax.inject.Inject; + +public class SyncAdapters { + private CaldavAccountManager caldavAccountManager; + + @Inject + public SyncAdapters(CaldavAccountManager caldavAccountManager) { + this.caldavAccountManager = caldavAccountManager; + } + + public boolean initiateManualSync() { + return caldavAccountManager.initiateManualSync(); + } + + public void requestSynchronization() { + caldavAccountManager.requestSynchronization(); + } + + public boolean isGoogleTaskSyncEnabled() { + return false; + } + + public void checkPlayServices(TaskListFragment taskListFragment) { + + } + + public boolean isMasterSyncEnabled() { + return ContentResolver.getMasterSyncAutomatically(); + } + + public boolean isSyncEnabled() { + return caldavAccountManager.getAccounts().size() > 0; + } +} diff --git a/app/src/generic/java/org/tasks/injection/ActivityComponent.java b/app/src/generic/java/org/tasks/injection/ActivityComponent.java index 5422257a0..f2b3af3e9 100644 --- a/app/src/generic/java/org/tasks/injection/ActivityComponent.java +++ b/app/src/generic/java/org/tasks/injection/ActivityComponent.java @@ -21,6 +21,7 @@ import org.tasks.activities.FilterSettingsActivity; import org.tasks.activities.GoogleTaskListSettingsActivity; import org.tasks.activities.TagSettingsActivity; import org.tasks.activities.TimePickerActivity; +import org.tasks.caldav.CalDAVSettingsActivity; import org.tasks.dashclock.DashClockSettings; import org.tasks.files.FileExplore; import org.tasks.files.MyFilePickerActivity; @@ -123,6 +124,8 @@ public interface ActivityComponent { void inject(GoogleTaskListSettingsActivity googleTaskListSettingsActivity); + void inject(CalDAVSettingsActivity calDAVSettingsActivity); + void inject(TaskerCreateTaskActivity taskerCreateTaskActivity); void inject(TaskListViewModel viewModel); diff --git a/app/src/generic/java/org/tasks/injection/DialogFragmentComponent.java b/app/src/generic/java/org/tasks/injection/DialogFragmentComponent.java index e1c2cd1ec..a321480ae 100644 --- a/app/src/generic/java/org/tasks/injection/DialogFragmentComponent.java +++ b/app/src/generic/java/org/tasks/injection/DialogFragmentComponent.java @@ -2,6 +2,8 @@ package org.tasks.injection; import org.tasks.activities.CalendarSelectionDialog; import org.tasks.activities.RemoteListSupportPicker; +import org.tasks.caldav.DeleteAccountDialog; +import org.tasks.caldav.RenameAccountDialog; import org.tasks.dialogs.AddAttachmentDialog; import org.tasks.dialogs.ColorPickerDialog; import org.tasks.dialogs.RecordAudioDialog; @@ -34,4 +36,8 @@ public interface DialogFragmentComponent { void inject(CustomRecurrenceDialog customRecurrenceDialog); void inject(RemoteListSupportPicker remoteListSupportPicker); + + void inject(DeleteAccountDialog deleteAccountDialog); + + void inject(RenameAccountDialog renameAccountDialog); } diff --git a/app/src/generic/java/org/tasks/sync/SyncAdapters.java b/app/src/generic/java/org/tasks/sync/SyncAdapters.java new file mode 100644 index 000000000..9f9ec03eb --- /dev/null +++ b/app/src/generic/java/org/tasks/sync/SyncAdapters.java @@ -0,0 +1,42 @@ +package org.tasks.sync; + +import android.content.ContentResolver; + +import com.todoroo.astrid.activity.TaskListFragment; + +import org.tasks.caldav.CaldavAccountManager; + +import javax.inject.Inject; + +public class SyncAdapters { + private CaldavAccountManager caldavAccountManager; + + @Inject + public SyncAdapters(CaldavAccountManager caldavAccountManager) { + this.caldavAccountManager = caldavAccountManager; + } + + public boolean initiateManualSync() { + return caldavAccountManager.initiateManualSync(); + } + + public void requestSynchronization() { + caldavAccountManager.requestSynchronization(); + } + + public boolean isGoogleTaskSyncEnabled() { + return false; + } + + public void checkPlayServices(TaskListFragment taskListFragment) { + + } + + public boolean isMasterSyncEnabled() { + return ContentResolver.getMasterSyncAutomatically(); + } + + public boolean isSyncEnabled() { + return caldavAccountManager.getAccounts().size() > 0; + } +} diff --git a/app/src/googleplay/AndroidManifest.xml b/app/src/googleplay/AndroidManifest.xml index 57af1cc1e..ea148cbac 100644 --- a/app/src/googleplay/AndroidManifest.xml +++ b/app/src/googleplay/AndroidManifest.xml @@ -16,19 +16,10 @@ - - - - - - - - - @@ -93,7 +84,7 @@ + android:resource="@xml/sync_adapter_gtask" /> diff --git a/app/src/googleplay/java/org/tasks/injection/ActivityComponent.java b/app/src/googleplay/java/org/tasks/injection/ActivityComponent.java index 7e708b7cf..8656e256c 100644 --- a/app/src/googleplay/java/org/tasks/injection/ActivityComponent.java +++ b/app/src/googleplay/java/org/tasks/injection/ActivityComponent.java @@ -25,6 +25,7 @@ import org.tasks.activities.FilterSettingsActivity; import org.tasks.activities.GoogleTaskListSettingsActivity; import org.tasks.activities.TagSettingsActivity; import org.tasks.activities.TimePickerActivity; +import org.tasks.caldav.CalDAVSettingsActivity; import org.tasks.dashclock.DashClockSettings; import org.tasks.files.FileExplore; import org.tasks.files.MyFilePickerActivity; @@ -132,6 +133,8 @@ public interface ActivityComponent { void inject(GoogleTaskListSettingsActivity googleTaskListSettingsActivity); + void inject(CalDAVSettingsActivity calDAVSettingsActivity); + void inject(TaskerCreateTaskActivity taskerCreateTaskActivity); void inject(TaskListViewModel taskListViewModel); diff --git a/app/src/googleplay/java/org/tasks/injection/DialogFragmentComponent.java b/app/src/googleplay/java/org/tasks/injection/DialogFragmentComponent.java index a6b2da604..0c20f8fd5 100644 --- a/app/src/googleplay/java/org/tasks/injection/DialogFragmentComponent.java +++ b/app/src/googleplay/java/org/tasks/injection/DialogFragmentComponent.java @@ -2,6 +2,8 @@ package org.tasks.injection; import org.tasks.activities.CalendarSelectionDialog; import org.tasks.activities.RemoteListSupportPicker; +import org.tasks.caldav.DeleteAccountDialog; +import org.tasks.caldav.RenameAccountDialog; import org.tasks.dialogs.AddAttachmentDialog; import org.tasks.dialogs.ColorPickerDialog; import org.tasks.dialogs.RecordAudioDialog; @@ -44,4 +46,8 @@ public interface DialogFragmentComponent { void inject(RenameListDialog renameListDialog); void inject(CustomRecurrenceDialog customRecurrenceDialog); + + void inject(DeleteAccountDialog deleteAccountDialog); + + void inject(RenameAccountDialog renameAccountDialog); } diff --git a/app/src/googleplay/java/org/tasks/sync/SyncAdapters.java b/app/src/googleplay/java/org/tasks/sync/SyncAdapters.java new file mode 100644 index 000000000..fb6f62fd7 --- /dev/null +++ b/app/src/googleplay/java/org/tasks/sync/SyncAdapters.java @@ -0,0 +1,51 @@ +package org.tasks.sync; + +import android.content.ContentResolver; + +import com.todoroo.astrid.activity.TaskListFragment; + +import org.tasks.caldav.CaldavAccountManager; +import org.tasks.gtasks.GtaskSyncAdapterHelper; + +import javax.inject.Inject; + +public class SyncAdapters { + + private final GtaskSyncAdapterHelper gtaskSyncAdapterHelper; + private final CaldavAccountManager caldavAccountManager; + + @Inject + public SyncAdapters(GtaskSyncAdapterHelper gtaskSyncAdapterHelper, CaldavAccountManager caldavAccountManager) { + this.gtaskSyncAdapterHelper = gtaskSyncAdapterHelper; + this.caldavAccountManager = caldavAccountManager; + } + + public void requestSynchronization() { + gtaskSyncAdapterHelper.requestSynchronization(); + caldavAccountManager.requestSynchronization(); + } + + public boolean initiateManualSync() { + return gtaskSyncAdapterHelper.initiateManualSync() | caldavAccountManager.initiateManualSync(); + } + + public boolean isMasterSyncEnabled() { + return ContentResolver.getMasterSyncAutomatically(); + } + + public boolean isSyncEnabled() { + return isGoogleTaskSyncEnabled() || isCaldavSyncEnabled(); + } + + public boolean isGoogleTaskSyncEnabled() { + return gtaskSyncAdapterHelper.isSyncEnabled(); + } + + public boolean isCaldavSyncEnabled() { + return caldavAccountManager.getAccounts().size() > 0; + } + + public void checkPlayServices(TaskListFragment taskListFragment) { + gtaskSyncAdapterHelper.checkPlayServices(taskListFragment); + } +} diff --git a/app/src/main/res/xml/syncadapter.xml b/app/src/googleplay/res/xml/sync_adapter_gtask.xml similarity index 100% rename from app/src/main/res/xml/syncadapter.xml rename to app/src/googleplay/res/xml/sync_adapter_gtask.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c80ae27f5..6385bec43 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,6 +53,19 @@ + + + + + + + + + + + + + @@ -297,6 +310,8 @@ + + @@ -508,6 +523,18 @@ + + + + + + + + diff --git a/app/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java b/app/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java index 6cafe833a..19fe7a21a 100644 --- a/app/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java +++ b/app/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java @@ -28,7 +28,6 @@ import com.todoroo.astrid.data.Task; import com.todoroo.astrid.gtasks.GtasksListService; import com.todoroo.astrid.gtasks.GtasksSubtaskListFragment; import com.todoroo.astrid.repeats.RepeatControlSet; -import com.todoroo.astrid.service.TaskCreator; import com.todoroo.astrid.subtasks.SubtasksHelper; import com.todoroo.astrid.subtasks.SubtasksListFragment; import com.todoroo.astrid.subtasks.SubtasksTagListFragment; @@ -48,13 +47,13 @@ import org.tasks.data.TagDataDao; import org.tasks.dialogs.SortDialog; import org.tasks.fragments.CommentBarFragment; import org.tasks.gtasks.RemoteListSelectionHandler; -import org.tasks.gtasks.GtaskSyncAdapterHelper; import org.tasks.injection.ActivityComponent; import org.tasks.injection.InjectingAppCompatActivity; import org.tasks.intents.TaskIntents; import org.tasks.preferences.DefaultFilterProvider; import org.tasks.preferences.Preferences; import org.tasks.receivers.RepeatConfirmationReceiver; +import org.tasks.sync.SyncAdapters; import org.tasks.tasklist.GtasksListFragment; import org.tasks.tasklist.TagListFragment; import org.tasks.themes.Theme; @@ -97,9 +96,8 @@ public class TaskListActivity extends InjectingAppCompatActivity implements @Inject TagDataDao tagDataDao; @Inject Theme theme; @Inject ThemeCache themeCache; - @Inject GtaskSyncAdapterHelper gtaskSyncAdapterHelper; + @Inject SyncAdapters syncAdapters; @Inject Tracker tracker; - @Inject TaskCreator taskCreator; @Inject TaskDao taskDao; @Inject CaldavDao caldavDao; @Inject LocalBroadcastManager localBroadcastManager; @@ -274,7 +272,7 @@ public class TaskListActivity extends InjectingAppCompatActivity implements localBroadcastManager.registerRepeatReceiver(repeatConfirmationReceiver); - gtaskSyncAdapterHelper.checkPlayServices(getTaskListFragment()); + syncAdapters.checkPlayServices(getTaskListFragment()); } public void restart() { diff --git a/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java b/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java index 470cc2d7d..fbce11a8f 100644 --- a/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java +++ b/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java @@ -50,12 +50,12 @@ import org.tasks.analytics.Tracker; import org.tasks.analytics.Tracking; import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.SortDialog; -import org.tasks.gtasks.GtaskSyncAdapterHelper; import org.tasks.injection.ForActivity; import org.tasks.injection.FragmentComponent; import org.tasks.injection.InjectingFragment; import org.tasks.preferences.Device; import org.tasks.preferences.Preferences; +import org.tasks.sync.SyncAdapters; import org.tasks.tasklist.TaskListRecyclerAdapter; import org.tasks.tasklist.ViewHolderFactory; import org.tasks.ui.CheckBoxes; @@ -97,7 +97,7 @@ public class TaskListFragment extends InjectingFragment implements // --- instance variables - @Inject GtaskSyncAdapterHelper gtaskSyncAdapterHelper; + @Inject SyncAdapters syncAdapters; @Inject TaskDeleter taskDeleter; @Inject TaskDuplicator taskDuplicator; @Inject @ForActivity Context context; @@ -135,7 +135,7 @@ public class TaskListFragment extends InjectingFragment implements @Override public void onRefresh() { - if (!gtaskSyncAdapterHelper.initiateManualSync()) { + if (!syncAdapters.initiateManualSync()) { refresh(); } } @@ -477,7 +477,7 @@ public class TaskListFragment extends InjectingFragment implements for (Task task : tasks) { onTaskCreated(task.getUuid()); } - gtaskSyncAdapterHelper.requestSynchronization(); + syncAdapters.requestSynchronization(); } public void onTaskCreated(String uuid) { diff --git a/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java b/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java index c40bde2ed..196bb5f99 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java +++ b/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java @@ -32,6 +32,7 @@ import com.todoroo.astrid.gtasks.GtasksPreferenceService; import org.tasks.R; import org.tasks.activities.GoogleTaskListSettingsActivity; import org.tasks.activities.TagSettingsActivity; +import org.tasks.caldav.CalDAVSettingsActivity; import org.tasks.filters.FilterCounter; import org.tasks.filters.FilterProvider; import org.tasks.filters.NavigationDrawerAction; @@ -319,6 +320,14 @@ public class FilterAdapter extends ArrayAdapter { 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)); + } + if (navigationDrawer) { add(new NavigationDrawerSeparator()); diff --git a/app/src/main/java/com/todoroo/astrid/core/DefaultsPreferences.java b/app/src/main/java/com/todoroo/astrid/core/DefaultsPreferences.java index f4f14f778..79b6b0756 100644 --- a/app/src/main/java/com/todoroo/astrid/core/DefaultsPreferences.java +++ b/app/src/main/java/com/todoroo/astrid/core/DefaultsPreferences.java @@ -13,7 +13,6 @@ import android.support.annotation.NonNull; import com.todoroo.astrid.api.CaldavFilter; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.GtasksFilter; -import com.todoroo.astrid.gtasks.GtasksPreferenceService; import org.tasks.R; import org.tasks.activities.CalendarSelectionActivity; @@ -21,7 +20,6 @@ import org.tasks.analytics.Tracker; import org.tasks.analytics.Tracking; import org.tasks.calendars.AndroidCalendar; import org.tasks.calendars.CalendarProvider; -import org.tasks.gtasks.GtaskSyncAdapterHelper; import org.tasks.gtasks.RemoteListSelectionHandler; import org.tasks.injection.ActivityComponent; import org.tasks.injection.InjectingPreferenceActivity; @@ -29,6 +27,7 @@ import org.tasks.preferences.ActivityPermissionRequestor; import org.tasks.preferences.DefaultFilterProvider; import org.tasks.preferences.PermissionRequestor; import org.tasks.preferences.Preferences; +import org.tasks.sync.SyncAdapters; import javax.inject.Inject; @@ -45,8 +44,7 @@ public class DefaultsPreferences extends InjectingPreferenceActivity implements @Inject CalendarProvider calendarProvider; @Inject ActivityPermissionRequestor permissionRequester; @Inject Tracker tracker; - @Inject GtasksPreferenceService gtasksPreferenceService; - @Inject GtaskSyncAdapterHelper gtaskSyncAdapterHelper; + @Inject SyncAdapters syncAdapters; @Inject DefaultFilterProvider defaultFilterProvider; private Preference defaultCalendarPref; @@ -69,7 +67,7 @@ public class DefaultsPreferences extends InjectingPreferenceActivity implements ? getString(R.string.dont_add_to_calendar) : defaultCalendarName); - if (gtaskSyncAdapterHelper.isEnabled()) { + if (syncAdapters.isSyncEnabled()) { findPreference(R.string.p_default_remote_list).setOnPreferenceClickListener(preference -> { newRemoteListNativePicker(defaultFilterProvider.getDefaultRemoteList()) .show(getFragmentManager(), FRAG_TAG_REMOTE_LIST_SELECTION); 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 4f27f85c9..d9f74688b 100644 --- a/app/src/main/java/com/todoroo/astrid/dao/TaskDao.java +++ b/app/src/main/java/com/todoroo/astrid/dao/TaskDao.java @@ -166,6 +166,9 @@ public abstract class TaskDao { @android.arch.persistence.room.Query("SELECT tasks.* FROM tasks INNER JOIN google_tasks ON google_tasks.task = tasks._id WHERE google_tasks.list_id = :googleTaskList") 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); + // --- save /** diff --git a/app/src/main/java/org/tasks/caldav/Account.java b/app/src/main/java/org/tasks/caldav/Account.java new file mode 100644 index 000000000..1a92a511f --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/Account.java @@ -0,0 +1,65 @@ +package org.tasks.caldav; + +import android.accounts.AccountManager; +import android.content.ContentResolver; +import android.os.Bundle; + +import java.util.concurrent.TimeUnit; + +public class Account { + + private static final String AUTHORITY = "org.tasks"; + public static final String EXTRA_UUID = "uuid"; + + private AccountManager accountManager; + private android.accounts.Account account; + + public Account(AccountManager accountManager, android.accounts.Account account) { + this.accountManager = accountManager; + this.account = account; + } + + public String getName() { + return account.name; + } + + public String getUuid() { + return accountManager.getUserData(account, EXTRA_UUID); + } + + String getPassword() { + return accountManager.getPassword(account); + } + + public android.accounts.Account getAccount() { + return account; + } + + void setPassword(String password) { + accountManager.setPassword(account, password); + } + + public void setUuid(String uuid) { + accountManager.setUserData(account, EXTRA_UUID, uuid); + } + + boolean isBackgroundSyncEnabled() { + return ContentResolver.getSyncAutomatically(account, AUTHORITY); + } + + void setSynchronizationEnabled(boolean enabled) { + ContentResolver.setSyncAutomatically(account, AUTHORITY, enabled); + if (enabled) { + ContentResolver.addPeriodicSync(account, AUTHORITY, Bundle.EMPTY, TimeUnit.HOURS.toSeconds(1)); + } else { + ContentResolver.removePeriodicSync(account, AUTHORITY, Bundle.EMPTY); + } + } + + @Override + public String toString() { + return "Account{" + + "account=" + account + + '}'; + } +} diff --git a/app/src/main/java/org/tasks/caldav/CalDAVAccountAuthenticator.java b/app/src/main/java/org/tasks/caldav/CalDAVAccountAuthenticator.java new file mode 100644 index 000000000..abcc78827 --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/CalDAVAccountAuthenticator.java @@ -0,0 +1,59 @@ +package org.tasks.caldav; + +import android.accounts.AbstractAccountAuthenticator; +import android.accounts.Account; +import android.accounts.AccountAuthenticatorResponse; +import android.accounts.AccountManager; +import android.accounts.NetworkErrorException; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +public class CalDAVAccountAuthenticator extends AbstractAccountAuthenticator { + + private final Context context; + + public CalDAVAccountAuthenticator(Context context) { + super(context); + this.context = context; + } + + @Override + public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { + return null; + } + + @Override + public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException { + Intent intent = new Intent(context, CalDAVSettingsActivity.class); + intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); + Bundle bundle = new Bundle(); + bundle.putParcelable(AccountManager.KEY_INTENT, intent); + return bundle; + } + + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException { + return null; + } + + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { + return null; + } + + @Override + public String getAuthTokenLabel(String authTokenType) { + return null; + } + + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { + return null; + } + + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException { + return null; + } +} diff --git a/app/src/main/java/org/tasks/caldav/CalDAVAccountAuthenticatorService.java b/app/src/main/java/org/tasks/caldav/CalDAVAccountAuthenticatorService.java new file mode 100644 index 000000000..d449b5dd2 --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/CalDAVAccountAuthenticatorService.java @@ -0,0 +1,25 @@ +package org.tasks.caldav; + +import android.accounts.AccountManager; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.support.annotation.Nullable; + +public class CalDAVAccountAuthenticatorService extends Service { + + private CalDAVAccountAuthenticator authenticator; + + @Override + public void onCreate() { + authenticator = new CalDAVAccountAuthenticator(this); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return AccountManager.ACTION_AUTHENTICATOR_INTENT.equals(intent.getAction()) + ? authenticator.getIBinder() + : null; + } +} diff --git a/app/src/main/java/org/tasks/caldav/CalDAVSettingsActivity.java b/app/src/main/java/org/tasks/caldav/CalDAVSettingsActivity.java new file mode 100644 index 000000000..d67453aac --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/CalDAVSettingsActivity.java @@ -0,0 +1,464 @@ +package org.tasks.caldav; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.design.widget.Snackbar; +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.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.todoroo.astrid.activity.TaskListActivity; +import com.todoroo.astrid.api.CaldavFilter; +import com.todoroo.astrid.helper.UUIDHelper; + +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.CaldavDao; +import org.tasks.dialogs.ColorPickerDialog; +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 java.net.IDN; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import butterknife.OnFocusChange; +import butterknife.OnTextChanged; +import timber.log.Timber; + +import static android.text.TextUtils.isEmpty; +import static org.tasks.caldav.DeleteAccountDialog.newDeleteAccountDialog; +import static org.tasks.caldav.RenameAccountDialog.newRenameAccountDialog; +import static org.tasks.time.DateTimeUtils.currentTimeMillis; + +public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity + implements Toolbar.OnMenuItemClickListener, DeleteAccountDialog.DeleteAccountDialogCallback, + RenameAccountDialog.RenameAccountDialogCallback { + + private static final String EXTRA_SELECTED_THEME = "extra_selected_theme"; + private static final String FRAG_TAG_RENAME_ACCOUNT = "frag_tag_rename_account"; + private static final String FRAG_TAG_DELETE_ACCOUNT = "frag_tag_delete_account"; + + private static final int REQUEST_COLOR_PICKER = 10109; + + public static final String EXTRA_CALDAV_DATA = "caldavData"; //$NON-NLS-1$ + public static final String EXTRA_CALDAV_UUID = "uuid"; //$NON-NLS-1$ + + public static final String ACTION_RELOAD = "accountRenamed"; + public static final String ACTION_DELETED = "accountDeleted"; + + private CaldavAccount caldavAccount; + private Account localAccount; + private int selectedTheme; + + @Inject DialogBuilder dialogBuilder; + @Inject Preferences preferences; + @Inject ThemeCache themeCache; + @Inject ThemeColor themeColor; + @Inject Tracker tracker; + @Inject CaldavDao caldavDao; + @Inject CaldavAccountManager caldavAccountManager; + @Inject SyncAdapters syncAdapters; + + @BindView(R.id.root_layout) LinearLayout root; + @BindView(R.id.name) TextInputEditText name; + @BindView(R.id.url) TextInputEditText url; + @BindView(R.id.user) TextInputEditText user; + @BindView(R.id.password) TextInputEditText password; + @BindView(R.id.name_layout) TextInputLayout nameLayout; + @BindView(R.id.url_layout) TextInputLayout urlLayout; + @BindView(R.id.user_layout) TextInputLayout userLayout; + @BindView(R.id.password_layout) TextInputLayout passwordLayout; + @BindView(R.id.color) TextInputEditText color; + @BindView(R.id.toolbar) Toolbar toolbar; + @BindView(R.id.background_sync) CheckBox backgroundSync; + @BindView(R.id.master_sync_warning) TextView masterSyncWarning; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_caldav_settings); + ButterKnife.bind(this); + + caldavAccount = getIntent().getParcelableExtra(EXTRA_CALDAV_DATA); + + if (caldavAccount != null) { + localAccount = caldavAccountManager.getAccount(caldavAccount.getUuid()); + } + + if (savedInstanceState == null) { + if (caldavAccount != null) { + selectedTheme = caldavAccount.getColor(); + name.setText(caldavAccount.getName()); + url.setText(caldavAccount.getUrl()); + user.setText(caldavAccount.getUsername()); + } + backgroundSync.setChecked(localAccount == null || localAccount.isBackgroundSyncEnabled()); + } else { + selectedTheme = savedInstanceState.getInt(EXTRA_SELECTED_THEME); + } + + final boolean backButtonSavesTask = preferences.backButtonSavesTask(); + toolbar.setTitle(caldavAccount == null ? getString(R.string.add_account) : caldavAccount.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(); + } + }); + toolbar.inflateMenu(R.menu.menu_tag_settings); + toolbar.setOnMenuItemClickListener(this); + toolbar.showOverflowMenu(); + + color.setInputType(InputType.TYPE_NULL); + + if (caldavAccount == null) { + toolbar.getMenu().findItem(R.id.delete).setVisible(false); + name.requestFocus(); + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(name, InputMethodManager.SHOW_IMPLICIT); + } + + updateTheme(); + } + + @OnTextChanged(R.id.name) + void onNameChanged(CharSequence text) { + nameLayout.setError(null); + } + + @OnTextChanged(R.id.url) + void onUrlChanged(CharSequence text) { + urlLayout.setError(null); + } + + @OnTextChanged(R.id.user) + void onUserChanged(CharSequence text) { + userLayout.setError(null); + } + + @OnTextChanged(R.id.password) + void onPasswordChanged(CharSequence text) { + passwordLayout.setError(null); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putInt(EXTRA_SELECTED_THEME, selectedTheme); + } + + @Override + protected void onResume() { + super.onResume(); + + masterSyncWarning.setVisibility(syncAdapters.isMasterSyncEnabled() ? View.GONE : View.VISIBLE); + } + + @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, ColorPickerDialog.ColorPalette.COLORS); + intent.putExtra(ColorPickerActivity.EXTRA_SHOW_NONE, true); + startActivityForResult(intent, REQUEST_COLOR_PICKER); + } + + @Override + public void inject(ActivityComponent component) { + component.inject(this); + } + + private String getNewName() { + return name.getText().toString().trim(); + } + + private String getNewURL() { + return url.getText().toString().trim(); + } + + private String getNewUsername() { + return user.getText().toString().trim(); + } + + private String getNewPassword() { + String input = password.getText().toString().trim(); + return localAccount == null || !isEmpty(input) ? input : localAccount.getPassword(); + } + + private boolean clashes(String newName) { + CaldavAccount existing = caldavDao.getAccountByName(newName); + return caldavAccount != null && existing != null && caldavAccount.getId() != existing.getId(); + } + + private void save() { + String newName = getNewName(); + String username = getNewUsername(); + String url = getNewURL(); + String password = getNewPassword(); + + boolean failed = false; + + if (isEmpty(newName)) { + nameLayout.setError(getString(R.string.name_cannot_be_empty)); + failed = true; + } else if (clashes(newName)) { + nameLayout.setError(getString(R.string.tag_already_exists)); + failed = true; + } + + if (isEmpty(url)) { + urlLayout.setError(getString(R.string.url_required)); + failed = true; + } else { + Uri baseURL = Uri.parse(url); + String scheme = baseURL.getScheme(); + if ("https".equalsIgnoreCase(scheme) || "http".equalsIgnoreCase(scheme)) { + String host = baseURL.getHost(); + if (isEmpty(host)) { + urlLayout.setError(getString(R.string.url_host_name_required)); + failed = true; + } else { + try { + host = IDN.toASCII(host); + } catch(Exception e) { + Timber.e(e.getMessage(), e); + } + String path = baseURL.getEncodedPath(); + int port = baseURL.getPort(); + try { + new URI(scheme, null, host, port, path, null, null); + } catch(URISyntaxException e) { + urlLayout.setError(e.getLocalizedMessage()); + failed = true; + } + } + } else { + urlLayout.setError(getString(R.string.url_invalid_scheme)); + failed = true; + } + } + + if (isEmpty(username)) { + userLayout.setError(getString(R.string.username_required)); + failed = true; + } + + if (localAccount == null && isEmpty(password)) { + passwordLayout.setError(getString(R.string.password_required)); + failed = true; + } + + if (failed) { + return; + } + + if (caldavAccount == null) { + CaldavAccount newAccount = new CaldavAccount(newName, UUIDHelper.newUUID()); + newAccount.setColor(selectedTheme); + newAccount.setUrl(url); + newAccount.setUsername(username); + if (caldavAccountManager.addAccount(newAccount, password)) { + Account account = caldavAccountManager.getAccount(newAccount.getUuid()); + if (account == null) { + showErrorSnackbar(); + } else { + newAccount.setId(caldavDao.insert(newAccount)); + setResult(RESULT_OK, new Intent().putExtra(TaskListActivity.OPEN_FILTER, new CaldavFilter(newAccount))); + finish(); + } + } else { + showErrorSnackbar(); + } + } else if (hasChanges()) { + if (localAccount == null) { + if (!caldavAccountManager.addAccount(caldavAccount, password)) { + localAccount = caldavAccountManager.getAccount(caldavAccount.getUuid()); + if (localAccount == null) { + showErrorSnackbar(); + return; + } + } + } else if (!newName.equals(localAccount.getName())) { + newRenameAccountDialog(caldavAccount.getUuid(), newName) + .show(getSupportFragmentManager(), FRAG_TAG_RENAME_ACCOUNT); + return; + } + + caldavAccount.setName(newName); + caldavAccount.setColor(selectedTheme); + caldavAccount.setUrl(url); + caldavAccount.setUsername(username); + caldavDao.update(caldavAccount); + if (!isEmpty(password)) { + localAccount.setPassword(password); + } + localAccount.setSynchronizationEnabled(backgroundSync.isChecked()); + setResult(RESULT_OK, new Intent(ACTION_RELOAD).putExtra(TaskListActivity.OPEN_FILTER, new CaldavFilter(caldavAccount))); + finish(); + } else { + finish(); + } + } + + private void showErrorSnackbar() { + Snackbar snackbar = Snackbar.make(root, getString(R.string.error_adding_account), 8000) + .setActionTextColor(ContextCompat.getColor(this, R.color.snackbar_text_color)); + snackbar.getView().setBackgroundColor(ContextCompat.getColor(this, R.color.snackbar_background)); + snackbar.show(); + } + + private boolean hasChanges() { + if (caldavAccount == null) { + return selectedTheme >= 0 || !isEmpty(getNewName()) || + !isEmpty(getNewPassword()) || !isEmpty(getNewURL()) || + !isEmpty(getNewUsername()) || !backgroundSync.isChecked(); + } + return localAccount == null || + selectedTheme != caldavAccount.getColor() || + !getNewName().equals(caldavAccount.getName()) || + !getNewURL().equals(caldavAccount.getUrl()) || + !getNewUsername().equals(caldavAccount.getUsername()) || + !getNewPassword().equals(localAccount.getPassword()) || + backgroundSync.isChecked() != localAccount.isBackgroundSyncEnabled(); + } + + @Override + public void finish() { + InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(name.getWindowToken(), 0); + super.finish(); + } + + @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 deleteAccount() { + dialogBuilder.newMessageDialog(R.string.delete_tag_confirmation, caldavAccount.getName()) + .setPositiveButton(R.string.delete, (dialog, which) -> { + if (localAccount == null) { + onListDeleted(); + } else { + newDeleteAccountDialog(localAccount.getUuid()) + .show(getSupportFragmentManager(), FRAG_TAG_DELETE_ACCOUNT); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + + 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); + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.delete: + deleteAccount(); + break; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onListDeleted() { + if (caldavAccount != null) { + caldavAccountManager.deleteAccount(caldavAccount); + setResult(RESULT_OK, new Intent(ACTION_DELETED).putExtra(EXTRA_CALDAV_UUID, caldavAccount.getUuid())); + } + finish(); + } + + @Override + public void onListRenamed() { + localAccount = caldavAccountManager.getAccount(caldavAccount.getUuid()); + save(); + } + + @Override + public void renameFailed() { + nameLayout.setError(getString(R.string.error_renaming_account)); + } + + @Override + public void deleteAccountFailed() { + Toast.makeText(this, R.string.error_deleting_account, Toast.LENGTH_LONG).show(); + } +} diff --git a/app/src/main/java/org/tasks/caldav/CaldavAccountManager.java b/app/src/main/java/org/tasks/caldav/CaldavAccountManager.java new file mode 100644 index 000000000..43c4fff64 --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/CaldavAccountManager.java @@ -0,0 +1,164 @@ +package org.tasks.caldav; + +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.content.ContentResolver; +import android.content.Context; +import android.os.Bundle; + +import com.google.common.base.Optional; +import com.google.common.base.Strings; +import com.todoroo.astrid.dao.TaskDao; +import com.todoroo.astrid.data.Task; +import com.todoroo.astrid.service.TaskDeleter; + +import org.tasks.LocalBroadcastManager; +import org.tasks.data.CaldavAccount; +import org.tasks.data.CaldavDao; +import org.tasks.injection.ApplicationScope; +import org.tasks.injection.ForApplication; +import org.tasks.preferences.PermissionChecker; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.inject.Inject; + +import timber.log.Timber; + +import static com.google.common.collect.Iterables.tryFind; +import static org.tasks.caldav.Account.EXTRA_UUID; + +@ApplicationScope +public class CaldavAccountManager { + + private static final String AUTHORITY = "org.tasks"; + private static final String ACCOUNT_TYPE = "org.tasks.caldav"; + + private final PermissionChecker permissionChecker; + private final android.accounts.AccountManager accountManager; + private final LocalBroadcastManager localBroadcastManager; + private final TaskDeleter taskDeleter; + private final CaldavDao caldavDao; + private final TaskDao taskDao; + + @Inject + public CaldavAccountManager(@ForApplication Context context, PermissionChecker permissionChecker, + CaldavDao caldavDao, TaskDeleter taskDeleter, + LocalBroadcastManager localBroadcastManager, TaskDao taskDao) { + this.permissionChecker = permissionChecker; + this.caldavDao = caldavDao; + this.taskDao = taskDao; + this.taskDeleter = taskDeleter; + this.localBroadcastManager = localBroadcastManager; + accountManager = android.accounts.AccountManager.get(context); + syncAccountList(); + } + + public String getUuid(android.accounts.Account account) { + return accountManager.getUserData(account, EXTRA_UUID); + } + + public Account getAccount(String uuid) { + for (Account account : getAccounts()) { + if (uuid.equals(account.getUuid())) { + return account; + } + } + return null; + } + + public List getAccounts() { + if (!permissionChecker.canAccessAccounts()) { + return Collections.emptyList(); + } + + List accounts = new ArrayList<>(); + for (android.accounts.Account account : accountManager.getAccountsByType(ACCOUNT_TYPE)) { + accounts.add(new Account(accountManager, account)); + } + return accounts; + } + + boolean removeAccount(Account account) { + return removeAccount(account.getAccount()); + } + + boolean removeAccount(android.accounts.Account account) { + try { + return accountManager + .removeAccount(account, null, null) + .getResult(); + } catch (OperationCanceledException | IOException | AuthenticatorException e) { + Timber.e(e.getMessage(), e); + } + return false; + } + + boolean addAccount(CaldavAccount caldavAccount, String password) { + Timber.d("Adding %s", caldavAccount); + android.accounts.Account account = new android.accounts.Account(caldavAccount.getName(), ACCOUNT_TYPE); + Bundle userdata = new Bundle(); + userdata.putString(EXTRA_UUID, caldavAccount.getUuid()); + return accountManager.addAccountExplicitly(account, password, userdata); + } + + private void createAccount(Account account) { + Timber.d("Adding %s", account); + String uuid = account.getUuid(); + if (!Strings.isNullOrEmpty(uuid)) { + caldavDao.insert(new CaldavAccount(account.getName(), uuid)); + } + } + + private void syncAccountList() { + List oldAccountList = caldavDao.getAllOrderedByName(); + List newAccountList = getAccounts(); + + for (CaldavAccount local : oldAccountList) { + Optional match = tryFind(newAccountList, remote -> local.getUuid().equals(remote.getUuid())); + if (match.isPresent()) { + Timber.d("found %s", match.get()); + } else { + addAccount(local, null); + } + } + + for (Account remote : newAccountList) { + Optional match = tryFind(oldAccountList, local -> remote.getUuid().equals(local.getUuid())); + if (match.isPresent()) { + Timber.d("found %s", match.get()); + } else { + createAccount(remote); + } + } + } + + void deleteAccount(CaldavAccount account) { + String uuid = account.getUuid(); + for (Task task : taskDao.getCaldavTasks(uuid)) { + taskDeleter.markDeleted(task); + } + caldavDao.deleteTasksForAccount(uuid); + caldavDao.delete(account); + localBroadcastManager.broadcastRefreshList(); + } + + public boolean initiateManualSync() { + for (org.tasks.caldav.Account account : getAccounts()) { + Bundle extras = new Bundle(); + extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); + ContentResolver.requestSync(account.getAccount(), AUTHORITY, extras); + } + return true; + } + + public void requestSynchronization() { + for (org.tasks.caldav.Account account : getAccounts()) { + ContentResolver.requestSync(account.getAccount(), AUTHORITY, new Bundle()); + } + } +} diff --git a/app/src/main/java/org/tasks/caldav/CaldavListFragment.java b/app/src/main/java/org/tasks/caldav/CaldavListFragment.java index fd0ced12d..a441009ab 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavListFragment.java +++ b/app/src/main/java/org/tasks/caldav/CaldavListFragment.java @@ -1,15 +1,22 @@ package org.tasks.caldav; +import android.content.Intent; import android.os.Bundle; import android.support.v7.widget.Toolbar; +import android.view.MenuItem; +import com.todoroo.astrid.activity.TaskListActivity; 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.injection.FragmentComponent; +import static android.app.Activity.RESULT_OK; +import static org.tasks.caldav.CalDAVSettingsActivity.EXTRA_CALDAV_DATA; + public class CaldavListFragment extends TaskListFragment { public static TaskListFragment newCaldavListFragment(CaldavFilter filter, CaldavAccount account) { @@ -20,6 +27,7 @@ public class CaldavListFragment extends TaskListFragment { } private static final String EXTRA_CALDAV_ACCOUNT = "extra_caldav_account"; + private static final int REQUEST_ACCOUNT_SETTINGS = 10101; protected CaldavAccount account; @@ -37,7 +45,39 @@ public class CaldavListFragment extends TaskListFragment { super.inflateMenu(toolbar); toolbar.inflateMenu(R.menu.menu_caldav_list_fragment); } - + + @Override + 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); + startActivityForResult(intent, REQUEST_ACCOUNT_SETTINGS); + return true; + default: + return super.onMenuItemClick(item); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_ACCOUNT_SETTINGS) { + if (resultCode == RESULT_OK) { + TaskListActivity activity = (TaskListActivity) getActivity(); + String action = data.getAction(); + if (CalDAVSettingsActivity.ACTION_DELETED.equals(action)) { + activity.onFilterItemClicked(null); + } else if (CalDAVSettingsActivity.ACTION_RELOAD.equals(action)) { + activity.getIntent().putExtra(TaskListActivity.OPEN_FILTER, + (Filter) data.getParcelableExtra(TaskListActivity.OPEN_FILTER)); + activity.recreate(); + } + } + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); diff --git a/app/src/main/java/org/tasks/caldav/DeleteAccountDialog.java b/app/src/main/java/org/tasks/caldav/DeleteAccountDialog.java new file mode 100644 index 000000000..dfbddc9ee --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/DeleteAccountDialog.java @@ -0,0 +1,93 @@ +package org.tasks.caldav; + +import android.app.Activity; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import org.tasks.R; +import org.tasks.dialogs.DialogBuilder; +import org.tasks.injection.DialogFragmentComponent; +import org.tasks.injection.InjectingDialogFragment; + +import javax.inject.Inject; + +public class DeleteAccountDialog extends InjectingDialogFragment { + + public static DeleteAccountDialog newDeleteAccountDialog(String uuid) { + DeleteAccountDialog dialog = new DeleteAccountDialog(); + Bundle args = new Bundle(); + args.putString(EXTRA_UUID, uuid); + dialog.setArguments(args); + return dialog; + } + + public interface DeleteAccountDialogCallback { + void onListDeleted(); + + void deleteAccountFailed(); + } + + private static final String EXTRA_UUID = "extra_uuid"; + + @Inject DialogBuilder dialogBuilder; + @Inject CaldavAccountManager caldavAccountManager; + + private DeleteAccountDialogCallback callback; + private String uuid; + private ProgressDialog dialog; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + Bundle arguments = getArguments(); + uuid = arguments.getString(EXTRA_UUID); + dialog = dialogBuilder.newProgressDialog(R.string.deleting_list); + execute(); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return dialog; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + callback = (DeleteAccountDialogCallback) activity; + } + + @Override + protected void inject(DialogFragmentComponent component) { + component.inject(this); + } + + private void execute() { + new AsyncTask() { + @Override + protected Boolean doInBackground(Void... voids) { + Account account = caldavAccountManager.getAccount(uuid); + return account == null || caldavAccountManager.removeAccount(account); + } + + @Override + protected void onPostExecute(Boolean result) { + if (dialog.isShowing()) { + dialog.dismiss(); + } + + if (result) { + callback.onListDeleted(); + } else { + callback.deleteAccountFailed(); + } + } + }.execute(); + } +} diff --git a/app/src/main/java/org/tasks/caldav/RenameAccountDialog.java b/app/src/main/java/org/tasks/caldav/RenameAccountDialog.java new file mode 100644 index 000000000..709acd4f4 --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/RenameAccountDialog.java @@ -0,0 +1,112 @@ +package org.tasks.caldav; + +import android.app.Activity; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.todoroo.astrid.helper.UUIDHelper; + +import org.tasks.R; +import org.tasks.data.CaldavAccount; +import org.tasks.data.CaldavDao; +import org.tasks.dialogs.DialogBuilder; +import org.tasks.injection.DialogFragmentComponent; +import org.tasks.injection.InjectingDialogFragment; + +import javax.inject.Inject; + +import static org.tasks.time.DateTimeUtils.currentTimeMillis; + +public class RenameAccountDialog extends InjectingDialogFragment { + + public static RenameAccountDialog newRenameAccountDialog(String uuid, String name) { + RenameAccountDialog dialog = new RenameAccountDialog(); + Bundle args = new Bundle(); + args.putString(EXTRA_NAME, name); + args.putString(EXTRA_UUID, uuid); + dialog.setArguments(args); + return dialog; + } + + public interface RenameAccountDialogCallback { + void onListRenamed(); + + void renameFailed(); + } + + private static final String EXTRA_NAME = "extra_name"; + private static final String EXTRA_UUID = "extra_uuid"; + + @Inject DialogBuilder dialogBuilder; + @Inject CaldavAccountManager caldavAccountManager; + @Inject CaldavDao caldavDao; + + private RenameAccountDialogCallback callback; + private String name; + private String uuid; + private ProgressDialog dialog; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + Bundle arguments = getArguments(); + name = arguments.getString(EXTRA_NAME); + uuid = arguments.getString(EXTRA_UUID); + dialog = dialogBuilder.newProgressDialog(R.string.renaming_list); + execute(); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return dialog; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + callback = (RenameAccountDialogCallback) activity; + } + + @Override + protected void inject(DialogFragmentComponent component) { + component.inject(this); + } + + private void execute() { + new AsyncTask() { + @Override + protected Boolean doInBackground(Void... voids) { + CaldavAccount caldavAccount = caldavDao.getAccount(uuid); + caldavAccount.setName(name); + Account old = caldavAccountManager.getAccount(uuid); + if (!caldavAccountManager.addAccount(caldavAccount, old.getPassword())) { + return false; + } + caldavDao.update(caldavAccount); + old.setUuid(null); + caldavAccountManager.removeAccount(old); + return true; + } + + @Override + protected void onPostExecute(Boolean result) { + if (dialog.isShowing()) { + dialog.dismiss(); + } + + if (result) { + callback.onListRenamed(); + } else { + callback.renameFailed(); + } + } + }.execute(); + } +} diff --git a/app/src/main/java/org/tasks/data/CaldavAccount.java b/app/src/main/java/org/tasks/data/CaldavAccount.java index 2bece0b82..36efd0c0f 100644 --- a/app/src/main/java/org/tasks/data/CaldavAccount.java +++ b/app/src/main/java/org/tasks/data/CaldavAccount.java @@ -26,29 +26,31 @@ public final class CaldavAccount implements Parcelable { @ColumnInfo(name = "color") private int color = -1; - @ColumnInfo(name = "deleted") - private long deleted; - @ColumnInfo(name = "ctag") private String ctag; @ColumnInfo(name = "url") - private String url; + private String url = ""; @ColumnInfo(name = "username") - private String username; + private String username = ""; public CaldavAccount() { } + @Ignore + public CaldavAccount(String name, String uuid) { + this.name = name; + this.uuid = uuid; + } + @Ignore public CaldavAccount(Parcel source) { id = source.readLong(); uuid = source.readString(); name = source.readString(); color = source.readInt(); - deleted = source.readLong(); ctag = source.readString(); url = source.readString(); username = source.readString(); @@ -86,14 +88,6 @@ public final class CaldavAccount implements Parcelable { this.color = color; } - public long getDeleted() { - return deleted; - } - - public void setDeleted(long deleted) { - this.deleted = deleted; - } - public String getCtag() { return ctag; } @@ -118,10 +112,6 @@ public final class CaldavAccount implements Parcelable { this.username = username; } - public boolean isDeleted() { - return deleted > 0; - } - public static Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public CaldavAccount createFromParcel(Parcel source) { @@ -145,7 +135,6 @@ public final class CaldavAccount implements Parcelable { dest.writeString(uuid); dest.writeString(name); dest.writeInt(color); - dest.writeLong(deleted); dest.writeString(ctag); dest.writeString(url); dest.writeString(username); @@ -158,7 +147,6 @@ public final class CaldavAccount implements Parcelable { ", uuid='" + uuid + '\'' + ", name='" + name + '\'' + ", color=" + color + - ", deleted=" + deleted + ", ctag='" + ctag + '\'' + ", url='" + url + '\'' + ", username='" + username + '\'' + diff --git a/app/src/main/java/org/tasks/data/CaldavDao.java b/app/src/main/java/org/tasks/data/CaldavDao.java index 700333a2b..462d62d4b 100644 --- a/app/src/main/java/org/tasks/data/CaldavDao.java +++ b/app/src/main/java/org/tasks/data/CaldavDao.java @@ -17,18 +17,15 @@ public interface CaldavDao { @Query("SELECT * FROM caldav_account WHERE uuid = :uuid LIMIT 1") CaldavAccount getByUuid(String uuid); - @Query("SELECT * FROM caldav_account WHERE deleted = 0 ORDER BY UPPER(name) ASC") + @Query("SELECT * FROM caldav_account ORDER BY UPPER(name) ASC") List getAllOrderedByName(); @Insert - void insert(CaldavAccount caldavAccount); + long insert(CaldavAccount caldavAccount); @Update void update(CaldavAccount caldavAccount); - @Query("UPDATE caldav_account SET deleted = (strftime('%s','now')*1000) WHERE uuid = :uuid") - void markAccountDeleted(String uuid); - @Insert void insert(CaldavTask caldavTask); @@ -55,4 +52,13 @@ public interface CaldavDao { @Query("SELECT * FROM caldav_account") List getAccounts(); + + @Delete + void delete(CaldavAccount caldavAccount); + + @Query("SELECT * FROM caldav_account WHERE uuid = :uuid LIMIT 1") + CaldavAccount getAccount(String uuid); + + @Query("DELETE FROM caldav_tasks WHERE account = :uuid") + void deleteTasksForAccount(String uuid); } diff --git a/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.java b/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.java index f0bee4397..42131594b 100644 --- a/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.java +++ b/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.java @@ -21,8 +21,8 @@ import org.tasks.data.GoogleTask; import org.tasks.data.GoogleTaskList; import org.tasks.data.Tag; import org.tasks.data.TagData; -import org.tasks.gtasks.GtaskSyncAdapterHelper; import org.tasks.injection.ForApplication; +import org.tasks.sync.SyncAdapters; import java.util.HashMap; import java.util.List; @@ -47,15 +47,15 @@ public class FilterCriteriaProvider { private final TagService tagService; private final GtasksListService gtasksListService; private final Resources r; - private final GtaskSyncAdapterHelper gtaskSyncAdapterHelper; + private final SyncAdapters syncAdapters; @Inject public FilterCriteriaProvider(@ForApplication Context context, TagService tagService, - GtasksListService gtasksListService, GtaskSyncAdapterHelper gtaskSyncAdapterHelper) { + GtasksListService gtasksListService, SyncAdapters syncAdapters) { this.context = context; this.tagService = tagService; this.gtasksListService = gtasksListService; - this.gtaskSyncAdapterHelper = gtaskSyncAdapterHelper; + this.syncAdapters = syncAdapters; r = context.getResources(); } @@ -68,7 +68,7 @@ public class FilterCriteriaProvider { result.add(getDueDateFilter()); result.add(getImportanceFilter()); result.add(getTaskTitleContainsFilter()); - if (gtaskSyncAdapterHelper.isEnabled()) { + if (syncAdapters.isGoogleTaskSyncEnabled()) { result.add(getGtasksFilterCriteria()); } diff --git a/app/src/main/java/org/tasks/fragments/TaskEditControlSetFragmentManager.java b/app/src/main/java/org/tasks/fragments/TaskEditControlSetFragmentManager.java index 6be89f958..783732668 100644 --- a/app/src/main/java/org/tasks/fragments/TaskEditControlSetFragmentManager.java +++ b/app/src/main/java/org/tasks/fragments/TaskEditControlSetFragmentManager.java @@ -16,8 +16,8 @@ import com.todoroo.astrid.ui.ReminderControlSet; import org.tasks.BuildConfig; import org.tasks.R; -import org.tasks.gtasks.GtaskSyncAdapterHelper; import org.tasks.preferences.Preferences; +import org.tasks.sync.SyncAdapters; import org.tasks.ui.CalendarControlSet; import org.tasks.ui.DeadlineControlSet; import org.tasks.ui.DescriptionControlSet; @@ -72,11 +72,11 @@ public class TaskEditControlSetFragmentManager { private final Map controlSetFragments = new LinkedHashMap<>(); private final List displayOrder; - private final GtaskSyncAdapterHelper gtaskSyncAdapterHelper; + private final SyncAdapters syncAdapters; private int numRows; - public TaskEditControlSetFragmentManager(Activity activity, Preferences preferences, GtaskSyncAdapterHelper gtaskSyncAdapterHelper) { - this.gtaskSyncAdapterHelper = gtaskSyncAdapterHelper; + public TaskEditControlSetFragmentManager(Activity activity, Preferences preferences, SyncAdapters syncAdapters) { + this.syncAdapters = syncAdapters; displayOrder = BeastModePreferences.constructOrderedControlList(preferences, activity); displayOrder.add(0, activity.getString(EditTitleControlSet.TAG)); displayOrder.add(1, activity.getString(CommentBarFragment.TAG)); @@ -151,7 +151,7 @@ public class TaskEditControlSetFragmentManager { case CommentBarFragment.TAG: return new CommentBarFragment(); case RemoteListFragment.TAG: - return gtaskSyncAdapterHelper.isEnabled() + return syncAdapters.isSyncEnabled() ? new RemoteListFragment() : null; default: diff --git a/app/src/main/java/org/tasks/injection/ActivityModule.java b/app/src/main/java/org/tasks/injection/ActivityModule.java index 041070251..f72b68631 100644 --- a/app/src/main/java/org/tasks/injection/ActivityModule.java +++ b/app/src/main/java/org/tasks/injection/ActivityModule.java @@ -5,8 +5,8 @@ import android.content.Context; import org.tasks.R; import org.tasks.fragments.TaskEditControlSetFragmentManager; -import org.tasks.gtasks.GtaskSyncAdapterHelper; import org.tasks.preferences.Preferences; +import org.tasks.sync.SyncAdapters; import org.tasks.themes.ThemeAccent; import org.tasks.themes.ThemeBase; import org.tasks.themes.ThemeCache; @@ -55,7 +55,7 @@ public class ActivityModule { @Provides @ActivityScope - public TaskEditControlSetFragmentManager getTaskEditControlSetFragmentManager(Preferences preferences, GtaskSyncAdapterHelper gtaskSyncAdapterHelper) { - return new TaskEditControlSetFragmentManager(activity, preferences, gtaskSyncAdapterHelper); + public TaskEditControlSetFragmentManager getTaskEditControlSetFragmentManager(Preferences preferences, SyncAdapters syncAdapters) { + return new TaskEditControlSetFragmentManager(activity, preferences, syncAdapters); } } diff --git a/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.java b/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.java index 6509beb5e..1de929f28 100644 --- a/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.java +++ b/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.java @@ -40,6 +40,7 @@ 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 final RefreshReceiver refreshReceiver = new RefreshReceiver(); @@ -82,7 +83,8 @@ public class NavigationDrawerFragment extends InjectingFragment { } } else if (requestCode == REQUEST_NEW_LIST || requestCode == ACTIVITY_REQUEST_NEW_FILTER || - requestCode == REQUEST_NEW_GTASK_LIST) { + requestCode == REQUEST_NEW_GTASK_LIST || + requestCode == REQUEST_NEW_CALDAV_ACCOUNT) { if (resultCode == RESULT_OK && data != null) { Filter newList = data.getParcelableExtra(TaskListActivity.OPEN_FILTER); if (newList != null) { diff --git a/app/src/main/res/layout/activity_caldav_settings.xml b/app/src/main/res/layout/activity_caldav_settings.xml new file mode 100644 index 000000000..264b1583d --- /dev/null +++ b/app/src/main/res/layout/activity_caldav_settings.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 035d44786..99ee50a64 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -11,6 +11,7 @@ 18dp 17dp 16sp + 18sp 12 diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 2c3611def..67a7ec559 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -287,6 +287,7 @@ Debug start_of_week use_native_datetime_pickers + org.tasks.caldav gtask_background_sync bundle_notifications strict_mode diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 927f846db..f03edf49f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -756,6 +756,11 @@ File %1$s contained %2$s.\n\n Send anonymous usage statistics and crash reports to help improve Tasks. No personal data will be collected. Tag already exists Name cannot be empty + Username required + Password required + URL required + Host name required + Must begin with http(s):// (No title) Back button saves task Default list @@ -832,6 +837,13 @@ File %1$s contained %2$s.\n\n Use locale default Use native date and time pickers CalDAV + Add account + User + Password + URL + Error adding account + Error renaming account + Error deleting account Account settings Manage notifications Manage battery optimizations diff --git a/app/src/main/res/xml/account_authenticator.xml b/app/src/main/res/xml/account_authenticator.xml new file mode 100644 index 000000000..7e3dd2637 --- /dev/null +++ b/app/src/main/res/xml/account_authenticator.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/app/src/main/res/xml/sync_adapter_caldav.xml b/app/src/main/res/xml/sync_adapter_caldav.xml new file mode 100644 index 000000000..6dd0a24ca --- /dev/null +++ b/app/src/main/res/xml/sync_adapter_caldav.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file