diff options
author | Stefan Niedermann <info@niedermann.it> | 2021-03-11 22:24:30 +0300 |
---|---|---|
committer | Niedermann IT-Dienstleistungen <stefan-niedermann@users.noreply.github.com> | 2021-03-12 00:51:04 +0300 |
commit | 4e6d0cf9f8d1ea1b1f48cf4aa9582c73bec26c68 (patch) | |
tree | 52b0154ffb04f6eb2a6dc245131a05a1eaea7f81 | |
parent | 2afb1ba66a2001af5c33b12570750172e3139a70 (diff) |
Support Deck API 1.1 (server version 1.3+)
Signed-off-by: Stefan Niedermann <info@niedermann.it>
12 files changed, 170 insertions, 79 deletions
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/api/ApiProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/api/ApiProvider.java index e2cfda22e..a6e8da168 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/api/ApiProvider.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/api/ApiProvider.java @@ -18,19 +18,18 @@ import retrofit2.NextcloudRetrofitApiBuilder; /** * Created by david on 26.05.17. */ - public class ApiProvider { - private static final String DECK_API_ENDPOINT = "/index.php/apps/deck/api/v1.0/"; + private static final String DECK_API_ENDPOINT = "/index.php/apps/deck/api/"; private static final String NC_API_ENDPOINT = "/ocs/v2.php/"; private DeckAPI deckAPI; private NextcloudServerAPI nextcloudAPI; @NonNull - private Context context; - private SingleSignOnAccount ssoAccount; + private final Context context; @Nullable - private String ssoAccountName; + private final String ssoAccountName; + private SingleSignOnAccount ssoAccount; public ApiProvider(Context context) { this(context, null); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/api/DeckAPI.java b/app/src/main/java/it/niedermann/nextcloud/deck/api/DeckAPI.java index 32e340f0f..0ccfec812 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/api/DeckAPI.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/api/DeckAPI.java @@ -12,6 +12,7 @@ import it.niedermann.nextcloud.deck.model.Board; import it.niedermann.nextcloud.deck.model.Card; import it.niedermann.nextcloud.deck.model.Label; import it.niedermann.nextcloud.deck.model.Stack; +import it.niedermann.nextcloud.deck.model.enums.EAttachmentType; import it.niedermann.nextcloud.deck.model.full.FullBoard; import it.niedermann.nextcloud.deck.model.full.FullCard; import it.niedermann.nextcloud.deck.model.full.FullStack; @@ -32,129 +33,146 @@ import retrofit2.http.Part; import retrofit2.http.Path; import retrofit2.http.Query; +/** + * @link <a href="https://deck.readthedocs.io/en/latest/API/">Deck REST API</a> + */ public interface DeckAPI { String MODIFIED_SINCE_HEADER = "If-Modified-Since"; String IF_NONE_MATCH = "If-None-Match"; - // ### BOARDS - @POST("boards") + + // Boards + + @POST("v1.0/boards") Observable<FullBoard> createBoard(@Body Board board); - @GET("boards/{id}") + @GET("v1.0/boards/{id}") Observable<FullBoard> getBoard(@Path("id") long id, @Header(MODIFIED_SINCE_HEADER) String lastSync); - @PUT("boards/{id}") + @PUT("v1.0/boards/{id}") Observable<FullBoard> updateBoard(@Path("id") long id, @Body Board board); - @DELETE("boards/{id}") + @DELETE("v1.0/boards/{id}") Observable<Void> deleteBoard(@Path("id") long id); - @DELETE("boards/{id}/undo_delete") + @DELETE("v1.0/boards/{id}/undo_delete") Observable<FullBoard> restoreBoard(@Path("id") long id); - @GET("boards") - Observable<ParsedResponse<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, @Header(IF_NONE_MATCH) String eTag); + + // Stacks - // ### Stacks - @POST("boards/{boardId}/stacks") - Observable<FullStack> createStack(@Path("boardId") long boardId, @Body Stack stack); + @POST("v1.0/boards/{boardId}/stacks") + Observable<FullStack> createStack(@Path("boardId") long boardId, @Body Stack stack); - @PUT("boards/{boardId}/stacks/{stackId}") + @PUT("v1.0/boards/{boardId}/stacks/{stackId}") Observable<FullStack> updateStack(@Path("boardId") long boardId, @Path("stackId") long id, @Body Stack stack); - @DELETE("boards/{boardId}/stacks/{stackId}") + @DELETE("v1.0/boards/{boardId}/stacks/{stackId}") Observable<Void> deleteStack(@Path("boardId") long boardId, @Path("stackId") long id); - @GET("boards/{boardId}/stacks/{stackId}") + @GET("v1.0/boards/{boardId}/stacks/{stackId}") Observable<FullStack> getStack(@Path("boardId") long boardId, @Path("stackId") long id, @Header(MODIFIED_SINCE_HEADER) String lastSync); - @GET("boards/{boardId}/stacks") + @GET("v1.0/boards/{boardId}/stacks") Observable<List<FullStack>> getStacks(@Path("boardId") long boardId, @Header(MODIFIED_SINCE_HEADER) String lastSync); - @GET("boards/{boardId}/stacks/archived") + @GET("v1.0/boards/{boardId}/stacks/archived") Observable<List<Stack>> getArchivedStacks(@Path("boardId") long boardId, @Header(MODIFIED_SINCE_HEADER) String lastSync); - // ### Cards - @POST("boards/{boardId}/stacks/{stackId}/cards") + // Cards + + @POST("v1.0/boards/{boardId}/stacks/{stackId}/cards") Observable<FullCard> createCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Body Card card); - @PUT("boards/{boardId}/stacks/{stackId}/cards/{cardId}") + @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); @FormUrlEncoded - @PUT("boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignLabel") + @PUT("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignLabel") Observable<Void> assignLabelToCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("labelId") long labelId); @FormUrlEncoded - @PUT("boards/{boardId}/stacks/{stackId}/cards/{cardId}/removeLabel") + @PUT("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/removeLabel") Observable<Void> unassignLabelFromCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("labelId") long labelId); @FormUrlEncoded - @PUT("boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignUser") + @PUT("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignUser") Observable<Void> assignUserToCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("userId") String userUID); @FormUrlEncoded - @PUT("boards/{boardId}/stacks/{stackId}/cards/{cardId}/unassignUser") - Observable<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}/unassignUser") + Observable<Void> unassignUserFromCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Field("userId") String userUID); - @PUT("boards/{boardId}/stacks/{stackId}/cards/{cardId}/reorder") + @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); - @DELETE("boards/{boardId}/stacks/{stackId}/cards/{cardId}") + @DELETE("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}") Observable<Void> deleteCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId); - @GET("boards/{boardId}/stacks/{stackId}/cards/{cardId}") - Observable<FullCard> getCard(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Header(MODIFIED_SINCE_HEADER) String lastSync); + /** + * @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> + */ + @Deprecated + @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); + + @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); - // ### LABELS - @GET("boards/{boardId}/labels/{labelId}") + // 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); - @PUT("boards/{boardId}/labels/{labelId}") + @PUT("v1.0/boards/{boardId}/labels/{labelId}") Observable<Label> updateLabel(@Path("boardId") long boardId, @Path("labelId") long labelId, @Body Label label); - @POST("boards/{boardId}/labels") + @POST("v1.0/boards/{boardId}/labels") Observable<Label> createLabel(@Path("boardId") long boardId, @Body Label label); - @DELETE("boards/{boardId}/labels/{labelId}") + @DELETE("v1.0/boards/{boardId}/labels/{labelId}") Observable<Void> deleteLabel(@Path("boardId") long boardId, @Path("labelId") long labelId); - // ### ATTACHMENTS - @GET("boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}") + // 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); - @GET("boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments") + @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); @Multipart - @POST("boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments") + @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); @Multipart - @PUT("boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments") + @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); - @DELETE("boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}") + @DELETE("v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}") Observable<Void> deleteAttachment(@Path("boardId") long boardId, @Path("stackId") long stackId, @Path("cardId") long cardId, @Path("attachmentId") long attachmentId); - @PUT("boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}/restore") + @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); - // ### ACL - @POST("boards/{boardId}/acl") + + // Access control lists + + @POST("v1.0/boards/{boardId}/acl") Observable<AccessControl> createAccessControl(@Path("boardId") long boardId, @Body AccessControl acl); - @PUT("boards/{boardId}/acl/{aclId}") + @PUT("v1.0/boards/{boardId}/acl/{aclId}") Observable<AccessControl> updateAccessControl(@Path("boardId") long boardId, @Path("aclId") long aclId, @Body AccessControl acl); - @DELETE("boards/{boardId}/acl/{aclId}") + @DELETE("v1.0/boards/{boardId}/acl/{aclId}") Observable<Void> deleteAccessControl(@Path("boardId") long boardId, @Path("aclId") long aclId, @Body AccessControl acl); - // ### COMMENTS - } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/api/NextcloudServerAPI.java b/app/src/main/java/it/niedermann/nextcloud/deck/api/NextcloudServerAPI.java index 7b93ace49..54e573d73 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/api/NextcloudServerAPI.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/api/NextcloudServerAPI.java @@ -24,14 +24,27 @@ import retrofit2.http.PUT; import retrofit2.http.Path; import retrofit2.http.Query; + +/** + * @link <a href="https://deck.readthedocs.io/en/latest/API-Nextcloud/">Nextcloud REST API</a> + */ public interface NextcloudServerAPI { + + // Capabilities + @GET("cloud/capabilities?format=json") Observable<ParsedResponse<Capabilities>> getCapabilities(@Header("If-None-Match") String eTag); + + // Projects + @GET("collaboration/resources/deck-card/{cardId}?format=json") Observable<OcsProjectList> getProjectsForCard(@Path("cardId") long cardId); + + // Users + @GET("apps/files_sharing/api/v1/sharees?format=json&perPage=20&itemType=0%2C1%2C7") Observable<OcsUserList> searchUser(@Query("search") String searchTerm); @@ -41,9 +54,15 @@ public interface NextcloudServerAPI { @GET("cloud/users/{search}?format=json") Observable<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); + + // Comments + @Headers({ "Accept: application/json", "Content-Type: application/json;charset=utf-8" diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java index f96ff1da0..a1051b98c 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java @@ -222,7 +222,13 @@ public class ServerAdapter { public void getCard(long boardId, long stackId, long cardId, IResponseCallback<FullCard> responseCallback) { ensureInternetConnection(); - RequestHelper.request(provider, () -> provider.getDeckAPI().getCard(boardId, stackId, cardId, getLastSyncDateFormatted(responseCallback.getAccount().getId())), responseCallback); + RequestHelper.request(provider, () -> { + final Account account = responseCallback.getAccount(); + if(account != null && account.getServerDeckVersionAsObject().supportsFileAttachments()) { + return provider.getDeckAPI().getCard_1_1(boardId, stackId, cardId, getLastSyncDateFormatted(responseCallback.getAccount().getId())); + } + return provider.getDeckAPI().getCard_1_0(boardId, stackId, cardId, getLastSyncDateFormatted(responseCallback.getAccount().getId())); + }, responseCallback); } public void createCard(long boardId, long stackId, Card card, IResponseCallback<FullCard> responseCallback) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java index ee2aff1a4..6871c8fa6 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java @@ -125,7 +125,7 @@ import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.widgets.fil FilterWidgetSort.class, }, exportSchema = false, - version = 28 + version = 29 ) @TypeConverters({DateTypeConverter.class, EnumConverter.class}) public abstract class DeckDatabase extends RoomDatabase { @@ -421,10 +421,13 @@ public abstract class DeckDatabase extends RoomDatabase { database.execSQL("ALTER TABLE `OcsProjectResource` ADD `etag` TEXT"); } }; + + /** + * Reset ETags (comments weren't loading due to bug) + */ private static final Migration MIGRATION_24_25 = new Migration(24, 25) { @Override public void migrate(SupportSQLiteDatabase database) { - // Reset etags (comments weren't loading due to bug) database.execSQL("UPDATE `Account` SET `boardsEtag` = NULL"); database.execSQL("UPDATE `Board` SET `etag` = NULL"); database.execSQL("UPDATE `Stack` SET `etag` = NULL"); @@ -506,6 +509,7 @@ public abstract class DeckDatabase extends RoomDatabase { database.execSQL("DROP TABLE `StackWidgetModel`"); } }; + private static final Migration MIGRATION_27_28 = new Migration(27, 28) { @Override public void migrate(SupportSQLiteDatabase database) { @@ -513,6 +517,20 @@ public abstract class DeckDatabase extends RoomDatabase { } }; + /** + * Reset ETags for cards because <a href="https://github.com/nextcloud/deck/issues/2874">the attachments for this card might not be complete</a>. + */ + private static final Migration MIGRATION_28_29 = new Migration(28, 29) { + @Override + public void migrate(SupportSQLiteDatabase database) { + database.execSQL("UPDATE `Account` SET `boardsEtag` = NULL"); + database.execSQL("UPDATE `Board` SET `etag` = NULL"); + database.execSQL("UPDATE `Stack` SET `etag` = NULL"); + database.execSQL("UPDATE `Card` SET `etag` = NULL"); + } + }; + + public static final RoomDatabase.Callback ON_CREATE_CALLBACK = new RoomDatabase.Callback() { @Override public void onCreate(@NonNull SupportSQLiteDatabase db) { @@ -593,6 +611,7 @@ public abstract class DeckDatabase extends RoomDatabase { .addMigrations(MIGRATION_25_26) .addMigrations(MIGRATION_26_27) .addMigrations(MIGRATION_27_28) + .addMigrations(MIGRATION_28_29) .fallbackToDestructiveMigration() .addCallback(ON_CREATE_CALLBACK) .build(); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/attachments/AttachmentViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/attachments/AttachmentViewHolder.java index 26cd21a3c..336e085d7 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/attachments/AttachmentViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/attachments/AttachmentViewHolder.java @@ -39,7 +39,7 @@ public class AttachmentViewHolder extends RecyclerView.ViewHolder { binding.preview.setTransitionName(parentContext.getString(R.string.transition_attachment_preview, String.valueOf(attachment.getLocalId()))); binding.preview.setImageResource(R.drawable.ic_image_grey600_24dp); binding.preview.post(() -> { - final String uri = AttachmentUtil.getThumbnailUrl(account.getServerDeckVersionAsObject(), account.getUrl(), cardRemoteId, attachment, binding.preview.getWidth()); + final String uri = AttachmentUtil.getThumbnailUrl(account, cardRemoteId, attachment, binding.preview.getWidth(), binding.preview.getHeight()); Glide.with(parentContext) .load(uri) .listener(new RequestListener<Drawable>() { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentViewHolder.java index 533bbe322..655ee3986 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentViewHolder.java @@ -26,7 +26,10 @@ public abstract class AttachmentViewHolder extends RecyclerView.ViewHolder { } public void bind(@NonNull Account account, @NonNull MenuInflater menuInflater, @NonNull FragmentManager fragmentManager, Long cardRemoteId, Attachment attachment, @Nullable View.OnClickListener onClickListener, @ColorInt int mainColor) { - bind(menuInflater, fragmentManager, cardRemoteId, attachment, onClickListener, mainColor, AttachmentUtil.getRemoteOrLocalUrl(account.getUrl(), cardRemoteId, attachment)); + final String downloadUrl = (attachment.getId() == null || cardRemoteId == null) + ? attachment.getLocalPath() + : AttachmentUtil.getCopyDownloadUrl(account, cardRemoteId, attachment); + bind(menuInflater, fragmentManager, cardRemoteId, attachment, onClickListener, mainColor, downloadUrl); } @CallSuper diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java index 74ea070a9..16af8bb8f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java @@ -114,7 +114,7 @@ public class CardAttachmentAdapter extends RecyclerView.Adapter<AttachmentViewHo } case VIEW_TYPE_DEFAULT: default: { - onClickListener = (event) -> openAttachmentInBrowser(context, account.getUrl(), cardRemoteId, attachment.getId()); + onClickListener = (event) -> openAttachmentInBrowser(account, context, cardRemoteId, attachment); break; } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java index 2b5358eb9..b41a4e9d1 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java @@ -40,7 +40,7 @@ public class DefaultAttachmentViewHolder extends AttachmentViewHolder { public void bind(@NonNull Account account, @NonNull MenuInflater menuInflater, @NonNull FragmentManager fragmentManager, Long cardRemoteId, Attachment attachment, @Nullable View.OnClickListener onClickListener, @ColorInt int mainColor) { super.bind(account, menuInflater, fragmentManager, cardRemoteId, attachment, onClickListener, mainColor); getPreview().setImageResource(getIconForMimeType(attachment.getMimetype())); - itemView.setOnClickListener((event) -> openAttachmentInBrowser(itemView.getContext(), account.getUrl(), cardRemoteId, attachment.getId())); + itemView.setOnClickListener((event) -> openAttachmentInBrowser(account, itemView.getContext(), cardRemoteId, attachment)); binding.filename.setText(attachment.getBasename()); binding.filesize.setText(Formatter.formatFileSize(binding.filesize.getContext(), attachment.getFilesize())); if (attachment.getLastModifiedLocal() != null) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/ImageAttachmentViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/ImageAttachmentViewHolder.java index 3c95da1b7..b93b3d025 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/ImageAttachmentViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/ImageAttachmentViewHolder.java @@ -37,10 +37,13 @@ public class ImageAttachmentViewHolder extends AttachmentViewHolder { } public void bind(@NonNull Account account, @NonNull MenuInflater menuInflater, @NonNull FragmentManager fragmentManager, Long cardRemoteId, Attachment attachment, @Nullable View.OnClickListener onClickListener, @ColorInt int mainColor) { - super.bind(menuInflater, fragmentManager, cardRemoteId, attachment, onClickListener, mainColor, AttachmentUtil.getRemoteOrLocalUrl(account.getUrl(), cardRemoteId, attachment)); + final String downloadUrl = (attachment.getId() == null || cardRemoteId == null) + ? attachment.getLocalPath() + : AttachmentUtil.getCopyDownloadUrl(account, cardRemoteId, attachment); + super.bind(menuInflater, fragmentManager, cardRemoteId, attachment, onClickListener, mainColor, downloadUrl); getPreview().post(() -> { - @Nullable final String uri = AttachmentUtil.getThumbnailUrl(account.getServerDeckVersionAsObject(), account.getUrl(), cardRemoteId, attachment, getPreview().getWidth()); + @Nullable final String uri = AttachmentUtil.getThumbnailUrl(account, cardRemoteId, attachment, getPreview().getWidth()); Glide.with(getPreview().getContext()) .load(uri) .placeholder(R.drawable.ic_image_grey600_24dp) diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/util/AttachmentUtil.java b/app/src/main/java/it/niedermann/nextcloud/deck/util/AttachmentUtil.java index aefd14b30..72151832e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/util/AttachmentUtil.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/util/AttachmentUtil.java @@ -21,6 +21,7 @@ import java.io.InputStream; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; +import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Attachment; import it.niedermann.nextcloud.deck.model.enums.EAttachmentType; import it.niedermann.nextcloud.deck.model.ocs.Version; @@ -39,43 +40,62 @@ public class AttachmentUtil { * If a thumbnail is not available (see {@link Version#supportsFileAttachments()}), a link to * the {@link Attachment} itself will be returned instead. */ - public static String getThumbnailUrl(@NonNull Version version, @NonNull String accountUrl, @NonNull Long cardRemoteId, @NonNull Attachment attachment, @Px int previewSize) { - return version.supportsFileAttachments() && + public static String getThumbnailUrl(@NonNull Account account, @NonNull Long cardRemoteId, @NonNull Attachment attachment, @Px int previewSize) { + return getThumbnailUrl(account, cardRemoteId, attachment, previewSize, previewSize); + } + + public static String getThumbnailUrl(@NonNull Account account, @NonNull Long cardRemoteId, @NonNull Attachment attachment, @Px int previewWidth, @Px int previewHeight) { + return account.getServerDeckVersionAsObject().supportsFileAttachments() && EAttachmentType.FILE.equals(attachment.getType()) && attachment.getFileId() != null - ? accountUrl + "/index.php/core/preview?fileId=" + attachment.getFileId() + "&x=" + previewSize + "&y=" + previewSize - : getRemoteOrLocalUrl(accountUrl, cardRemoteId, attachment); + ? account.getUrl() + "/index.php/core/preview?fileId=" + attachment.getFileId() + "&x=" + previewWidth + "&y=" + previewHeight + "&a=true" + : getRemoteOrLocalUrl(account.getUrl(), cardRemoteId, attachment); } /** - * @return {@link AttachmentUtil#getRemoteUrl} or {@link Attachment#getLocalPath()} as fallback + * @return {@link AttachmentUtil#getDeck_1_0_RemoteUrl} or {@link Attachment#getLocalPath()} as fallback * in case this {@param attachment} has not yet been synced. */ @Nullable - public static String getRemoteOrLocalUrl(@NonNull String accountUrl, @Nullable Long cardRemoteId, @NonNull Attachment attachment) { + private static String getRemoteOrLocalUrl(@NonNull String accountUrl, @Nullable Long cardRemoteId, @NonNull Attachment attachment) { return (attachment.getId() == null || cardRemoteId == null) ? attachment.getLocalPath() - : getRemoteUrl(accountUrl, cardRemoteId, attachment.getId()); + : getDeck_1_0_RemoteUrl(accountUrl, cardRemoteId, attachment.getId()); } /** * Tries to open the given {@link Attachment} in web browser. Displays a toast on failure. */ - public static void openAttachmentInBrowser(@NonNull Context context, @NonNull String accountUrl, Long cardRemoteId, Long attachmentRemoteId) { + public static void openAttachmentInBrowser(@NonNull Account account, @NonNull Context context, Long cardRemoteId, Attachment attachment) { if (cardRemoteId == null) { Toast.makeText(context, R.string.card_does_not_yet_exist, Toast.LENGTH_LONG).show(); DeckLog.logError(new IllegalArgumentException("cardRemoteId must not be null.")); return; } - if (attachmentRemoteId == null) { + + try { + context.startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse(getCopyDownloadUrl(account, cardRemoteId, attachment)))); + } catch (IllegalArgumentException e) { Toast.makeText(context, R.string.attachment_does_not_yet_exist, Toast.LENGTH_LONG).show(); DeckLog.logError(new IllegalArgumentException("attachmentRemoteId must not be null.")); - return; } - context.startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse(AttachmentUtil.getRemoteUrl(accountUrl, cardRemoteId, attachmentRemoteId)))); } - private static String getRemoteUrl(@NonNull String accountUrl, @NonNull Long cardRemoteId, @NonNull Long attachmentRemoteId) { + public static String getCopyDownloadUrl(@NonNull Account account, @NonNull Long cardRemoteId, @NonNull Attachment attachment) { + if (attachment.getId() == null) { + throw new IllegalArgumentException("attachment id must not be null"); + } + + return (attachment.getFileId() != null) + ? account.getUrl() + "/f/" + attachment.getFileId() + : getDeck_1_0_RemoteUrl(account.getUrl(), cardRemoteId, attachment.getId()); + } + + /** + * Attention! This does only work for attachments of type "deck_file" which are a legacy of Deck API 1.0 + */ + @Deprecated + private static String getDeck_1_0_RemoteUrl(@NonNull String accountUrl, @NonNull Long cardRemoteId, @NonNull Long attachmentRemoteId) { return accountUrl + "/index.php/apps/deck/cards/" + cardRemoteId + "/attachment/" + attachmentRemoteId; } diff --git a/app/src/test/java/it/niedermann/nextcloud/deck/util/AttachmentUtilTest.java b/app/src/test/java/it/niedermann/nextcloud/deck/util/AttachmentUtilTest.java index 653657540..d2d79adb2 100644 --- a/app/src/test/java/it/niedermann/nextcloud/deck/util/AttachmentUtilTest.java +++ b/app/src/test/java/it/niedermann/nextcloud/deck/util/AttachmentUtilTest.java @@ -7,9 +7,9 @@ import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Attachment; import it.niedermann.nextcloud.deck.model.enums.EAttachmentType; -import it.niedermann.nextcloud.deck.model.ocs.Version; import static org.junit.Assert.assertEquals; @@ -19,27 +19,31 @@ public class AttachmentUtilTest { @Test public void testGetThumbnailUrl() { - final Version versionThatDoesSupportFileAttachments = new Version("1.3.0", 1, 3, 0); - final Version versionThatDoesNotSupportFileAttachments = new Version("1.2.0", 1, 2, 0); final String accountUrl = "https://example.com"; + final Account accountThatDoesSupportFileAttachments = new Account(); + final Account accountThatDoesNotSupportFileAttachments = new Account(); + accountThatDoesSupportFileAttachments.setUrl(accountUrl); + accountThatDoesSupportFileAttachments.setServerDeckVersion("1.3.0"); + accountThatDoesNotSupportFileAttachments.setUrl(accountUrl); + accountThatDoesNotSupportFileAttachments.setServerDeckVersion("1.2.0"); final Attachment attachment1 = new Attachment(); attachment1.setFileId(1337L); attachment1.setType(EAttachmentType.FILE); - final String thumbnailUrl1 = AttachmentUtil.getThumbnailUrl(versionThatDoesSupportFileAttachments, accountUrl, -1L, attachment1, 500); + final String thumbnailUrl1 = AttachmentUtil.getThumbnailUrl(accountThatDoesSupportFileAttachments, -1L, attachment1, 500); assertEquals("https://example.com/index.php/core/preview?fileId=1337&x=500&y=500", thumbnailUrl1); final Attachment attachment2 = new Attachment(); attachment2.setFileId(815L); attachment2.setType(EAttachmentType.FILE); - final String thumbnailUrl2 = AttachmentUtil.getThumbnailUrl(versionThatDoesSupportFileAttachments, accountUrl, 0L, attachment2, 4711); + final String thumbnailUrl2 = AttachmentUtil.getThumbnailUrl(accountThatDoesSupportFileAttachments, 0L, attachment2, 4711); assertEquals("https://example.com/index.php/core/preview?fileId=815&x=4711&y=4711", thumbnailUrl2); // Given there is an invalid fileId... final Attachment attachment3 = new Attachment(); attachment3.setId(999L); attachment3.setFileId(null); - final String thumbnailUrl3 = AttachmentUtil.getThumbnailUrl(versionThatDoesSupportFileAttachments, accountUrl, 15L, attachment3, 205); + final String thumbnailUrl3 = AttachmentUtil.getThumbnailUrl(accountThatDoesSupportFileAttachments, 15L, attachment3, 205); // ... a fallback to the attachment itself should be returned assertEquals("https://example.com/index.php/apps/deck/cards/15/attachment/999", thumbnailUrl3); @@ -47,7 +51,7 @@ public class AttachmentUtilTest { final Attachment attachment4 = new Attachment(); attachment4.setId(111L); attachment4.setFileId(222L); - final String thumbnailUrl4 = AttachmentUtil.getThumbnailUrl(versionThatDoesNotSupportFileAttachments, accountUrl, 333L, attachment4, 444); + final String thumbnailUrl4 = AttachmentUtil.getThumbnailUrl(accountThatDoesNotSupportFileAttachments,333L, attachment4, 444); // ... a fallback to the attachment itself should be returned assertEquals("https://example.com/index.php/apps/deck/cards/333/attachment/111", thumbnailUrl4); } |