diff --git a/app/schemas/com.todoroo.astrid.dao.Database/59.json b/app/schemas/com.todoroo.astrid.dao.Database/59.json index 16c832d02..2b3376afd 100644 --- a/app/schemas/com.todoroo.astrid.dao.Database/59.json +++ b/app/schemas/com.todoroo.astrid.dao.Database/59.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 59, - "identityHash": "1320507f7cf294e38cdd2d22d1ce4496", + "identityHash": "d4c55b9b8440776e4eb5fdfc9bddaa76", "entities": [ { "tableName": "notification", @@ -820,7 +820,7 @@ }, { "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)", + "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, `error` TEXT)", "fields": [ { "fieldPath": "id", @@ -857,6 +857,12 @@ "columnName": "password", "affinity": "TEXT", "notNull": false + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false } ], "primaryKey": { @@ -870,7 +876,7 @@ }, { "tableName": "google_task_accounts", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `error` TEXT)", "fields": [ { "fieldPath": "id", @@ -883,6 +889,12 @@ "columnName": "account", "affinity": "TEXT", "notNull": false + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false } ], "primaryKey": { @@ -897,7 +909,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, \"1320507f7cf294e38cdd2d22d1ce4496\")" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"d4c55b9b8440776e4eb5fdfc9bddaa76\")" ] } } \ No newline at end of file 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 8856a30c1..bb5f915e6 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java +++ b/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java @@ -6,6 +6,7 @@ package com.todoroo.astrid.adapter; import static android.support.v4.content.ContextCompat.getColor; +import static com.google.common.base.Strings.isNullOrEmpty; import static com.todoroo.andlib.utility.AndroidUtilities.preLollipop; import static org.tasks.caldav.CaldavCalendarSettingsActivity.EXTRA_CALDAV_ACCOUNT; @@ -48,6 +49,7 @@ import org.tasks.filters.NavigationDrawerSeparator; import org.tasks.filters.NavigationDrawerSubheader; import org.tasks.locale.Locale; import org.tasks.preferences.BasicPreferences; +import org.tasks.sync.SynchronizationPreferences; import org.tasks.themes.Theme; import org.tasks.themes.ThemeCache; import org.tasks.ui.NavigationDrawerFragment; @@ -161,6 +163,9 @@ public class FilterAdapter extends ArrayAdapter { case SUBHEADER: convertView = inflater.inflate(R.layout.filter_adapter_subheader, parent, false); viewHolder.name = convertView.findViewById(R.id.subheader_text); + viewHolder.icon = convertView.findViewById(R.id.subheader_icon); + viewHolder.icon.setOnClickListener( + v -> activity.startActivity(new Intent(activity, SynchronizationPreferences.class))); break; } viewHolder.view = convertView; @@ -224,20 +229,21 @@ public class FilterAdapter extends ArrayAdapter { return getView(position, convertView, parent); } - private void addSubMenu(final int titleResource, List filters, boolean hideIfEmpty) { - addSubMenu(activity.getResources().getString(titleResource), filters, hideIfEmpty); + private void addSubMenu( + final int titleResource, boolean error, List filters, boolean hideIfEmpty) { + addSubMenu(activity.getResources().getString(titleResource), error, filters, hideIfEmpty); } /* ====================================================================== * ============================================================= receiver * ====================================================================== */ - private void addSubMenu(String title, List filters, boolean hideIfEmpty) { + private void addSubMenu(String title, boolean error, List filters, boolean hideIfEmpty) { if (hideIfEmpty && filters.isEmpty()) { return; } - add(new NavigationDrawerSubheader(title)); + add(new NavigationDrawerSubheader(title, error)); for (FilterListItem filterListItem : filters) { add(filterListItem); @@ -258,11 +264,13 @@ public class FilterAdapter extends ArrayAdapter { add(item); for (Pair> filters : filterProvider.getGoogleTaskFilters()) { - addSubMenu(filters.first.getAccount(), filters.second, true); + GoogleTaskAccount account = filters.first; + addSubMenu(account.getAccount(), !isNullOrEmpty(account.getError()), filters.second, true); } for (Pair> filters : filterProvider.getCaldavFilters()) { - addSubMenu(filters.first.getName(), filters.second, true); + CaldavAccount account = filters.first; + addSubMenu(account.getName(), !isNullOrEmpty(account.getError()), filters.second, true); } notifyDataSetChanged(); @@ -273,7 +281,7 @@ public class FilterAdapter extends ArrayAdapter { add(filterProvider.getMyTasksFilter()); - addSubMenu(R.string.filters, filterProvider.getFilters(), false); + addSubMenu(R.string.filters, false, filterProvider.getFilters(), false); if (navigationDrawer) { add( @@ -284,7 +292,7 @@ public class FilterAdapter extends ArrayAdapter { NavigationDrawerFragment.ACTIVITY_REQUEST_NEW_FILTER)); } - addSubMenu(R.string.tags, filterProvider.getTags(), false); + addSubMenu(R.string.tags, false, filterProvider.getTags(), false); if (navigationDrawer) { add( @@ -297,7 +305,11 @@ public class FilterAdapter extends ArrayAdapter { for (Pair> filters : filterProvider.getGoogleTaskFilters()) { GoogleTaskAccount account = filters.first; - addSubMenu(account.getAccount(), filters.second, !navigationDrawer); + addSubMenu( + account.getAccount(), + !isNullOrEmpty(account.getError()), + filters.second, + !navigationDrawer); if (navigationDrawer) { add( @@ -312,7 +324,8 @@ public class FilterAdapter extends ArrayAdapter { for (Pair> filters : filterProvider.getCaldavFilters()) { CaldavAccount account = filters.first; - addSubMenu(account.getName(), filters.second, !navigationDrawer); + addSubMenu( + account.getName(), !isNullOrEmpty(account.getError()), filters.second, !navigationDrawer); if (navigationDrawer) { add( @@ -382,12 +395,13 @@ public class FilterAdapter extends ArrayAdapter { } private void populateHeader(ViewHolder viewHolder) { - FilterListItem filter = viewHolder.item; + NavigationDrawerSubheader filter = (NavigationDrawerSubheader) viewHolder.item; if (filter == null) { return; } viewHolder.name.setText(filter.listingTitle); + viewHolder.icon.setVisibility(filter.error ? View.VISIBLE : View.GONE); } /* ====================================================================== diff --git a/app/src/main/java/com/todoroo/astrid/gtasks/auth/GtasksLoginActivity.java b/app/src/main/java/com/todoroo/astrid/gtasks/auth/GtasksLoginActivity.java index 38b689203..e46ec5a6d 100644 --- a/app/src/main/java/com/todoroo/astrid/gtasks/auth/GtasksLoginActivity.java +++ b/app/src/main/java/com/todoroo/astrid/gtasks/auth/GtasksLoginActivity.java @@ -65,12 +65,16 @@ public class GtasksLoginActivity extends InjectingAppCompatActivity { new AuthResultHandler() { @Override public void authenticationSuccessful(String accountName) { - if (googleTaskListDao.getAccount(accountName) == null) { - GoogleTaskAccount googleTaskAccount = new GoogleTaskAccount(); - googleTaskAccount.setAccount(accountName); - googleTaskListDao.insert(googleTaskAccount); - setResult(RESULT_OK); + GoogleTaskAccount account = googleTaskListDao.getAccount(accountName); + if (account == null) { + account = new GoogleTaskAccount(); + account.setAccount(accountName); + googleTaskListDao.insert(account); + } else { + account.setError(""); + googleTaskListDao.update(account); } + setResult(RESULT_OK); finish(); DialogUtilities.dismissDialog(GtasksLoginActivity.this, pd); } diff --git a/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java b/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java index f51b0fe94..c1d230078 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java +++ b/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java @@ -306,6 +306,7 @@ public class CaldavAccountSettingsActivity extends ThemedInjectingAppCompatActiv caldavAccount.setName(getNewName()); caldavAccount.setUrl(principal); caldavAccount.setUsername(getNewUsername()); + caldavAccount.setError(""); if (passwordChanged()) { caldavAccount.setPassword(encryption.encrypt(getNewPassword())); } diff --git a/app/src/main/java/org/tasks/caldav/CaldavClient.java b/app/src/main/java/org/tasks/caldav/CaldavClient.java index 4ee51b073..a2c290e17 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavClient.java +++ b/app/src/main/java/org/tasks/caldav/CaldavClient.java @@ -7,7 +7,6 @@ import static at.bitfire.dav4android.XmlUtils.NS_WEBDAV; import static java.util.Arrays.asList; import at.bitfire.dav4android.BasicDigestAuthHandler; -import at.bitfire.dav4android.DavCalendar; import at.bitfire.dav4android.DavResource; import at.bitfire.dav4android.PropertyCollection; import at.bitfire.dav4android.XmlUtils; @@ -136,14 +135,9 @@ class CaldavClient { .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 null; - } + public List getCalendars() throws IOException, HttpException, DavException { + davResource.propfind( + 1, ResourceType.NAME, DisplayName.NAME, SupportedCalendarComponentSet.NAME, GetCTag.NAME); List urls = new ArrayList<>(); for (DavResource member : davResource.getMembers()) { PropertyCollection properties = member.getProperties(); diff --git a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java index 3b21eb621..e19ba3eb5 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java +++ b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java @@ -48,6 +48,7 @@ import okhttp3.RequestBody; import okhttp3.ResponseBody; import org.tasks.BuildConfig; import org.tasks.LocalBroadcastManager; +import org.tasks.R; import org.tasks.data.CaldavAccount; import org.tasks.data.CaldavCalendar; import org.tasks.data.CaldavDao; @@ -94,12 +95,21 @@ public class CaldavSynchronizer { Thread.currentThread().setContextClassLoader(context.getClassLoader()); for (CaldavAccount account : caldavDao.getAccounts()) { if (isNullOrEmpty(account.getPassword())) { + account.setError(context.getString(R.string.password_required)); + caldavDao.update(account); + localBroadcastManager.broadcastRefreshList(); Timber.e("Missing password for %s", account); continue; } CaldavClient caldavClient = new CaldavClient(account, encryption); - List resources = caldavClient.getCalendars(); - if (resources == null) { + List resources; + try { + resources = caldavClient.getCalendars(); + } catch (IOException | DavException | HttpException e) { + account.setError(e.getMessage()); + caldavDao.update(account); + localBroadcastManager.broadcastRefreshList(); + Timber.e(e); continue; } Set urls = newHashSet(transform(resources, c -> c.getLocation().toString())); @@ -109,7 +119,6 @@ public class CaldavSynchronizer { taskDeleter.markDeleted(caldavDao.getTasksByCalendar(deleted.getUuid())); caldavDao.deleteTasksForCalendar(deleted.getUuid()); caldavDao.delete(deleted); - localBroadcastManager.broadcastRefreshList(); } for (DavResource resource : resources) { String url = resource.getLocation().toString(); @@ -122,10 +131,12 @@ public class CaldavSynchronizer { calendar.setUrl(url); calendar.setUuid(UUIDHelper.newUUID()); calendar.setId(caldavDao.insert(calendar)); - localBroadcastManager.broadcastRefreshList(); } sync(calendar, resource); } + account.setError(""); + caldavDao.update(account); + localBroadcastManager.broadcastRefreshList(); } } diff --git a/app/src/main/java/org/tasks/data/CaldavAccount.java b/app/src/main/java/org/tasks/data/CaldavAccount.java index 3d6a3e15a..abc811ea0 100644 --- a/app/src/main/java/org/tasks/data/CaldavAccount.java +++ b/app/src/main/java/org/tasks/data/CaldavAccount.java @@ -8,7 +8,6 @@ import android.arch.persistence.room.Ignore; import android.arch.persistence.room.PrimaryKey; import android.os.Parcel; import android.os.Parcelable; -import java.util.Arrays; @Entity(tableName = "caldav_account") public class CaldavAccount implements Parcelable { @@ -46,6 +45,9 @@ public class CaldavAccount implements Parcelable { @ColumnInfo(name = "password") private transient String password = ""; + @ColumnInfo(name = "error") + private transient String error = ""; + public CaldavAccount() {} @Ignore @@ -56,6 +58,7 @@ public class CaldavAccount implements Parcelable { url = source.readString(); username = source.readString(); password = source.readString(); + error = source.readString(); } public long getId() { @@ -106,16 +109,38 @@ public class CaldavAccount implements Parcelable { this.password = password; } + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + @Override public String toString() { - return "CaldavAccount{" + - "id=" + id + - ", uuid='" + uuid + '\'' + - ", name='" + name + '\'' + - ", url='" + url + '\'' + - ", username='" + username + '\'' + - ", password='" + password + '\'' + - '}'; + return "CaldavAccount{" + + "id=" + + id + + ", uuid='" + + uuid + + '\'' + + ", name='" + + name + + '\'' + + ", url='" + + url + + '\'' + + ", username='" + + username + + '\'' + + ", password='" + + password + + '\'' + + ", error='" + + error + + '\'' + + '}'; } @Override @@ -144,7 +169,10 @@ public class CaldavAccount implements Parcelable { 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 error != null ? error.equals(that.error) : that.error == null; } @Override @@ -155,6 +183,7 @@ public class CaldavAccount implements Parcelable { 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 + (error != null ? error.hashCode() : 0); return result; } @@ -171,5 +200,6 @@ public class CaldavAccount implements Parcelable { dest.writeString(url); dest.writeString(username); dest.writeString(password); + dest.writeString(error); } } diff --git a/app/src/main/java/org/tasks/data/GoogleTaskAccount.java b/app/src/main/java/org/tasks/data/GoogleTaskAccount.java index 7ae29ec86..447979afd 100644 --- a/app/src/main/java/org/tasks/data/GoogleTaskAccount.java +++ b/app/src/main/java/org/tasks/data/GoogleTaskAccount.java @@ -16,12 +16,16 @@ public class GoogleTaskAccount implements Parcelable { @ColumnInfo(name = "account") private String account; + @ColumnInfo(name = "error") + private transient String error = ""; + public GoogleTaskAccount() {} @Ignore public GoogleTaskAccount(Parcel source) { id = source.readLong(); account = source.readString(); + error = source.readString(); } @Ignore @@ -45,6 +49,14 @@ public class GoogleTaskAccount implements Parcelable { this.account = account; } + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -59,19 +71,32 @@ public class GoogleTaskAccount implements Parcelable { if (id != that.id) { return false; } - return account != null ? account.equals(that.account) : that.account == null; + if (account != null ? !account.equals(that.account) : that.account != null) { + return false; + } + return error != null ? error.equals(that.error) : that.error == null; } @Override public int hashCode() { int result = (int) (id ^ (id >>> 32)); result = 31 * result + (account != null ? account.hashCode() : 0); + result = 31 * result + (error != null ? error.hashCode() : 0); return result; } @Override public String toString() { - return "GoogleTaskAccount{" + "id=" + id + ", account='" + account + '\'' + '}'; + return "GoogleTaskAccount{" + + "id=" + + id + + ", account='" + + account + + '\'' + + ", error='" + + error + + '\'' + + '}'; } public static final Creator CREATOR = @@ -96,5 +121,6 @@ public class GoogleTaskAccount implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeLong(id); dest.writeString(account); + dest.writeString(error); } } diff --git a/app/src/main/java/org/tasks/data/GoogleTaskDao.java b/app/src/main/java/org/tasks/data/GoogleTaskDao.java index b337908a0..920fa154e 100644 --- a/app/src/main/java/org/tasks/data/GoogleTaskDao.java +++ b/app/src/main/java/org/tasks/data/GoogleTaskDao.java @@ -51,4 +51,7 @@ public interface GoogleTaskDao { @Query("SELECT DISTINCT list_id FROM google_tasks WHERE deleted = 0 AND task IN (:tasks)") List getLists(List tasks); + + @Query("SELECT task FROM google_tasks WHERE deleted = 0 AND list_id = :listId") + List getActiveTasks(String listId); } diff --git a/app/src/main/java/org/tasks/data/GoogleTaskListDao.java b/app/src/main/java/org/tasks/data/GoogleTaskListDao.java index bdee5333d..3578b4d60 100644 --- a/app/src/main/java/org/tasks/data/GoogleTaskListDao.java +++ b/app/src/main/java/org/tasks/data/GoogleTaskListDao.java @@ -1,6 +1,7 @@ package org.tasks.data; import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Delete; import android.arch.persistence.room.Insert; import android.arch.persistence.room.OnConflictStrategy; import android.arch.persistence.room.Query; @@ -34,6 +35,9 @@ public interface GoogleTaskListDao { @Query("SELECT * FROM google_task_lists WHERE deleted = 0") List getAllActiveLists(); + @Query("DELETE FROM google_task_lists WHERE _id = :id") + void deleteById(long id); + @Insert(onConflict = OnConflictStrategy.REPLACE) long insertOrReplace(GoogleTaskList googleTaskList); @@ -46,6 +50,12 @@ public interface GoogleTaskListDao { @Update void update(GoogleTaskList googleTaskList); - @Query("DELETE FROM google_task_lists WHERE _id = :id") - void deleteById(long id); + @Update + void update(GoogleTaskAccount account); + + @Delete + void delete(GoogleTaskList list); + + @Delete + void delete(GoogleTaskAccount account); } diff --git a/app/src/main/java/org/tasks/db/Migrations.java b/app/src/main/java/org/tasks/db/Migrations.java index 0a1021b51..e5c300b63 100644 --- a/app/src/main/java/org/tasks/db/Migrations.java +++ b/app/src/main/java/org/tasks/db/Migrations.java @@ -197,9 +197,11 @@ public class Migrations { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL( - "CREATE TABLE IF NOT EXISTS `google_task_accounts` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT)"); + "CREATE TABLE IF NOT EXISTS `google_task_accounts` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `error` TEXT)"); database.execSQL( "ALTER TABLE `google_task_lists` ADD COLUMN `account` TEXT"); + database.execSQL( + "ALTER TABLE `caldav_account` ADD COLUMN `error` TEXT"); } }; diff --git a/app/src/main/java/org/tasks/filters/NavigationDrawerSubheader.java b/app/src/main/java/org/tasks/filters/NavigationDrawerSubheader.java index 36698954a..5815abfca 100644 --- a/app/src/main/java/org/tasks/filters/NavigationDrawerSubheader.java +++ b/app/src/main/java/org/tasks/filters/NavigationDrawerSubheader.java @@ -24,12 +24,27 @@ public class NavigationDrawerSubheader extends FilterListItem { } }; + public boolean error; + private NavigationDrawerSubheader() {} - public NavigationDrawerSubheader(String listingTitle) { + public NavigationDrawerSubheader(String listingTitle, boolean error) { + this.error = error; this.listingTitle = listingTitle; } + @Override + protected void readFromParcel(Parcel source) { + super.readFromParcel(source); + error = source.readInt() == 1; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(error ? 1 : 0); + } + @Override public Type getItemType() { return Type.SUBHEADER; diff --git a/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java b/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java index cee7c2ad0..9c764592b 100644 --- a/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java +++ b/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java @@ -31,6 +31,7 @@ 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; @@ -41,6 +42,7 @@ import org.tasks.data.GoogleTaskListDao; import org.tasks.injection.ForApplication; import org.tasks.notifications.NotificationManager; import org.tasks.preferences.DefaultFilterProvider; +import org.tasks.preferences.PermissionChecker; import org.tasks.preferences.Preferences; import org.tasks.time.DateTime; import timber.log.Timber; @@ -62,6 +64,9 @@ public class GoogleTaskSynchronizer { private final TaskCreator taskCreator; private final DefaultFilterProvider defaultFilterProvider; private final PlayServices playServices; + private final PermissionChecker permissionChecker; + private final GoogleAccountManager googleAccountManager; + private final LocalBroadcastManager localBroadcastManager; @Inject public GoogleTaskSynchronizer( @@ -77,7 +82,10 @@ public class GoogleTaskSynchronizer { GoogleTaskDao googleTaskDao, TaskCreator taskCreator, DefaultFilterProvider defaultFilterProvider, - PlayServices playServices) { + PlayServices playServices, + PermissionChecker permissionChecker, + GoogleAccountManager googleAccountManager, + LocalBroadcastManager localBroadcastManager) { this.context = context; this.googleTaskListDao = googleTaskListDao; this.gtasksSyncService = gtasksSyncService; @@ -91,6 +99,9 @@ public class GoogleTaskSynchronizer { this.taskCreator = taskCreator; this.defaultFilterProvider = defaultFilterProvider; this.playServices = playServices; + this.permissionChecker = permissionChecker; + this.googleAccountManager = googleAccountManager; + this.localBroadcastManager = localBroadcastManager; } public static void mergeDates(long remoteDueDate, Task local) { @@ -113,14 +124,19 @@ public class GoogleTaskSynchronizer { Timber.d("%s: start sync", account); try { synchronize(account); + account.setError(""); } catch (UserRecoverableAuthIOException e) { Timber.e(e); sendNotification(context, e.getIntent()); } catch (IOException e) { + account.setError(e.getMessage()); Timber.e(e); } catch (Exception e) { + account.setError(e.getMessage()); tracker.reportException(e); } finally { + googleTaskListDao.update(account); + localBroadcastManager.broadcastRefreshList(); Timber.d("%s: end sync", account); } } @@ -145,6 +161,13 @@ public class GoogleTaskSynchronizer { } private void synchronize(GoogleTaskAccount account) throws IOException { + if (!permissionChecker.canAccessAccounts() || googleAccountManager.getAccount(account.getAccount()) == null) { + account.setError(context.getString(R.string.cannot_access_account)); + googleTaskListDao.update(account); + localBroadcastManager.broadcastRefreshList(); + return; + } + GtasksInvoker gtasksInvoker = new GtasksInvoker(context, playServices, account.getAccount()); pushLocalChanges(gtasksInvoker); diff --git a/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java b/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java index 61c9f56ca..baea0fb39 100644 --- a/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java +++ b/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java @@ -5,6 +5,7 @@ */ package org.tasks.sync; +import static java.util.Arrays.asList; import static org.tasks.PermissionUtil.verifyPermissions; import android.content.Intent; @@ -12,7 +13,10 @@ import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceCategory; import android.support.annotation.NonNull; +import android.support.v7.app.AlertDialog; import com.todoroo.astrid.gtasks.auth.GtasksLoginActivity; +import com.todoroo.astrid.service.TaskDeleter; +import java.util.List; import javax.inject.Inject; import org.tasks.R; import org.tasks.analytics.Tracker; @@ -24,6 +28,7 @@ import org.tasks.data.CaldavAccount; import org.tasks.data.CaldavDao; import org.tasks.data.GoogleTaskAccount; import org.tasks.data.GoogleTaskDao; +import org.tasks.data.GoogleTaskList; import org.tasks.data.GoogleTaskListDao; import org.tasks.dialogs.DialogBuilder; import org.tasks.gtasks.GoogleAccountManager; @@ -36,7 +41,6 @@ import org.tasks.preferences.ActivityPermissionRequestor; import org.tasks.preferences.PermissionChecker; import org.tasks.preferences.PermissionRequestor; import org.tasks.preferences.Preferences; -import timber.log.Timber; public class SynchronizationPreferences extends InjectingPreferenceActivity { @@ -61,6 +65,7 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity { @Inject JobManager jobManager; @Inject CaldavDao caldavDao; @Inject Inventory inventory; + @Inject TaskDeleter taskDeleter; @Override public void onCreate(Bundle savedInstanceState) { @@ -73,7 +78,7 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity { for (CaldavAccount caldavAccount : caldavDao.getAccounts()) { Preference accountPreferences = new Preference(this); accountPreferences.setTitle(caldavAccount.getName()); - accountPreferences.setSummary(caldavAccount.getUrl()); + accountPreferences.setSummary(caldavAccount.getError()); accountPreferences.setOnPreferenceClickListener( preference -> { Intent intent = new Intent(this, CaldavAccountSettingsActivity.class); @@ -92,19 +97,24 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity { (PreferenceCategory) findPreference(getString(R.string.gtasks_GPr_header)); for (GoogleTaskAccount googleTaskAccount : googleTaskListDao.getAccounts()) { String account = googleTaskAccount.getAccount(); - if (googleAccountManager.getAccount(account) == null) { - Timber.e("Can't access %s", account); - continue; - } Preference accountPreferences = new Preference(this); accountPreferences.setTitle(account); + accountPreferences.setSummary(googleTaskAccount.getError()); accountPreferences.setOnPreferenceClickListener( preference -> { - if (!playServices.refreshAndCheck()) { - playServices.resolve(SynchronizationPreferences.this); - } else if (permissionRequestor.requestAccountPermissions()) { - requestLogin(); - } + dialogBuilder + .newDialog() + .setTitle(account) + .setItems( + asList(getString(R.string.reinitialize_account), getString(R.string.logout)), + (dialog, which) -> { + if (which == 0) { + addGoogleTaskAccount(); + } else { + logoutConfirmation(googleTaskAccount); + } + }) + .showThemedListView(); return false; }); googleTaskPreferences.addPreference(accountPreferences); @@ -128,6 +138,28 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity { }); } + private void logoutConfirmation(GoogleTaskAccount account) { + String name = account.getAccount(); + AlertDialog alertDialog = + dialogBuilder + .newMessageDialog(R.string.logout_warning, name) + .setPositiveButton( + R.string.logout, + (dialog, which) -> { + for (GoogleTaskList list : googleTaskListDao.getActiveLists(name)) { + taskDeleter.markDeleted(googleTaskDao.getActiveTasks(list.getRemoteId())); + googleTaskListDao.delete(list); + } + googleTaskListDao.delete(account); + restart(); + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + alertDialog.setCanceledOnTouchOutside(false); + alertDialog.setCancelable(false); + alertDialog.show(); + } + private void requestLogin() { startActivityForResult( new Intent(SynchronizationPreferences.this, GtasksLoginActivity.class), REQUEST_LOGIN); @@ -149,7 +181,7 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity { }); addGoogleTasks.setOnPreferenceClickListener( preference -> { - requestLogin(); + addGoogleTaskAccount(); return false; }); } else { @@ -164,7 +196,7 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity { addGoogleTasks.setSummary(null); addGoogleTasks.setOnPreferenceClickListener( preference -> { - requestLogin(); + addGoogleTaskAccount(); return false; }); } else { @@ -183,6 +215,14 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity { } } + private void addGoogleTaskAccount() { + if (!playServices.refreshAndCheck()) { + playServices.resolve(this); + } else if (permissionRequestor.requestAccountPermissions()) { + requestLogin(); + } + } + private void addCaldavAccount() { startActivityForResult( new Intent(this, CaldavAccountSettingsActivity.class), REQUEST_CALDAV_SETTINGS); diff --git a/app/src/main/res/drawable/ic_sync_problem_black_24dp.xml b/app/src/main/res/drawable/ic_sync_problem_black_24dp.xml new file mode 100644 index 000000000..5d2720bc8 --- /dev/null +++ b/app/src/main/res/drawable/ic_sync_problem_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/filter_adapter_subheader.xml b/app/src/main/res/layout/filter_adapter_subheader.xml index 6162ce091..aca22fff9 100644 --- a/app/src/main/res/layout/filter_adapter_subheader.xml +++ b/app/src/main/res/layout/filter_adapter_subheader.xml @@ -11,17 +11,40 @@ style="@style/horizontal_divider" android:layout_gravity="top"/> + + Dashclock extension Create new collection Requires pro subscription - + Log out + Log out of %s? All data for this account will be removed from your device + Cannot access account + Reinitialize