diff options
52 files changed, 514 insertions, 362 deletions
diff --git a/app/build.gradle b/app/build.gradle index e711f2889..7ef485dd1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -130,9 +130,6 @@ dependencies { // Retrofit implementation 'com.squareup.retrofit2:retrofit:2.9.0' - // ReactiveX - implementation 'io.reactivex.rxjava2:rxjava:2.2.21' - // Zoom Layout implementation("com.otaliastudios:zoomlayout:1.9.0") diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/DeckLog.java b/app/src/main/java/it/niedermann/nextcloud/deck/DeckLog.java index 1fc69c3b2..d8fd475a3 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/DeckLog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/DeckLog.java @@ -39,6 +39,12 @@ public class DeckLog { } } + public static String getStacktraceAsString(Throwable e) { + final var sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } + public enum Severity { VERBOSE, DEBUG, LOG, INFO, WARN, ERROR, WTF } @@ -87,24 +93,12 @@ public class DeckLog { .append("\n"); } switch (severity) { - case DEBUG: - Log.d(TAG, print); - break; - case INFO: - Log.i(TAG, print); - break; - case WARN: - Log.w(TAG, print); - break; - case ERROR: - Log.e(TAG, print); - break; - case WTF: - Log.wtf(TAG, print); - break; - default: - Log.v(TAG, print); - break; + case DEBUG -> Log.d(TAG, print); + case INFO -> Log.i(TAG, print); + case WARN -> Log.w(TAG, print); + case ERROR -> Log.e(TAG, print); + case WTF -> Log.wtf(TAG, print); + default -> Log.v(TAG, print); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java index a95ab7a8c..a502f9a8e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java @@ -111,7 +111,7 @@ public class DataBaseAdapter { private final int defaultColor; public DataBaseAdapter(@NonNull Context appContext) { - this(appContext, DeckDatabase.getInstance(appContext), Executors.newCachedThreadPool(), ExecutorServiceProvider.getLinkedBlockingQueueExecutor()); + this(appContext, DeckDatabase.getInstance(appContext),Executors.newCachedThreadPool(), ExecutorServiceProvider.getLinkedBlockingQueueExecutor()); } @VisibleForTesting @@ -1801,4 +1801,12 @@ public class DataBaseAdapter { ? entities.get(position - 1).getLocalId() : entities.get(position + 1).getLocalId()); } + + // TODO TEST stuff, remove when done + public List<Long> getAllStackIDs() { + return db.getStackDao().getAllIDs(); + } + public List<Long> getAllCardIDs() { + return db.getCardDao().getAllIDs(); + } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java b/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java index 0f067381e..6778354d9 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java @@ -140,4 +140,6 @@ public interface CardDao extends GenericDao<Card> { "and c.archived = 0 " + "order by s.`order`, c.`order`") LiveData<List<FullCard>> searchCard(long accountId, long localBoardId, String term); + @Query("SELECT s.localId FROM card s") + List<Long> getAllIDs(); }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/StackDao.java b/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/StackDao.java index 5e3093ad2..072bad751 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/StackDao.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/StackDao.java @@ -67,4 +67,7 @@ public interface StackDao extends GenericDao<Stack> { @Query("SELECT s.localId FROM stack s join Board b on s.boardId = b.localId where b.archived <> 0 and b.accountId in (:accountIds)") List<Long> getLocalStackIdsInArchivedBoardsByAccountIdsDirectly(List<Long> accountIds); + + @Query("SELECT s.localId FROM stack s") + List<Long> getAllIDs(); }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/exceptions/OfflineException.java b/app/src/main/java/it/niedermann/nextcloud/deck/exceptions/OfflineException.java index 1415659a5..946f7b049 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/exceptions/OfflineException.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/exceptions/OfflineException.java @@ -27,6 +27,7 @@ public class OfflineException extends IllegalStateException { OFFLINE("Device is currently offline", R.string.error_dialog_tip_offline_no_internet), CONNECTION_REFUSED("Connection refused", R.string.error_dialog_tip_offline_connection_refused), CONNECTION_TIMEOUT("Connection timeout", R.string.error_dialog_tip_offline_connection_timeout), + CONNECTION_REJECTED("Connection rejected", R.string.error_dialog_tip_connection_rejected), ; private final String key; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/adapters/ServerAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/adapters/ServerAdapter.java index ec5dc551b..261e03263 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/adapters/ServerAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/adapters/ServerAdapter.java @@ -10,15 +10,12 @@ import android.webkit.MimeTypeMap; import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; -import com.nextcloud.android.sso.api.EmptyResponse; -import com.nextcloud.android.sso.api.ParsedResponse; import com.nextcloud.android.sso.model.SingleSignOnAccount; import java.io.File; import java.util.List; import it.niedermann.nextcloud.deck.R; -import it.niedermann.nextcloud.deck.exceptions.OfflineException; import it.niedermann.nextcloud.deck.model.AccessControl; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Attachment; @@ -55,6 +52,7 @@ public class ServerAdapter { private final String prefKeyEtags; private final SharedPreferences sharedPreferences; private final ApiProvider provider; + private final RequestHelper requestHelper; public ServerAdapter(@NonNull Context context, @NonNull SingleSignOnAccount ssoAccount, @@ -65,10 +63,18 @@ public class ServerAdapter { public ServerAdapter(@NonNull Context context, @NonNull ApiProvider apiProvider, @NonNull ConnectivityUtil connectivityUtil) { + this(context, apiProvider, connectivityUtil, new RequestHelper(apiProvider, connectivityUtil)); + } + + public ServerAdapter(@NonNull Context context, + @NonNull ApiProvider apiProvider, + @NonNull ConnectivityUtil connectivityUtil, + @NonNull RequestHelper requestHelper) { this.prefKeyEtags = context.getResources().getString(R.string.pref_key_etags); this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); this.connectivityUtil = connectivityUtil; this.provider = apiProvider; + this.requestHelper = requestHelper; } @Deprecated() @@ -76,13 +82,6 @@ public class ServerAdapter { return connectivityUtil.hasInternetConnection(); } - public void ensureInternetConnection() { - final boolean isConnected = connectivityUtil.hasInternetConnection(); - if (!isConnected) { - throw new OfflineException(); - } - } - // TODO what is this? private String getLastSyncDateFormatted(long accountId) { return null; @@ -95,8 +94,8 @@ public class ServerAdapter { // return lastSyncHeader; } - public void getBoards(@NonNull ResponseCallback<ParsedResponse<List<FullBoard>>> responseCallback) { - RequestHelper.request(provider, () -> isEtagsEnabled() + public void getBoards(@NonNull ResponseCallback<List<FullBoard>> responseCallback) { + this.requestHelper.request(() -> isEtagsEnabled() ? provider.getDeckAPI().getBoards(true, getLastSyncDateFormatted(responseCallback.getAccount().getId()), responseCallback.getAccount().getBoardsEtag()) : provider.getDeckAPI().getBoards(true, getLastSyncDateFormatted(responseCallback.getAccount().getId())), responseCallback); } @@ -105,96 +104,78 @@ public class ServerAdapter { return sharedPreferences.getBoolean(prefKeyEtags, true); } - public void getCapabilities(String eTag, @NonNull ResponseCallback<ParsedResponse<Capabilities>> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getNextcloudAPI().getCapabilities(eTag), responseCallback); + public void getCapabilities(String eTag, @NonNull ResponseCallback<Capabilities> responseCallback) { + this.requestHelper.request(() -> provider.getNextcloudAPI().getCapabilities(eTag), responseCallback); } public void getProjectsForCard(long remoteCardId, @NonNull ResponseCallback<OcsProjectList> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getNextcloudAPI().getProjectsForCard(remoteCardId), responseCallback); + this.requestHelper.request(() -> provider.getNextcloudAPI().getProjectsForCard(remoteCardId), responseCallback); } public void searchUser(String searchTerm, @NonNull ResponseCallback<OcsUserList> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getNextcloudAPI().searchUser(searchTerm), responseCallback); + this.requestHelper.request(() -> provider.getNextcloudAPI().searchUser(searchTerm), responseCallback); } public void getSingleUserData(String userUid, @NonNull ResponseCallback<OcsUser> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getNextcloudAPI().getSingleUserData(userUid), responseCallback); + this.requestHelper.request(() -> provider.getNextcloudAPI().getSingleUserData(userUid), responseCallback); } public void searchGroupMembers(String groupUID, @NonNull ResponseCallback<GroupMemberUIDs> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getNextcloudAPI().searchGroupMembers(groupUID), responseCallback); + this.requestHelper.request(() -> provider.getNextcloudAPI().searchGroupMembers(groupUID), responseCallback); } public void getActivitiesForCard(long cardId, @NonNull ResponseCallback<List<Activity>> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getNextcloudAPI().getActivitiesForCard(cardId), responseCallback); + this.requestHelper.request(() -> provider.getNextcloudAPI().getActivitiesForCard(cardId), responseCallback); } public void createBoard(Board board, @NonNull ResponseCallback<FullBoard> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().createBoard(board), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().createBoard(board), responseCallback); } - public void deleteBoard(Board board, @NonNull ResponseCallback<EmptyResponse> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().deleteBoard(board.getId()), responseCallback); + public void deleteBoard(Board board, @NonNull ResponseCallback<Void> responseCallback) { + this.requestHelper.request(() -> provider.getDeckAPI().deleteBoard(board.getId()), responseCallback); } public void updateBoard(Board board, @NonNull ResponseCallback<FullBoard> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().updateBoard(board.getId(), board), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().updateBoard(board.getId(), board), responseCallback); } public void createAccessControl(long remoteBoardId, AccessControl acl, @NonNull ResponseCallback<AccessControl> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().createAccessControl(remoteBoardId, acl), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().createAccessControl(remoteBoardId, acl), responseCallback); } public void updateAccessControl(long remoteBoardId, AccessControl acl, @NonNull ResponseCallback<AccessControl> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().updateAccessControl(remoteBoardId, acl.getId(), acl), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().updateAccessControl(remoteBoardId, acl.getId(), acl), responseCallback); } - public void deleteAccessControl(long remoteBoardId, AccessControl acl, @NonNull ResponseCallback<EmptyResponse> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().deleteAccessControl(remoteBoardId, acl.getId(), acl), responseCallback); + public void deleteAccessControl(long remoteBoardId, AccessControl acl, @NonNull ResponseCallback<Void> responseCallback) { + this.requestHelper.request(() -> provider.getDeckAPI().deleteAccessControl(remoteBoardId, acl.getId(), acl), responseCallback); } public void getStacks(long boardId, @NonNull ResponseCallback<List<FullStack>> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().getStacks(boardId, getLastSyncDateFormatted(responseCallback.getAccount().getId())), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().getStacks(boardId, getLastSyncDateFormatted(responseCallback.getAccount().getId())), responseCallback); } public void getStack(long boardId, long stackId, @NonNull ResponseCallback<FullStack> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().getStack(boardId, stackId, getLastSyncDateFormatted(responseCallback.getAccount().getId())), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().getStack(boardId, stackId, getLastSyncDateFormatted(responseCallback.getAccount().getId())), responseCallback); } public void createStack(Board board, Stack stack, @NonNull ResponseCallback<FullStack> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().createStack(board.getId(), stack), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().createStack(board.getId(), stack), responseCallback); } - public void deleteStack(Board board, Stack stack, @NonNull ResponseCallback<EmptyResponse> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().deleteStack(board.getId(), stack.getId()), responseCallback); + public void deleteStack(Board board, Stack stack, @NonNull ResponseCallback<Void> responseCallback) { + this.requestHelper.request(() -> provider.getDeckAPI().deleteStack(board.getId(), stack.getId()), responseCallback); } public void updateStack(Board board, Stack stack, @NonNull ResponseCallback<FullStack> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().updateStack(board.getId(), stack.getId(), stack), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().updateStack(board.getId(), stack.getId(), stack), responseCallback); } public void getCard(long boardId, long stackId, long cardId, @NonNull ResponseCallback<FullCard> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> { + this.requestHelper.request(() -> { final Account account = responseCallback.getAccount(); if (account.getServerDeckVersionAsObject().supportsFileAttachments()) { return provider.getDeckAPI().getCard_1_1(boardId, stackId, cardId, getLastSyncDateFormatted(responseCallback.getAccount().getId())); @@ -204,75 +185,63 @@ public class ServerAdapter { } public void createCard(long boardId, long stackId, Card card, @NonNull ResponseCallback<FullCard> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().createCard(boardId, stackId, card), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().createCard(boardId, stackId, card), responseCallback); } - public void deleteCard(long boardId, long stackId, Card card, @NonNull ResponseCallback<EmptyResponse> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().deleteCard(boardId, stackId, card.getId()), responseCallback); + public void deleteCard(long boardId, long stackId, Card card, @NonNull ResponseCallback<Void> responseCallback) { + this.requestHelper.request(() -> provider.getDeckAPI().deleteCard(boardId, stackId, card.getId()), responseCallback); } public void updateCard(long boardId, long stackId, CardUpdate card, @NonNull ResponseCallback<FullCard> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().updateCard(boardId, stackId, card.getId(), card), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().updateCard(boardId, stackId, card.getId(), card), responseCallback); } - public void assignUserToCard(long boardId, long stackId, long cardId, String userUID, @NonNull ResponseCallback<EmptyResponse> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().assignUserToCard(boardId, stackId, cardId, userUID), responseCallback); + public void assignUserToCard(long boardId, long stackId, long cardId, String userUID, @NonNull ResponseCallback<Void> responseCallback) { + this.requestHelper.request(() -> provider.getDeckAPI().assignUserToCard(boardId, stackId, cardId, userUID), responseCallback); } - public void unassignUserFromCard(long boardId, long stackId, long cardId, String userUID, @NonNull ResponseCallback<EmptyResponse> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().unassignUserFromCard(boardId, stackId, cardId, userUID), responseCallback); + public void unassignUserFromCard(long boardId, long stackId, long cardId, String userUID, @NonNull ResponseCallback<Void> responseCallback) { + this.requestHelper.request(() -> provider.getDeckAPI().unassignUserFromCard(boardId, stackId, cardId, userUID), responseCallback); } - public void assignLabelToCard(long boardId, long stackId, long cardId, long labelId, @NonNull ResponseCallback<EmptyResponse> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().assignLabelToCard(boardId, stackId, cardId, labelId), responseCallback); + public void assignLabelToCard(long boardId, long stackId, long cardId, long labelId, @NonNull ResponseCallback<Void> responseCallback) { + this.requestHelper.request(() -> provider.getDeckAPI().assignLabelToCard(boardId, stackId, cardId, labelId), responseCallback); } - public void unassignLabelFromCard(long boardId, long stackId, long cardId, long labelId, @NonNull ResponseCallback<EmptyResponse> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().unassignLabelFromCard(boardId, stackId, cardId, labelId), responseCallback); + public void unassignLabelFromCard(long boardId, long stackId, long cardId, long labelId, @NonNull ResponseCallback<Void> responseCallback) { + this.requestHelper.request(() -> provider.getDeckAPI().unassignLabelFromCard(boardId, stackId, cardId, labelId), responseCallback); } // Labels public void createLabel(long boardId, Label label, @NonNull ResponseCallback<Label> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().createLabel(boardId, label), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().createLabel(boardId, label), responseCallback); } - public void deleteLabel(long boardId, Label label, @NonNull ResponseCallback<EmptyResponse> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().deleteLabel(boardId, label.getId()), responseCallback); + public void deleteLabel(long boardId, Label label, @NonNull ResponseCallback<Void> responseCallback) { + this.requestHelper.request(() -> provider.getDeckAPI().deleteLabel(boardId, label.getId()), responseCallback); } public void updateLabel(long boardId, Label label, @NonNull ResponseCallback<Label> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().updateLabel(boardId, label.getId(), label), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().updateLabel(boardId, label.getId(), label), responseCallback); } public void reorder(long boardId, long currentStackId, long cardId, long newStackId, int newPosition, @NonNull ResponseCallback<List<FullCard>> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().moveCard(boardId, currentStackId, cardId, new Reorder(newPosition, (int) newStackId)), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().moveCard(boardId, currentStackId, cardId, new Reorder(newPosition, (int) newStackId)), responseCallback); } // Attachments public void uploadAttachment(Long remoteBoardId, long remoteStackId, long remoteCardId, File attachment, @NonNull ResponseCallback<Attachment> responseCallback) { - ensureInternetConnection(); final Account account = responseCallback.getAccount(); final String type = account.getServerDeckVersionAsObject().supportsFileAttachments() ? EAttachmentType.FILE.getValue() : EAttachmentType.DECK_FILE.getValue(); final MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", attachment.getName(), RequestBody.create(MediaType.parse(getMimeType(attachment)), attachment)); final MultipartBody.Part typePart = MultipartBody.Part.createFormData("type", null, RequestBody.create(MediaType.parse(TEXT_PLAIN), type)); - RequestHelper.request(provider, () -> provider.getDeckAPI().uploadAttachment(remoteBoardId, remoteStackId, remoteCardId, typePart, filePart), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().uploadAttachment(remoteBoardId, remoteStackId, remoteCardId, typePart, filePart), responseCallback); } @NonNull @@ -290,48 +259,40 @@ public class ServerAdapter { } public void updateAttachment(Long remoteBoardId, long remoteStackId, long remoteCardId, long remoteAttachmentId, String contentType, Uri attachmentUri, @NonNull ResponseCallback<Attachment> responseCallback) { - ensureInternetConnection(); final File attachment = new File(attachmentUri.getPath()); final String type = responseCallback.getAccount().getServerDeckVersionAsObject().supportsFileAttachments() ? EAttachmentType.FILE.getValue() : EAttachmentType.DECK_FILE.getValue(); final MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", attachment.getName(), RequestBody.create(MediaType.parse(contentType), attachment)); final MultipartBody.Part typePart = MultipartBody.Part.createFormData("type", attachment.getName(), RequestBody.create(MediaType.parse(TEXT_PLAIN), type)); - RequestHelper.request(provider, () -> provider.getDeckAPI().updateAttachment(remoteBoardId, remoteStackId, remoteCardId, remoteAttachmentId, typePart, filePart), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().updateAttachment(remoteBoardId, remoteStackId, remoteCardId, remoteAttachmentId, typePart, filePart), responseCallback); } public void downloadAttachment(Long remoteBoardId, long remoteStackId, long remoteCardId, long remoteAttachmentId, @NonNull ResponseCallback<ResponseBody> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().downloadAttachment(remoteBoardId, remoteStackId, remoteCardId, remoteAttachmentId), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().downloadAttachment(remoteBoardId, remoteStackId, remoteCardId, remoteAttachmentId), responseCallback); } - public void deleteAttachment(Long remoteBoardId, long remoteStackId, long remoteCardId, long remoteAttachmentId, @NonNull ResponseCallback<EmptyResponse> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().deleteAttachment(remoteBoardId, remoteStackId, remoteCardId, remoteAttachmentId), responseCallback); + public void deleteAttachment(Long remoteBoardId, long remoteStackId, long remoteCardId, long remoteAttachmentId, @NonNull ResponseCallback<Void> responseCallback) { + this.requestHelper.request(() -> provider.getDeckAPI().deleteAttachment(remoteBoardId, remoteStackId, remoteCardId, remoteAttachmentId), responseCallback); } public void restoreAttachment(Long remoteBoardId, long remoteStackId, long remoteCardId, long remoteAttachmentId, @NonNull ResponseCallback<Attachment> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().restoreAttachment(remoteBoardId, remoteStackId, remoteCardId, remoteAttachmentId), responseCallback); + this.requestHelper.request(() -> provider.getDeckAPI().restoreAttachment(remoteBoardId, remoteStackId, remoteCardId, remoteAttachmentId), responseCallback); } public void getCommentsForRemoteCardId(Long remoteCardId, @NonNull ResponseCallback<OcsComment> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getNextcloudAPI().getCommentsForCard(remoteCardId), responseCallback); + this.requestHelper.request(() -> provider.getNextcloudAPI().getCommentsForCard(remoteCardId), responseCallback); } public void createCommentForCard(DeckComment comment, @NonNull ResponseCallback<OcsComment> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getNextcloudAPI().createCommentForCard(comment.getObjectId(), comment), responseCallback); + this.requestHelper.request(() -> provider.getNextcloudAPI().createCommentForCard(comment.getObjectId(), comment), responseCallback); } public void updateCommentForCard(DeckComment comment, @NonNull ResponseCallback<OcsComment> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getNextcloudAPI().updateCommentForCard(comment.getObjectId(), comment.getId(), comment), responseCallback); + this.requestHelper.request(() -> provider.getNextcloudAPI().updateCommentForCard(comment.getObjectId(), comment.getId(), comment), responseCallback); } - public void deleteCommentForCard(DeckComment comment, @NonNull ResponseCallback<EmptyResponse> responseCallback) { - ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getNextcloudAPI().deleteCommentForCard(comment.getObjectId(), comment.getId()), responseCallback); + public void deleteCommentForCard(DeckComment comment, @NonNull ResponseCallback<Void> responseCallback) { + this.requestHelper.request(() -> provider.getNextcloudAPI().deleteCommentForCard(comment.getObjectId(), comment.getId()), responseCallback); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/ApiProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/ApiProvider.java index cad3457a8..cee04e2dc 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/ApiProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/ApiProvider.java @@ -36,6 +36,11 @@ public class ApiProvider { } } + @NonNull + public Context getContext() { + return this.context; + } + public DeckAPI getDeckAPI() { return deckAPI; } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/DeckAPI.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/DeckAPI.java index 1f17d2daa..b6a35c926 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/DeckAPI.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/DeckAPI.java @@ -1,12 +1,8 @@ package it.niedermann.nextcloud.deck.remote.api; -import com.nextcloud.android.sso.api.EmptyResponse; -import com.nextcloud.android.sso.api.ParsedResponse; - import java.util.List; -import io.reactivex.Observable; import it.niedermann.nextcloud.deck.model.AccessControl; import it.niedermann.nextcloud.deck.model.Attachment; import it.niedermann.nextcloud.deck.model.Board; @@ -21,6 +17,7 @@ import it.niedermann.nextcloud.deck.model.propagation.CardUpdate; import it.niedermann.nextcloud.deck.model.propagation.Reorder; import okhttp3.MultipartBody; import okhttp3.ResponseBody; +import retrofit2.Call; import retrofit2.http.Body; import retrofit2.http.DELETE; import retrofit2.http.Field; @@ -46,136 +43,136 @@ public interface DeckAPI { // Boards @POST("v1.0/boards") - Observable<FullBoard> createBoard(@Body Board board); + Call<FullBoard> createBoard(@Body Board board); @GET("v1.0/boards/{id}") - Observable<FullBoard> getBoard(@Path("id") long id, @Header(MODIFIED_SINCE_HEADER) String lastSync); + Call<FullBoard> getBoard(@Path("id") long id, @Header(MODIFIED_SINCE_HEADER) String lastSync); @PUT("v1.0/boards/{id}") - Observable<FullBoard> updateBoard(@Path("id") long id, @Body Board board); + Call<FullBoard> updateBoard(@Path("id") long id, @Body Board board); @DELETE("v1.0/boards/{id}") - Observable<EmptyResponse> deleteBoard(@Path("id") long id); + Call<Void> deleteBoard(@Path("id") long id); @DELETE("v1.0/boards/{id}/undo_delete") - Observable<FullBoard> restoreBoard(@Path("id") long id); + Call<FullBoard> restoreBoard(@Path("id") long id); @GET("v1.0/boards") - Observable<ParsedResponse<List<FullBoard>>> getBoards(@Query("details") boolean verbose, @Header(MODIFIED_SINCE_HEADER) String lastSync, @Header(IF_NONE_MATCH) String eTag); + Call<List<FullBoard>> getBoards(@Query("details") boolean verbose, @Header(MODIFIED_SINCE_HEADER) String lastSync, @Header(IF_NONE_MATCH) String eTag); @GET("v1.0/boards") - Observable<ParsedResponse<List<FullBoard>>> getBoards(@Query("details") boolean verbose, @Header(MODIFIED_SINCE_HEADER) String lastSync); + Call<List<FullBoard>> getBoards(@Query("details") boolean verbose, @Header(MODIFIED_SINCE_HEADER) String lastSync); // Stacks @POST("v1.0/boards/{boardId}/stacks") - Observable<FullStack> createStack(@Path("boardId") long boardId, @Body Stack stack); + Call<FullStack> createStack(@Path("boardId") long boardId, @Body Stack stack); @PUT("v1.0/boards/{boardId}/stacks/{stackId}") - Observable<FullStack> updateStack(@Path("boardId") long boardId, @Path("stackId") long id, @Body Stack stack); + Call<FullStack> updateStack(@Path("boardId") long boardId, @Path("stackId") long id, @Body Stack stack); @DELETE("v1.0/boards/{boardId}/stacks/{stackId}") - Observable<EmptyResponse> deleteStack(@Path("boardId") long boardId, @Path("stackId") long id); + Call<Void> deleteStack(@Path("boardId") long boardId, @Path("stackId") long id); @GET("v1.0/boards/{boardId}/stacks/{stackId}") - Observable<FullStack> getStack(@Path("boardId") long boardId, @Path("stackId") long id, @Header(MODIFIED_SINCE_HEADER) String lastSync); + Call<FullStack> getStack(@Path("boardId") long boardId, @Path("stackId") long id, @Header(MODIFIED_SINCE_HEADER) String lastSync); @GET("v1.0/boards/{boardId}/stacks") - Observable<List<FullStack>> getStacks(@Path("boardId") long boardId, @Header(MODIFIED_SINCE_HEADER) String lastSync); + Call<List<FullStack>> getStacks(@Path("boardId") long boardId, @Header(MODIFIED_SINCE_HEADER) String lastSync); @GET("v1.0/boards/{boardId}/stacks/archived") - Observable<List<Stack>> getArchivedStacks(@Path("boardId") long boardId, @Header(MODIFIED_SINCE_HEADER) String lastSync); + Call<List<Stack>> getArchivedStacks(@Path("boardId") long boardId, @Header(MODIFIED_SINCE_HEADER) String lastSync); // Cards @POST("v1.0/boards/{boardId}/stacks/{stackId}/cards") - Observable<FullCard> createCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Body Card card); + Call<FullCard> createCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Body Card card); @PUT("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}") - Observable<FullCard> updateCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Body CardUpdate card); + Call<FullCard> updateCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Body CardUpdate card); @FormUrlEncoded @PUT("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignLabel") - Observable<EmptyResponse> assignLabelToCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("labelId") long labelId); + Call<Void> assignLabelToCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("labelId") long labelId); @FormUrlEncoded @PUT("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/removeLabel") - Observable<EmptyResponse> unassignLabelFromCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("labelId") long labelId); + Call<Void> unassignLabelFromCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("labelId") long labelId); @FormUrlEncoded @PUT("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignUser") - Observable<EmptyResponse> assignUserToCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("userId") String userUID); + Call<Void> assignUserToCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("userId") String userUID); @FormUrlEncoded @PUT("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/unassignUser") - Observable<EmptyResponse> unassignUserFromCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("userId") String userUID); + Call<Void> unassignUserFromCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("userId") String userUID); @PUT("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/reorder") - Observable<List<FullCard>> moveCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Body Reorder reorder); + Call<List<FullCard>> moveCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Body Reorder reorder); @DELETE("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}") - Observable<EmptyResponse> deleteCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId); + Call<Void> deleteCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId); /** * @see <a href="https://github.com/nextcloud/deck/issues/2874">This endpoint does only return {@link Attachment}s of type {@link EAttachmentType.DECK_FILE}</a> */ @SuppressWarnings("JavadocReference") @GET("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}") - Observable<FullCard> getCard_1_0(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Header(MODIFIED_SINCE_HEADER) String lastSync); + Call<FullCard> getCard_1_0(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Header(MODIFIED_SINCE_HEADER) String lastSync); @GET("v1.1/boards/{boardId}/stacks/{stackId}/cards/{cardId}") - Observable<FullCard> getCard_1_1(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Header(MODIFIED_SINCE_HEADER) String lastSync); + Call<FullCard> getCard_1_1(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Header(MODIFIED_SINCE_HEADER) String lastSync); // Labels @GET("v1.0/boards/{boardId}/labels/{labelId}") - Observable<Label> getLabel(@Path("boardId") long boardId, @Path("labelId") long labelId, @Header(MODIFIED_SINCE_HEADER) String lastSync); + Call<Label> getLabel(@Path("boardId") long boardId, @Path("labelId") long labelId, @Header(MODIFIED_SINCE_HEADER) String lastSync); @PUT("v1.0/boards/{boardId}/labels/{labelId}") - Observable<Label> updateLabel(@Path("boardId") long boardId, @Path("labelId") long labelId, @Body Label label); + Call<Label> updateLabel(@Path("boardId") long boardId, @Path("labelId") long labelId, @Body Label label); @POST("v1.0/boards/{boardId}/labels") - Observable<Label> createLabel(@Path("boardId") long boardId, @Body Label label); + Call<Label> createLabel(@Path("boardId") long boardId, @Body Label label); @DELETE("v1.0/boards/{boardId}/labels/{labelId}") - Observable<EmptyResponse> deleteLabel(@Path("boardId") long boardId, @Path("labelId") long labelId); + Call<Void> deleteLabel(@Path("boardId") long boardId, @Path("labelId") long labelId); // Attachments @GET("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}") - Observable<ResponseBody> downloadAttachment(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Path("attachmentId") long attachmentId); + Call<ResponseBody> downloadAttachment(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Path("attachmentId") long attachmentId); @GET("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments") - Observable<List<Attachment>> getAttachments(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId); + Call<List<Attachment>> getAttachments(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId); @Multipart @POST("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments") - Observable<Attachment> uploadAttachment(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Part MultipartBody.Part type, @Part MultipartBody.Part attachment); + Call<Attachment> uploadAttachment(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Part MultipartBody.Part type, @Part MultipartBody.Part attachment); @Multipart @PUT("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments") - Observable<Attachment> updateAttachment(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Path("attachmentId") long attachmentId, @Part MultipartBody.Part type, @Part MultipartBody.Part attachment); + Call<Attachment> updateAttachment(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Path("attachmentId") long attachmentId, @Part MultipartBody.Part type, @Part MultipartBody.Part attachment); @DELETE("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}") - Observable<EmptyResponse> deleteAttachment(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Path("attachmentId") long attachmentId); + Call<Void> deleteAttachment(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Path("attachmentId") long attachmentId); @PUT("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}/restore") - Observable<Attachment> restoreAttachment(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Path("attachmentId") long attachmentId); + Call<Attachment> restoreAttachment(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Path("attachmentId") long attachmentId); // Access control lists @POST("v1.0/boards/{boardId}/acl") - Observable<AccessControl> createAccessControl(@Path("boardId") long boardId, @Body AccessControl acl); + Call<AccessControl> createAccessControl(@Path("boardId") long boardId, @Body AccessControl acl); @PUT("v1.0/boards/{boardId}/acl/{aclId}") - Observable<AccessControl> updateAccessControl(@Path("boardId") long boardId, @Path("aclId") long aclId, @Body AccessControl acl); + Call<AccessControl> updateAccessControl(@Path("boardId") long boardId, @Path("aclId") long aclId, @Body AccessControl acl); @DELETE("v1.0/boards/{boardId}/acl/{aclId}") - Observable<EmptyResponse> deleteAccessControl(@Path("boardId") long boardId, @Path("aclId") long aclId, @Body AccessControl acl); + Call<Void> deleteAccessControl(@Path("boardId") long boardId, @Path("aclId") long aclId, @Body AccessControl acl); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/IResponseCallback.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/IResponseCallback.java index 51afb2155..cf39f77f1 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/IResponseCallback.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/IResponseCallback.java @@ -3,6 +3,7 @@ package it.niedermann.nextcloud.deck.remote.api; import androidx.annotation.CallSuper; import it.niedermann.nextcloud.deck.DeckLog; +import okhttp3.Headers; public interface IResponseCallback<T> { @@ -13,6 +14,9 @@ public interface IResponseCallback<T> { DeckLog.logError(throwable); } + default void onResponseWithHeaders(T response, Headers headers) { + onResponse(response); + } /** * @return a default {@link IResponseCallback} which does nothing {@link #onResponse(Object)} and the default action fo {@link #onError(Throwable)} */ diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/NextcloudServerAPI.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/NextcloudServerAPI.java index 2e005d35f..d3856ddc6 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/NextcloudServerAPI.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/NextcloudServerAPI.java @@ -1,12 +1,8 @@ package it.niedermann.nextcloud.deck.remote.api; -import com.nextcloud.android.sso.api.EmptyResponse; -import com.nextcloud.android.sso.api.ParsedResponse; - import java.util.List; -import io.reactivex.Observable; import it.niedermann.nextcloud.deck.model.ocs.Activity; import it.niedermann.nextcloud.deck.model.ocs.Capabilities; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; @@ -15,6 +11,7 @@ import it.niedermann.nextcloud.deck.model.ocs.projects.OcsProjectList; import it.niedermann.nextcloud.deck.model.ocs.user.GroupMemberUIDs; import it.niedermann.nextcloud.deck.model.ocs.user.OcsUser; import it.niedermann.nextcloud.deck.model.ocs.user.OcsUserList; +import retrofit2.Call; import retrofit2.http.Body; import retrofit2.http.DELETE; import retrofit2.http.GET; @@ -35,31 +32,31 @@ public interface NextcloudServerAPI { // Capabilities @GET("cloud/capabilities?format=json") - Observable<ParsedResponse<Capabilities>> getCapabilities(@Header("If-None-Match") String eTag); + Call<Capabilities> getCapabilities(@Header("If-None-Match") String eTag); // Projects @GET("collaboration/resources/deck-card/{cardId}?format=json") - Observable<OcsProjectList> getProjectsForCard(@Path("cardId") long cardId); + Call<OcsProjectList> getProjectsForCard(@Path("cardId") long cardId); // Users @GET("apps/files_sharing/api/v1/sharees?format=json&lookup=false&perPage=20&itemType=0%2C1%2C7") - Observable<OcsUserList> searchUser(@Query("search") String searchTerm); + Call<OcsUserList> searchUser(@Query("search") String searchTerm); @GET("cloud/groups/{search}?format=json") - Observable<GroupMemberUIDs> searchGroupMembers(@Path("search") String groupUid); + Call<GroupMemberUIDs> searchGroupMembers(@Path("search") String groupUid); @GET("cloud/users/{search}?format=json") - Observable<OcsUser> getSingleUserData(@Path("search") String userUid); + Call<OcsUser> getSingleUserData(@Path("search") String userUid); // Activities @GET("apps/activity/api/v2/activity/filter?format=json&object_type=deck_card&limit=50&since=-1&sort=asc") - Observable<List<Activity>> getActivitiesForCard(@Query("object_id") long cardId); + Call<List<Activity>> getActivitiesForCard(@Query("object_id") long cardId); // Comments @@ -69,26 +66,26 @@ public interface NextcloudServerAPI { "Content-Type: application/json;charset=utf-8" }) @GET("apps/deck/api/v1.0/cards/{cardId}/comments") - Observable<OcsComment> getCommentsForCard(@Path("cardId") long cardId); + Call<OcsComment> getCommentsForCard(@Path("cardId") long cardId); @Headers({ "Accept: application/json", "Content-Type: application/json;charset=utf-8" }) @POST("apps/deck/api/v1.0/cards/{cardId}/comments") - Observable<OcsComment> createCommentForCard(@Path("cardId") long cardId, @Body DeckComment comment); + Call<OcsComment> createCommentForCard(@Path("cardId") long cardId, @Body DeckComment comment); @Headers({ "Accept: application/json", "Content-Type: application/json;charset=utf-8" }) @PUT("apps/deck/api/v1.0/cards/{cardId}/comments/{commentId}") - Observable<OcsComment> updateCommentForCard(@Path("cardId") long cardId, @Path("commentId") long commentId, @Body DeckComment comment); + Call<OcsComment> updateCommentForCard(@Path("cardId") long cardId, @Path("commentId") long commentId, @Body DeckComment comment); @Headers({ "Accept: application/json", "Content-Type: application/json;charset=utf-8" }) @DELETE("apps/deck/api/v1.0/cards/{cardId}/comments/{commentId}") - Observable<EmptyResponse> deleteCommentForCard(@Path("cardId") long cardId, @Path("commentId") long commentId); + Call<Void> deleteCommentForCard(@Path("cardId") long cardId, @Path("commentId") long commentId); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/RequestHelper.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/RequestHelper.java index e7533b66c..d9aeee656 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/RequestHelper.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/RequestHelper.java @@ -1,60 +1,98 @@ package it.niedermann.nextcloud.deck.remote.api; +import android.content.Context; + import androidx.annotation.NonNull; -import io.reactivex.Observable; -import io.reactivex.disposables.Disposable; -import io.reactivex.functions.Consumer; -import io.reactivex.plugins.RxJavaPlugins; -import io.reactivex.schedulers.Schedulers; +import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; + +import java.util.function.Supplier; + import it.niedermann.nextcloud.deck.DeckLog; +import it.niedermann.nextcloud.deck.exceptions.OfflineException; +import it.niedermann.nextcloud.deck.remote.helpers.util.ConnectivityUtil; import it.niedermann.nextcloud.deck.util.ExecutorServiceProvider; +import okhttp3.Request; +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; public class RequestHelper { - static { - RxJavaPlugins.setErrorHandler(DeckLog::logError); + @NonNull + private final ApiProvider apiProvider; + + @NonNull + private final ConnectivityUtil connectivityUtil; + + public RequestHelper( + @NonNull ApiProvider apiProvider, + @NonNull ConnectivityUtil connectivityUtil + ) { + this.apiProvider = apiProvider; + this.connectivityUtil = connectivityUtil; } - public static <T> Disposable request(@NonNull final ApiProvider provider, @NonNull final ObservableProvider<T> call, @NonNull final ResponseCallback<T> callback) { - if (provider.getDeckAPI() == null) { - provider.initSsoApi(callback::onError); + public <T> void request(@NonNull final Supplier<Call<T>> callProvider, + @NonNull final ResponseCallback<T> callback) { + + if (!connectivityUtil.hasInternetConnection()) { + throw new OfflineException(); } - final ResponseConsumer<T> cb = new ResponseConsumer<>(callback); - return call.getObservableFromCall() - .subscribeOn(Schedulers.from(ExecutorServiceProvider.getLinkedBlockingQueueExecutor())) - .subscribe(cb, cb.getExceptionConsumer()); - } + if (this.apiProvider.getDeckAPI() == null) { + this.apiProvider.initSsoApi(callback::onError); + } - public interface ObservableProvider<T> { - Observable<T> getObservableFromCall(); + final var cb = new ResponseConsumer<>(this.apiProvider.getContext(), callback); + ExecutorServiceProvider.getLinkedBlockingQueueExecutor().submit(() -> callProvider.get().enqueue(cb)); } - public static class ResponseConsumer<T> implements Consumer<T> { + private static class ResponseConsumer<T> implements Callback<T> { @NonNull - private final ResponseCallback<T> callback; + private final Context context; @NonNull - private final Consumer<Throwable> exceptionConsumer = new Consumer<>() { - @Override - public void accept(final Throwable throwable) { - callback.onError(ServerCommunicationErrorHandler.translateError(throwable)); - } - }; + private final ResponseCallback<T> callback; - private ResponseConsumer(@NonNull ResponseCallback<T> callback) { + private ResponseConsumer(@NonNull Context context, @NonNull ResponseCallback<T> callback) { + this.context = context; this.callback = callback; } @Override - public void accept(final T t) { - callback.fillAccountIDs(t); - callback.onResponse(t); + public void onResponse(@NonNull Call<T> call, Response<T> response) { + if (response.isSuccessful()) { + T responseObject = response.body(); + callback.fillAccountIDs(responseObject); + callback.onResponseWithHeaders(responseObject, response.headers()); + } else { + onFailure(call, new NextcloudHttpRequestFailedException(context, response.code(), buildCause(response))); + } } - @NonNull - private Consumer<Throwable> getExceptionConsumer() { - return exceptionConsumer; + private RuntimeException buildCause(Response<T> response) { + Request request = response.raw().request(); + String url = request.url().toString(); + String method = request.method(); + int code = response.code(); + String responseBody = "<empty>"; + try (ResponseBody body = response.errorBody()) { + if (body != null) { + responseBody = body.string(); + } + } catch (Exception e) { + responseBody = "<unable to build response body: " + e.getMessage() + ">"; + } + return new RuntimeException("HTTP StatusCode wasn't 2xx:\n" + + "Got [HTTP " + code + "] for Call [" + method + " " + url + "] with Message:\n" + + "[" + responseBody + "]"); + } + + @Override + public void onFailure(@NonNull Call<T> call, @NonNull Throwable t) { + DeckLog.logError(t); + callback.onError(ServerCommunicationErrorHandler.translateError(t)); } } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/ServerCommunicationErrorHandler.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/ServerCommunicationErrorHandler.java index 562e9c3cb..656aed0ce 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/ServerCommunicationErrorHandler.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/ServerCommunicationErrorHandler.java @@ -2,6 +2,7 @@ package it.niedermann.nextcloud.deck.remote.api; import androidx.annotation.NonNull; +import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import com.nextcloud.android.sso.exceptions.UnknownErrorException; import java.util.Arrays; @@ -16,7 +17,8 @@ public class ServerCommunicationErrorHandler { private static final Handler[] handlers = new Handler[]{ new Handler(UnknownErrorException.class, Arrays.asList("econnrefused", "unable to resolve host", "connection refused", "no address associated with hostname"), OfflineException.Reason.CONNECTION_REFUSED), - new Handler(ClassNotFoundException.class, Collections.singletonList("connecttimeoutexception"), OfflineException.Reason.CONNECTION_TIMEOUT) + new Handler(ClassNotFoundException.class, Collections.singletonList("connecttimeoutexception"), OfflineException.Reason.CONNECTION_TIMEOUT), + new Handler(NextcloudHttpRequestFailedException.class, Collections.singletonList("520"), OfflineException.Reason.CONNECTION_REJECTED) }; public static Throwable translateError(Throwable error) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/DataPropagationHelper.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/DataPropagationHelper.java index 6b6549ac7..ac6bdedc2 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/DataPropagationHelper.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/DataPropagationHelper.java @@ -3,8 +3,6 @@ package it.niedermann.nextcloud.deck.remote.helpers; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.nextcloud.android.sso.api.EmptyResponse; - import java.util.concurrent.ExecutorService; import java.util.function.BiConsumer; @@ -111,7 +109,7 @@ public class DataPropagationHelper { } } - public <T extends IRemoteEntity> void deleteEntity(@NonNull final AbstractSyncDataProvider<T> provider, @NonNull T entity, @NonNull ResponseCallback<EmptyResponse> callback){ + public <T extends IRemoteEntity> void deleteEntity(@NonNull final AbstractSyncDataProvider<T> provider, @NonNull T entity, @NonNull ResponseCallback<Void> callback){ final long accountId = callback.getAccount().getId(); // known to server? if (entity.getId() != null) { @@ -124,7 +122,7 @@ public class DataPropagationHelper { try { provider.deleteOnServer(serverAdapter, accountId, new ResponseCallback<>(new Account(accountId)) { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { executor.submit(() -> { provider.deletePhysicallyInDB(dataBaseAdapter, accountId, entity); callback.onResponse(null); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/SyncHelper.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/SyncHelper.java index 4028e3ad3..8f2697523 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/SyncHelper.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/SyncHelper.java @@ -1,9 +1,12 @@ package it.niedermann.nextcloud.deck.remote.helpers; +import android.annotation.SuppressLint; +import android.database.sqlite.SQLiteConstraintException; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.nextcloud.android.sso.api.EmptyResponse; +import com.google.gson.Gson; import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import java.net.HttpURLConnection; @@ -20,6 +23,7 @@ import it.niedermann.nextcloud.deck.remote.adapters.ServerAdapter; import it.niedermann.nextcloud.deck.remote.api.ResponseCallback; import it.niedermann.nextcloud.deck.remote.helpers.providers.AbstractSyncDataProvider; import it.niedermann.nextcloud.deck.remote.helpers.providers.IRelationshipProvider; +import it.niedermann.nextcloud.deck.util.ExecutorServiceProvider; public class SyncHelper { @NonNull @@ -44,12 +48,16 @@ public class SyncHelper { // Sync Server -> App public <T extends IRemoteEntity> void doSyncFor(@NonNull final AbstractSyncDataProvider<T> provider) { + doSyncFor(provider, true); + } + public <T extends IRemoteEntity> void doSyncFor(@NonNull final AbstractSyncDataProvider<T> provider, boolean parallel) { provider.registerChildInParent(provider); provider.getAllFromServer(serverAdapter, dataBaseAdapter, accountId, new ResponseCallback<>(account) { @Override public void onResponse(List<T> response) { if (response != null) { provider.goingDeeper(); + for (T entityFromServer : response) { if (entityFromServer == null) { // see https://github.com/stefan-niedermann/nextcloud-deck/issues/574 @@ -57,10 +65,18 @@ public class SyncHelper { continue; } entityFromServer.setAccountId(accountId); + T existingEntity = provider.getSingleFromDB(dataBaseAdapter, accountId, entityFromServer); if (existingEntity == null) { - provider.createInDB(dataBaseAdapter, accountId, entityFromServer); + try { + ExecutorServiceProvider.awaitExecution(() -> provider.createInDB(dataBaseAdapter, accountId, entityFromServer)); + } catch (SQLiteConstraintException e) { + provider.onInsertFailed(dataBaseAdapter, e, account, accountId, response, entityFromServer); + throw new RuntimeException("ConstraintViolation! Entity: " + provider.getClass().getSimpleName()+"\n" + +entityFromServer.getClass().getSimpleName()+": "+ new Gson().toJson(entityFromServer), + e); + } } else { //TODO: how to handle deletes? what about archived? if (existingEntity.getStatus() != DBStatus.UP_TO_DATE.getId()) { @@ -75,7 +91,33 @@ public class SyncHelper { } } existingEntity = provider.getSingleFromDB(dataBaseAdapter, accountId, entityFromServer); - provider.goDeeper(SyncHelper.this, existingEntity, entityFromServer, responseCallback); + final T tmp = existingEntity; + if (parallel) { + provider.goDeeper(SyncHelper.this, existingEntity, entityFromServer, responseCallback); + } else { + DeckLog.verbose("### SYNC Sequencial!"+tmp.getId()); + CountDownLatch latch = new CountDownLatch(1); + provider.goDeeper(SyncHelper.this, existingEntity, entityFromServer, new ResponseCallback<>(responseCallback.getAccount()) { + @Override + public void onResponse(Boolean response) { + DeckLog.verbose("### SYNC board "+tmp.getId()+" done! Changes: "+response); + latch.countDown(); + } + + @SuppressLint("MissingSuperCall") + @Override + public void onError(Throwable throwable) { + DeckLog.verbose("### SYNC board done (error)! "); + responseCallback.onError(throwable); + latch.countDown(); + } + }); + try { + latch.await(); + } catch (InterruptedException e) { + onError(e); + } + } } provider.handleDeletes(serverAdapter, dataBaseAdapter, accountId, response); @@ -134,10 +176,10 @@ public class SyncHelper { } } - private <T extends IRemoteEntity> ResponseCallback<EmptyResponse> getDeleteCallback(@NonNull AbstractSyncDataProvider<T> provider, T entity) { + private <T extends IRemoteEntity> ResponseCallback<Void> getDeleteCallback(@NonNull AbstractSyncDataProvider<T> provider, T entity) { return new ResponseCallback<>(account) { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { provider.deletePhysicallyInDB(dataBaseAdapter, accountId, entity); provider.goDeeperForUpSync(SyncHelper.this, serverAdapter, dataBaseAdapter, responseCallback); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AbstractSyncDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AbstractSyncDataProvider.java index 4347af0b5..4251d9a05 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AbstractSyncDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AbstractSyncDataProvider.java @@ -2,14 +2,13 @@ package it.niedermann.nextcloud.deck.remote.helpers.providers; import androidx.annotation.Nullable; -import com.nextcloud.android.sso.api.EmptyResponse; - import java.time.Instant; import java.util.ArrayList; import java.util.List; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.database.DataBaseAdapter; +import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.interfaces.IRemoteEntity; import it.niedermann.nextcloud.deck.remote.adapters.ServerAdapter; import it.niedermann.nextcloud.deck.remote.api.ResponseCallback; @@ -107,7 +106,7 @@ public abstract class AbstractSyncDataProvider<T extends IRemoteEntity> { public abstract void updateOnServer(ServerAdapter serverAdapter, DataBaseAdapter dataBaseAdapter, long accountId, ResponseCallback<T> callback, T entity); - public abstract void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, T entity, DataBaseAdapter dataBaseAdapter); + public abstract void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, T entity, DataBaseAdapter dataBaseAdapter); public void childDone(AbstractSyncDataProvider<?> child, ResponseCallback<Boolean> responseCallback, boolean syncChangedSomething) { removeChild(child); @@ -148,4 +147,8 @@ public abstract class AbstractSyncDataProvider<T extends IRemoteEntity> { public T applyUpdatesFromRemote(T localEntity, T remoteEntity, Long accountId) { return remoteEntity; } + + public void onInsertFailed(DataBaseAdapter dataBaseAdapter, RuntimeException cause, Account account, long accountId, List<T> response, T entityFromServer) { + throw cause; + } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AccessControlDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AccessControlDataProvider.java index 030669158..75d9d510a 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AccessControlDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AccessControlDataProvider.java @@ -2,8 +2,6 @@ package it.niedermann.nextcloud.deck.remote.helpers.providers; import androidx.annotation.Nullable; -import com.nextcloud.android.sso.api.EmptyResponse; - import java.time.Instant; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -42,7 +40,7 @@ public class AccessControlDataProvider extends AbstractSyncDataProvider<AccessCo public void onResponse(GroupMemberUIDs response) { accessControl.setGroupMemberUIDs(response); if (response.getUids().size() > 0) { - ensureGroupMembersInDB(getAccount(), dataBaseAdapter, serverAdapter, response); + ensureGroupMembersInDB(responder.getAccount(), dataBaseAdapter, serverAdapter, response); } latch.countDown(); } @@ -74,7 +72,17 @@ public class AccessControlDataProvider extends AbstractSyncDataProvider<AccessCo user.setUid(response.getId()); user.setPrimaryKey(response.getId()); user.setDisplayname(response.getDisplayName()); - dataBaseAdapter.createUser(account.getId(), user); + try { + dataBaseAdapter.createUser(getAccount().getId(), user); + } catch (Exception e) { + try { + // retry... if still nothing: skip. + Thread.sleep(500); + dataBaseAdapter.createUser(getAccount().getId(), user); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } memberLatch.countDown(); } @@ -176,7 +184,7 @@ public class AccessControlDataProvider extends AbstractSyncDataProvider<AccessCo } @Override - public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, AccessControl entity, DataBaseAdapter dataBaseAdapter) { + public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, AccessControl entity, DataBaseAdapter dataBaseAdapter) { serverAdapter.deleteAccessControl(board.getBoard().getId(), entity, callback); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/ActivityDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/ActivityDataProvider.java index b4981bf08..018ac0282 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/ActivityDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/ActivityDataProvider.java @@ -2,8 +2,6 @@ package it.niedermann.nextcloud.deck.remote.helpers.providers; import androidx.annotation.NonNull; -import com.nextcloud.android.sso.api.EmptyResponse; - import java.time.Instant; import java.util.Collections; import java.util.List; @@ -65,7 +63,7 @@ public class ActivityDataProvider extends AbstractSyncDataProvider<Activity> { } @Override - public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, Activity entity, DataBaseAdapter dataBaseAdapter) { + public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, Activity entity, DataBaseAdapter dataBaseAdapter) { // nope. } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AttachmentDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AttachmentDataProvider.java index e3f84fdaf..db02a6fc6 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AttachmentDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/AttachmentDataProvider.java @@ -3,17 +3,18 @@ package it.niedermann.nextcloud.deck.remote.helpers.providers; import android.annotation.SuppressLint; import android.net.Uri; -import com.nextcloud.android.sso.api.EmptyResponse; - import java.io.File; import java.io.IOException; import java.time.Instant; import java.util.List; +import java.util.stream.Collectors; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.database.DataBaseAdapter; +import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Attachment; import it.niedermann.nextcloud.deck.model.Board; +import it.niedermann.nextcloud.deck.model.Card; import it.niedermann.nextcloud.deck.model.Stack; import it.niedermann.nextcloud.deck.model.full.FullCard; import it.niedermann.nextcloud.deck.remote.adapters.ServerAdapter; @@ -35,6 +36,19 @@ public class AttachmentDataProvider extends AbstractSyncDataProvider<Attachment> } @Override + public void onInsertFailed(DataBaseAdapter dataBaseAdapter, RuntimeException cause, Account account, long accountId, List<Attachment> response, Attachment entityFromServer) { + Account foundAccount = dataBaseAdapter.getAccountByIdDirectly(accountId); + Card foundCard = dataBaseAdapter.getCardByLocalIdDirectly(accountId, entityFromServer.getCardId()); + List<Long> accountIDs = dataBaseAdapter.getAllAccountsDirectly().stream().map(Account::getId).collect(Collectors.toList()); + List<Long> allCardIDs = dataBaseAdapter.getAllCardIDs(); + throw new RuntimeException("Error creating Attachment.\n" + + "AccountID: "+accountId+" (parent-DataProvider gave CardID: "+card.getLocalId()+" in account "+card.getAccountId()+") (existing: "+(foundAccount != null)+")\n" + + "cardID: "+entityFromServer.getCardId()+" (existing: "+(foundCard != null)+")\n" + + "all existing account-IDs: "+accountIDs + "\n" + + "all existing card-IDs: "+allCardIDs, cause); + } + + @Override public void getAllFromServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<List<Attachment>> responder, Instant lastSync) { responder.onResponse(attachments); } @@ -103,7 +117,7 @@ public class AttachmentDataProvider extends AbstractSyncDataProvider<Attachment> } @Override - public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, Attachment entity, DataBaseAdapter dataBaseAdapter) { + public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, Attachment entity, DataBaseAdapter dataBaseAdapter) { serverAdapter.deleteAttachment(board.getId(), stack.getId(), card.getId(), entity.getId(), callback); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/BoardDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/BoardDataProvider.java index a4d29f4bc..4e0c8dd8b 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/BoardDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/BoardDataProvider.java @@ -5,9 +5,6 @@ import android.util.Pair; import androidx.lifecycle.MutableLiveData; -import com.nextcloud.android.sso.api.EmptyResponse; -import com.nextcloud.android.sso.api.ParsedResponse; - import java.time.Instant; import java.util.ArrayList; import java.util.Collections; @@ -27,13 +24,17 @@ import it.niedermann.nextcloud.deck.remote.adapters.ServerAdapter; import it.niedermann.nextcloud.deck.remote.api.ResponseCallback; import it.niedermann.nextcloud.deck.remote.helpers.SyncHelper; import it.niedermann.nextcloud.deck.remote.helpers.util.AsyncUtil; +import okhttp3.Headers; public class BoardDataProvider extends AbstractSyncDataProvider<FullBoard> { private int progressTotal = 0; private int progressDone = 0; + private boolean isParallel = true; private MutableLiveData<Pair<Integer, Integer>> progress = null; + private ResponseCallback<Boolean> stepByStepCallback; + public BoardDataProvider() { super(null); } @@ -43,19 +44,28 @@ public class BoardDataProvider extends AbstractSyncDataProvider<FullBoard> { this.progress = progress; } + public BoardDataProvider(MutableLiveData<Pair<Integer, Integer>> progress$, boolean isParallel) { + this(progress$); + this.isParallel = isParallel; + } + @Override public void getAllFromServer(ServerAdapter serverAdapter, DataBaseAdapter dataBaseAdapter, long accountId, ResponseCallback<List<FullBoard>> responder, Instant lastSync) { serverAdapter.getBoards(new ResponseCallback<>(responder.getAccount()) { @Override - public void onResponse(ParsedResponse<List<FullBoard>> response) { - progressTotal = response.getResponse().size(); + public void onResponse(List<FullBoard> response) { + } + + @Override + public void onResponseWithHeaders(List<FullBoard> response, Headers headers) { + progressTotal = response.size(); updateProgress(); - String etag = response.getHeaders().get("ETag"); + String etag = headers.get("ETag"); if (etag != null && !etag.equals(account.getBoardsEtag())) { account.setBoardsEtag(etag); dataBaseAdapter.updateAccount(account); } - responder.onResponse(response.getResponse()); + responder.onResponse(response); } @SuppressLint("MissingSuperCall") @@ -160,6 +170,9 @@ public class BoardDataProvider extends AbstractSyncDataProvider<FullBoard> { @Override public void goDeeper(SyncHelper syncHelper, FullBoard existingEntity, FullBoard entityFromServer, ResponseCallback<Boolean> callback) { + if (!isParallel) { + stepByStepCallback = callback; + } List<Label> labels = entityFromServer.getLabels(); if (labels != null && !labels.isEmpty()) { syncHelper.doSyncFor(new LabelDataProvider(this, existingEntity.getBoard(), labels)); @@ -179,6 +192,20 @@ public class BoardDataProvider extends AbstractSyncDataProvider<FullBoard> { } @Override + public void childDone(AbstractSyncDataProvider<?> child, ResponseCallback<Boolean> responseCallback, boolean syncChangedSomething) { + removeChild(child); + if (!stillGoingDeeper && children.isEmpty()) { + if (parent != null) { + parent.childDone(this, responseCallback, syncChangedSomething); + } else { + responseCallback.onResponse(syncChangedSomething); + } + } else if (!isParallel && children.isEmpty()) { + stepByStepCallback.onResponse(syncChangedSomething); + } + } + + @Override public void createOnServer(ServerAdapter serverAdapter, DataBaseAdapter dataBaseAdapter, long accountId, ResponseCallback<FullBoard> responder, FullBoard entity) { serverAdapter.createBoard(entity.getBoard(), responder); } @@ -243,7 +270,7 @@ public class BoardDataProvider extends AbstractSyncDataProvider<FullBoard> { } @Override - public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, FullBoard entity, DataBaseAdapter dataBaseAdapter) { + public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, FullBoard entity, DataBaseAdapter dataBaseAdapter) { serverAdapter.deleteBoard(entity.getBoard(), callback); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/CardDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/CardDataProvider.java index a1e87897e..b67e94f8f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/CardDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/CardDataProvider.java @@ -2,7 +2,6 @@ package it.niedermann.nextcloud.deck.remote.helpers.providers; import android.annotation.SuppressLint; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import java.time.Instant; @@ -11,6 +10,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.database.DataBaseAdapter; @@ -23,6 +23,7 @@ import it.niedermann.nextcloud.deck.model.Card; import it.niedermann.nextcloud.deck.model.JoinCardWithLabel; import it.niedermann.nextcloud.deck.model.JoinCardWithUser; import it.niedermann.nextcloud.deck.model.Label; +import it.niedermann.nextcloud.deck.model.Stack; import it.niedermann.nextcloud.deck.model.User; import it.niedermann.nextcloud.deck.model.enums.DBStatus; import it.niedermann.nextcloud.deck.model.full.FullCard; @@ -47,6 +48,19 @@ public class CardDataProvider extends AbstractSyncDataProvider<FullCard> { } @Override + public void onInsertFailed(DataBaseAdapter dataBaseAdapter, RuntimeException cause, Account account, long accountId, List<FullCard> response, FullCard entityFromServer) { + Account foundAccount = dataBaseAdapter.getAccountByIdDirectly(accountId); + Stack foundStack = dataBaseAdapter.getStackByLocalIdDirectly(entityFromServer.getEntity().getStackId()); + List<Long> accountIDs = dataBaseAdapter.getAllAccountsDirectly().stream().map(Account::getId).collect(Collectors.toList()); + List<Long> allStackIDs = dataBaseAdapter.getAllStackIDs(); + throw new RuntimeException("Error creating Card.\n" + + "AccountID: "+accountId+" (parent-DataProvider gave StackID: "+stack.getLocalId()+" in account "+stack.getAccountId()+") (existing: "+(foundAccount != null)+")\n" + + "stackID: "+entityFromServer.getEntity().getStackId()+" (existing: "+(foundStack != null)+")\n" + + "all existing account-IDs: "+accountIDs + "\n" + + "all existing stack-IDs: "+ allStackIDs, cause); + } + + @Override public void getAllFromServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<List<FullCard>> responder, Instant lastSync) { @@ -204,7 +218,7 @@ public class CardDataProvider extends AbstractSyncDataProvider<FullCard> { } @Override - public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, FullCard entity, DataBaseAdapter dataBaseAdapter) { + public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, FullCard entity, DataBaseAdapter dataBaseAdapter) { serverAdapter.deleteCard(board.getId(), stack.getId(), entity.getCard(), callback); } @@ -256,7 +270,7 @@ public class CardDataProvider extends AbstractSyncDataProvider<FullCard> { } else { serverAdapter.unassignLabelFromCard(board.getId(), stack.getId(), changedLabel.getCardId(), changedLabel.getLabelId(), new ResponseCallback<>(account) { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { dataBaseAdapter.deleteJoinedLabelForCardPhysicallyByRemoteIDs(account.getId(), changedLabel.getCardId(), changedLabel.getLabelId()); } }); @@ -271,7 +285,7 @@ public class CardDataProvider extends AbstractSyncDataProvider<FullCard> { LABEL_JOINS_IN_SYNC.add(changedLabel); serverAdapter.assignLabelToCard(board.getId(), stack.getId(), changedLabel.getCardId(), changedLabel.getLabelId(), new ResponseCallback<>(account) { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { Label label = dataBaseAdapter.getLabelByRemoteIdDirectly(account.getId(), changedLabel.getLabelId()); dataBaseAdapter.setStatusForJoinCardWithLabel(card.getLocalId(), label.getLocalId(), DBStatus.UP_TO_DATE.getId()); LABEL_JOINS_IN_SYNC.remove(changedLabel); @@ -323,14 +337,14 @@ public class CardDataProvider extends AbstractSyncDataProvider<FullCard> { if (changedUser.getStatusEnum() == DBStatus.LOCAL_DELETED) { serverAdapter.unassignUserFromCard(board.getId(), stack.getId(), changedUser.getCardId(), user.getUid(), new ResponseCallback<>(account) { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { dataBaseAdapter.deleteJoinedUserForCardPhysicallyByRemoteIDs(account.getId(), changedUser.getCardId(), user.getUid()); } }); } else if (changedUser.getStatusEnum() == DBStatus.LOCAL_EDITED) { serverAdapter.assignUserToCard(board.getId(), stack.getId(), changedUser.getCardId(), user.getUid(), new ResponseCallback<>(account) { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { dataBaseAdapter.setStatusForJoinCardWithUser(card.getLocalId(), user.getLocalId(), DBStatus.UP_TO_DATE.getId()); } }); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/CardPropagationDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/CardPropagationDataProvider.java index c91ac9740..e9028c685 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/CardPropagationDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/CardPropagationDataProvider.java @@ -2,8 +2,6 @@ package it.niedermann.nextcloud.deck.remote.helpers.providers; import android.annotation.SuppressLint; -import com.nextcloud.android.sso.api.EmptyResponse; - import it.niedermann.nextcloud.deck.database.DataBaseAdapter; import it.niedermann.nextcloud.deck.model.Board; import it.niedermann.nextcloud.deck.model.Card; @@ -85,7 +83,7 @@ public class CardPropagationDataProvider extends CardDataProvider { } @Override - public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, FullCard entity, DataBaseAdapter dataBaseAdapter) { + public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, FullCard entity, DataBaseAdapter dataBaseAdapter) { serverAdapter.deleteCard(board.getId(), stack.getId(), entity.getCard(), callback); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/DeckCommentsDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/DeckCommentsDataProvider.java index ee0b357f0..ac88f3219 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/DeckCommentsDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/DeckCommentsDataProvider.java @@ -2,17 +2,17 @@ package it.niedermann.nextcloud.deck.remote.helpers.providers; import android.annotation.SuppressLint; -import com.nextcloud.android.sso.api.EmptyResponse; - import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.stream.Collectors; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.database.DataBaseAdapter; +import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Card; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; import it.niedermann.nextcloud.deck.model.ocs.comment.Mention; @@ -50,6 +50,22 @@ public class DeckCommentsDataProvider extends AbstractSyncDataProvider<OcsCommen }); } + @Override + public void onInsertFailed(DataBaseAdapter dataBaseAdapter, RuntimeException cause, Account account, long accountId, List<OcsComment> response, OcsComment entityFromServer) { + Account foundAccount = dataBaseAdapter.getAccountByIdDirectly(accountId); + DeckComment comment = entityFromServer.getSingle(); + Card foundCard = dataBaseAdapter.getCardByLocalIdDirectly(accountId, comment.getObjectId()); + DeckComment foundComment = dataBaseAdapter.getCommentByLocalIdDirectly(accountId, comment.getParentId()); + List<Long> accountIDs = dataBaseAdapter.getAllAccountsDirectly().stream().map(Account::getId).collect(Collectors.toList()); + List<Long> allCardIDs = dataBaseAdapter.getAllCardIDs(); + throw new RuntimeException("Error creating Comment.\n" + + "AccountID: "+accountId+" (existing: "+(foundAccount != null)+")\n" + + "cardID: "+comment.getObjectId()+" (parent-DataProvider gave CardID: "+card.getLocalId()+" in account "+card.getAccountId()+") (existing: "+(foundCard != null)+")\n" + + "parentID: "+comment.getParentId()+" (existing: "+(foundComment != null)+")\n" + + "all existing account-IDs: "+accountIDs + "\n" + + "all existing card-IDs: "+allCardIDs, cause); + } + private void verifyCommentListIntegrity(List<OcsComment> comments) { List<Long> knownIDs = new ArrayList<>(); for (OcsComment comment : comments) { @@ -156,7 +172,7 @@ public class DeckCommentsDataProvider extends AbstractSyncDataProvider<OcsCommen } @Override - public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, OcsComment entity, DataBaseAdapter dataBaseAdapter) { + public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, OcsComment entity, DataBaseAdapter dataBaseAdapter) { DeckComment comment = entity.getSingle(); comment.setObjectId(card.getId()); serverAdapter.deleteCommentForCard(comment, callback); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/LabelDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/LabelDataProvider.java index b01ca2b51..9259391bb 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/LabelDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/LabelDataProvider.java @@ -2,8 +2,6 @@ package it.niedermann.nextcloud.deck.remote.helpers.providers; import android.annotation.SuppressLint; -import com.nextcloud.android.sso.api.EmptyResponse; - import java.time.Instant; import java.util.List; @@ -96,7 +94,7 @@ public class LabelDataProvider extends AbstractSyncDataProvider<Label> { } @Override - public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, Label entity, DataBaseAdapter dataBaseAdapter) { + public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, Label entity, DataBaseAdapter dataBaseAdapter) { serverAdapter.deleteLabel(board.getId(), entity, callback); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/OcsProjectDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/OcsProjectDataProvider.java index 0f5d28ff6..23552f2ed 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/OcsProjectDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/OcsProjectDataProvider.java @@ -1,6 +1,5 @@ package it.niedermann.nextcloud.deck.remote.helpers.providers; -import com.nextcloud.android.sso.api.EmptyResponse; import java.time.Instant; import java.util.Collections; @@ -107,7 +106,7 @@ public class OcsProjectDataProvider extends AbstractSyncDataProvider<OcsProject> } @Override - public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, OcsProject entity, DataBaseAdapter dataBaseAdapter) { + public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, OcsProject entity, DataBaseAdapter dataBaseAdapter) { // Do Nothing } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/StackDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/StackDataProvider.java index 16eea8692..a690eb471 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/StackDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/StackDataProvider.java @@ -1,6 +1,5 @@ package it.niedermann.nextcloud.deck.remote.helpers.providers; -import com.nextcloud.android.sso.api.EmptyResponse; import java.time.Instant; import java.util.Collections; @@ -93,7 +92,7 @@ public class StackDataProvider extends AbstractSyncDataProvider<FullStack> { } @Override - public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, FullStack entity, DataBaseAdapter dataBaseAdapter) { + public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, FullStack entity, DataBaseAdapter dataBaseAdapter) { entity.getStack().setBoardId(board.getId()); serverAdapter.deleteStack(board.getBoard(), entity.getStack(), callback); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/UserDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/UserDataProvider.java index 7b322b5e9..36e2af2cc 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/UserDataProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/helpers/providers/UserDataProvider.java @@ -1,6 +1,5 @@ package it.niedermann.nextcloud.deck.remote.helpers.providers; -import com.nextcloud.android.sso.api.EmptyResponse; import java.time.Instant; import java.util.List; @@ -64,7 +63,7 @@ public class UserDataProvider extends AbstractSyncDataProvider<User> { } @Override - public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<EmptyResponse> callback, User entity, DataBaseAdapter dataBaseAdapter) { + public void deleteOnServer(ServerAdapter serverAdapter, long accountId, ResponseCallback<Void> callback, User entity, DataBaseAdapter dataBaseAdapter) { //TODO: implement } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/repository/SyncRepository.java b/app/src/main/java/it/niedermann/nextcloud/deck/repository/SyncRepository.java index b08cbc8a7..1c3bf3f88 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/repository/SyncRepository.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/repository/SyncRepository.java @@ -18,8 +18,6 @@ import androidx.lifecycle.MediatorLiveData; import androidx.lifecycle.MutableLiveData; import com.nextcloud.android.sso.AccountImporter; -import com.nextcloud.android.sso.api.EmptyResponse; -import com.nextcloud.android.sso.api.ParsedResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import com.nextcloud.android.sso.model.SingleSignOnAccount; @@ -79,6 +77,7 @@ import it.niedermann.nextcloud.deck.remote.helpers.providers.StackDataProvider; import it.niedermann.nextcloud.deck.remote.helpers.providers.partial.BoardWithAclDownSyncDataProvider; import it.niedermann.nextcloud.deck.remote.helpers.providers.partial.BoardWithStacksAndLabelsUpSyncDataProvider; import it.niedermann.nextcloud.deck.remote.helpers.util.ConnectivityUtil; +import okhttp3.Headers; /** * Extends {@link BaseRepository} by synchronization capabilities. @@ -127,7 +126,7 @@ public class SyncRepository extends BaseRepository { } @AnyThread - public void fetchBoardsFromServer(@NonNull ResponseCallback<ParsedResponse<List<FullBoard>>> callback) { + public void fetchBoardsFromServer(@NonNull ResponseCallback<List<FullBoard>> callback) { executor.submit(() -> serverAdapter.getBoards(callback)); } @@ -183,7 +182,7 @@ public class SyncRepository extends BaseRepository { syncHelper.setResponseCallback(callback); try { - syncHelper.doSyncFor(new BoardDataProvider(progress$)); + syncHelper.doSyncFor(new BoardDataProvider(progress$, false), false); } catch (Throwable e) { DeckLog.logError(e); responseCallback.onError(e); @@ -289,12 +288,15 @@ public class SyncRepository extends BaseRepository { Account accountForEtag = dataBaseAdapter.getAccountByIdDirectly(callback.getAccount().getId()); serverAdapter.getCapabilities(accountForEtag.getEtag(), new ResponseCallback<>(callback.getAccount()) { @Override - public void onResponse(ParsedResponse<Capabilities> response) { + public void onResponse(Capabilities response) {} + + @Override + public void onResponseWithHeaders(Capabilities response, Headers headers) { Account acc = dataBaseAdapter.getAccountByIdDirectly(account.getId()); - acc.applyCapabilities(response.getResponse(), response.getHeaders().get("ETag")); + acc.applyCapabilities(response, headers.get("ETag")); dataBaseAdapter.updateAccount(acc); callback.getAccount().setServerDeckVersion(acc.getServerDeckVersion()); - callback.onResponse(response.getResponse()); + callback.onResponse(response); } @SuppressLint("MissingSuperCall") @@ -308,7 +310,7 @@ public class SyncRepository extends BaseRepository { final Capabilities capabilities = GsonConfig.getGson().fromJson(errorString, Capabilities.class); if (capabilities.isMaintenanceEnabled()) { DeckLog.verbose("Yes, it is in maintenance mode according to the capabilities"); - executor.submit(() -> onResponse(ParsedResponse.of(capabilities))); + executor.submit(() -> onResponseWithHeaders(capabilities, Headers.of())); } else { DeckLog.error("No, it is not in maintenance mode according to the capabilities."); callback.onError(throwable); @@ -560,7 +562,7 @@ public class SyncRepository extends BaseRepository { } @AnyThread - public void deleteComment(long accountId, long localCardId, long localCommentId, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteComment(long accountId, long localCardId, long localCommentId, @NonNull IResponseCallback<Void> callback) { executor.submit(() -> { final Account account = dataBaseAdapter.getAccountByIdDirectly(accountId); final Card card = dataBaseAdapter.getCardByLocalIdDirectly(accountId, localCardId); @@ -572,7 +574,7 @@ public class SyncRepository extends BaseRepository { } @AnyThread - public void deleteBoard(@NonNull Board board, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteBoard(@NonNull Board board, @NonNull IResponseCallback<Void> callback) { executor.submit(() -> { long accountId = board.getAccountId(); Account account = dataBaseAdapter.getAccountByIdDirectly(accountId); @@ -615,14 +617,14 @@ public class SyncRepository extends BaseRepository { } @AnyThread - public void deleteAccessControl(@NonNull AccessControl entity, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteAccessControl(@NonNull AccessControl entity, @NonNull IResponseCallback<Void> callback) { executor.submit(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(entity.getAccountId()); FullBoard board = dataBaseAdapter.getFullBoardByLocalIdDirectly(entity.getAccountId(), entity.getBoardId()); new DataPropagationHelper(serverAdapter, dataBaseAdapter, executor).deleteEntity( new AccessControlDataProvider(null, board, Collections.singletonList(entity)), entity, new ResponseCallback<>(account) { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { // revoked own board-access? if (entity.getAccountId() == entity.getAccountId() && entity.getUser().getUid().equals(account.getUserName())) { dataBaseAdapter.saveNeighbourOfBoard(board.getAccountId(), board.getLocalId()); @@ -659,7 +661,7 @@ public class SyncRepository extends BaseRepository { } @AnyThread - public void deleteStack(long accountId, long boardLocalId, long stackLocalId, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteStack(long accountId, long boardLocalId, long stackLocalId, @NonNull IResponseCallback<Void> callback) { executor.submit(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(accountId); FullStack fullStack = dataBaseAdapter.getFullStackByLocalIdDirectly(stackLocalId); @@ -829,7 +831,7 @@ public class SyncRepository extends BaseRepository { } @AnyThread - public void deleteCard(@NonNull Card card, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteCard(@NonNull Card card, @NonNull IResponseCallback<Void> callback) { executor.submit(() -> { FullCard fullCard = dataBaseAdapter.getFullCardByLocalIdDirectly(card.getAccountId(), card.getLocalId()); if (fullCard == null) { @@ -869,7 +871,7 @@ public class SyncRepository extends BaseRepository { } @AnyThread - public void archiveCardsInStack(long accountId, long stackLocalId, @NonNull FilterInformation filterInformation, @NonNull IResponseCallback<EmptyResponse> callback) { + public void archiveCardsInStack(long accountId, long stackLocalId, @NonNull FilterInformation filterInformation, @NonNull IResponseCallback<Void> callback) { executor.submit(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(accountId); FullStack stack = dataBaseAdapter.getFullStackByLocalIdDirectly(stackLocalId); @@ -1034,7 +1036,7 @@ public class SyncRepository extends BaseRepository { */ @SuppressWarnings("JavadocReference") @AnyThread - public void moveCard(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId, @NonNull IResponseCallback<EmptyResponse> callback) { + public void moveCard(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId, @NonNull IResponseCallback<Void> callback) { executor.submit(() -> { final FullCard originalCard = dataBaseAdapter.getFullCardByLocalIdDirectly(originAccountId, originCardLocalId); final int newIndex = dataBaseAdapter.getHighestCardOrderInStack(targetStackLocalId) + 1; @@ -1208,7 +1210,7 @@ public class SyncRepository extends BaseRepository { } @AnyThread - public void deleteLabel(@NonNull Label label, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteLabel(@NonNull Label label, @NonNull IResponseCallback<Void> callback) { executor.submit(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(label.getAccountId()); Board board = dataBaseAdapter.getBoardByLocalIdDirectly(label.getBoardId()); @@ -1244,7 +1246,7 @@ public class SyncRepository extends BaseRepository { serverAdapter.assignUserToCard(board.getId(), stack.getId(), card.getId(), user.getUid(), new ResponseCallback<>(account) { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { dataBaseAdapter.setStatusForJoinCardWithUser(localCardId, localUserId, DBStatus.UP_TO_DATE.getId()); } }); @@ -1273,7 +1275,7 @@ public class SyncRepository extends BaseRepository { serverAdapterToUse.assignLabelToCard(board.getId(), stack.getId(), card.getId(), label.getId(), new ResponseCallback<>(account) { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { dataBaseAdapter.setStatusForJoinCardWithLabel(localCardId, localLabelId, DBStatus.UP_TO_DATE.getId()); } }); @@ -1291,7 +1293,7 @@ public class SyncRepository extends BaseRepository { if (connectivityUtil.hasInternetConnection()) { serverAdapter.unassignLabelFromCard(board.getId(), stack.getId(), card.getId(), label.getId(), new ResponseCallback<>(account) { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { dataBaseAdapter.deleteJoinedLabelForCardPhysically(card.getLocalId(), label.getLocalId()); } }); @@ -1309,7 +1311,7 @@ public class SyncRepository extends BaseRepository { Account account = dataBaseAdapter.getAccountByIdDirectly(card.getAccountId()); serverAdapter.unassignUserFromCard(board.getId(), stack.getId(), card.getId(), user.getUid(), new ResponseCallback<>(account) { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { dataBaseAdapter.deleteJoinedUserForCardPhysically(card.getLocalId(), user.getLocalId()); } }); @@ -1500,7 +1502,7 @@ public class SyncRepository extends BaseRepository { } @AnyThread - public void deleteAttachmentOfCard(long accountId, long localCardId, long localAttachmentId, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteAttachmentOfCard(long accountId, long localCardId, long localAttachmentId, @NonNull IResponseCallback<Void> callback) { executor.submit(() -> { if (connectivityUtil.hasInternetConnection()) { FullCard card = dataBaseAdapter.getFullCardByLocalIdDirectly(accountId, localCardId); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/ImportAccountActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/ImportAccountActivity.java index 1bcb26662..5440c17c5 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/ImportAccountActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/ImportAccountActivity.java @@ -23,7 +23,6 @@ import androidx.preference.PreferenceManager; import com.bumptech.glide.Glide; import com.nextcloud.android.sso.AccountImporter; -import com.nextcloud.android.sso.api.ParsedResponse; import com.nextcloud.android.sso.exceptions.AccountImportCancelledException; import com.nextcloud.android.sso.exceptions.AndroidGetAccountsPermissionNotGranted; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; @@ -188,7 +187,7 @@ public class ImportAccountActivity extends AppCompatActivity { if (response.getDeckVersion().firstCallHasDifferentResponseStructure()) { syncManager.fetchBoardsFromServer(new ResponseCallback<>(account) { @Override - public void onResponse(ParsedResponse<List<FullBoard>> response) { + public void onResponse(List<FullBoard> response) { callback.onResponse(createdAccount); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java index 7e2535755..93902d9f8 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java @@ -10,8 +10,6 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.lifecycle.ViewModelProvider; -import com.nextcloud.android.sso.api.EmptyResponse; - import it.niedermann.android.reactivelivedata.ReactiveLiveData; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ActivityPickStackBinding; @@ -63,7 +61,7 @@ public abstract class PickStackActivity extends AppCompatActivity implements The viewModel.setSubmitInProgress(true); onSubmit(viewModel.getAccount(), viewModel.getBoardLocalId(), viewModel.getStackLocalId(), new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { runOnUiThread(() -> viewModel.setSubmitInProgress(false)); } @@ -115,7 +113,7 @@ public abstract class PickStackActivity extends AppCompatActivity implements The utils.material.colorTextInputLayout(binding.inputWrapper); } - abstract protected void onSubmit(Account account, long boardLocalId, long stackId, @NonNull IResponseCallback<EmptyResponse> callback); + abstract protected void onSubmit(Account account, long boardLocalId, long stackId, @NonNull IResponseCallback<Void> callback); abstract protected boolean showBoardsWithoutEditPermission(); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActivity.java index 866f539ba..58237dbd5 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActivity.java @@ -10,8 +10,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; -import com.nextcloud.android.sso.api.EmptyResponse; - import it.niedermann.android.reactivelivedata.ReactiveLiveData; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.databinding.ActivityArchivedBinding; @@ -84,7 +82,7 @@ public class ArchivedBoardsActivity extends AppCompatActivity implements Themed, public void onBoardDeleted(Board board) { archivedBoardsViewModel.deleteBoard(board, new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.info("Successfully deleted board", board.getTitle()); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsViewModel.java index 4b54017ba..bcd9345fc 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsViewModel.java @@ -5,7 +5,6 @@ import android.app.Application; import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import java.util.Collections; @@ -33,7 +32,7 @@ public class ArchivedBoardsViewModel extends SyncViewModel { syncRepository.updateBoard(board, callback); } - public void deleteBoard(@NonNull Board board, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteBoard(@NonNull Board board, @NonNull IResponseCallback<Void> callback) { syncRepository.deleteBoard(board, callback); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java index d25f1507b..aeb2e62c4 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java @@ -16,7 +16,6 @@ import androidx.fragment.app.DialogFragment; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import java.util.List; @@ -142,7 +141,7 @@ public class AccessControlDialogFragment extends DialogFragment implements Acces public void deleteAccessControl(AccessControl ac) { accessControlViewModel.deleteAccessControl(ac, new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.info("Successfully deleted access control for user", ac.getUser().getDisplayname()); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlViewModel.java index 570eacf3c..a83ead085 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlViewModel.java @@ -5,7 +5,6 @@ import android.app.Application; import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import java.util.List; @@ -43,7 +42,7 @@ public class AccessControlViewModel extends SyncViewModel { syncRepository.updateAccessControl(entity, callback); } - public void deleteAccessControl(@NonNull AccessControl entity, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteAccessControl(@NonNull AccessControl entity, @NonNull IResponseCallback<Void> callback) { syncRepository.deleteAccessControl(entity, callback); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/LabelsViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/LabelsViewModel.java index 3537b8521..699d7ed8e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/LabelsViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/LabelsViewModel.java @@ -5,7 +5,6 @@ import android.app.Application; import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import it.niedermann.android.reactivelivedata.ReactiveLiveData; @@ -33,7 +32,7 @@ public class LabelsViewModel extends SyncViewModel { syncRepository.createLabel(account.getId(), label, localBoardId, callback); } - public void deleteLabel(@NonNull Label label, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteLabel(@NonNull Label label, @NonNull IResponseCallback<Void> callback) { syncRepository.deleteLabel(label, callback); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java index 07bc4c215..6accebc80 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java @@ -13,7 +13,6 @@ import androidx.annotation.UiThread; import androidx.fragment.app.DialogFragment; import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.nextcloud.android.sso.api.EmptyResponse; import java.util.Random; @@ -163,7 +162,7 @@ public class ManageLabelsDialogFragment extends ThemedDialogFragment implements private void deleteLabel(@NonNull Label label) { labelsViewModel.deleteLabel(label, new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.info("Successfully deleted label", label.getTitle()); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java index 9d201659b..2d2eb585f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java @@ -13,7 +13,6 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.preference.PreferenceManager; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import java.io.File; @@ -191,7 +190,7 @@ public class EditCardViewModel extends BaseViewModel { syncRepository.addAttachmentToCard(accountId, localCardId, mimeType, file, callback); } - public void deleteAttachmentOfCard(long accountId, long localCardId, long localAttachmentId, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteAttachmentOfCard(long accountId, long localCardId, long localAttachmentId, @NonNull IResponseCallback<Void> callback) { syncRepository.deleteAttachmentOfCard(accountId, localCardId, localAttachmentId, callback); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java index 4de403ba7..4f47c5186 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java @@ -46,7 +46,6 @@ import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.snackbar.Snackbar; import com.nextcloud.android.common.ui.theme.utils.ColorRole; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import java.io.File; @@ -502,7 +501,7 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentDelet if (attachment.getLocalId() != null) { editViewModel.deleteAttachmentOfCard(editViewModel.getAccount().getId(), editViewModel.getFullCard().getLocalId(), attachment.getLocalId(), new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.info("Successfully delete", Attachment.class.getSimpleName(), attachment.getFilename(), "from", Card.class.getSimpleName(), editViewModel.getFullCard().getCard().getTitle()); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java index caa13cb07..495aabd50 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java @@ -21,7 +21,6 @@ import androidx.lifecycle.ViewModelProvider; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; -import com.nextcloud.android.sso.api.EmptyResponse; import java.time.Instant; @@ -173,7 +172,7 @@ public class CardCommentsFragment extends Fragment implements Themed, CommentEdi public void onCommentDeleted(Long localId) { commentsViewModel.deleteComment(editCardViewModel.getAccount().getId(), editCardViewModel.getFullCard().getLocalId(), localId, new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.info("Successfully deleted comment with localId", localId); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CommentsViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CommentsViewModel.java index 41179ce37..79b0043fe 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CommentsViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CommentsViewModel.java @@ -8,7 +8,6 @@ import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import java.util.List; @@ -48,7 +47,7 @@ public class CommentsViewModel extends SyncViewModel { syncRepository.updateComment(accountId, localCardId, localCommentId, comment); } - public void deleteComment(long accountId, long localCardId, long localCommentId, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteComment(long accountId, long localCardId, long localCommentId, @NonNull IResponseCallback<Void> callback) { syncRepository.deleteComment(accountId, localCardId, localCommentId, callback); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/MainActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/MainActivity.java index 4388d0ae3..6ce29a27e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/MainActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/MainActivity.java @@ -45,7 +45,6 @@ import com.bumptech.glide.request.transition.Transition; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.tabs.TabLayoutMediator; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import com.nextcloud.android.sso.exceptions.UnknownErrorException; @@ -707,7 +706,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen final var filterInformation = Optional.ofNullable(filterViewModel.getFilterInformation().getValue()).orElse(new FilterInformation()); mainViewModel.archiveCardsInStack(stack.getAccountId(), stackLocalId, filterInformation, new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.info("Successfully archived all cards in stack local id", stackLocalId); } @@ -861,7 +860,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen public void onDeleteStack(long accountId, long boardId, long stackId) { mainViewModel.deleteStack(accountId, boardId, stackId, new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.info("Successfully deleted stack with local id", stackId, "and remote id", stackId); } @@ -879,7 +878,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen public void onBoardDeleted(Board board) { mainViewModel.deleteBoard(board, new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.info("Successfully deleted board", board.getTitle()); } @@ -970,7 +969,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen public void onDelete(@NonNull FullCard fullCard) { mainViewModel.deleteCard(fullCard.getCard(), new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.info("Successfully deleted card", fullCard.getCard().getTitle()); } @@ -1031,7 +1030,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen public void move(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId) { mainViewModel.moveCard(originAccountId, originCardLocalId, targetAccountId, targetBoardLocalId, targetStackLocalId, new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.log("Moved", Card.class.getSimpleName(), originCardLocalId, "to", Stack.class.getSimpleName(), targetStackLocalId); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/MainViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/MainViewModel.java index 54c39600e..8fe3ce0a5 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/MainViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/MainViewModel.java @@ -11,7 +11,6 @@ import androidx.core.util.Pair; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import java.io.File; @@ -130,7 +129,7 @@ public class MainViewModel extends BaseViewModel { } } - public void deleteBoard(@NonNull Board board, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteBoard(@NonNull Board board, @NonNull IResponseCallback<Void> callback) { if (syncRepository == null) { callback.onError(getInvalidSyncManagerException()); } else { @@ -173,7 +172,7 @@ public class MainViewModel extends BaseViewModel { } } - public void deleteStack(long accountId, long boardId, long stackLocalId, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteStack(long accountId, long boardId, long stackLocalId, @NonNull IResponseCallback<Void> callback) { if (syncRepository == null) { callback.onError(getInvalidSyncManagerException()); } else { @@ -197,7 +196,7 @@ public class MainViewModel extends BaseViewModel { } } - public void archiveCardsInStack(long accountId, long stackId, @NonNull FilterInformation filterInformation, @NonNull IResponseCallback<EmptyResponse> callback) { + public void archiveCardsInStack(long accountId, long stackId, @NonNull FilterInformation filterInformation, @NonNull IResponseCallback<Void> callback) { if (syncRepository == null) { callback.onError(getInvalidSyncManagerException()); } else { @@ -275,7 +274,7 @@ public class MainViewModel extends BaseViewModel { } } - public void deleteCard(@NonNull Card card, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteCard(@NonNull Card card, @NonNull IResponseCallback<Void> callback) { if (syncRepository == null) { callback.onError(getInvalidSyncManagerException()); } else { @@ -305,7 +304,7 @@ public class MainViewModel extends BaseViewModel { return baseRepository.getUserByUidDirectly(accountId, uid); } - public void moveCard(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId, @NonNull IResponseCallback<EmptyResponse> callback) { + public void moveCard(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId, @NonNull IResponseCallback<Void> callback) { if (syncRepository == null) { callback.onError(getInvalidSyncManagerException()); } else { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java index fd39beefb..1a87cd2c7 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java @@ -10,8 +10,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.ViewModelProvider; -import com.nextcloud.android.sso.api.EmptyResponse; - import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.full.FullCard; @@ -35,7 +33,7 @@ public class PrepareCreateActivity extends PickStackActivity { } @Override - protected void onSubmit(Account account, long boardId, long stackId, @NonNull IResponseCallback<EmptyResponse> callback) { + protected void onSubmit(Account account, long boardId, long stackId, @NonNull IResponseCallback<Void> callback) { Toast.makeText(this, R.string.saving_new_card, Toast.LENGTH_SHORT).show(); final FullCard fullCard; if (requireContent()) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java index 6e328b7d7..33925a8f5 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java @@ -20,7 +20,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.nextcloud.android.common.ui.theme.utils.ColorRole; -import com.nextcloud.android.sso.api.EmptyResponse; import it.niedermann.android.crosstabdnd.DragAndDropTab; import it.niedermann.android.reactivelivedata.ReactiveLiveData; @@ -171,7 +170,7 @@ public class StackFragment extends Fragment implements Themed, DragAndDropTab<Ca public void move(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId) { stackViewModel.moveCard(originAccountId, originCardLocalId, targetAccountId, targetBoardLocalId, targetStackLocalId, new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.log("Moved", Card.class.getSimpleName(), originCardLocalId, "to", Stack.class.getSimpleName(), targetStackLocalId); } @@ -229,7 +228,7 @@ public class StackFragment extends Fragment implements Themed, DragAndDropTab<Ca public void onDelete(@NonNull FullCard fullCard) { stackViewModel.deleteCard(fullCard.getCard(), new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.info("Successfully deleted card", fullCard.getCard().getTitle()); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackViewModel.java index d7fdb3364..c6746449c 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackViewModel.java @@ -8,7 +8,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.LiveData; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import java.util.List; @@ -30,7 +29,7 @@ public class StackViewModel extends SyncViewModel { super(application, account); } - public void moveCard(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId, @NonNull IResponseCallback<EmptyResponse> callback) { + public void moveCard(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId, @NonNull IResponseCallback<Void> callback) { syncRepository.moveCard(originAccountId, originCardLocalId, targetAccountId, targetBoardLocalId, targetStackLocalId, callback); } @@ -70,7 +69,7 @@ public class StackViewModel extends SyncViewModel { } - public void deleteCard(@NonNull Card card, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteCard(@NonNull Card card, @NonNull IResponseCallback<Void> callback) { syncRepository.deleteCard(card, callback); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsActivity.java index aed6d5f48..1d2e68633 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsActivity.java @@ -11,7 +11,6 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.ViewModelProvider; import com.nextcloud.android.common.ui.theme.utils.ColorRole; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import it.niedermann.nextcloud.deck.DeckLog; @@ -85,7 +84,7 @@ public class UpcomingCardsActivity extends AppCompatActivity implements Themed, }), card -> viewModel.deleteCard(card, new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.info("Successfully deleted card", card.getTitle()); } @@ -129,7 +128,7 @@ public class UpcomingCardsActivity extends AppCompatActivity implements Themed, public void move(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId) { viewModel.moveCard(originAccountId, originCardLocalId, targetAccountId, targetBoardLocalId, targetStackLocalId, new IResponseCallback<>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { DeckLog.log("Moved", Card.class.getSimpleName(), originCardLocalId, "to", Stack.class.getSimpleName(), targetStackLocalId); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsViewModel.java index 870a23cdc..c3dbcb8a7 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsViewModel.java @@ -5,7 +5,6 @@ import android.app.Application; import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; -import com.nextcloud.android.sso.api.EmptyResponse; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import java.util.List; @@ -50,7 +49,7 @@ public class UpcomingCardsViewModel extends BaseViewModel { }); } - public void deleteCard(@NonNull Card card, @NonNull IResponseCallback<EmptyResponse> callback) { + public void deleteCard(@NonNull Card card, @NonNull IResponseCallback<Void> callback) { executor.submit(() -> { final var account = baseRepository.readAccountDirectly(card.getAccountId()); try { @@ -62,7 +61,7 @@ public class UpcomingCardsViewModel extends BaseViewModel { }); } - public void moveCard(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId, @NonNull IResponseCallback<EmptyResponse> callback) { + public void moveCard(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId, @NonNull IResponseCallback<Void> callback) { executor.submit(() -> { final var account = baseRepository.readAccountDirectly(originAccountId); try { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/stack/StackWidgetConfigurationActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/stack/StackWidgetConfigurationActivity.java index 965c2b938..eaa8ba264 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/stack/StackWidgetConfigurationActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/stack/StackWidgetConfigurationActivity.java @@ -9,8 +9,6 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.lifecycle.ViewModelProvider; -import com.nextcloud.android.sso.api.EmptyResponse; - import java.util.Collections; import it.niedermann.nextcloud.deck.DeckLog; @@ -55,7 +53,7 @@ public class StackWidgetConfigurationActivity extends PickStackActivity { } @Override - protected void onSubmit(Account account, long boardId, long stackId, @NonNull IResponseCallback<EmptyResponse> callback) { + protected void onSubmit(Account account, long boardId, long stackId, @NonNull IResponseCallback<Void> callback) { final FilterWidget config = new FilterWidget(appWidgetId, EWidgetType.STACK_WIDGET); final FilterWidgetAccount filterWidgetAccount = new FilterWidgetAccount(account.getId(), false); filterWidgetAccount.setIncludeNoProject(false); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/util/ExecutorServiceProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/util/ExecutorServiceProvider.java index 8ef8c5771..a307f19c3 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/util/ExecutorServiceProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/util/ExecutorServiceProvider.java @@ -1,16 +1,27 @@ package it.niedermann.nextcloud.deck.util; +import androidx.annotation.NonNull; + +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import it.niedermann.nextcloud.deck.DeckLog; + public class ExecutorServiceProvider { private static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); private static final ExecutorService EXECUTOR = new ThreadPoolExecutor(NUMBER_OF_CORES >> 1, NUMBER_OF_CORES, - 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); + 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()) { + @Override + public Future<?> submit(Runnable task) { + return super.submit(new RetryableRunnable(task)); + } + }; private ExecutorServiceProvider() { // hide Constructor @@ -19,4 +30,49 @@ public class ExecutorServiceProvider { public static ExecutorService getLinkedBlockingQueueExecutor() { return EXECUTOR; } + + public static void awaitExecution(@NonNull Runnable runnable) { + final var latch = new CountDownLatch(1); + EXECUTOR.submit(() -> { + runnable.run(); + latch.countDown(); + }); + try { + latch.await(); + } catch (Throwable e) { + DeckLog.error(e); + } + } + + private static class RetryableRunnable implements Runnable { + private final int maxRetries; + @NonNull + private final Runnable runnable; + private int retriesLeft; + + public RetryableRunnable(@NonNull Runnable runnable) { + this(runnable, 5); + } + + public RetryableRunnable(@NonNull Runnable runnable, int maxRetries) { + this.runnable = runnable; + this.maxRetries = maxRetries; + this.retriesLeft = maxRetries; + } + + @Override + public void run() { + try { + runnable.run(); + } catch (Exception e) { + if (retriesLeft < 1) { + DeckLog.error("Error executing task, already retried", maxRetries, " times, giving up. Error causing this:", DeckLog.getStacktraceAsString(e)); + throw e; + } + DeckLog.error("Error executing task, retrying for", retriesLeft, " more times. Error causing this:", DeckLog.getStacktraceAsString(e)); + retriesLeft--; + EXECUTOR.submit(this); + } + } + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4b59ce20d..0143ccc3c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -233,6 +233,7 @@ <string name="error_dialog_tip_offline_no_internet">It looks like you tried to trigger a synchronization without an internet connection.</string> <string name="error_dialog_tip_offline_connection_refused">Connection was refused, please check if your server is reachable via web browser.</string> <string name="error_dialog_tip_offline_connection_timeout">Connection timed out, please check if you\'re connected to the internet. This may also happen when your server is busy.</string> + <string name="error_dialog_tip_connection_rejected">Connection was rejected, please check if your server is protected by an anti-flooding mechanism. This is most likely a problem caused by Cloudflare protected Servers.</string> <string name="error_dialog_tip_sync_only_on_wifi">If you have enabled the "Sync only on Wi-Fi" setting, you can only synchronize when you are connected to a Wi-Fi.</string> <string name="error_dialog_tip_files_force_stop">Something seems to be wrong with your Nextcloud app. Please try to force stop both, the Nextcloud app and the Nextcloud Deck app.</string> <string name="error_dialog_tip_files_delete_storage">If force stopping them does not help, you can try to clear the storage of both apps.</string> diff --git a/app/src/test/java/it/niedermann/nextcloud/deck/remote/api/ResponseCallbackTest.java b/app/src/test/java/it/niedermann/nextcloud/deck/remote/api/ResponseCallbackTest.java index 4b5786564..14c398132 100644 --- a/app/src/test/java/it/niedermann/nextcloud/deck/remote/api/ResponseCallbackTest.java +++ b/app/src/test/java/it/niedermann/nextcloud/deck/remote/api/ResponseCallbackTest.java @@ -10,8 +10,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import com.nextcloud.android.sso.api.EmptyResponse; - import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -52,9 +50,9 @@ public class ResponseCallbackTest { @Test public void testFrom() { // No lambda, since Mockito requires a non final class for a spy - final var originalCallback = new IResponseCallback<EmptyResponse>() { + final var originalCallback = new IResponseCallback<Void>() { @Override - public void onResponse(EmptyResponse response) { + public void onResponse(Void response) { // Do nothing... } }; diff --git a/app/src/test/java/it/niedermann/nextcloud/deck/repository/SyncRepositoryTest.java b/app/src/test/java/it/niedermann/nextcloud/deck/repository/SyncRepositoryTest.java index 0273a1b64..076234917 100644 --- a/app/src/test/java/it/niedermann/nextcloud/deck/repository/SyncRepositoryTest.java +++ b/app/src/test/java/it/niedermann/nextcloud/deck/repository/SyncRepositoryTest.java @@ -28,7 +28,6 @@ import androidx.lifecycle.MutableLiveData; import androidx.test.core.app.ApplicationProvider; import com.google.common.util.concurrent.MoreExecutors; -import com.nextcloud.android.sso.api.ParsedResponse; import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import org.junit.Before; @@ -40,7 +39,6 @@ import org.robolectric.RobolectricTestRunner; import java.time.Instant; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.concurrent.ExecutionException; import it.niedermann.nextcloud.deck.TestUtil; @@ -64,6 +62,7 @@ import it.niedermann.nextcloud.deck.remote.helpers.providers.AbstractSyncDataPro import it.niedermann.nextcloud.deck.remote.helpers.providers.CardDataProvider; import it.niedermann.nextcloud.deck.remote.helpers.providers.StackDataProvider; import it.niedermann.nextcloud.deck.remote.helpers.util.ConnectivityUtil; +import okhttp3.Headers; @RunWith(RobolectricTestRunner.class) public class SyncRepositoryTest { @@ -211,8 +210,8 @@ public class SyncRepositoryTest { final var callback = mock(IResponseCallback.class); when(dataBaseAdapter.createAccountDirectly(any(Account.class))).thenReturn(account); doAnswer(invocation -> { - ((ResponseCallback<ParsedResponse<List<FullBoard>>>) invocation.getArgument(0)) - .onResponse(ParsedResponse.of(Collections.emptyList())); + ((ResponseCallback<List<FullBoard>>) invocation.getArgument(0)) + .onResponse(Collections.emptyList()); return null; }).when(serverAdapter).getBoards(any()); @@ -228,7 +227,7 @@ public class SyncRepositoryTest { final var callback = mock(IResponseCallback.class); when(dataBaseAdapter.createAccountDirectly(any(Account.class))).thenReturn(account); doAnswer(invocation -> { - ((ResponseCallback<ParsedResponse<Capabilities>>) invocation.getArgument(0)) + ((ResponseCallback<Capabilities>) invocation.getArgument(0)) .onError(new NextcloudHttpRequestFailedException(ApplicationProvider.getApplicationContext(), 404, new RuntimeException())); return null; }).when(serverAdapter).getBoards(any()); @@ -269,11 +268,9 @@ public class SyncRepositoryTest { public void testRefreshCapabilities() throws ExecutionException, InterruptedException { final var account = new Account(1337L, "Test", "Peter", "example.com"); account.setEtag("This-Is-The-Old_ETag"); - final var mockedResponse = mock(ParsedResponse.class); final var serverResponse = new Capabilities(); serverResponse.setDeckVersion(Version.of("1.0.0")); - when(mockedResponse.getResponse()).thenReturn(serverResponse); - when(mockedResponse.getHeaders()).thenReturn(Map.of("ETag", "New-ETag")); + final var headers = Headers.of("ETag", "New-ETag"); when(dataBaseAdapter.getAccountByIdDirectly(anyLong())).thenReturn(account); // Happy path @@ -282,8 +279,8 @@ public class SyncRepositoryTest { assertEquals("The old eTag must be passed to the " + ServerAdapter.class.getSimpleName(), "This-Is-The-Old_ETag", invocation.getArgument(0)); //noinspection unchecked - ((ResponseCallback<ParsedResponse<Capabilities>>) invocation.getArgument(1)) - .onResponse(mockedResponse); + ((ResponseCallback<Capabilities>) invocation.getArgument(1)) + .onResponseWithHeaders(serverResponse, headers); return null; }).when(serverAdapter).getCapabilities(anyString(), any()); @@ -307,7 +304,7 @@ public class SyncRepositoryTest { account.setMaintenanceEnabled(true); doAnswer(invocation -> { //noinspection unchecked - ((ResponseCallback<ParsedResponse<Capabilities>>) invocation.getArgument(1)) + ((ResponseCallback<Capabilities>) invocation.getArgument(1)) .onError(new NextcloudHttpRequestFailedException(ApplicationProvider.getApplicationContext(), 304, new RuntimeException())); return null; }).when(serverAdapter).getCapabilities(anyString(), any()); @@ -333,7 +330,7 @@ public class SyncRepositoryTest { doAnswer(invocation -> { //noinspection unchecked - ((ResponseCallback<ParsedResponse<Capabilities>>) invocation.getArgument(1)) + ((ResponseCallback<Capabilities>) invocation.getArgument(1)) .onError(new NextcloudHttpRequestFailedException(ApplicationProvider.getApplicationContext(), 500, new RuntimeException())); return null; }).when(serverAdapter).getCapabilities(anyString(), any()); @@ -356,7 +353,7 @@ public class SyncRepositoryTest { doAnswer(invocation -> { //noinspection unchecked - ((ResponseCallback<ParsedResponse<Capabilities>>) invocation.getArgument(1)) + ((ResponseCallback<Capabilities>) invocation.getArgument(1)) .onError(new NextcloudHttpRequestFailedException(ApplicationProvider.getApplicationContext(), 503, new RuntimeException("{\"ocs\": {\"meta\": {\"statuscode\": 503}, \"data\": {\"version\": {\"major\": 20, \"minor\": 0, \"patch\": 1}}}}"))); return null; }).when(serverAdapter).getCapabilities(anyString(), any()); @@ -378,7 +375,7 @@ public class SyncRepositoryTest { doAnswer(invocation -> { //noinspection unchecked - ((ResponseCallback<ParsedResponse<Capabilities>>) invocation.getArgument(1)) + ((ResponseCallback<Capabilities>) invocation.getArgument(1)) .onError(new NetworkErrorException()); return null; }).when(serverAdapter).getCapabilities(anyString(), any()); @@ -424,7 +421,7 @@ public class SyncRepositoryTest { // Act as if refreshing capabilities is always successful doAnswer((invocation -> { //noinspection unchecked - ((IResponseCallback<Capabilities>) invocation.getArgument(0)).onResponse(capabilities); + ((IResponseCallback<Capabilities>) invocation.getArgument(0)).onResponseWithHeaders(capabilities, Headers.of()); return null; })).when(syncManagerSpy).refreshCapabilities(any()); @@ -475,9 +472,9 @@ public class SyncRepositoryTest { } @Override - public <T extends IRemoteEntity> void doSyncFor(@NonNull AbstractSyncDataProvider<T> provider) { + public <T extends IRemoteEntity> void doSyncFor(@NonNull AbstractSyncDataProvider<T> provider, boolean parallel) { if (success) { - cb.onResponse(true); + cb.onResponseWithHeaders(true, Headers.of()); } else { cb.onError(new RuntimeException("Bad path mocking")); } |