use of com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException in project nextcloud-notes by stefan-niedermann.
the class MainViewModel method synchronizeCapabilities.
/**
* Updates the network status if necessary and pulls the latest {@link Capabilities} of the given {@param localAccount}
*/
public void synchronizeCapabilities(@NonNull Account localAccount, @NonNull IResponseCallback<Void> callback) {
executor.submit(() -> {
if (!repo.isSyncPossible()) {
repo.updateNetworkStatus();
}
if (repo.isSyncPossible()) {
try {
final var ssoAccount = AccountImporter.getSingleSignOnAccount(getApplication(), localAccount.getAccountName());
try {
final var capabilities = CapabilitiesClient.getCapabilities(getApplication(), ssoAccount, localAccount.getCapabilitiesETag(), ApiProvider.getInstance());
repo.updateCapabilitiesETag(localAccount.getId(), capabilities.getETag());
repo.updateBrand(localAccount.getId(), capabilities.getColor(), capabilities.getTextColor());
localAccount.setColor(capabilities.getColor());
localAccount.setTextColor(capabilities.getTextColor());
BrandingUtil.saveBrandColors(getApplication(), localAccount.getColor(), localAccount.getTextColor());
repo.updateApiVersion(localAccount.getId(), capabilities.getApiVersion());
callback.onSuccess(null);
} catch (Throwable t) {
if (t.getClass() == NextcloudHttpRequestFailedException.class || t instanceof NextcloudHttpRequestFailedException) {
if (((NextcloudHttpRequestFailedException) t).getStatusCode() == HTTP_NOT_MODIFIED) {
Log.d(TAG, "Server returned HTTP Status Code " + ((NextcloudHttpRequestFailedException) t).getStatusCode() + " - Capabilities not modified.");
callback.onSuccess(null);
return;
}
}
callback.onError(t);
}
} catch (NextcloudFilesAppAccountNotFoundException e) {
repo.deleteAccount(localAccount);
callback.onError(e);
}
} else {
if (repo.isNetworkConnected() && repo.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");
}
use of com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException in project nextcloud-notes by stefan-niedermann.
the class NotesServerSyncTask method pushLocalChanges.
/**
* Push local changes: for each locally created/edited/deleted Note, use NotesClient in order to push the changed to the server.
*/
private boolean pushLocalChanges() {
Log.d(TAG, "pushLocalChanges()");
boolean success = true;
final var notes = repo.getLocalModifiedNotes(localAccount.getId());
for (Note note : notes) {
Log.d(TAG, " Process Local Note: " + (BuildConfig.DEBUG ? note : note.getTitle()));
try {
Note remoteNote;
switch(note.getStatus()) {
case LOCAL_EDITED:
Log.v(TAG, " ...create/edit");
if (note.getRemoteId() != null) {
Log.v(TAG, " ...Note has remoteId → try to edit");
final var editResponse = notesAPI.editNote(note).execute();
if (editResponse.isSuccessful()) {
remoteNote = editResponse.body();
if (remoteNote == null) {
Log.e(TAG, " ...Tried to edit \"" + note.getTitle() + "\" (#" + note.getId() + ") but the server response was null.");
throw new Exception("Server returned null after editing \"" + note.getTitle() + "\" (#" + note.getId() + ")");
}
} else if (editResponse.code() == HTTP_NOT_FOUND) {
Log.v(TAG, " ...Note does no longer exist on server → recreate");
final var createResponse = notesAPI.createNote(note).execute();
if (createResponse.isSuccessful()) {
remoteNote = createResponse.body();
if (remoteNote == null) {
Log.e(TAG, " ...Tried to recreate \"" + note.getTitle() + "\" (#" + note.getId() + ") but the server response was null.");
throw new Exception("Server returned null after recreating \"" + note.getTitle() + "\" (#" + note.getId() + ")");
}
} else {
throw new Exception(createResponse.message());
}
} else {
throw new Exception(editResponse.message());
}
} else {
Log.v(TAG, " ...Note does not have a remoteId yet → create");
final var createResponse = notesAPI.createNote(note).execute();
if (createResponse.isSuccessful()) {
remoteNote = createResponse.body();
if (remoteNote == null) {
Log.e(TAG, " ...Tried to create \"" + note.getTitle() + "\" (#" + note.getId() + ") but the server response was null.");
throw new Exception("Server returned null after creating \"" + note.getTitle() + "\" (#" + note.getId() + ")");
}
repo.updateRemoteId(note.getId(), remoteNote.getRemoteId());
} else {
throw new Exception(createResponse.message());
}
}
// Please note, that db.updateNote() realized an optimistic conflict resolution, which is required for parallel changes of this Note from the UI.
repo.updateIfNotModifiedLocallyDuringSync(note.getId(), remoteNote.getModified().getTimeInMillis(), remoteNote.getTitle(), remoteNote.getFavorite(), remoteNote.getETag(), remoteNote.getContent(), generateNoteExcerpt(remoteNote.getContent(), remoteNote.getTitle()), note.getContent(), note.getCategory(), note.getFavorite());
break;
case LOCAL_DELETED:
if (note.getRemoteId() == null) {
Log.v(TAG, " ...delete (only local, since it has never been synchronized)");
} else {
Log.v(TAG, " ...delete (from server and local)");
final var deleteResponse = notesAPI.deleteNote(note.getRemoteId()).execute();
if (!deleteResponse.isSuccessful()) {
if (deleteResponse.code() == HTTP_NOT_FOUND) {
Log.v(TAG, " ...delete (note has already been deleted remotely)");
} else {
throw new Exception(deleteResponse.message());
}
}
}
// Please note, that db.deleteNote() realizes an optimistic conflict resolution, which is required for parallel changes of this Note from the UI.
repo.deleteByNoteId(note.getId(), LOCAL_DELETED);
break;
default:
throw new IllegalStateException("Unknown State of Note " + note + ": " + note.getStatus());
}
} catch (NextcloudHttpRequestFailedException e) {
if (e.getStatusCode() == HTTP_NOT_MODIFIED) {
Log.d(TAG, "Server returned HTTP Status Code 304 - Not Modified");
} else {
exceptions.add(e);
success = false;
}
} catch (Exception e) {
if (e instanceof TokenMismatchException) {
apiProvider.invalidateAPICache(ssoAccount);
}
exceptions.add(e);
success = false;
}
}
return success;
}
use of com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException in project nextcloud-notes by stefan-niedermann.
the class MainActivity method setupNotesList.
private void setupNotesList() {
adapter = new ItemAdapter(this, gridView);
listView.setAdapter(adapter);
listView.setItemAnimator(null);
if (gridView) {
final int spanCount = getResources().getInteger(R.integer.grid_view_span_count);
final var gridLayoutManager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL);
listView.setLayoutManager(gridLayoutManager);
listView.addItemDecoration(new GridItemDecoration(adapter, spanCount, getResources().getDimensionPixelSize(R.dimen.spacer_3x), getResources().getDimensionPixelSize(R.dimen.spacer_5x), getResources().getDimensionPixelSize(R.dimen.spacer_3x), getResources().getDimensionPixelSize(R.dimen.spacer_1x), getResources().getDimensionPixelSize(R.dimen.spacer_activity_sides) + getResources().getDimensionPixelSize(R.dimen.spacer_1x)));
} else {
final var layoutManager = new LinearLayoutManager(this);
listView.setLayoutManager(layoutManager);
listView.addItemDecoration(new SectionItemDecoration(adapter, getResources().getDimensionPixelSize(R.dimen.spacer_activity_sides) + getResources().getDimensionPixelSize(R.dimen.spacer_1x) + getResources().getDimensionPixelSize(R.dimen.spacer_3x) + getResources().getDimensionPixelSize(R.dimen.spacer_2x), getResources().getDimensionPixelSize(R.dimen.spacer_5x), getResources().getDimensionPixelSize(R.dimen.spacer_1x), 0));
}
listView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (dy > 0)
fabCreate.hide();
else if (dy < 0)
fabCreate.show();
}
});
swipeRefreshLayout.setOnRefreshListener(() -> {
CustomAppGlideModule.clearCache(this);
final var syncLiveData = mainViewModel.getCurrentAccount();
final Observer<Account> syncObserver = currentAccount -> {
syncLiveData.removeObservers(this);
mainViewModel.synchronizeCapabilitiesAndNotes(currentAccount, new IResponseCallback<>() {
@Override
public void onSuccess(Void v) {
Log.d(TAG, "Successfully synchronized capabilities and notes for " + currentAccount.getAccountName());
}
@Override
public void onError(@NonNull Throwable t) {
runOnUiThread(() -> {
swipeRefreshLayout.setRefreshing(false);
if (t instanceof IntendedOfflineException) {
Log.i(TAG, "Capabilities and notes not updated because " + currentAccount.getAccountName() + " is offline by intention.");
} else if (t instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) t).getStatusCode() == HttpURLConnection.HTTP_UNAVAILABLE) {
BrandedSnackbar.make(coordinatorLayout, R.string.error_maintenance_mode, Snackbar.LENGTH_LONG).show();
} else if (t instanceof NetworkErrorException) {
BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
} else {
BrandedSnackbar.make(coordinatorLayout, R.string.error_synchronization, Snackbar.LENGTH_LONG).setAction(R.string.simple_more, v -> ExceptionDialogFragment.newInstance(t).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName())).show();
}
});
}
});
};
syncLiveData.observe(this, syncObserver);
});
tracker = ItemSelectionTracker.build(listView, adapter);
adapter.setTracker(tracker);
tracker.addObserver(new SelectionTracker.SelectionObserver<Long>() {
@Override
public void onSelectionChanged() {
super.onSelectionChanged();
if (tracker.hasSelection() && mActionMode == null) {
mActionMode = startSupportActionMode(new MultiSelectedActionModeCallback(MainActivity.this, coordinatorLayout, mainViewModel, MainActivity.this, canMoveNoteToAnotherAccounts, tracker, getSupportFragmentManager()));
}
if (mActionMode != null) {
if (tracker.hasSelection()) {
int selected = tracker.getSelection().size();
mActionMode.setTitle(getResources().getQuantityString(R.plurals.ab_selected, selected, selected));
} else {
mActionMode.finish();
mActionMode = null;
}
}
}
});
itemTouchHelper = new NotesListViewItemTouchHelper(this, mainViewModel, this, tracker, adapter, swipeRefreshLayout, coordinatorLayout, gridView);
itemTouchHelper.attachToRecyclerView(listView);
}
use of com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException in project nextcloud-notes by stefan-niedermann.
the class TipsAdapter method setThrowables.
public void setThrowables(@NonNull List<Throwable> throwables) {
for (final var throwable : throwables) {
if (throwable instanceof TokenMismatchException) {
add(R.string.error_dialog_tip_token_mismatch_retry);
add(R.string.error_dialog_tip_token_mismatch_clear_storage);
final var intent = new Intent(ACTION_APPLICATION_DETAILS_SETTINGS).setData(Uri.parse("package:" + BuildConfig.APPLICATION_ID)).putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_open_deck_info);
add(R.string.error_dialog_tip_clear_storage, intent);
} else if (throwable instanceof NextcloudFilesAppNotSupportedException) {
add(R.string.error_dialog_tip_files_outdated);
} else if (throwable instanceof NextcloudApiNotRespondingException) {
if (VERSION.SDK_INT >= VERSION_CODES.M) {
add(R.string.error_dialog_tip_disable_battery_optimizations, new Intent().setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS).putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_open_battery_settings));
} else {
add(R.string.error_dialog_tip_disable_battery_optimizations);
}
add(R.string.error_dialog_tip_files_force_stop);
add(R.string.error_dialog_tip_files_delete_storage);
final var intent = new Intent(ACTION_APPLICATION_DETAILS_SETTINGS).setData(Uri.parse("package:" + BuildConfig.APPLICATION_ID)).putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_open_deck_info);
add(R.string.error_dialog_tip_clear_storage, intent);
} else if (throwable instanceof SocketTimeoutException || throwable instanceof ConnectException) {
add(R.string.error_dialog_timeout_instance);
add(R.string.error_dialog_timeout_toggle, new Intent(Settings.ACTION_WIFI_SETTINGS).putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_open_network));
} else if (throwable instanceof JSONException || throwable instanceof NullPointerException) {
add(R.string.error_dialog_check_server);
} else if (throwable instanceof NextcloudHttpRequestFailedException) {
final int statusCode = ((NextcloudHttpRequestFailedException) throwable).getStatusCode();
switch(statusCode) {
case 302:
add(R.string.error_dialog_server_app_enabled);
add(R.string.error_dialog_redirect);
break;
case 500:
add(R.string.error_dialog_check_server_logs);
break;
case 503:
add(R.string.error_dialog_check_maintenance);
break;
case 507:
add(R.string.error_dialog_insufficient_storage);
break;
}
} else if (throwable instanceof UnknownErrorException) {
if ("com.nextcloud.android.sso.QueryParam".equals(throwable.getMessage())) {
add(R.string.error_dialog_min_version, new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.nextcloud.client")).putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_update_files_app));
}
}
}
notifyDataSetChanged();
}
Aggregations