Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/stefan-niedermann/nextcloud-notes.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Niedermann <info@niedermann.it>2021-04-20 10:47:53 +0300
committerStefan Niedermann <info@niedermann.it>2021-04-20 10:47:53 +0300
commitfa03e56883e93d3181c48c5eb9dc1d6c325469ad (patch)
tree6ad0234043055b8810d8d4dcb0175ea1e9699d23 /app/src/main/java/it/niedermann/owncloud
parente8e350e99cb322ad0dc3da2eb9493ae7ed21e2a7 (diff)
#831 Better handling of error states while synchronizing capabilities and notes by using callbacks instead of LiveData (which is not really error aware)
Diffstat (limited to 'app/src/main/java/it/niedermann/owncloud')
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/exception/IntendedOfflineException.java14
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java69
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java143
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/shared/model/IResponseCallback.java9
4 files changed, 149 insertions, 86 deletions
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/exception/IntendedOfflineException.java b/app/src/main/java/it/niedermann/owncloud/notes/exception/IntendedOfflineException.java
new file mode 100644
index 00000000..1cd1f206
--- /dev/null
+++ b/app/src/main/java/it/niedermann/owncloud/notes/exception/IntendedOfflineException.java
@@ -0,0 +1,14 @@
+package it.niedermann.owncloud.notes.exception;
+
+import androidx.annotation.NonNull;
+
+/**
+ * This type of {@link Exception} occurs, when a user has an active internet connection but decided by intention not to use it.
+ * Example: "Sync only on Wi-Fi" is set to <code>true</code>, Wi-Fi is not connected, mobile data is available
+ */
+public class IntendedOfflineException extends Exception {
+
+ public IntendedOfflineException(@NonNull String message) {
+ super(message);
+ }
+}
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java
index f36c0c02..3600b0d6 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java
@@ -58,6 +58,7 @@ import it.niedermann.owncloud.notes.edit.EditNoteActivity;
import it.niedermann.owncloud.notes.edit.category.CategoryDialogFragment;
import it.niedermann.owncloud.notes.edit.category.CategoryViewModel;
import it.niedermann.owncloud.notes.exception.ExceptionDialogFragment;
+import it.niedermann.owncloud.notes.exception.IntendedOfflineException;
import it.niedermann.owncloud.notes.importaccount.ImportAccountActivity;
import it.niedermann.owncloud.notes.main.items.ItemAdapter;
import it.niedermann.owncloud.notes.main.items.grid.GridItemDecoration;
@@ -74,6 +75,7 @@ import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.persistence.entity.Note;
import it.niedermann.owncloud.notes.shared.model.Capabilities;
import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod;
+import it.niedermann.owncloud.notes.shared.model.IResponseCallback;
import it.niedermann.owncloud.notes.shared.model.NavigationCategory;
import it.niedermann.owncloud.notes.shared.model.NoteClickListener;
import it.niedermann.owncloud.notes.shared.util.NoteUtil;
@@ -265,11 +267,21 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
.apply(RequestOptions.circleCropTransform())
.into(activityBinding.launchAccountSwitcher);
- final LiveData<Boolean> syncLiveData = mainViewModel.synchronize();
- syncLiveData.observe(this, (syncSuccess) -> {
- syncLiveData.removeObservers(this);
- if (!syncSuccess) {
- BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
+ mainViewModel.synchronizeNotes(nextAccount, new IResponseCallback() {
+ @Override
+ public void onSuccess() {
+ Log.d(TAG, "Successfully synchronized notes for " + nextAccount.getAccountName());
+ }
+
+ @Override
+ public void onError(@NonNull Throwable t) {
+ runOnUiThread(() -> {
+ if (t.getClass() == IntendedOfflineException.class || t instanceof IntendedOfflineException) {
+ Log.i(TAG, "Capabilities and notes not updated because " + nextAccount.getAccountName() + " is offline by intention.");
+ } else {
+ BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
+ }
+ });
}
});
fabCreate.show();
@@ -294,12 +306,20 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
@Override
protected void onResume() {
- final LiveData<Boolean> syncLiveData = mainViewModel.synchronize();
- syncLiveData.observe(this, (syncSuccess) -> {
- syncLiveData.removeObservers(this);
- if (!syncSuccess) {
- BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
- }
+ final LiveData<Account> accountLiveData = mainViewModel.getCurrentAccount();
+ accountLiveData.observe(this, (currentAccount) -> {
+ accountLiveData.removeObservers(this);
+ mainViewModel.synchronizeNotes(currentAccount, new IResponseCallback() {
+ @Override
+ public void onSuccess() {
+ Log.d(TAG, "Successfully synchronized notes for " + currentAccount.getAccountName());
+ }
+
+ @Override
+ public void onError(@NonNull Throwable t) {
+ t.printStackTrace();
+ }
+ });
});
super.onResume();
}
@@ -404,13 +424,28 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
new Thread(() -> {
Log.i(TAG, "Clearing Glide disk cache");
Glide.get(getApplicationContext()).clearDiskCache();
- }).start();
- final LiveData<Boolean> syncLiveData = mainViewModel.performFullSynchronizationForCurrentAccount();
- final Observer<Boolean> syncObserver = syncSuccess -> {
- if (!syncSuccess) {
- BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
- }
+ }, "CLEAR_GLIDE_CACHE").start();
+ final LiveData<Account> syncLiveData = mainViewModel.getCurrentAccount();
+ final Observer<Account> syncObserver = currentAccount -> {
syncLiveData.removeObservers(this);
+ mainViewModel.synchronizeCapabilitiesAndNotes(currentAccount, new IResponseCallback() {
+ @Override
+ public void onSuccess() {
+ Log.d(TAG, "Successfully synchronized capabilities and notes for " + currentAccount.getAccountName());
+ }
+
+ @Override
+ public void onError(@NonNull Throwable t) {
+ runOnUiThread(() -> {
+ swipeRefreshLayout.setRefreshing(false);
+ if (t.getClass() == IntendedOfflineException.class || t instanceof IntendedOfflineException) {
+ Log.i(TAG, "Capabilities and notes not updated because " + currentAccount.getAccountName() + " is offline by intention.");
+ } else {
+ BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
+ }
+ });
+ }
+ });
};
syncLiveData.observe(this, syncObserver);
});
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java
index b528941c..0241951e 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java
@@ -1,5 +1,6 @@
package it.niedermann.owncloud.notes.main;
+import android.accounts.NetworkErrorException;
import android.app.Application;
import android.content.Context;
import android.text.TextUtils;
@@ -29,6 +30,7 @@ import java.util.stream.Collectors;
import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.branding.BrandingUtil;
+import it.niedermann.owncloud.notes.exception.IntendedOfflineException;
import it.niedermann.owncloud.notes.main.navigation.NavigationAdapter;
import it.niedermann.owncloud.notes.main.navigation.NavigationItem;
import it.niedermann.owncloud.notes.persistence.CapabilitiesClient;
@@ -40,6 +42,7 @@ import it.niedermann.owncloud.notes.persistence.entity.Note;
import it.niedermann.owncloud.notes.persistence.entity.SingleNoteWidgetData;
import it.niedermann.owncloud.notes.shared.model.Capabilities;
import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod;
+import it.niedermann.owncloud.notes.shared.model.IResponseCallback;
import it.niedermann.owncloud.notes.shared.model.Item;
import it.niedermann.owncloud.notes.shared.model.NavigationCategory;
@@ -366,32 +369,83 @@ public class MainViewModel extends AndroidViewModel {
return items;
}
+ public void synchronizeCapabilitiesAndNotes(@NonNull Account localAccount, @NonNull IResponseCallback callback) {
+ Log.i(TAG, "[synchronizeCapabilitiesAndNotes] Synchronize capabilities for " + localAccount.getAccountName());
+ synchronizeCapabilities(localAccount, new IResponseCallback() {
+ @Override
+ public void onSuccess() {
+ Log.i(TAG, "[synchronizeCapabilitiesAndNotes] Synchronize notes for " + localAccount.getAccountName());
+ synchronizeNotes(localAccount, callback);
+ }
+
+ @Override
+ public void onError(@NonNull Throwable t) {
+ callback.onError(t);
+ }
+ });
+ }
+
/**
- * @return <code>true</code>, if a synchronization could successfully be triggered, <code>false</code> if not.
+ * Updates the network status if necessary and pulls the latest {@link Capabilities} of the given {@param localAccount}
*/
- public LiveData<Boolean> synchronize() {
- return switchMap(getCurrentAccount(), currentAccount -> {
- if (currentAccount == null) {
- return new MutableLiveData<>(false);
- } else {
- Log.v(TAG, "[synchronize] - currentAccount: " + currentAccount.getAccountName());
- NotesServerSyncHelper syncHelper = db.getNoteServerSyncHelper();
- if (!syncHelper.isSyncPossible()) {
- syncHelper.updateNetworkStatus();
- }
- if (syncHelper.isSyncPossible()) {
- syncHelper.scheduleSync(currentAccount, false);
- return new MutableLiveData<>(true);
- } else { // Sync is not possible
- if (syncHelper.isNetworkConnected() && syncHelper.isSyncOnlyOnWifi()) {
- Log.d(TAG, "Network is connected, but sync is not possible");
+ public void synchronizeCapabilities(@NonNull Account localAccount, @NonNull IResponseCallback callback) {
+ new Thread(() -> {
+ final NotesServerSyncHelper syncHelper = db.getNoteServerSyncHelper();
+ if (!syncHelper.isSyncPossible()) {
+ syncHelper.updateNetworkStatus();
+ }
+ if (syncHelper.isSyncPossible()) {
+ try {
+ final Capabilities capabilities = CapabilitiesClient.getCapabilities(getApplication(), AccountImporter.getSingleSignOnAccount(getApplication(), localAccount.getAccountName()), localAccount.getCapabilitiesETag());
+ db.getAccountDao().updateCapabilitiesETag(localAccount.getId(), capabilities.getETag());
+ db.getAccountDao().updateBrand(localAccount.getId(), capabilities.getColor(), capabilities.getTextColor());
+ localAccount.setColor(capabilities.getColor());
+ localAccount.setTextColor(capabilities.getTextColor());
+ BrandingUtil.saveBrandColors(getApplication(), localAccount.getColor(), localAccount.getTextColor());
+ db.updateApiVersion(localAccount.getId(), capabilities.getApiVersion());
+ callback.onSuccess();
+ } catch (NextcloudFilesAppAccountNotFoundException e) {
+ db.getAccountDao().deleteAccount(localAccount);
+ callback.onError(e);
+ } catch (Exception e) {
+ if (e instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) e).getStatusCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
+ Log.i(TAG, "[synchronizeCapabilities] Capabilities not modified.");
+ callback.onSuccess();
} else {
- Log.d(TAG, "Sync is not possible, because network is not connected");
+ callback.onError(e);
}
}
- return new MutableLiveData<>(false);
+ } else {
+ if (syncHelper.isNetworkConnected() && syncHelper.isSyncOnlyOnWifi()) {
+ callback.onError(new IntendedOfflineException("Network is connected, but sync is not possible."));
+ } else {
+ callback.onError(new NetworkErrorException("Sync is not possible, because network is not connected."));
+ }
}
- });
+ }, "SYNC_CAPABILITIES").start();
+ }
+
+ /**
+ * Updates the network status if necessary and pulls the latest notes of the given {@param localAccount}
+ */
+ public void synchronizeNotes(@NonNull Account currentAccount, @NonNull IResponseCallback callback) {
+ new Thread(() -> {
+ Log.v(TAG, "[synchronize] - currentAccount: " + currentAccount.getAccountName());
+ final NotesServerSyncHelper syncHelper = db.getNoteServerSyncHelper();
+ if (!syncHelper.isSyncPossible()) {
+ syncHelper.updateNetworkStatus();
+ }
+ if (syncHelper.isSyncPossible()) {
+ syncHelper.scheduleSync(currentAccount, false);
+ callback.onSuccess();
+ } else { // Sync is not possible
+ if (syncHelper.isNetworkConnected() && syncHelper.isSyncOnlyOnWifi()) {
+ callback.onError(new IntendedOfflineException("Network is connected, but sync is not possible."));
+ } else {
+ callback.onError(new NetworkErrorException("Sync is not possible, because network is not connected."));
+ }
+ }
+ }, "SYNC_NOTES").start();
}
public LiveData<Boolean> getSyncStatus() {
@@ -406,55 +460,6 @@ public class MainViewModel extends AndroidViewModel {
return map(db.getAccountDao().countAccounts$(), (counter) -> counter != null && counter > 1);
}
- public LiveData<Boolean> performFullSynchronizationForCurrentAccount() {
- final MutableLiveData<Boolean> insufficientInformation = new MutableLiveData<>();
- return switchMap(getCurrentAccount(), localAccount -> {
- Log.v(TAG, "[performFullSynchronizationForCurrentAccount] - currentAccount: " + localAccount);
- if (localAccount == null) {
- return insufficientInformation;
- } else {
- Log.i(TAG, "[performFullSynchronizationForCurrentAccount] Refreshing capabilities for " + localAccount.getAccountName());
- final MutableLiveData<Boolean> syncCapabilitiesLiveData = new MutableLiveData<>();
- new Thread(() -> {
- final Capabilities capabilities;
- try {
- capabilities = CapabilitiesClient.getCapabilities(getApplication(), AccountImporter.getSingleSignOnAccount(getApplication(), localAccount.getAccountName()), localAccount.getCapabilitiesETag());
- db.getAccountDao().updateCapabilitiesETag(localAccount.getId(), capabilities.getETag());
- db.getAccountDao().updateBrand(localAccount.getId(), capabilities.getColor(), capabilities.getTextColor());
- localAccount.setColor(capabilities.getColor());
- localAccount.setTextColor(capabilities.getTextColor());
- BrandingUtil.saveBrandColors(getApplication(), localAccount.getColor(), localAccount.getTextColor());
- db.updateApiVersion(localAccount.getId(), capabilities.getApiVersion());
- Log.i(TAG, capabilities.toString());
- syncCapabilitiesLiveData.postValue(true);
- } catch (NextcloudFilesAppAccountNotFoundException e) {
- e.printStackTrace();
- db.getAccountDao().deleteAccount(localAccount);
- syncCapabilitiesLiveData.postValue(false);
- } catch (Exception e) {
- if (e instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) e).getStatusCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
- Log.i(TAG, "[performFullSynchronizationForCurrentAccount] Capabilities not modified.");
- } else {
- e.printStackTrace();
- }
- // Capabilities couldn't be update correctly, we can still try to sync the notes list.
- syncCapabilitiesLiveData.postValue(true);
- }
-
- }).start();
- return switchMap(syncCapabilitiesLiveData, capabilitiesSyncedSuccessfully -> {
- if (Boolean.TRUE.equals(capabilitiesSyncedSuccessfully)) {
- Log.v(TAG, "[performFullSynchronizationForCurrentAccount] Capabilities refreshed successfully - synchronize notes for " + localAccount.getAccountName());
- return synchronize();
- } else {
- Log.w(TAG, "[performFullSynchronizationForCurrentAccount] Capabilities could not be refreshed correctly - end synchronization process here.");
- return new MutableLiveData<>(true);
- }
- });
- }
- });
- }
-
@WorkerThread
public Account getLocalAccountByAccountName(String accountName) {
return db.getAccountDao().getAccountByName(accountName);
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/IResponseCallback.java b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/IResponseCallback.java
new file mode 100644
index 00000000..2c329727
--- /dev/null
+++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/IResponseCallback.java
@@ -0,0 +1,9 @@
+package it.niedermann.owncloud.notes.shared.model;
+
+import androidx.annotation.NonNull;
+
+public interface IResponseCallback {
+ void onSuccess();
+
+ void onError(@NonNull Throwable t);
+}