From 14462c7e8beaf151de9a15bb03df9c670aea3569 Mon Sep 17 00:00:00 2001 From: zhwanng <48609908+zhwanng@users.noreply.github.com> Date: Wed, 25 Dec 2024 18:34:03 +0800 Subject: [PATCH] Improve ImagePreview page - fix other issues --- app/src/main/AndroidManifest.xml | 1 - .../com/seafile/seadroid2/SeafException.java | 1 + .../seafile/seadroid2/account/Account.java | 2 +- .../seadroid2/account/AccountUtils.java | 9 +- .../seadroid2/config/GlideLoadConfig.java | 24 ++ .../seafile/seadroid2/config/OriGlideUrl.java | 38 +++ .../seafile/seadroid2/context/NavContext.java | 66 ++++ .../data/db/dao/FileTransferDAO.java | 7 +- .../data/db/entities/FileTransferEntity.java | 50 +-- .../framework/data/model/ContextModel.java | 13 + .../model/sdoc/FileProfileConfigModel.java | 18 +- .../data/model/sdoc/MetadataConfigModel.java | 4 + .../data/model/sdoc/SDocPageOptionsModel.java | 182 +--------- .../framework/datastore/DataStoreKeys.java | 2 + .../file_monitor/FileSyncService.java | 1 + .../seadroid2/framework/util/GlideCache.java | 3 +- .../framework/util/ThumbnailUtils.java | 11 + .../worker/BackgroundJobManagerImpl.java | 3 + .../framework/worker/TransferEvent.java | 7 +- .../worker/download/BaseDownloadWorker.java | 2 +- .../worker/upload/BaseUploadWorker.java | 113 ++++--- .../upload/FolderBackupScannerWorker.java | 6 +- .../upload/MediaBackupScannerWorker.java | 3 +- .../upload/UploadFileManuallyWorker.java | 2 +- .../UploadFolderFileAutomaticallyWorker.java | 71 +++- .../UploadMediaFileAutomaticallyWorker.java | 64 +++- .../FileTransferProgressListener.java | 18 +- .../ContextStackPreferenceHelper.java | 35 ++ .../ui/camera_upload/CameraUploadManager.java | 3 + .../DocsCommentViewModel.java | 157 +-------- .../ui/docs_comment/DocsCommentsActivity.java | 10 - .../seadroid2/ui/main/MainActivity.java | 5 +- .../seadroid2/ui/main/MainViewModel.java | 1 + .../seadroid2/ui/media/PhotoViewModel.java | 259 +++++++++++++++ .../image_preview/ImagePreviewActivity.java | 314 ------------------ .../image_preview/ImagePreviewViewModel.java | 30 +- .../ui/media/image_preview/PhotoFragment.java | 244 ++++++++++---- .../media/image_preview2/CarouselAdapter.java | 57 ++-- .../CarouselImagePreviewActivity.java | 143 ++++++-- .../image_preview2/PagerSnapBinders.java | 48 --- .../seadroid2/ui/repo/RepoQuickAdapter.java | 57 ++-- .../seadroid2/ui/repo/RepoQuickFragment.java | 50 ++- .../seadroid2/ui/repo/RepoViewModel.java | 1 + .../seadroid2/ui/repo/ScrollState.java | 8 + .../seadroid2/ui/sdoc/DocsCommentService.java | 2 +- .../seadroid2/ui/sdoc/SDocViewModel.java | 201 +++++++++++ .../ui/sdoc/SDocWebViewActivity.java | 59 +++- .../sdoc/outline/SDocOutlineRemoteDialog.java | 7 +- .../ui/settings/SettingsFragment.java | 4 +- .../ui/settings/TabSettingsFragment.java | 39 ++- .../ui/transfer_list/TransferListAdapter.java | 8 +- .../transfer_list/TransferListFragment.java | 8 - .../ui/transfer_list/UploadListFragment.java | 3 +- .../snap_recyclerview}/GravitySnapHelper.java | 17 +- .../GravitySnapRecyclerView.java | 150 +++++++++ .../OrientationAwareRecyclerView.java | 81 +++++ .../view/webview/OnWebPageListener.java | 7 + .../seadroid2/view/webview/SeaWebView.java | 6 + .../view/webview/SeaWebViewClient.java | 15 + .../main/res/drawable/baseline_delete_24.xml | 19 +- .../res/drawable/baseline_download_24.xml | 15 + .../main/res/drawable/baseline_share_24.xml | 24 +- .../res/drawable/baseline_star_filled_24.xml | 9 - .../res/drawable/baseline_star_outline_24.xml | 9 - .../main/res/drawable/baseline_starred_32.xml | 9 - .../drawable/baseline_starred_filled_24.xml | 9 + .../drawable/baseline_starred_filled_32.xml | 9 + .../drawable/baseline_starred_outline_24.xml | 9 + .../drawable/shape_solid_grey100_radius_4.xml | 5 + .../activity_carousel_image_preview.xml | 13 +- .../res/layout/activity_image_preview.xml | 8 +- .../res/layout/gallery_activity_layout.xml | 131 -------- .../main/res/menu/bottom_navigation_menu.xml | 2 +- .../menu/bottom_sheet_op_transfer_list.xml | 9 +- .../main/res/menu/bottom_sheet_unstarred.xml | 2 +- app/src/main/res/values/attrs.xml | 15 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/values/styles.xml | 4 + 78 files changed, 1775 insertions(+), 1278 deletions(-) create mode 100644 app/src/main/java/com/seafile/seadroid2/config/OriGlideUrl.java create mode 100644 app/src/main/java/com/seafile/seadroid2/framework/data/model/ContextModel.java create mode 100644 app/src/main/java/com/seafile/seadroid2/framework/util/ThumbnailUtils.java create mode 100644 app/src/main/java/com/seafile/seadroid2/preferences/ContextStackPreferenceHelper.java rename app/src/main/java/com/seafile/seadroid2/ui/{sdoc => docs_comment}/DocsCommentViewModel.java (68%) create mode 100644 app/src/main/java/com/seafile/seadroid2/ui/media/PhotoViewModel.java delete mode 100644 app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewActivity.java delete mode 100644 app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/PagerSnapBinders.java create mode 100644 app/src/main/java/com/seafile/seadroid2/ui/sdoc/SDocViewModel.java rename app/src/main/java/com/seafile/seadroid2/{ui/media/image_preview2 => view/snap_recyclerview}/GravitySnapHelper.java (98%) create mode 100644 app/src/main/java/com/seafile/seadroid2/view/snap_recyclerview/GravitySnapRecyclerView.java create mode 100644 app/src/main/java/com/seafile/seadroid2/view/snap_recyclerview/OrientationAwareRecyclerView.java create mode 100644 app/src/main/java/com/seafile/seadroid2/view/webview/OnWebPageListener.java create mode 100644 app/src/main/res/drawable/baseline_download_24.xml delete mode 100644 app/src/main/res/drawable/baseline_star_filled_24.xml delete mode 100644 app/src/main/res/drawable/baseline_star_outline_24.xml delete mode 100644 app/src/main/res/drawable/baseline_starred_32.xml create mode 100644 app/src/main/res/drawable/baseline_starred_filled_24.xml create mode 100644 app/src/main/res/drawable/baseline_starred_filled_32.xml create mode 100644 app/src/main/res/drawable/baseline_starred_outline_24.xml create mode 100644 app/src/main/res/drawable/shape_solid_grey100_radius_4.xml delete mode 100644 app/src/main/res/layout/gallery_activity_layout.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 673f3f2be..7bb73c189 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -204,7 +204,6 @@ - diff --git a/app/src/main/java/com/seafile/seadroid2/SeafException.java b/app/src/main/java/com/seafile/seadroid2/SeafException.java index 75c7c2d77..15f12c0fe 100644 --- a/app/src/main/java/com/seafile/seadroid2/SeafException.java +++ b/app/src/main/java/com/seafile/seadroid2/SeafException.java @@ -34,6 +34,7 @@ public class SeafException extends Exception { public static final SeafException notFoundException = new SeafException(404, "Not found"); public static final SeafException notFoundUserException = new SeafException(20, "Not logged in"); public static final SeafException notLoggedInException = new SeafException(21, "Not logged in"); + public static final SeafException transferFileException = new SeafException(22, "The file transfer is abnormal"); public static final SeafException OUT_OF_QUOTA = new SeafException(HTTP_ABOVE_QUOTA, SeadroidApplication.getAppContext().getString(R.string.above_quota)); public static final SeafException REQUEST_EXCEPTION = new SeafException(400, "Request Failed"); diff --git a/app/src/main/java/com/seafile/seadroid2/account/Account.java b/app/src/main/java/com/seafile/seadroid2/account/Account.java index cc176c260..215fa791a 100644 --- a/app/src/main/java/com/seafile/seadroid2/account/Account.java +++ b/app/src/main/java/com/seafile/seadroid2/account/Account.java @@ -103,7 +103,7 @@ public String getSpaceUsed() { /** * in fact, the value should be less than 0. - * however, in some cases, it may be 0, and should also return unlimited. + * however, in some cases, it may be 0, and should also return non-limit. * even if the non-limit is returned, App does not need to verify "Out of quota" status. * and the "Out of quota" error will be returned in the file upload result. */ diff --git a/app/src/main/java/com/seafile/seadroid2/account/AccountUtils.java b/app/src/main/java/com/seafile/seadroid2/account/AccountUtils.java index 439fba31a..4df312d1a 100644 --- a/app/src/main/java/com/seafile/seadroid2/account/AccountUtils.java +++ b/app/src/main/java/com/seafile/seadroid2/account/AccountUtils.java @@ -9,6 +9,7 @@ import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; +import com.seafile.seadroid2.preferences.ContextStackPreferenceHelper; import com.seafile.seadroid2.preferences.Settings; import com.seafile.seadroid2.ssl.CertsManager; import com.seafile.seadroid2.ui.camera_upload.CameraUploadManager; @@ -17,11 +18,14 @@ public class AccountUtils { public static void logout(Account account) { - // turn off the gesture lock anyway + // turn off the gesture lock GestureLockSharePreferenceHelper.disable(); Settings.initUserSettings(); + // clear + ContextStackPreferenceHelper.clearStack(); + NotificationUtils.cancelAll(); // sign out operations @@ -57,6 +61,9 @@ public static void switchAccount(Account account) { NotificationUtils.cancelAll(); + // clear + ContextStackPreferenceHelper.clearStack(); + // Settings.initUserSettings(); diff --git a/app/src/main/java/com/seafile/seadroid2/config/GlideLoadConfig.java b/app/src/main/java/com/seafile/seadroid2/config/GlideLoadConfig.java index 785a181a8..c8cf72948 100644 --- a/app/src/main/java/com/seafile/seadroid2/config/GlideLoadConfig.java +++ b/app/src/main/java/com/seafile/seadroid2/config/GlideLoadConfig.java @@ -1,5 +1,8 @@ package com.seafile.seadroid2.config; +import androidx.annotation.DrawableRes; + +import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.load.model.LazyHeaders; import com.bumptech.glide.request.RequestOptions; @@ -38,12 +41,33 @@ public static RequestOptions getAvatarOptions() { .override(WidgetUtils.getThumbnailWidth(), WidgetUtils.getThumbnailWidth()); } + private final static RequestOptions _cacheableThumbnailOptions = new RequestOptions() + .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) + .error(R.drawable.icon_image_error_filled) + .override(128); + + /** + * Get cacheable thumbnail options, width and height are both 128 + */ + public static RequestOptions getCacheableThumbnailOptions() { + return _cacheableThumbnailOptions; + } + public static RequestOptions getOptions() { return new RequestOptions() + .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) .fallback(R.drawable.file_image) .placeholder(R.drawable.file_image); } + + public static RequestOptions getCustomDrawableOptions(@DrawableRes int resId) { + return new RequestOptions() + .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) + .fallback(resId) + .placeholder(resId); + } + public static RequestOptions getOptions(String key) { return new RequestOptions() .fallback(R.drawable.file_image) diff --git a/app/src/main/java/com/seafile/seadroid2/config/OriGlideUrl.java b/app/src/main/java/com/seafile/seadroid2/config/OriGlideUrl.java new file mode 100644 index 000000000..bd76802b2 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/config/OriGlideUrl.java @@ -0,0 +1,38 @@ +package com.seafile.seadroid2.config; + +import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.load.model.Headers; +import com.seafile.seadroid2.framework.util.SLogs; + +import java.net.URL; + +public class OriGlideUrl extends GlideUrl { + private String oriKey; + + public OriGlideUrl(URL url) { + super(url); + } + + public OriGlideUrl(String url) { + super(url); + } + + public OriGlideUrl(String url, String oriKey) { + super(url); + this.oriKey = oriKey; + } + + + public OriGlideUrl(URL url, Headers headers) { + super(url, headers); + } + + public OriGlideUrl(String url, Headers headers) { + super(url, headers); + } + + @Override + public String getCacheKey() { + return oriKey; + } +} diff --git a/app/src/main/java/com/seafile/seadroid2/context/NavContext.java b/app/src/main/java/com/seafile/seadroid2/context/NavContext.java index 590b8d29e..91770f282 100644 --- a/app/src/main/java/com/seafile/seadroid2/context/NavContext.java +++ b/app/src/main/java/com/seafile/seadroid2/context/NavContext.java @@ -5,7 +5,9 @@ import com.seafile.seadroid2.framework.data.model.BaseModel; import com.seafile.seadroid2.framework.data.db.entities.DirentModel; import com.seafile.seadroid2.framework.data.db.entities.RepoModel; +import com.seafile.seadroid2.framework.data.model.ContextModel; import com.seafile.seadroid2.framework.util.Utils; +import com.seafile.seadroid2.preferences.ContextStackPreferenceHelper; import java.util.Stack; @@ -32,6 +34,9 @@ public void clear() { } } + /** + * Push a model to the stack + */ public void push(BaseModel model) { if (model instanceof RepoModel) { //clear @@ -39,14 +44,70 @@ public void push(BaseModel model) { //push navStack.push(model); + + saveToSp(); + } else if (model instanceof DirentModel) { //stack navStack.push(model); + saveToSp(); + } else { throw new IllegalArgumentException("model must be RepoMode or DirentsModel."); } } + public void restoreNavContextFromSp() { + + navStack.clear(); + + Stack stack = ContextStackPreferenceHelper.getStack(); + if (stack != null && !stack.isEmpty()) { + for (ContextModel contextModel : stack) { + if (contextModel.type.equals("repo")) { + RepoModel repoModel = new RepoModel(); + repoModel.repo_id = contextModel.repo_id; + repoModel.repo_name = contextModel.repo_name; + navStack.push(repoModel); + } else if (contextModel.type.equals("dirent")) { + DirentModel direntModel = new DirentModel(); + direntModel.repo_id = contextModel.repo_id; + direntModel.repo_name = contextModel.repo_name; + direntModel.full_path = contextModel.full_path; + direntModel.parent_dir = Utils.getParentPath(direntModel.full_path); + direntModel.name = Utils.getFileNameFromPath(contextModel.full_path); + direntModel.uid = direntModel.getUID(); + navStack.push(direntModel); + } + } + } + + } + + private void saveToSp() { + Stack stack = new Stack<>(); + if (!navStack.isEmpty()) { + for (BaseModel baseModel : navStack) { + ContextModel contextModel = new ContextModel(); + + if (baseModel instanceof RepoModel e) { + contextModel.repo_id = e.repo_id; + contextModel.repo_name = e.repo_name; + contextModel.type = "repo"; + contextModel.full_path = "/"; + } else if (baseModel instanceof DirentModel e) { + contextModel.repo_id = e.repo_id; + contextModel.repo_name = e.repo_name; + contextModel.type = "dirent"; + contextModel.full_path = e.full_path; + } + stack.add(contextModel); + } + } + + ContextStackPreferenceHelper.saveStack(stack); + } + public void pop() { if (navStack.empty()) { return; @@ -54,6 +115,9 @@ public void pop() { //stack navStack.pop(); + + saveToSp(); + } public void switchToPath(RepoModel repoModel, String full_path) { @@ -87,6 +151,8 @@ public void switchToPath(RepoModel repoModel, String full_path) { for (DirentModel model : stack) { navStack.push(model); } + + saveToSp(); } /** diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/FileTransferDAO.java b/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/FileTransferDAO.java index 86585bf64..dd645b45c 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/FileTransferDAO.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/FileTransferDAO.java @@ -130,8 +130,11 @@ public interface FileTransferDAO { @Query("select * from file_transfer_list where repo_id = :repoId and transfer_action = :transfer_action order by created_at asc limit :limit offset :offset") List getPageListSync(String repoId, TransferAction transfer_action, int limit, int offset); - @Query("select * from file_transfer_list where related_account = :related_account and transfer_action = :transferAction and full_path = :full_path and data_status = 0 order by created_at") - List getListByFullPathSync(String related_account, TransferAction transferAction, String full_path); + @Query("select * from file_transfer_list where repo_id = :repoId and transfer_action = :transferAction and full_path = :full_path and data_status = 0 order by created_at") + List getListByFullPathSync(String repoId, TransferAction transferAction, String full_path); + + @Query("select * from file_transfer_list where repo_id = :repoId and transfer_action = :transferAction and full_path = :full_path and data_status = 0 order by created_at") + Single> getListByFullPathAsync(String repoId, TransferAction transferAction, String full_path); @Query("select COUNT(*) from file_transfer_list where repo_id = :repoId and full_path = :fullPath and transfer_action = :transfer_action and data_source = :feature and data_status = 0 ") int checkOneByFullPath(String repoId, String fullPath, TransferAction transfer_action, TransferDataSource feature); diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/FileTransferEntity.java b/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/FileTransferEntity.java index 0ca8f6f12..46d39a1c7 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/FileTransferEntity.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/FileTransferEntity.java @@ -361,44 +361,6 @@ public static FileTransferEntity convertDirentModel2This(boolean is_block, boole return entity; } - public static FileTransferEntity convertDirentFileModel2This(RepoModel repoModel, String full_path, boolean is_auto_transfer, DirentFileModel direntModel) { - FileTransferEntity entity = new FileTransferEntity(); - entity.full_path = full_path; -// entity.target_path = direntModel.full_path; - entity.data_source = TransferDataSource.DOWNLOAD; - entity.repo_id = repoModel.repo_id; - entity.repo_name = repoModel.repo_name; - entity.related_account = repoModel.related_account; - - - entity.file_id = direntModel.id; - entity.setParent_path(Utils.getParentPath(full_path)); - entity.file_name = direntModel.name; - entity.file_format = FileTools.getFileExtension(entity.full_path); - entity.mime_type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(entity.file_format); - entity.file_size = direntModel.size; - entity.file_md5 = null; - - entity.is_auto_transfer = is_auto_transfer; - -// entity.is_block = repoModel.canLocalDecrypt(); - entity.file_strategy = ExistingFileStrategy.AUTO; - - entity.is_copy_to_local = true; - - long now = System.currentTimeMillis(); - entity.created_at = now; - entity.modified_at = direntModel.mtime * 1000; - entity.action_end_at = 0L; - - entity.transfer_action = TransferAction.DOWNLOAD; - entity.transfer_status = TransferStatus.WAITING; - entity.transfer_result = TransferResult.NO_RESULT; - - entity.uid = entity.getUID(); - - return entity; - } public static FileTransferEntity convertDirentRecursiveModel2This(RepoModel repoModel, DirentRecursiveFileModel model) { @@ -444,7 +406,7 @@ public static FileTransferEntity convertDirentRecursiveModel2This(RepoModel repo return entity; } - public static FileTransferEntity convert2ThisForUploadFileSyncWorker(Account account, RepoModel repoModel, File file, String backupPath) { + public static FileTransferEntity convert2ThisForUploadFileSyncWorker(Account account, File file, String backupPath) { if (!file.isFile()) { return null; } @@ -466,8 +428,8 @@ public static FileTransferEntity convert2ThisForUploadFileSyncWorker(Account acc entity.mime_type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(entity.file_format); // entity.is_block = repoModel.encrypted; - entity.repo_id = repoModel.repo_id; - entity.repo_name = repoModel.repo_name; +// entity.repo_id = repoModel.repo_id; +// entity.repo_name = repoModel.repo_name; entity.related_account = account.getSignature(); entity.data_source = TransferDataSource.FOLDER_BACKUP; entity.created_at = System.currentTimeMillis(); @@ -489,7 +451,7 @@ public static FileTransferEntity convert2ThisForUploadFileSyncWorker(Account acc } - public static FileTransferEntity convert2ThisForUploadMediaSyncWorker(Account account, String repo_id, String repo_name, File file, String parenPath, long dateAdd, boolean isRemoteExists) { + public static FileTransferEntity convert2ThisForUploadMediaSyncWorker(Account account, File file, String parenPath, long dateAdd, boolean isRemoteExists) { long now = System.currentTimeMillis(); FileTransferEntity entity = new FileTransferEntity(); @@ -502,8 +464,8 @@ public static FileTransferEntity convert2ThisForUploadMediaSyncWorker(Account ac entity.file_md5 = FileUtils.getFileMD5ToString(entity.full_path).toLowerCase(); entity.mime_type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(entity.file_format); // entity.is_block = false; //album backup is not store in encrypted repo. - entity.repo_id = repo_id; - entity.repo_name = repo_name; +// entity.repo_id = repo_id; +// entity.repo_name = repo_name; entity.related_account = account.getSignature(); entity.created_at = now; entity.modified_at = now; diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/model/ContextModel.java b/app/src/main/java/com/seafile/seadroid2/framework/data/model/ContextModel.java new file mode 100644 index 000000000..cce811099 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/model/ContextModel.java @@ -0,0 +1,13 @@ +package com.seafile.seadroid2.framework.data.model; + +public class ContextModel { + public String repo_id; + public String repo_name; //repo_name + + public String type; //repo/dirent + + /** + * parent_dir + name + */ + public String full_path; +} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/FileProfileConfigModel.java b/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/FileProfileConfigModel.java index 0b63f210d..13a9549dd 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/FileProfileConfigModel.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/FileProfileConfigModel.java @@ -4,23 +4,23 @@ public class FileProfileConfigModel { public UserWrapperModel users; - public MetadataConfigModel metadata; public FileDetailModel detail; + public MetadataConfigModel metadataConfigModel; - public UserWrapperModel getUsers() { - return users; + public MetadataConfigModel getMetadataConfigModel() { + return metadataConfigModel; } - public void setUsers(UserWrapperModel users) { - this.users = users; + public void setMetadataConfigModel(MetadataConfigModel metadataConfigModel) { + this.metadataConfigModel = metadataConfigModel; } - public MetadataConfigModel getMetadata() { - return metadata; + public UserWrapperModel getUsers() { + return users; } - public void setMetadata(MetadataConfigModel metadata) { - this.metadata = metadata; + public void setUsers(UserWrapperModel users) { + this.users = users; } public FileDetailModel getDetail() { diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/MetadataConfigModel.java b/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/MetadataConfigModel.java index 78fbf7894..74fe5d623 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/MetadataConfigModel.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/MetadataConfigModel.java @@ -2,4 +2,8 @@ public class MetadataConfigModel { public boolean enabled; + //public boolean tags_enabled; + //public String tags_lang; + //public String details_settings; + //public boolean ocr_enabled; } diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/SDocPageOptionsModel.java b/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/SDocPageOptionsModel.java index 74e1c0119..cdb715378 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/SDocPageOptionsModel.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/model/sdoc/SDocPageOptionsModel.java @@ -13,6 +13,8 @@ public class SDocPageOptionsModel implements Parcelable { public boolean isLocked; public boolean isStarred; + public boolean enableMetadataManagement; + @Override public String toString() { return "SDocPageOptionsModel{" + @@ -42,6 +44,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.repoName); dest.writeByte(this.isLocked ? (byte) 1 : (byte) 0); dest.writeByte(this.isStarred ? (byte) 1 : (byte) 0); + dest.writeByte(this.enableMetadataManagement ? (byte) 1 : (byte) 0); } public SDocPageOptionsModel() { @@ -56,6 +59,7 @@ protected SDocPageOptionsModel(Parcel in) { this.repoName = in.readString(); this.isLocked = in.readByte() != 0; this.isStarred = in.readByte() != 0; + this.enableMetadataManagement = in.readByte() != 0; } public static final Creator CREATOR = new Creator() { @@ -69,180 +73,4 @@ public SDocPageOptionsModel[] newArray(int size) { return new SDocPageOptionsModel[size]; } }; -} - -// -// \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/com/seafile/seadroid2/framework/datastore/DataStoreKeys.java b/app/src/main/java/com/seafile/seadroid2/framework/datastore/DataStoreKeys.java index 243f74ebf..35871b1be 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/datastore/DataStoreKeys.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/datastore/DataStoreKeys.java @@ -36,4 +36,6 @@ public class DataStoreKeys { public static final String KEY_DARK_MODE = "key_dark_mode"; public static final String KEY_SERVER_CERT_INFO = "key_server_cert_info"; + + public static final String KEY_NAV_CONTEXT_STACK = "key_nav_context_stack"; } \ No newline at end of file diff --git a/app/src/main/java/com/seafile/seadroid2/framework/file_monitor/FileSyncService.java b/app/src/main/java/com/seafile/seadroid2/framework/file_monitor/FileSyncService.java index 18a4d3b0f..ebf7543f9 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/file_monitor/FileSyncService.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/file_monitor/FileSyncService.java @@ -294,6 +294,7 @@ public void onStop(FileAlterationObserver observer) { public void onDestroy() { super.onDestroy(); + SLogs.e("file monitor service destroy"); stopFolderMonitor(); // diff --git a/app/src/main/java/com/seafile/seadroid2/framework/util/GlideCache.java b/app/src/main/java/com/seafile/seadroid2/framework/util/GlideCache.java index f69075797..e18e22d25 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/util/GlideCache.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/util/GlideCache.java @@ -45,8 +45,7 @@ public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder File[] externalMediaDirs = SeadroidApplication.getAppContext().getExternalMediaDirs(); String rootPath = externalMediaDirs[0].getAbsolutePath(); File dirPath = new File(rootPath + "/GlideCache/"); - builder.setDiskCache(new DiskLruCacheFactory(dirPath.getAbsolutePath(), 1024 * 1024 * 100)); - GlideApp.tearDown(); + builder.setDiskCache(new DiskLruCacheFactory(dirPath.getAbsolutePath(), 1024 * 1024 * 500)); } @Override diff --git a/app/src/main/java/com/seafile/seadroid2/framework/util/ThumbnailUtils.java b/app/src/main/java/com/seafile/seadroid2/framework/util/ThumbnailUtils.java new file mode 100644 index 000000000..28218c789 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/framework/util/ThumbnailUtils.java @@ -0,0 +1,11 @@ +package com.seafile.seadroid2.framework.util; + +import com.blankj.utilcode.util.EncodeUtils; + +public class ThumbnailUtils { + + public static String convertThumbnailUrl(String serverUrl, String repoId, String fullPath) { + String newFilePath = EncodeUtils.urlEncode(fullPath); + return String.format("%sapi2/repos/%s/thumbnail/?p=%s&size=%s", serverUrl, repoId, newFilePath, 128); + } +} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/BackgroundJobManagerImpl.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/BackgroundJobManagerImpl.java index bb5a51740..0bce10fdb 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/BackgroundJobManagerImpl.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/BackgroundJobManagerImpl.java @@ -2,6 +2,7 @@ import android.text.TextUtils; +import androidx.work.BackoffPolicy; import androidx.work.Constraints; import androidx.work.Data; import androidx.work.ExistingWorkPolicy; @@ -149,6 +150,7 @@ private OneTimeWorkRequest getMediaUploadRequest() { return oneTimeRequestBuilder(UploadMediaFileAutomaticallyWorker.class) .setConstraints(constraints) + .setBackoffCriteria(BackoffPolicy.LINEAR, 5, TimeUnit.SECONDS) .setInitialDelay(1, TimeUnit.SECONDS) .setId(UploadMediaFileAutomaticallyWorker.UID) .build(); @@ -204,6 +206,7 @@ private OneTimeWorkRequest getFolderUploadRequest() { return oneTimeRequestBuilder(UploadFolderFileAutomaticallyWorker.class) .setConstraints(constraints) + .setBackoffCriteria(BackoffPolicy.LINEAR, 5, TimeUnit.SECONDS) .setInitialDelay(1, TimeUnit.SECONDS) .setId(UploadFolderFileAutomaticallyWorker.UID) .build(); diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/TransferEvent.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/TransferEvent.java index 6adf0e260..a747ff15b 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/TransferEvent.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/TransferEvent.java @@ -41,11 +41,16 @@ public class TransferEvent { /** * because of an OUT_OF_QUOTA error, the current upload worker was canceled */ - public static final String EVENT_CANCEL_OUT_OF_QUOTA = "transfer_cancel_with_out_of_quota"; + public static final String EVENT_CANCEL_WITH_OUT_OF_QUOTA = "transfer_cancel_with_out_of_quota"; /** * because of an NETWORK error, the current upload worker was canceled */ public static final String EVENT_CANCEL_WITH_NETWORK_ERR = "transfer_cancel_with_network_err"; + /** + * Manual or passive cancellation + */ + public static final String EVENT_CANCEL_WITH_BY_STOPPED = "transfer_cancel_with_stopped"; + } diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/download/BaseDownloadWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/download/BaseDownloadWorker.java index 976508f3d..b108685fd 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/download/BaseDownloadWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/download/BaseDownloadWorker.java @@ -90,7 +90,7 @@ public String isInterrupt(TransferResult result) { } else if (result == TransferResult.FILE_NOT_FOUND) { // finishFlagEvent = null; } else if (result == TransferResult.OUT_OF_QUOTA) { - finishFlagEvent = TransferEvent.EVENT_CANCEL_OUT_OF_QUOTA; + finishFlagEvent = TransferEvent.EVENT_CANCEL_WITH_OUT_OF_QUOTA; } else if (result == TransferResult.NETWORK_CONNECTION) { finishFlagEvent = TransferEvent.EVENT_CANCEL_WITH_NETWORK_ERR; } else if (result == TransferResult.SSL_EXCEPTION) { diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/BaseUploadWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/BaseUploadWorker.java index 04662712e..541ab61af 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/BaseUploadWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/BaseUploadWorker.java @@ -49,6 +49,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.net.SocketException; import java.net.SocketTimeoutException; import java.nio.file.Files; import java.security.NoSuchAlgorithmException; @@ -102,7 +103,14 @@ private TransferResult parseTransferException(Exception e) { return TransferResult.SSL_EXCEPTION; } else if (e instanceof SocketTimeoutException) { return TransferResult.NETWORK_CONNECTION; + } else if (e instanceof SocketException) { +// SocketException exception = (SocketException) e; +// SLogs.e(exception); + return TransferResult.USER_CANCELLED; } else if (e instanceof IOException) { + if (TextUtils.equals("Canceled", e.getMessage())) { + return TransferResult.USER_CANCELLED; + } return TransferResult.NETWORK_CONNECTION; } @@ -140,7 +148,7 @@ public String isInterrupt(TransferResult result) { } else if (result == TransferResult.FILE_NOT_FOUND) { // finishFlagEvent = null; } else if (result == TransferResult.OUT_OF_QUOTA) { - finishFlagEvent = TransferEvent.EVENT_CANCEL_OUT_OF_QUOTA; + finishFlagEvent = TransferEvent.EVENT_CANCEL_WITH_OUT_OF_QUOTA; } else if (result == TransferResult.NETWORK_CONNECTION) { finishFlagEvent = TransferEvent.EVENT_CANCEL_WITH_NETWORK_ERR; } else if (result == TransferResult.SSL_EXCEPTION) { @@ -151,6 +159,8 @@ public String isInterrupt(TransferResult result) { finishFlagEvent = TransferEvent.EVENT_FINISH; } else if (result == TransferResult.UNKNOWN) { finishFlagEvent = TransferEvent.EVENT_FINISH; + } else if (result == TransferResult.USER_CANCELLED) { + finishFlagEvent = TransferEvent.EVENT_CANCEL_WITH_BY_STOPPED; } return finishFlagEvent; @@ -361,6 +371,11 @@ public void onStopped() { // cancelNotification(); + SLogs.e("BaseUploadWorker onStopped"); + currentTransferEntity.transfer_status = TransferStatus.CANCELLED; + currentTransferEntity.transfer_result = TransferResult.USER_CANCELLED; + AppDatabase.getInstance().fileTransferDAO().update(currentTransferEntity); + if (newCall != null && !newCall.isCanceled()) { newCall.cancel(); } @@ -376,59 +391,67 @@ private void notifyProgress(String fileName, int percent) { showForegroundAsync(f); } + private FileTransferEntity currentTransferEntity; + public void transferFile(Account account, FileTransferEntity transferEntity) throws IOException, SeafException, JSONException { SLogs.d("start transfer, full_path: " + transferEntity.full_path); + currentTransferEntity = transferEntity; List repoModels = AppDatabase.getInstance().repoDao().getByIdSync(transferEntity.repo_id); if (CollectionUtils.isEmpty(repoModels)) { - SLogs.d("no repo for repoId: " + transferEntity.repo_id); + SLogs.d("no repo for repoId: " + currentTransferEntity.repo_id); - transferEntity.transfer_status = TransferStatus.FAILED; - transferEntity.transfer_result = TransferResult.CANCELLED; - AppDatabase.getInstance().fileTransferDAO().update(transferEntity); + currentTransferEntity.transfer_status = TransferStatus.FAILED; + currentTransferEntity.transfer_result = TransferResult.CANCELLED; + AppDatabase.getInstance().fileTransferDAO().update(currentTransferEntity); return; } //update modified_at field - transferEntity.modified_at = System.currentTimeMillis(); - AppDatabase.getInstance().fileTransferDAO().update(transferEntity); - notifyProgress(transferEntity.file_name, 0); - SLogs.d("start transfer, target_path: " + transferEntity.target_path); + currentTransferEntity.modified_at = System.currentTimeMillis(); + AppDatabase.getInstance().fileTransferDAO().update(currentTransferEntity); +// + //show notification + notifyProgress(currentTransferEntity.file_name, 0); + SLogs.d("start transfer, target_path: " + currentTransferEntity.target_path); RepoModel repo = repoModels.get(0); - if (repo.canLocalDecrypt()) { - uploadBlockFile(account, repo, transferEntity); - } else { - uploadFile(account, repo, transferEntity); - } + uploadFile(account, repo); + +// RepoModel repo = repoModels.get(0); +// if (repo.canLocalDecrypt()) { +// uploadBlockFile(account, repo, transferEntity); +// } else { +// uploadFile(account, repo, transferEntity); +// } } /** * upload file */ - private void uploadFile(Account account, RepoModel repoModel, FileTransferEntity transferEntity) throws IOException, SeafException { + private void uploadFile(Account account, RepoModel repoModel) throws IOException, SeafException { if (isStopped()) { return; } - File file = new File(transferEntity.full_path); + File file = new File(currentTransferEntity.full_path); if (!file.exists()) { throw SeafException.notFoundException; } - ExistingFileStrategy fileStrategy = transferEntity.file_strategy; + ExistingFileStrategy fileStrategy = currentTransferEntity.file_strategy; if (fileStrategy == ExistingFileStrategy.AUTO) { - fileStrategy = checkRemoteFileExists(account, repoModel, transferEntity); + fileStrategy = checkRemoteFileExists(account, repoModel, currentTransferEntity); } if (fileStrategy == ExistingFileStrategy.SKIP) { - SLogs.d("folder backup: skip file(remote exists): " + transferEntity.target_path); + SLogs.d("folder backup: skip file(remote exists): " + currentTransferEntity.target_path); - transferEntity.transfer_status = TransferStatus.SUCCEEDED; - transferEntity.transfer_result = TransferResult.TRANSMITTED; - AppDatabase.getInstance().fileTransferDAO().update(transferEntity); + currentTransferEntity.transfer_status = TransferStatus.SUCCEEDED; + currentTransferEntity.transfer_result = TransferResult.TRANSMITTED; + AppDatabase.getInstance().fileTransferDAO().update(currentTransferEntity); return; } @@ -437,8 +460,8 @@ private void uploadFile(Account account, RepoModel repoModel, FileTransferEntity MultipartBody.Builder builder = new MultipartBody.Builder(); builder.setType(MultipartBody.FORM); - if (transferEntity.file_strategy == ExistingFileStrategy.REPLACE) { - builder.addFormDataPart("target_file", transferEntity.target_path); + if (currentTransferEntity.file_strategy == ExistingFileStrategy.REPLACE) { + builder.addFormDataPart("target_file", currentTransferEntity.target_path); } else { //parent_dir: / is repo root builder.addFormDataPart("parent_dir", "/"); @@ -446,7 +469,7 @@ private void uploadFile(Account account, RepoModel repoModel, FileTransferEntity // parent_dir is the root directory. // when select the root of the repo, relative_path is null. - String dir = transferEntity.getParent_path(); + String dir = currentTransferEntity.getParent_path(); dir = StringUtils.removeStart(dir, "/"); // builder.addFormDataPart("relative_path", dir); @@ -454,11 +477,11 @@ private void uploadFile(Account account, RepoModel repoModel, FileTransferEntity // - fileTransferProgressListener.setFileTransferEntity(transferEntity); + fileTransferProgressListener.setFileTransferEntity(currentTransferEntity); //db - transferEntity.transfer_status = TransferStatus.IN_PROGRESS; - AppDatabase.getInstance().fileTransferDAO().update(transferEntity); + currentTransferEntity.transfer_status = TransferStatus.IN_PROGRESS; + AppDatabase.getInstance().fileTransferDAO().update(currentTransferEntity); ProgressRequestBody progressRequestBody = new ProgressRequestBody(file, fileTransferProgressListener); @@ -467,7 +490,7 @@ private void uploadFile(Account account, RepoModel repoModel, FileTransferEntity RequestBody requestBody = builder.build(); //get upload link - String uploadUrl = getFileUploadUrl(transferEntity.repo_id, transferEntity.getParent_path(), transferEntity.file_strategy == ExistingFileStrategy.REPLACE); + String uploadUrl = getFileUploadUrl(currentTransferEntity.repo_id, currentTransferEntity.getParent_path(), currentTransferEntity.file_strategy == ExistingFileStrategy.REPLACE); if (TextUtils.isEmpty(uploadUrl)) { throw SeafException.networkException; } @@ -507,7 +530,7 @@ private void uploadFile(Account account, RepoModel repoModel, FileTransferEntity String fileId = str.replace("\"", ""); SLogs.d("result,file ID:" + str); - updateSuccess(transferEntity, fileId, file); + updateSuccess(fileId, file); } catch (Exception e) { throw e; } @@ -744,37 +767,35 @@ private void commitUpload(String link, List blkIds, FileTransferEntity t String fileId = response.body().string(); - updateSuccess(transferEntity, fileId, file); + updateSuccess(fileId, file); } catch (Exception e) { throw e; } } - private void updateSuccess(FileTransferEntity transferEntity, String fileId, File file) { + private void updateSuccess(String fileId, File file) { //db - transferEntity.file_id = fileId; - transferEntity.transferred_size = file.length(); - transferEntity.action_end_at = System.currentTimeMillis(); - transferEntity.modified_at = transferEntity.action_end_at; - transferEntity.file_original_modified_at = file.lastModified(); - transferEntity.transfer_result = TransferResult.TRANSMITTED; - transferEntity.transfer_status = TransferStatus.SUCCEEDED; + currentTransferEntity.file_id = fileId; + currentTransferEntity.transferred_size = file.length(); + currentTransferEntity.action_end_at = System.currentTimeMillis(); + currentTransferEntity.modified_at = currentTransferEntity.action_end_at; + currentTransferEntity.file_original_modified_at = file.lastModified(); + currentTransferEntity.transfer_result = TransferResult.TRANSMITTED; + currentTransferEntity.transfer_status = TransferStatus.SUCCEEDED; - AppDatabase.getInstance().fileTransferDAO().update(transferEntity); + AppDatabase.getInstance().fileTransferDAO().update(currentTransferEntity); //update - List direntList = AppDatabase.getInstance().direntDao().getListByFullPathSync(transferEntity.repo_id, transferEntity.full_path); + List direntList = AppDatabase.getInstance().direntDao().getListByFullPathSync(currentTransferEntity.repo_id, currentTransferEntity.full_path); if (!CollectionUtils.isEmpty(direntList)) { DirentModel direntModel = direntList.get(0); - direntModel.last_modified_at = transferEntity.modified_at; + direntModel.last_modified_at = currentTransferEntity.modified_at; direntModel.id = fileId; - direntModel.size = transferEntity.file_size; - direntModel.transfer_status = transferEntity.transfer_status; + direntModel.size = currentTransferEntity.file_size; + direntModel.transfer_status = currentTransferEntity.transfer_status; AppDatabase.getInstance().direntDao().update(direntModel); } - - } private final int BUFFER_SIZE = 2 * 1024 * 1024; diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/FolderBackupScannerWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/FolderBackupScannerWorker.java index 576941ec5..9c8850697 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/FolderBackupScannerWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/FolderBackupScannerWorker.java @@ -126,8 +126,8 @@ private Data getOutData() { } private boolean checkCanScan() { - boolean isOpenBackup = FolderBackupSharePreferenceHelper.readBackupSwitch(); - if (!isOpenBackup) { + boolean isTurnOn = FolderBackupSharePreferenceHelper.readBackupSwitch(); + if (!isTurnOn) { return false; } @@ -212,7 +212,7 @@ private void compareToLocalAndInsert(Account account, RepoModel repoModel, Strin List tList = CollectionUtils.newArrayList(); for (File file : subFiles) { - FileTransferEntity fEntity = FileTransferEntity.convert2ThisForUploadFileSyncWorker(account, repoModel, file, backupPath); + FileTransferEntity fEntity = FileTransferEntity.convert2ThisForUploadFileSyncWorker(account, file, backupPath); if (fEntity != null) { tList.add(fEntity); } diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/MediaBackupScannerWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/MediaBackupScannerWorker.java index a54daf2a8..6f0224a1d 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/MediaBackupScannerWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/MediaBackupScannerWorker.java @@ -97,7 +97,6 @@ public Result doWork() { return Result.success(getOutData()); } - //todo String title = getApplicationContext().getString(R.string.settings_camera_upload_info_title); String subTitle = getApplicationContext().getString(R.string.is_scanning); @@ -517,7 +516,7 @@ private void checkAndInsert(String parent, List> fi String parentPath = Utils.pathJoin(parent, "/"); - FileTransferEntity fileTransferEntity = FileTransferEntity.convert2ThisForUploadMediaSyncWorker(account, repoConfig.getRepoID(), repoConfig.getRepoName(), file, parentPath, absPathPair.getThird(), isRemoteExists); + FileTransferEntity fileTransferEntity = FileTransferEntity.convert2ThisForUploadMediaSyncWorker(account, file, parentPath, absPathPair.getThird(), isRemoteExists); transferList.add(fileTransferEntity); } diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFileManuallyWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFileManuallyWorker.java index 6333bf534..4eaebe57e 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFileManuallyWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFileManuallyWorker.java @@ -98,7 +98,7 @@ private ListenableWorker.Result start() { getGeneralNotificationHelper().showErrorNotification(R.string.above_quota, R.string.settings_folder_backup_info_title); AppDatabase.getInstance().fileTransferDAO().cancelWithFileBackup(TransferResult.OUT_OF_QUOTA); - finishFlagEvent = TransferEvent.EVENT_CANCEL_OUT_OF_QUOTA; + finishFlagEvent = TransferEvent.EVENT_CANCEL_WITH_OUT_OF_QUOTA; break; } } catch (Exception e) { diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFolderFileAutomaticallyWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFolderFileAutomaticallyWorker.java index 174e2813f..2af843848 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFolderFileAutomaticallyWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFolderFileAutomaticallyWorker.java @@ -27,6 +27,7 @@ import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; import com.seafile.seadroid2.framework.worker.TransferEvent; import com.seafile.seadroid2.framework.worker.TransferWorker; +import com.seafile.seadroid2.ui.folder_backup.RepoConfig; import java.util.List; import java.util.UUID; @@ -65,36 +66,46 @@ public void onStopped() { super.onStopped(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - SLogs.e("文件夹备份已停止:" + getStopReason()); + SLogs.e("Folder backup stopped, reason:" + getStopReason()); } } + private boolean isFirstShow = true; + private void startShowNotification(){ + if (!isFirstShow) { + return; + } + + isFirstShow = false; + ForegroundInfo foregroundInfo = notificationManager.getForegroundNotification(); + showForegroundAsync(foregroundInfo); + + + sendEvent(TransferEvent.EVENT_TRANSFERRING, TransferDataSource.FILE_BACKUP); + } + private Result start() { // notificationManager.cancel(); + SLogs.d("start upload file worker"); Account account = SupportAccountManager.getInstance().getCurrentAccount(); if (account == null) { return Result.success(); } - boolean isEnable = FolderBackupSharePreferenceHelper.readBackupSwitch(); - if (!isEnable) { + boolean canExec = can(); + if (!canExec) { return Result.success(); } - boolean isUploaded = false; - - - ForegroundInfo foregroundInfo = notificationManager.getForegroundNotification(); - showForegroundAsync(foregroundInfo); + if (repoConfig == null) { + return Result.success(); + } -// notificationManager.showNotification(); + boolean isUploaded = false; String finishFlagEvent = null; - SLogs.d("start upload file worker"); - sendEvent(TransferEvent.EVENT_TRANSFERRING, TransferDataSource.FILE_BACKUP); - while (true) { if (isStopped()) { break; @@ -113,9 +124,14 @@ private Result start() { break; } - FileTransferEntity transferEntity = transferList.get(0); + startShowNotification(); + isUploaded = true; + FileTransferEntity transferEntity = transferList.get(0); + transferEntity.repo_id = repoConfig.getRepoID(); + transferEntity.repo_name = repoConfig.getRepoName(); + try { transferFile(account, transferEntity); @@ -126,15 +142,19 @@ private Result start() { TransferResult transferResult = onException(transferEntity, e); - notifyError(transferResult); + if (!isStopped()) { + + notifyError(transferResult); - sendTransferEvent(transferEntity, false); + sendTransferEvent(transferEntity, false); + } String finishFlag = isInterrupt(transferResult); if (!TextUtils.isEmpty(finishFlag)) { finishFlagEvent = finishFlag; break; } + } } @@ -170,4 +190,25 @@ private Result start() { return Result.success(outputData); } + private RepoConfig repoConfig; + + private boolean can() { + boolean isTurnOn = FolderBackupSharePreferenceHelper.readBackupSwitch(); + if (!isTurnOn) { + return false; + } + + List backupPaths = FolderBackupSharePreferenceHelper.readBackupPathsAsList(); + if (CollectionUtils.isEmpty(backupPaths)) { + return false; + } + + repoConfig = FolderBackupSharePreferenceHelper.readRepoConfig(); + if (repoConfig == null) { + return false; + } + + return true; + } + } diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadMediaFileAutomaticallyWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadMediaFileAutomaticallyWorker.java index dd568ac18..9a6c6f590 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadMediaFileAutomaticallyWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadMediaFileAutomaticallyWorker.java @@ -22,12 +22,14 @@ import com.seafile.seadroid2.enums.TransferDataSource; import com.seafile.seadroid2.enums.TransferResult; import com.seafile.seadroid2.framework.datastore.sp_livedata.AlbumBackupSharePreferenceHelper; +import com.seafile.seadroid2.framework.datastore.sp_livedata.FolderBackupSharePreferenceHelper; import com.seafile.seadroid2.framework.notification.AlbumBackupNotificationHelper; import com.seafile.seadroid2.framework.notification.base.BaseTransferNotificationHelper; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; import com.seafile.seadroid2.framework.worker.TransferEvent; import com.seafile.seadroid2.framework.worker.TransferWorker; +import com.seafile.seadroid2.ui.folder_backup.RepoConfig; import java.util.List; import java.util.UUID; @@ -54,9 +56,24 @@ public BaseTransferNotificationHelper getNotification() { return albumNotificationHelper; } + boolean isFirstShow = true; + private void startShowNotification(){ + if (!isFirstShow) { + return; + } + + //send start transfer event + sendEvent(TransferEvent.EVENT_TRANSFERRING, TransferDataSource.ALBUM_BACKUP); + + // show foreground notification + ForegroundInfo foregroundInfo = albumNotificationHelper.getForegroundNotification(); + showForegroundAsync(foregroundInfo); + } + @NonNull @Override public ListenableWorker.Result doWork() { + SLogs.d("start upload media worker"); Account account = SupportAccountManager.getInstance().getCurrentAccount(); if (account == null) { @@ -66,17 +83,14 @@ public ListenableWorker.Result doWork() { return ListenableWorker.Result.success(); } - boolean isEnable = AlbumBackupSharePreferenceHelper.readBackupSwitch(); - if (!isEnable) { - return ListenableWorker.Result.success(); + boolean canExec = can(); + if (!canExec) { + return Result.success(); } - //send start transfer event - sendEvent(TransferEvent.EVENT_TRANSFERRING, TransferDataSource.ALBUM_BACKUP); - - // show foreground notification - ForegroundInfo foregroundInfo = albumNotificationHelper.getForegroundNotification(); - showForegroundAsync(foregroundInfo); + if (repoConfig == null) { + return Result.success(); + } // String finishFlagEvent = null; @@ -86,8 +100,6 @@ public ListenableWorker.Result doWork() { break; } - SLogs.d("start upload media worker"); - List transferList = AppDatabase .getInstance() .fileTransferDAO() @@ -99,11 +111,15 @@ public ListenableWorker.Result doWork() { break; } - FileTransferEntity transferEntity = transferList.get(0); + startShowNotification(); isUploaded = true; - try { + FileTransferEntity transferEntity = transferList.get(0); + transferEntity.repo_id = repoConfig.getRepoID(); + transferEntity.repo_name = repoConfig.getRepoName(); + + try { transferFile(account, transferEntity); sendTransferEvent(transferEntity, true); @@ -113,9 +129,11 @@ public ListenableWorker.Result doWork() { TransferResult transferResult = onException(transferEntity, e); - notifyError(transferResult); + if (!isStopped()) { + notifyError(transferResult); - sendTransferEvent(transferEntity, false); + sendTransferEvent(transferEntity, false); + } String finishFlag = isInterrupt(transferResult); if (!TextUtils.isEmpty(finishFlag)) { @@ -156,4 +174,20 @@ public ListenableWorker.Result doWork() { .build(); return Result.success(outputData); } + + private RepoConfig repoConfig; + + private boolean can() { + boolean isTurnOn = AlbumBackupSharePreferenceHelper.readBackupSwitch(); + if (!isTurnOn) { + return false; + } + + repoConfig = AlbumBackupSharePreferenceHelper.readRepoConfig(); + if (repoConfig == null) { + return false; + } + + return true; + } } diff --git a/app/src/main/java/com/seafile/seadroid2/listener/FileTransferProgressListener.java b/app/src/main/java/com/seafile/seadroid2/listener/FileTransferProgressListener.java index 67ea45d50..426afc6cd 100644 --- a/app/src/main/java/com/seafile/seadroid2/listener/FileTransferProgressListener.java +++ b/app/src/main/java/com/seafile/seadroid2/listener/FileTransferProgressListener.java @@ -43,10 +43,26 @@ public void onProgressNotify(long cur, long total) { fileTransferEntity.transferred_size = cur; - int percent = (int) ((float) cur / (float) total * 100); + int percent = calcu(cur, total); progressListener.onProgressNotify(fileTransferEntity, percent, cur, total); } + public int onProgress(long cur, long total) { + + long nowt = System.currentTimeMillis(); + if (nowt - temp < 1000) { + return -1; + } + + temp = nowt; + return calcu(cur, total); + } + + public int calcu(long cur, long total) { + int percent = (int) ((float) cur / (float) total * 100); + return percent; + } + public interface TransferProgressListener { void onProgressNotify(FileTransferEntity fileTransferEntity, int percent, long transferredSize, long totalSize); } diff --git a/app/src/main/java/com/seafile/seadroid2/preferences/ContextStackPreferenceHelper.java b/app/src/main/java/com/seafile/seadroid2/preferences/ContextStackPreferenceHelper.java new file mode 100644 index 000000000..ae26f1a30 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/preferences/ContextStackPreferenceHelper.java @@ -0,0 +1,35 @@ +package com.seafile.seadroid2.preferences; + +import com.blankj.utilcode.util.GsonUtils; +import com.google.gson.reflect.TypeToken; +import com.seafile.seadroid2.framework.data.model.ContextModel; +import com.seafile.seadroid2.framework.datastore.DataStoreKeys; + +import java.lang.reflect.Type; +import java.util.Stack; + +public class ContextStackPreferenceHelper { + private static final String STACK_KEY = DataStoreKeys.KEY_NAV_CONTEXT_STACK; + + // 保存 Stack 到 SharedPreferences + public static void saveStack(Stack stack) { + String json = GsonUtils.toJson(stack); // 将 Stack 转换为 JSON 字符串 + Settings.getCommonPreferences().edit().putString(STACK_KEY, json).apply(); + } + + public static void clearStack() { + Settings.getCommonPreferences().edit().remove(STACK_KEY).apply(); + } + + // 从 SharedPreferences 中获取 Stack + public static Stack getStack() { + String json = Settings.getCommonPreferences().getString(STACK_KEY, null); + if (json == null) { + return new Stack<>(); // 返回空栈 + } + + Type type = new TypeToken>() { + }.getType(); + return GsonUtils.fromJson(json, type); // 从 JSON 恢复 Stack + } +} diff --git a/app/src/main/java/com/seafile/seadroid2/ui/camera_upload/CameraUploadManager.java b/app/src/main/java/com/seafile/seadroid2/ui/camera_upload/CameraUploadManager.java index eef46c498..7b049fabe 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/camera_upload/CameraUploadManager.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/camera_upload/CameraUploadManager.java @@ -8,6 +8,7 @@ import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; import com.seafile.seadroid2.framework.util.SLogs; +import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; import java.util.List; @@ -122,6 +123,8 @@ public void disableCameraUpload() { ContentResolver.cancelSync(account.getAndroidAccount(), AUTHORITY); ContentResolver.setIsSyncable(account.getAndroidAccount(), AUTHORITY, 0); } + + BackgroundJobManagerImpl.getInstance().cancelAllMediaWorker(); } /** diff --git a/app/src/main/java/com/seafile/seadroid2/ui/sdoc/DocsCommentViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/docs_comment/DocsCommentViewModel.java similarity index 68% rename from app/src/main/java/com/seafile/seadroid2/ui/sdoc/DocsCommentViewModel.java rename to app/src/main/java/com/seafile/seadroid2/ui/docs_comment/DocsCommentViewModel.java index 960c0a30f..aa5893611 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/sdoc/DocsCommentViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/docs_comment/DocsCommentViewModel.java @@ -1,4 +1,4 @@ -package com.seafile.seadroid2.ui.sdoc; +package com.seafile.seadroid2.ui.docs_comment; import android.content.ContentResolver; import android.net.Uri; @@ -8,7 +8,6 @@ import com.blankj.utilcode.util.CloneUtils; import com.blankj.utilcode.util.CollectionUtils; -import com.blankj.utilcode.util.RegexUtils; import com.blankj.utilcode.util.TimeUtils; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; @@ -31,6 +30,7 @@ import com.seafile.seadroid2.framework.util.StringUtils; import com.seafile.seadroid2.framework.util.Utils; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; +import com.seafile.seadroid2.ui.sdoc.DocsCommentService; import com.seafile.seadroid2.view.rich_edittext.RichEditText; import java.util.Comparator; @@ -53,166 +53,17 @@ public class DocsCommentViewModel extends BaseViewModel { - private final MutableLiveData _fileProfileConfigLiveData = new MutableLiveData<>(); - private final MutableLiveData _fileRecordLiveData = new MutableLiveData<>(); private final MutableLiveData _fileCommentLiveData = new MutableLiveData<>(); - private final MutableLiveData> _sdocElementListLiveData = new MutableLiveData<>(); private final MutableLiveData _postCommentLiveData = new MutableLiveData<>(); public MutableLiveData getPostCommentLiveData() { return _postCommentLiveData; } - public MutableLiveData getFileDetailLiveData() { - return _fileProfileConfigLiveData; - } - - public MutableLiveData getSdocRecordLiveData() { - return _fileRecordLiveData; - } - public MutableLiveData getSdocCommentLiveData() { return _fileCommentLiveData; } - public MutableLiveData> getSdocElementLiveData() { - return _sdocElementListLiveData; - } - - public void loadFileConfig(String repoId, String path) { - Single userSingle = HttpIO.getCurrentInstance().execute(DocsCommentService.class).getRelatedUsers(repoId); - Single metadataSingle = HttpIO.getCurrentInstance().execute(DocsCommentService.class).getMetadata(repoId); - Single detailSingle = HttpIO.getCurrentInstance().execute(DocsCommentService.class).getFileDetail(repoId, path); - - Single s = Single.zip(detailSingle, userSingle, metadataSingle, new Function3() { - @Override - public FileProfileConfigModel apply(FileDetailModel docDetailModel, UserWrapperModel userWrapperModel, MetadataConfigModel metadataConfigModel) throws Exception { - FileProfileConfigModel configModel = new FileProfileConfigModel(); - configModel.setDetail(docDetailModel); - configModel.setUsers(userWrapperModel); - configModel.setMetadata(metadataConfigModel); - return configModel; - } - }); - - addSingleDisposable(s, new Consumer() { - @Override - public void accept(FileProfileConfigModel fileProfileConfigModel) throws Exception { - getFileDetailLiveData().setValue(fileProfileConfigModel); - } - }); - } - - public void loadRecords(String repoId, String path) { - if (TextUtils.isEmpty(path) || TextUtils.equals("/", path)) { - return; - } - - String parent_dir; - String name; - - // 1. /a/b/c/t.txt - // 2. /a/t.txt - // 3. /t.txt - // 4. t.txt - // 5. / - if (path.contains("/")) { - parent_dir = path.substring(0, path.lastIndexOf("/")); - name = path.substring(path.lastIndexOf("/") + 1); - } else { - parent_dir = null; - name = path; - } - - if (TextUtils.isEmpty(parent_dir)) { - parent_dir = "/"; - } - - Single single = HttpIO.getCurrentInstance().execute(DocsCommentService.class).getRecords(repoId, parent_dir, name); - addSingleDisposable(single, new Consumer() { - @Override - public void accept(FileRecordWrapperModel fileRecordWrapperModel) throws Exception { - getSdocRecordLiveData().setValue(fileRecordWrapperModel); - } - }); - } - - public static final List _AllowedElementTypes = List.of("header1", "header2", "header3"); - - public void loadSdocElements(SDocPageOptionsModel pageOptionsModel) { - if (TextUtils.isEmpty(pageOptionsModel.seadocServerUrl)) { - return; - } - getRefreshLiveData().setValue(true); - - String sdocServerUrl = pageOptionsModel.seadocServerUrl; - if (!sdocServerUrl.endsWith("/")) { - sdocServerUrl = sdocServerUrl + "/"; - } - - Account curAccount = SupportAccountManager.getInstance().getCurrentAccount(); - Account partialAccount = CloneUtils.deepClone(curAccount, Account.class); - partialAccount.setToken(pageOptionsModel.seadocAccessToken); - partialAccount.setServer(sdocServerUrl); - - Single single = HttpIO.getInstanceByAccount(partialAccount).execute(DocsCommentService.class).getElements(pageOptionsModel.docUuid); - addSingleDisposable(single, new Consumer() { - @Override - public void accept(SDocOutlineWrapperModel wrapperModel) throws Exception { - - if (wrapperModel == null || wrapperModel.elements == null) { - getSdocElementLiveData().setValue(null); - return; - } - - List newList = wrapperModel.elements.stream().filter(new Predicate() { - @Override - public boolean test(OutlineItemModel sDocModel) { - if (!_AllowedElementTypes.contains(sDocModel.type)) { - return false; - } - - if (TextUtils.isEmpty(sDocModel.text) && CollectionUtils.isEmpty(sDocModel.children)) { - return false; - } - - return true; - } - }).map(new Function() { - @Override - public OutlineItemModel apply(OutlineItemModel sDocModel) { - if (!TextUtils.isEmpty(sDocModel.text)) { - return sDocModel; - } - - if (CollectionUtils.isEmpty(sDocModel.children)) { - return sDocModel; - } - - String text = ""; - for (OutlineItemModel child : sDocModel.children) { - if (!TextUtils.isEmpty(child.text)) { - String nt = StringUtils.trim(child.text, "\n").trim(); - text = text.concat(nt); - } - } - sDocModel.text = text; - return sDocModel; - } - }).collect(Collectors.toList()); - - getSdocElementLiveData().setValue(newList); - getRefreshLiveData().setValue(false); - } - }, new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - SLogs.e(throwable); - getRefreshLiveData().setValue(false); - } - }); - } - public void loadDocComments(SDocPageOptionsModel pageOptionsModel) { if (TextUtils.isEmpty(pageOptionsModel.seadocServerUrl)) { return; @@ -374,7 +225,7 @@ private List getImageMdResult(String s) { return models; } - public void markResolve(String sdocServerUrl,String token, String sdocUid, long commentId, Consumer consumer) { + public void markResolve(String sdocServerUrl, String token, String sdocUid, long commentId, Consumer consumer) { getRefreshLiveData().setValue(true); if (!sdocServerUrl.endsWith("/")) { @@ -406,7 +257,7 @@ public void accept(Throwable throwable) { }); } - public void delete(String sdocServerUrl,String token, String sdocUid, long commentId, Consumer consumer) { + public void delete(String sdocServerUrl, String token, String sdocUid, long commentId, Consumer consumer) { getRefreshLiveData().setValue(true); if (!sdocServerUrl.endsWith("/")) { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/docs_comment/DocsCommentsActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/docs_comment/DocsCommentsActivity.java index 1df2df0b9..85739856c 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/docs_comment/DocsCommentsActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/docs_comment/DocsCommentsActivity.java @@ -1,6 +1,5 @@ package com.seafile.seadroid2.ui.docs_comment; -import android.animation.ValueAnimator; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; @@ -11,15 +10,12 @@ import android.view.MenuItem; import android.view.View; import android.view.WindowManager; -import android.view.animation.Animation; -import android.widget.LinearLayout; import androidx.annotation.NonNull; import androidx.appcompat.widget.PopupMenu; import androidx.appcompat.widget.Toolbar; import androidx.lifecycle.Observer; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import com.blankj.utilcode.util.CollectionUtils; import com.blankj.utilcode.util.KeyboardUtils; @@ -28,8 +24,6 @@ import com.chad.library.adapter4.QuickAdapterHelper; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.seafile.seadroid2.R; -import com.seafile.seadroid2.account.Account; -import com.seafile.seadroid2.account.SupportAccountManager; import com.seafile.seadroid2.databinding.ActivityDocCommentBinding; import com.seafile.seadroid2.databinding.ToolbarActionbarBinding; import com.seafile.seadroid2.framework.data.model.docs_comment.DocsCommentModel; @@ -37,10 +31,6 @@ import com.seafile.seadroid2.framework.data.model.sdoc.SDocPageOptionsModel; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.ui.base.BaseMediaSelectorActivity; -import com.seafile.seadroid2.ui.bottomsheetmenu.BottomSheetHelper; -import com.seafile.seadroid2.ui.bottomsheetmenu.BottomSheetMenuFragment; -import com.seafile.seadroid2.ui.bottomsheetmenu.OnMenuClickListener; -import com.seafile.seadroid2.ui.sdoc.DocsCommentViewModel; import com.seafile.seadroid2.view.rich_edittext.RichEditText; import java.util.List; diff --git a/app/src/main/java/com/seafile/seadroid2/ui/main/MainActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/main/MainActivity.java index 8703ecb37..fba8aabb6 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/main/MainActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/main/MainActivity.java @@ -57,6 +57,7 @@ import com.seafile.seadroid2.framework.data.model.dirents.DirentFileModel; import com.seafile.seadroid2.framework.file_monitor.FileSyncService; import com.seafile.seadroid2.framework.helper.NightModeHelper; +import com.seafile.seadroid2.framework.util.GlideApp; import com.seafile.seadroid2.framework.util.PermissionUtil; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.util.TakeCameras; @@ -505,7 +506,7 @@ private void refreshToolbarTitle() { private void bindService() { Intent syncIntent = new Intent(this, FileSyncService.class); bindService(syncIntent, syncConnection, Context.BIND_AUTO_CREATE); - startService(syncIntent); +// startService(syncIntent); //It doesn't need to run in the background } private final ServiceConnection syncConnection = new ServiceConnection() { @@ -1208,7 +1209,7 @@ private void doSelectedMultiFile(List uriList) { mainViewModel.checkLocalDirent(curAccount, this, repoModel, parent_dir, uriList, new Consumer>() { @Override - public void accept(List newUris) throws Exception { + public void accept(List newUris) { dismissProgressDialog(); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/main/MainViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/main/MainViewModel.java index 691070670..e1692f1b4 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/main/MainViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/main/MainViewModel.java @@ -28,6 +28,7 @@ import com.seafile.seadroid2.framework.datastore.DataManager; import com.seafile.seadroid2.framework.util.Utils; import com.seafile.seadroid2.framework.worker.ExistingFileStrategy; +import com.seafile.seadroid2.preferences.Settings; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.context.NavContext; import com.seafile.seadroid2.framework.data.ServerInfo; diff --git a/app/src/main/java/com/seafile/seadroid2/ui/media/PhotoViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/media/PhotoViewModel.java new file mode 100644 index 000000000..48a5fed35 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/ui/media/PhotoViewModel.java @@ -0,0 +1,259 @@ +package com.seafile.seadroid2.ui.media; + +import android.text.TextUtils; + +import androidx.lifecycle.MutableLiveData; + +import com.blankj.utilcode.util.CollectionUtils; +import com.blankj.utilcode.util.FileUtils; +import com.seafile.seadroid2.SeafException; +import com.seafile.seadroid2.account.Account; +import com.seafile.seadroid2.account.SupportAccountManager; +import com.seafile.seadroid2.enums.TransferAction; +import com.seafile.seadroid2.enums.TransferResult; +import com.seafile.seadroid2.enums.TransferStatus; +import com.seafile.seadroid2.framework.data.db.AppDatabase; +import com.seafile.seadroid2.framework.data.db.entities.DirentModel; +import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; +import com.seafile.seadroid2.framework.datastore.DataManager; +import com.seafile.seadroid2.framework.http.HttpIO; +import com.seafile.seadroid2.framework.util.SLogs; +import com.seafile.seadroid2.framework.worker.ExistingFileStrategy; +import com.seafile.seadroid2.listener.FileTransferProgressListener; +import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; +import com.seafile.seadroid2.ui.file.FileService; + +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.file.Path; +import java.util.List; + +import io.reactivex.Single; +import io.reactivex.SingleEmitter; +import io.reactivex.SingleOnSubscribe; +import io.reactivex.SingleSource; +import io.reactivex.functions.BiFunction; +import io.reactivex.functions.Consumer; +import io.reactivex.functions.Function; +import kotlin.Pair; +import okhttp3.Call; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class PhotoViewModel extends BaseViewModel { + private final MutableLiveData _downloadedUrlLiveData = new MutableLiveData<>(); + private final MutableLiveData _originalUrlLiveData = new MutableLiveData<>(); + + private final MutableLiveData> _checkLocalLiveData = new MutableLiveData<>(); + + public MutableLiveData getDownloadedPathLiveData() { + return _downloadedUrlLiveData; + } + + public MutableLiveData getOriginalUrlLiveData() { + return _originalUrlLiveData; + } + + public MutableLiveData> getCheckLocalLiveData() { + return _checkLocalLiveData; + } + + public void checkLocal(String repoId, String fullPath) { + Single> direntSingle = AppDatabase.getInstance().direntDao().getListByFullPathAsync(repoId, fullPath); + Single> transferSingle = AppDatabase.getInstance().fileTransferDAO().getListByFullPathAsync(repoId, TransferAction.DOWNLOAD, fullPath); + Single> rSingle = Single.zip(direntSingle, transferSingle, new BiFunction, List, Pair>() { + @Override + public Pair apply(List direntModels, List fileTransferEntities) throws Exception { + if (CollectionUtils.isEmpty(direntModels)) { + return null; + } + + if (CollectionUtils.isEmpty(fileTransferEntities)) { + return new Pair<>(direntModels.get(0), null); + } + + return new Pair<>(direntModels.get(0), fileTransferEntities.get(0)); + } + }); + + addSingleDisposable(rSingle, new Consumer>() { + @Override + public void accept(Pair pair) throws Exception { + getCheckLocalLiveData().setValue(pair); + } + }); + + } + + private final int SEGMENT_SIZE = 8192; + private final FileTransferProgressListener fileTransferProgressListener = new FileTransferProgressListener(); + + public void requestOriginalUrl(DirentModel direntModel) { + Single downloadUrlSingle = HttpIO.getCurrentInstance() + .execute(FileService.class) + .getFileDownloadLinkAsync(direntModel.repo_id, direntModel.full_path); + + addSingleDisposable(downloadUrlSingle, new Consumer() { + @Override + public void accept(String dlink) throws Exception { + // + dlink = StringUtils.replace(dlink, "\"", ""); + int i = dlink.lastIndexOf('/'); + if (i == -1) { + return; + } + + dlink = dlink.substring(0, i) + "/" + URLEncoder.encode(dlink.substring(i + 1), "UTF-8"); + + getOriginalUrlLiveData().setValue(dlink); + } + }); + } + + public void download(DirentModel direntModel) { + Single downloadUrlSingle = HttpIO.getCurrentInstance() + .execute(FileService.class) + .getFileDownloadLinkAsync(direntModel.repo_id, direntModel.full_path); + + Single downloadedFilePathSingle = downloadUrlSingle.flatMap(new Function>() { + @Override + public SingleSource apply(String dlink) throws Exception { + // + dlink = StringUtils.replace(dlink, "\"", ""); + int i = dlink.lastIndexOf('/'); + if (i == -1) { + return null; + } + + dlink = dlink.substring(0, i) + "/" + URLEncoder.encode(dlink.substring(i + 1), "UTF-8"); + + return Single.just(dlink); + } + }).flatMap(new Function>() { + @Override + public SingleSource apply(String s) throws Exception { + if (TextUtils.isEmpty(s)) { + //download url is null + throw SeafException.networkException; + } + + FileTransferEntity transferEntity = FileTransferEntity.convertDirentModel2This(false, direntModel); + + return getDownloadSingle(transferEntity, s); + } + }).flatMap(new Function>() { + @Override + public SingleSource apply(FileTransferEntity transferEntity) throws Exception { + return Single.just(transferEntity.target_path); + } + }); + + addSingleDisposable(downloadedFilePathSingle, new Consumer() { + @Override + public void accept(String s) { + getDownloadedPathLiveData().setValue(s); + } + }, new Consumer() { + @Override + public void accept(Throwable throwable) throws Exception { + SeafException seafException = getExceptionByThrowable(throwable); + getSeafExceptionLiveData().setValue(seafException); + } + }); + } + + private Single getDownloadSingle(FileTransferEntity transferEntity, String dlink) { + return Single.create(new SingleOnSubscribe() { + @Override + public void subscribe(SingleEmitter emitter) throws Exception { + + Account currentAccount = SupportAccountManager.getInstance().getCurrentAccount(); + File localFile = DataManager.getLocalRepoFile(currentAccount, transferEntity); + + transferEntity.target_path = localFile.getAbsolutePath(); + AppDatabase.getInstance().fileTransferDAO().insert(transferEntity); + + Request request = new Request.Builder() + .url(dlink) + .get() + .build(); + + Call newCall = HttpIO.getCurrentInstance().getOkHttpClient().getOkClient().newCall(request); + + try (Response response = newCall.execute()) { + if (!response.isSuccessful()) { + emitter.onError(SeafException.networkException); + return; + } + + ResponseBody responseBody = response.body(); + if (responseBody == null) { + emitter.onError(SeafException.networkException); + return; + } + + long fileSize = responseBody.contentLength(); + if (fileSize == -1) { + SLogs.d("download file error -> contentLength is -1"); + SLogs.d(localFile.getAbsolutePath()); + + fileSize = transferEntity.file_size; + } + + File tempFile = DataManager.createTempFile(); + try (InputStream inputStream = responseBody.byteStream(); + FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) { + + long totalBytesRead = 0; + + int bytesRead; + byte[] buffer = new byte[SEGMENT_SIZE]; + while ((bytesRead = inputStream.read(buffer, 0, buffer.length)) != -1) { + fileOutputStream.write(buffer, 0, bytesRead); + totalBytesRead += bytesRead; + + int p = fileTransferProgressListener.onProgress(totalBytesRead, fileSize); + SLogs.e(transferEntity.file_name + ", progress: " + p); + } + int p = fileTransferProgressListener.onProgress(fileSize, fileSize); + SLogs.e(transferEntity.file_name + ", progress: " + p); + } + + //important + Path path = java.nio.file.Files.move(tempFile.toPath(), localFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING); + boolean isSuccess = path.toFile().exists(); + if (isSuccess) { + SLogs.e("move file success: " + path); + transferEntity.transferred_size = fileSize; + transferEntity.action_end_at = System.currentTimeMillis(); + transferEntity.file_original_modified_at = transferEntity.action_end_at; + transferEntity.transfer_result = TransferResult.TRANSMITTED; + transferEntity.transfer_status = TransferStatus.SUCCEEDED; + + transferEntity.file_md5 = FileUtils.getFileMD5ToString(transferEntity.target_path).toLowerCase(); + + AppDatabase.getInstance().fileTransferDAO().update(transferEntity); + + emitter.onSuccess(transferEntity); + } else { + SLogs.e("move file failed: " + path); + + transferEntity.transfer_result = TransferResult.FILE_ERROR; + transferEntity.transfer_status = TransferStatus.FAILED; + AppDatabase.getInstance().fileTransferDAO().update(transferEntity); + + emitter.onError(SeafException.transferFileException); + } + } catch (Exception e) { + emitter.onError(e); + } + } + }); + } + +} diff --git a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewActivity.java deleted file mode 100644 index 5d67f4232..000000000 --- a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewActivity.java +++ /dev/null @@ -1,314 +0,0 @@ -package com.seafile.seadroid2.ui.media.image_preview; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; - -import androidx.activity.OnBackPressedCallback; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.lifecycle.Observer; -import androidx.viewpager2.widget.ViewPager2; - -import com.blankj.utilcode.util.BarUtils; -import com.blankj.utilcode.util.CollectionUtils; -import com.blankj.utilcode.util.NetworkUtils; -import com.blankj.utilcode.util.ToastUtils; -import com.seafile.seadroid2.R; -import com.seafile.seadroid2.databinding.ActivityImagePreviewBinding; -import com.seafile.seadroid2.framework.data.db.entities.DirentModel; -import com.seafile.seadroid2.framework.data.db.entities.RepoModel; -import com.seafile.seadroid2.framework.data.db.entities.StarredModel; -import com.seafile.seadroid2.framework.data.model.activities.ActivityModel; -import com.seafile.seadroid2.framework.data.model.search.SearchModel; -import com.seafile.seadroid2.framework.util.Objs; -import com.seafile.seadroid2.ui.adapter.ViewPager2Adapter; -import com.seafile.seadroid2.ui.base.BaseActivityWithVM; -import com.seafile.seadroid2.ui.dialog_fragment.DeleteFileDialogFragment; -import com.seafile.seadroid2.ui.dialog_fragment.listener.OnRefreshDataListener; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import io.reactivex.functions.Consumer; - -@Deprecated -public class ImagePreviewActivity extends BaseActivityWithVM { - private ActivityImagePreviewBinding binding; - private ViewPager2Adapter adapter; - private DirentModel currentDirent; - private List direntList; - private boolean isDataOperated = false; - - public static Intent startThisFromRepo(Context context, DirentModel direntModel) { - Intent intent = new Intent(context, ImagePreviewActivity.class); - intent.putExtra("dirent", direntModel); - intent.putExtra("query_db", true); - return intent; - } - - public static Intent startThisFromStarred(Context context, StarredModel starredModel) { - Intent intent = new Intent(context, ImagePreviewActivity.class); - intent.putExtra("dirent", StarredModel.convert2DirentModel(starredModel)); - return intent; - } - - public static Intent startThisFromActivity(Context context, ActivityModel starredModel) { - Intent intent = new Intent(context, ImagePreviewActivity.class); - intent.putExtra("dirent", ActivityModel.convert2DirentModel(starredModel)); - return intent; - } - - - public static void startThisFromSearch(Context context, SearchModel starredModel) { - Intent intent = new Intent(context, ImagePreviewActivity.class); - intent.putExtra("dirent", SearchModel.convert2DirentModel(starredModel)); - context.startActivity(intent); - } - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - binding = ActivityImagePreviewBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - BarUtils.setNavBarVisibility(this, false); - BarUtils.setStatusBarVisibility(this, false); - - initData(); - - initView(); - initViewModel(); - - getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) { - @Override - public void handleOnBackPressed() { - if (isDataOperated) { - setResult(RESULT_OK); - } - finish(); - } - }); - } - - @Override - protected void onPostCreate(@Nullable Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - - loadData(); - } - - private void initData() { - - if (getIntent() == null) { - throw new IllegalArgumentException("Intent is null"); - } - - currentDirent = getIntent().getParcelableExtra("dirent"); - } - - private void initView() { - View.OnClickListener onClickListener = v -> { - int id = v.getId(); - if (id == R.id.gallery_download_photo) { - downloadFile(); - } else if (id == R.id.gallery_delete_photo) { - deleteFile(); - } else if (id == R.id.gallery_star_photo) { - starFile(); - } else if (id == R.id.gallery_share_photo) { - shareFile(); - } - }; - - binding.galleryDownloadPhoto.setOnClickListener(onClickListener); - binding.galleryDeletePhoto.setOnClickListener(onClickListener); - binding.galleryStarPhoto.setOnClickListener(onClickListener); - binding.gallerySharePhoto.setOnClickListener(onClickListener); - } - - private void initViewModel() { - getViewModel().getRefreshLiveData().observe(this, new Observer() { - @Override - public void onChanged(Boolean aBoolean) { - if (aBoolean) { - showProgressDialog(); - } else { - dismissProgressDialog(); - } - } - }); - - getViewModel().getStarLiveData().observe(this, new Observer() { - @Override - public void onChanged(Boolean aBoolean) { - isDataOperated = true; - - if (aBoolean) { - - }else{ - - } - ToastUtils.showLong(aBoolean ? R.string.star_file_succeed : R.string.star_file_failed); - } - }); - - getViewModel().getListLiveData().observe(this, new Observer>() { - @Override - public void onChanged(List direntModels) { - - if (CollectionUtils.isEmpty(direntModels)) { - direntModels = CollectionUtils.newArrayList(currentDirent); - } - - direntList = direntModels; - - notifyFragmentList(); - } - }); - } - - private void loadData() { - getViewModel().getRepoModelFromDB(currentDirent.repo_id, new Consumer() { - @Override - public void accept(RepoModel repoModel) { - if (!repoModel.hasWritePermission()) { - binding.galleryDeletePhoto.setVisibility(View.GONE); - } - - getViewModel().loadData(currentDirent.repo_id, currentDirent.parent_dir); - - } - }); - } - - private void notifyFragmentList() { - if (CollectionUtils.isEmpty(direntList)) { - return; - } - - adapter = new ViewPager2Adapter(this); - List fragments = new ArrayList<>(); - for (DirentModel direntModel : direntList) { - PhotoFragment photoFragment = PhotoFragment.newInstance(direntModel); - photoFragment.setOnPhotoTapListener((view, x, y) -> hideOrShowToolBar()); - fragments.add(photoFragment); - } - - adapter.addFragments(fragments); - - binding.pager.setAdapter(adapter); - binding.pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { - @Override - public void onPageSelected(int position) { - super.onPageSelected(position); - - String fs = String.format(Locale.ROOT, "%d/%d", (position + 1), direntList.size()); - binding.galleryPageIndex.setText(fs); - - DirentModel model = direntList.get(position); - binding.galleryPageName.setText(model.name); - } - }); - - navToSelectedPage(); - } - - private boolean showToolBar = false; - - private void hideOrShowToolBar() { - binding.galleryToolBar.setVisibility(!showToolBar ? View.VISIBLE : View.GONE); - binding.pageIndexContainer.setVisibility(!showToolBar ? View.VISIBLE : View.GONE); - showToolBar = !showToolBar; - } - - /** - * Dynamically navigate to the starting page index selected by user - * by default the starting page index is 0 - */ - private void navToSelectedPage() { - int size = direntList.size(); - int mPageIndex = -1; - for (int i = 0; i < size; i++) { - if (direntList.get(i).name.equals(currentDirent.name)) { - binding.pager.setCurrentItem(i); - binding.galleryPageIndex.setText(String.valueOf(i + 1)); - mPageIndex = i; - break; - } - } - - if (mPageIndex != -1) { - binding.galleryPageIndex.setText(String.format(Locale.ROOT, "%d/%d", (mPageIndex + 1), size)); - } - } - - private DirentModel getSelectedDirent() { - int index = binding.pager.getCurrentItem(); - return direntList.get(index); - } - - private void deleteFile() { - - int position = binding.pager.getCurrentItem(); - - DirentModel direntModel = getSelectedDirent(); - - DeleteFileDialogFragment dialogFragment = DeleteFileDialogFragment.newInstance(CollectionUtils.newArrayList(direntModel.uid)); - dialogFragment.setRefreshListener(new OnRefreshDataListener() { - @Override - public void onActionStatus(boolean isDone) { - if (isDone) { - isDataOperated = true; - - ToastUtils.showLong(R.string.delete_successful); - adapter.removeFragment(position); - adapter.notifyItemRemoved(position); - - if (adapter.getItemCount() == 0) { - setResult(RESULT_OK); - finish(); - } else { - String fs = String.format(Locale.ROOT, "%d/%d", (position + 1), adapter.getFragments().size()); - binding.galleryPageIndex.setText(fs); - } - } - } - }); - dialogFragment.show(getSupportFragmentManager(), DeleteFileDialogFragment.class.getSimpleName()); - } - - private void starFile() { - if (!NetworkUtils.isConnected()) { - ToastUtils.showLong(R.string.network_down); - return; - } - - DirentModel direntModel = getSelectedDirent(); - if (direntModel.starred) { - getViewModel().unStar(direntModel.repo_id, direntModel.full_path); - } else { - getViewModel().star(direntModel.repo_id, direntModel.full_path); - } - } - - private void shareFile() { - DirentModel direntModel = getSelectedDirent(); - Objs.showCreateShareLinkDialog(this, getSupportFragmentManager(), direntModel, false); - } - - - private void downloadFile() { - isDataOperated = true; - - DirentModel direntModel = getSelectedDirent(); - getViewModel().download(direntModel.repo_id, direntModel.full_path); - } - - - -} diff --git a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewViewModel.java index b3a380029..dc5d9a2d5 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewViewModel.java @@ -64,16 +64,14 @@ public void getFileDetail(String repoId, String path) { getRefreshLiveData().setValue(true); Single userSingle = HttpIO.getCurrentInstance().execute(DocsCommentService.class).getRelatedUsers(repoId); - Single metadataSingle = HttpIO.getCurrentInstance().execute(DocsCommentService.class).getMetadata(repoId); Single detailSingle = HttpIO.getCurrentInstance().execute(DocsCommentService.class).getFileDetail(repoId, path); - Single s = Single.zip(detailSingle, userSingle, metadataSingle, new Function3() { + Single s = Single.zip(detailSingle, userSingle, new BiFunction() { @Override - public FileProfileConfigModel apply(FileDetailModel docDetailModel, UserWrapperModel userWrapperModel, MetadataConfigModel metadataConfigModel) throws Exception { + public FileProfileConfigModel apply(FileDetailModel docDetailModel, UserWrapperModel userWrapperModel) throws Exception { FileProfileConfigModel configModel = new FileProfileConfigModel(); configModel.setDetail(docDetailModel); configModel.setUsers(userWrapperModel); - configModel.setMetadata(metadataConfigModel); return configModel; } }); @@ -86,34 +84,12 @@ public void accept(FileProfileConfigModel fileProfileConfigModel) throws Excepti } }, new Consumer() { @Override - public void accept(Throwable throwable) throws Exception { + public void accept(Throwable throwable) { getRefreshLiveData().setValue(false); } }); } - public void getRepoModelFromDB(String repoId, Consumer consumer) { - //from db - Single> singleDb = AppDatabase.getInstance().repoDao().getRepoById(repoId); - addSingleDisposable(singleDb, new Consumer>() { - @Override - public void accept(List repoModels) throws Exception { - if (consumer != null) { - if (CollectionUtils.isEmpty(repoModels)) { - //no data in sqlite, request RepoApi again - consumer.accept(null); - } else { - consumer.accept(repoModels.get(0)); - } - } - } - }, new Consumer() { - @Override - public void accept(Throwable throwable) throws Exception { - SLogs.e(throwable); - } - }); - } public void load(String repoId, String parentPath, String name, boolean isLoadOtherImagesInSameDirectory) { if (TextUtils.isEmpty(repoId) || TextUtils.isEmpty(parentPath) || TextUtils.isEmpty(name)) { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/PhotoFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/PhotoFragment.java index dd865c41c..db0aeff56 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/PhotoFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/PhotoFragment.java @@ -6,43 +6,47 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.webkit.MimeTypeMap; import android.widget.ImageView; -import android.widget.ProgressBar; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.lifecycle.Observer; -import com.blankj.utilcode.util.EncodeUtils; -import com.blankj.utilcode.util.SizeUtils; +import com.blankj.utilcode.util.EncryptUtils; +import com.blankj.utilcode.util.FileUtils; import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; +import com.bumptech.glide.load.resource.gif.GifDrawable; import com.bumptech.glide.request.RequestListener; -import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.Target; +import com.bumptech.glide.signature.ObjectKey; import com.github.chrisbanes.photoview.OnPhotoTapListener; import com.seafile.seadroid2.R; -import com.seafile.seadroid2.account.Account; -import com.seafile.seadroid2.account.SupportAccountManager; -import com.seafile.seadroid2.compat.ContextCompatKt; +import com.seafile.seadroid2.SeafException; +import com.seafile.seadroid2.config.OriGlideUrl; import com.seafile.seadroid2.databinding.FragmentPhotoViewBinding; import com.seafile.seadroid2.framework.data.db.entities.DirentModel; -import com.seafile.seadroid2.framework.datastore.DataManager; +import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; import com.seafile.seadroid2.framework.util.GlideApp; -import com.seafile.seadroid2.ui.base.fragment.BaseFragment; +import com.seafile.seadroid2.framework.util.GlideRequest; +import com.seafile.seadroid2.framework.util.SLogs; +import com.seafile.seadroid2.framework.util.ThumbnailUtils; +import com.seafile.seadroid2.ui.base.fragment.BaseFragmentWithVM; +import com.seafile.seadroid2.ui.media.PhotoViewModel; -import java.io.File; +import kotlin.Pair; -public class PhotoFragment extends BaseFragment { - - private Account account; +public class PhotoFragment extends BaseFragmentWithVM { private String repoId, repoName, fullPath; private String imageUrl; private OnPhotoTapListener onPhotoTapListener; private FragmentPhotoViewBinding binding; - + private String serverUrl; public void setOnPhotoTapListener(OnPhotoTapListener onPhotoTapListener) { this.onPhotoTapListener = onPhotoTapListener; @@ -56,35 +60,25 @@ public static PhotoFragment newInstance(String url) { return fragment; } - public static PhotoFragment newInstance(String repoId, String repoName, String fullPath) { - + public static PhotoFragment newInstance(String serverUrl, String repoId, String repoName, String fullPath) { Bundle args = new Bundle(); args.putString("repoId", repoId); args.putString("repoName", repoName); args.putString("fullPath", fullPath); - + args.putString("serverUrl", serverUrl); PhotoFragment fragment = new PhotoFragment(); fragment.setArguments(args); return fragment; } - public static PhotoFragment newInstance(DirentModel direntModel) { - Bundle args = new Bundle(); - - args.putString("repoId", direntModel.repo_id); - args.putString("repoName", direntModel.repo_name); - args.putString("fullPath", direntModel.full_path); - PhotoFragment fragment = new PhotoFragment(); - fragment.setArguments(args); - return fragment; + public static PhotoFragment newInstance(String serverUrl, DirentModel direntModel) { + return newInstance(serverUrl, direntModel.repo_id, direntModel.repo_name, direntModel.full_path); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - account = SupportAccountManager.getInstance().getCurrentAccount(); - Bundle args = getArguments(); if (args == null) { return; @@ -94,6 +88,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { repoName = args.getString("repoName"); fullPath = args.getString("fullPath"); imageUrl = args.getString("image_url"); + serverUrl = args.getString("serverUrl"); if (TextUtils.isEmpty(repoId) && TextUtils.isEmpty(imageUrl)) { throw new IllegalStateException("the args is invalid"); @@ -124,52 +119,83 @@ public void onPhotoTap(ImageView view, float x, float y) { } }); - loadImage(); - } + intViewModel(); - private void loadImage() { if (!TextUtils.isEmpty(imageUrl)) { - glideLoad(imageUrl); - return; + loadSingleUrl(imageUrl); + } else { + loadThumbnailAndRequestRawUrl(); } + } - File file = DataManager.getLocalRepoFile(account, repoId, repoName, fullPath); - if (file.exists()) { - binding.progressBar.setVisibility(View.GONE); + private void intViewModel() { + getViewModel().getSeafExceptionLiveData().observe(getViewLifecycleOwner(), new Observer() { + @Override + public void onChanged(SeafException e) { + binding.photoView.setImageResource(R.drawable.icon_image_error_filled); + binding.progressBar.setVisibility(View.GONE); + } + }); - GlideApp.with(requireContext()) - .load(file) - .into(binding.photoView); - return; - } + getViewModel().getCheckLocalLiveData().observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(Pair pair) { + DirentModel direntModel = pair.getFirst(); - String url = getUrl(); - if (url == null) { - binding.photoView.setImageResource(R.drawable.icon_image_error_filled); - return; - } + if (direntModel == null) { + binding.photoView.setImageResource(R.drawable.icon_image_error_filled); + binding.progressBar.setVisibility(View.GONE); + return; + } + + FileTransferEntity transferEntity = pair.getSecond(); + if (transferEntity != null && FileUtils.isFileExists(transferEntity.target_path)) { + //no exists local file + if (isGif(fullPath)) { + loadOriGifUrl(transferEntity.target_path); + } else { + loadOriUrl(transferEntity.target_path); + } + } else { + if (isGif(fullPath)) { + getViewModel().download(direntModel); + } else { + getViewModel().requestOriginalUrl(direntModel); + } + } + } + }); + + getViewModel().getOriginalUrlLiveData().observe(getViewLifecycleOwner(), new Observer() { + @Override + public void onChanged(String oriUrl) { + loadOriUrl(oriUrl); + } + }); - glideLoad(url); + getViewModel().getDownloadedPathLiveData().observe(getViewLifecycleOwner(), new Observer() { + @Override + public void onChanged(String rawPath) { + loadSingleUrl(rawPath); + } + }); } - private void glideLoad(String url) { - RequestOptions opt = new RequestOptions() - .skipMemoryCache(true) - .error(R.drawable.icon_image_error_filled) - .diskCacheStrategy(DiskCacheStrategy.NONE); - GlideApp.with(requireContext()) + private void loadSingleUrl(String url) { + GlideApp.with(this) .load(url) - .apply(opt) - .fitCenter() + .error(R.drawable.icon_image_error_filled) + .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) + .transition(DrawableTransitionOptions.withCrossFade()) .listener(new RequestListener() { @Override - public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { binding.progressBar.setVisibility(View.GONE); return false; } @Override - public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { binding.progressBar.setVisibility(View.GONE); return false; } @@ -177,20 +203,100 @@ public boolean onResourceReady(Drawable resource, Object model, Target .into(binding.photoView); } - private String getUrl() { - Account account = SupportAccountManager.getInstance().getCurrentAccount(); - if (account == null) { - return null; + private void loadThumbnailAndRequestRawUrl() { + getViewModel().checkLocal(repoId, fullPath); + } + + private void loadOriUrl(String oriUrl) { + String thumbnailUrl = convertThumbnailUrl(fullPath); + String thumbKey = EncryptUtils.encryptMD5ToString(thumbnailUrl); + // load thumbnail first + GlideRequest thumbnailRequest = GlideApp.with(this) + .load(thumbnailUrl) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .signature(new ObjectKey(thumbKey)) + .addListener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + return false; + } + + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { +// SLogs.e("缩略图:" + dataSource.name() + ": " + isFirstResource + ": " + thumbKey + ": " + thumbnailUrl); + return false; + } + }); + + String oriCacheKey = EncryptUtils.encryptMD5ToString(repoId + fullPath); + GlideApp.with(this) + .load(new OriGlideUrl(oriUrl, oriCacheKey)) + .thumbnail(thumbnailRequest) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .error(R.drawable.icon_image_error_filled) + .fallback(R.drawable.icon_image_error_filled) + .transition(DrawableTransitionOptions.withCrossFade()) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + binding.progressBar.setVisibility(View.GONE); + return false; + } + + @Override + public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + binding.progressBar.setVisibility(View.GONE); + // 图片加载成功 +// SLogs.e("原图:" + dataSource.name() + ": " + isFirstResource + ": " + oriCacheKey + ": " + oriUrl); + return false; + } + }) + .into(binding.photoView); + } + + + private void loadOriGifUrl(String rawUrl) { + GlideApp.with(this) + .asGif() + .load(rawUrl) + .diskCacheStrategy(DiskCacheStrategy.NONE)// + .error(R.drawable.icon_image_error_filled) + .fallback(R.drawable.icon_image_error_filled) + .placeholder(binding.photoView.getDrawable()) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target target, boolean isFirstResource) { + binding.progressBar.setVisibility(View.GONE); + return false; + } + + @Override + public boolean onResourceReady(@NonNull GifDrawable resource, @NonNull Object model, Target target, @NonNull DataSource dataSource, boolean isFirstResource) { + binding.progressBar.setVisibility(View.GONE); + // 图片加载成功 + SLogs.e(dataSource.name() + ": " + isFirstResource + ": " + rawUrl); + return false; + } + }) + .into(binding.photoView); + } + + private boolean isGif(String fileName) { + if (TextUtils.isEmpty(fileName)) { + return false; } -// //https://dev.seafile.com/seafhttp/repos/4809a6f3-250c-4435-bdd8-b68f34c128d1/files//6f64603fd19f9ec45d05ec379e69e22.gif/?op=download -// //https://dev.seafile.com/seahub/repo/4809a6f3-250c-4435-bdd8-b68f34c128d1/raw/6f64603fd19f9ec45d05ec379e69e22.gif -// if (direntModel.name.toLowerCase().endsWith(".gif")) { -// return String.format(Locale.ROOT, "%srepo/%s/raw/%s", account.getServer(), direntModel.repo_id, direntModel.name); -// } + String f = MimeTypeMap.getFileExtensionFromUrl(fileName); + String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(f); + return mime != null && mime.equalsIgnoreCase("image/gif"); + } -// return String.format("%srepo/%s/raw%s", account.getServer(), repoId, fileFullPath); - int size = SizeUtils.dp2px(300); - return String.format("%sapi2/repos/%s/thumbnail/?p=%s&size=%s", account.getServer(), repoId, EncodeUtils.urlEncode(fullPath), size); + private String convertThumbnailUrl(String fullPath) { + if (TextUtils.isEmpty(serverUrl)) { + return null; + } + + return ThumbnailUtils.convertThumbnailUrl(serverUrl, repoId, fullPath); } + } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/CarouselAdapter.java b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/CarouselAdapter.java index 04e15a79a..9b6e2a5b4 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/CarouselAdapter.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/CarouselAdapter.java @@ -31,19 +31,25 @@ import androidx.recyclerview.widget.RecyclerView; import com.blankj.utilcode.util.EncodeUtils; +import com.blankj.utilcode.util.EncryptUtils; import com.blankj.utilcode.util.ScreenUtils; import com.blankj.utilcode.util.SizeUtils; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.signature.ObjectKey; import com.google.common.base.Strings; import com.seafile.seadroid2.R; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; import com.seafile.seadroid2.framework.data.db.entities.DirentModel; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.GlideApp; +import com.seafile.seadroid2.framework.util.ThumbnailUtils; public class CarouselAdapter extends ListAdapter { + boolean isLogin = SupportAccountManager.getInstance().isLogin(); + private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback<>() { @Override @@ -129,40 +135,49 @@ private void bind(CarouselItemViewHolder holder, int pos) { holder.imageView.setVisibility(View.VISIBLE); holder.itemView.getLayoutParams().width = itemWidth; - String url = getUrl(model.repo_id, model.full_path); - if (url == null) { - holder.imageView.setImageResource(R.drawable.icon_image_error_filled); + + String thumbnailUrl = convertThumbnailUrl(model.repo_id, model.full_path); + if (TextUtils.isEmpty(thumbnailUrl)) { + holder.imageView.setImageResource(R.drawable.shape_solid_grey100_radius_4); return; } - - RequestOptions opt = new RequestOptions() - .skipMemoryCache(true) - .error(R.drawable.icon_image_error_filled) - .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC); + String thumbKey = EncryptUtils.encryptMD5ToString(thumbnailUrl); GlideApp.with(holder.imageView) - .load(url) + .load(thumbnailUrl) + .signature(new ObjectKey(thumbKey)) .apply(opt) .fitCenter() .into(holder.imageView); } - private String getUrl(String repoId, String fullPath) { - Account account = SupportAccountManager.getInstance().getCurrentAccount(); - if (account == null) { + private final RequestOptions opt = new RequestOptions() + .placeholder(R.drawable.shape_solid_grey100_radius_4) + .fallback(R.drawable.shape_solid_grey100_radius_4) + .error(R.drawable.icon_format_pic) + .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC); + + private String convertThumbnailUrl(String repoId, String fullPath) { + String serverUrl = getServerUrl(); + if (TextUtils.isEmpty(serverUrl)) { return null; } -// //https://dev.seafile.com/seafhttp/repos/4809a6f3-250c-4435-bdd8-b68f34c128d1/files//6f64603fd19f9ec45d05ec379e69e22.gif/?op=download -// //https://dev.seafile.com/seahub/repo/4809a6f3-250c-4435-bdd8-b68f34c128d1/raw/6f64603fd19f9ec45d05ec379e69e22.gif -// if (direntModel.name.toLowerCase().endsWith(".gif")) { -// return String.format(Locale.ROOT, "%srepo/%s/raw/%s", account.getServer(), direntModel.repo_id, direntModel.name); -// } - -// return String.format("%srepo/%s/raw%s", account.getServer(), repoId, fileFullPath); - int size = SizeUtils.dp2px(300); - return String.format("%sapi2/repos/%s/thumbnail/?p=%s&size=%s", account.getServer(), repoId, EncodeUtils.urlEncode(fullPath), size); + return ThumbnailUtils.convertThumbnailUrl(serverUrl, repoId, fullPath); } + private String serverUrl; + + private String getServerUrl() { + if (!isLogin) { + return null; + } + + if (TextUtils.isEmpty(serverUrl)) { + serverUrl = HttpIO.getCurrentInstance().getServerUrl(); + } + + return serverUrl; + } public static class CarouselItemViewHolder extends RecyclerView.ViewHolder { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/CarouselImagePreviewActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/CarouselImagePreviewActivity.java index 0a4f0bb6f..7ea911e93 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/CarouselImagePreviewActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/CarouselImagePreviewActivity.java @@ -3,6 +3,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; import android.os.Bundle; import android.text.TextUtils; import android.util.Pair; @@ -21,13 +22,16 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager2.widget.ViewPager2; import com.blankj.utilcode.util.BarUtils; import com.blankj.utilcode.util.CollectionUtils; import com.blankj.utilcode.util.NetworkUtils; +import com.blankj.utilcode.util.ScreenUtils; import com.blankj.utilcode.util.ToastUtils; import com.seafile.seadroid2.R; +import com.seafile.seadroid2.account.SupportAccountManager; import com.seafile.seadroid2.compat.ContextCompatKt; import com.seafile.seadroid2.context.CopyMoveContext; import com.seafile.seadroid2.databinding.ActivityCarouselImagePreviewBinding; @@ -38,7 +42,9 @@ import com.seafile.seadroid2.framework.data.model.activities.ActivityModel; import com.seafile.seadroid2.framework.data.model.sdoc.FileProfileConfigModel; import com.seafile.seadroid2.framework.data.model.search.SearchModel; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.Objs; +import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.util.Utils; import com.seafile.seadroid2.ui.adapter.ViewPager2Adapter; import com.seafile.seadroid2.ui.base.BaseActivityWithVM; @@ -49,6 +55,7 @@ import com.seafile.seadroid2.ui.media.image_preview.ImagePreviewViewModel; import com.seafile.seadroid2.ui.media.image_preview.PhotoFragment; import com.seafile.seadroid2.ui.selector.ObjSelectorActivity; +import com.seafile.seadroid2.view.snap_recyclerview.GravitySnapHelper; import java.util.ArrayList; import java.util.HashMap; @@ -62,11 +69,14 @@ public class CarouselImagePreviewActivity extends BaseActivityWithVM direntList; private List carouselDirentList; - private boolean isLightMode = true; + private boolean isHide = false; + private boolean isNightMode = false; private boolean isDataOperated = false; private String repoId, repoName, parentDir, name; private boolean load_other_images_in_same_directory = false; + private int carouselItemWidth = 0; + private int carouselItemMargin = 0; // public static Intent startThisFromDocsComment(Context context, String url) { // Intent intent = new Intent(context, CarouselImagePreviewActivity.class); @@ -125,15 +135,22 @@ protected void onCreate(Bundle savedInstanceState) { // full screen getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + isNightMode = currentNightMode == Configuration.UI_MODE_NIGHT_YES; + //init status bar - int color = ContextCompatKt.getColorCompat(this, R.color.material_grey_100_translucent); + int color = ContextCompatKt.getColorCompat(this, R.color.bar_background_color); BarUtils.setStatusBarColor(this, color); - BarUtils.setStatusBarLightMode(this, isLightMode); + BarUtils.setStatusBarLightMode(this, !isNightMode); //init toolbar margin top ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) binding.toolbarActionbar.getLayoutParams(); layoutParams.topMargin = BarUtils.getStatusBarHeight(); + + carouselItemWidth = getResources().getDimensionPixelSize(R.dimen.carousel_item_width); + carouselItemMargin = getResources().getDimensionPixelSize(R.dimen.carousel_item_margin); + Toolbar toolbar = getActionBarToolbar(); setSupportActionBar(toolbar); if (getSupportActionBar() != null) { @@ -162,9 +179,10 @@ public void handleOnBackPressed() { initView(); - initAdapter(); - initCarouselAdapter(); - bindPager(); + initPager(); + initCarouselRecyclerView(); + +// bindPager(); initViewModel(); @@ -260,7 +278,7 @@ public void onChanged(FileProfileConfigModel configModel) { }); } - private void initAdapter() { + private void initPager() { adapter = new ViewPager2Adapter(this); binding.pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override @@ -269,13 +287,20 @@ public void onPageSelected(int position) { notifyCurrentStarredStatus(); } }); + binding.pager.setOffscreenPageLimit(7); binding.pager.setAdapter(adapter); + + // + if (isNightMode) { + int color = ContextCompatKt.getColorCompat(this, R.color.bar_background_color); + binding.pager.setBackgroundColor(color); + } } private final LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); private final GravitySnapHelper gravitySnapHelper = new GravitySnapHelper(Gravity.CENTER); - private void initCarouselAdapter() { + private void initCarouselRecyclerView() { carouselAdapter = new CarouselAdapter(this, new CarouselAdapter.CarouselItemListener() { @Override public void onItemClicked(DirentModel item, int snapPosition) { @@ -288,11 +313,49 @@ public void onItemClicked(DirentModel item, int snapPosition) { binding.recyclerView.addOnScrollListener(new CenterScaleXYRecyclerViewScrollListener(this)); + int screenWidth = ScreenUtils.getAppScreenWidth();//1080/3=360 + int sidePadding = (screenWidth - carouselItemWidth) / 2 - carouselItemMargin * 2;//170-4=166 + binding.recyclerView.addItemDecoration(new LinearEdgeDecoration(sidePadding, sidePadding, RecyclerView.HORIZONTAL, false)); + gravitySnapHelper.attachToRecyclerView(binding.recyclerView); } private void bindPager() { - PagerSnapBinders.bind(binding.pager, gravitySnapHelper); + bindPager(binding.pager, gravitySnapHelper); + } + + private int whoScroll = -1; + + public void bindPager(ViewPager2 pager, GravitySnapHelper snapHelper) { + pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + if (whoScroll == 1) { + whoScroll = -1; + return; + } + + whoScroll = 0; + SLogs.e("currentPagerPosition: " + position); + snapHelper.smoothScrollToPosition(position); + } + }); + + snapHelper.setSnapListener(new GravitySnapHelper.SnapListener() { + @Override + public void onSnap(int snapPosition) { + + if (whoScroll == 0) { + whoScroll = -1; + return; + } + + whoScroll = 1; + SLogs.e("currentSnapPosition: " + snapPosition); + pager.setCurrentItem(snapPosition, false); + } + }); } private void submitData(Pair> pair) { @@ -311,10 +374,9 @@ private void submitData(Pair> pair) { direntList = pair.second; // - List fragments = new ArrayList<>(); for (DirentModel direntModel : direntList) { - PhotoFragment photoFragment = PhotoFragment.newInstance(direntModel); + PhotoFragment photoFragment = PhotoFragment.newInstance(getServerUrl(), direntModel); photoFragment.setOnPhotoTapListener((view, x, y) -> hideOrShowToolBar()); fragments.add(photoFragment); } @@ -323,9 +385,9 @@ private void submitData(Pair> pair) { adapter.notifyItemRangeInserted(0, direntList.size()); carouselDirentList = new ArrayList<>(); - carouselDirentList.add(new DirentModel()); +// carouselDirentList.add(new DirentModel()); carouselDirentList.addAll(direntList); - carouselDirentList.add(new DirentModel()); +// carouselDirentList.add(new DirentModel()); carouselAdapter.submitList(carouselDirentList); @@ -334,27 +396,44 @@ private void submitData(Pair> pair) { public void run() { navToSelectedPage(); } - }, 100); + }, 50); + } + + private String server_url; + private final boolean isLogin = SupportAccountManager.getInstance().isLogin(); + + private String getServerUrl() { + if (!TextUtils.isEmpty(server_url)) { + return server_url; + } + + if (!isLogin) { + return null; + } + + server_url = HttpIO.getCurrentInstance().getServerUrl(); + return server_url; } private void hideOrShowToolBar() { - binding.galleryToolBar.setVisibility(isLightMode ? View.GONE : View.VISIBLE); - binding.recyclerView.setVisibility(isLightMode ? View.GONE : View.VISIBLE); - binding.toolbarActionbar.setVisibility(isLightMode ? View.INVISIBLE : View.VISIBLE); + binding.galleryToolBar.setVisibility(isHide ? View.VISIBLE : View.GONE); + binding.recyclerView.setVisibility(isHide ? View.VISIBLE : View.GONE); + binding.toolbarActionbar.setVisibility(isHide ? View.VISIBLE : View.INVISIBLE); - if (isLightMode) { - binding.pager.setBackgroundColor(ContextCompatKt.getColorCompat(this, R.color.material_grey_919)); + if (isNightMode) { + //The background color has been set in the #initPager(), and no longer updated in night mode } else { - binding.pager.setBackgroundColor(ContextCompatKt.getColorCompat(this, R.color.material_grey_100)); - } + int color = ContextCompatKt.getColorCompat(this, isHide ? R.color.material_grey_100 : R.color.material_grey_911); + binding.pager.setBackgroundColor(color); - int color = ContextCompatKt.getColorCompat(this, isLightMode ? R.color.material_grey_919_ff : R.color.material_grey_100_translucent); - BarUtils.setNavBarColor(this, color); - BarUtils.setStatusBarColor(this, color); - BarUtils.setStatusBarLightMode(this, !isLightMode); - BarUtils.setNavBarLightMode(this, !isLightMode); + BarUtils.setNavBarColor(this, color); + BarUtils.setStatusBarColor(this, color); + + BarUtils.setStatusBarLightMode(this, isHide); + BarUtils.setNavBarLightMode(this, isHide); + } - isLightMode = !isLightMode; + isHide = !isHide; } /** @@ -373,8 +452,12 @@ private void navToSelectedPage() { } if (index != -1) { - binding.pager.setCurrentItem(index); + int x = (carouselItemWidth + carouselItemMargin * 2) * index; + binding.recyclerView.scrollBy(x, 0); + binding.pager.setCurrentItem(index, false); } + + bindPager(); } private void notifyCurrentStarredStatus() { @@ -384,9 +467,9 @@ private void notifyCurrentStarredStatus() { } if (direntModel.starred) { - binding.galleryStarPhoto.setImageResource(R.drawable.baseline_star_filled_24); + binding.galleryStarPhoto.setImageResource(R.drawable.baseline_starred_filled_24); } else { - binding.galleryStarPhoto.setImageResource(R.drawable.baseline_star_outline_24); + binding.galleryStarPhoto.setImageResource(R.drawable.baseline_starred_outline_24); } } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/PagerSnapBinders.java b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/PagerSnapBinders.java deleted file mode 100644 index 0d227c48d..000000000 --- a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/PagerSnapBinders.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.seafile.seadroid2.ui.media.image_preview2; - -import androidx.viewpager2.widget.ViewPager2; - -import com.seafile.seadroid2.framework.util.SLogs; - -public class PagerSnapBinders { - private static int whoScroll = -1; - - public static void bind(ViewPager2 pager, GravitySnapHelper snapHelper) { - - pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { - @Override - public void onPageSelected(int position) { - super.onPageSelected(position); - if (whoScroll == 1) { - whoScroll = -1; - return; - } - - whoScroll = 0; - - SLogs.e("currentPagerPosition: " + position); - position++; - snapHelper.smoothScrollToPosition(position); - } - }); - - snapHelper.setSnapListener(new GravitySnapHelper.SnapListener() { - @Override - public void onSnap(int snapPosition) { - - if (whoScroll == 0) { - whoScroll = -1; - return; - } - - whoScroll = 1; - - SLogs.e("currentSnapPosition: " + snapPosition); - - snapPosition--; - pager.setCurrentItem(snapPosition, true); - } - }); - } - -} diff --git a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickAdapter.java b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickAdapter.java index 929eee320..eb57606ed 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickAdapter.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickAdapter.java @@ -16,8 +16,13 @@ import com.blankj.utilcode.util.CollectionUtils; import com.blankj.utilcode.util.EncodeUtils; +import com.blankj.utilcode.util.EncryptUtils; import com.blankj.utilcode.util.FileUtils; import com.blankj.utilcode.util.SizeUtils; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.signature.ObjectKey; import com.seafile.seadroid2.R; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; @@ -41,6 +46,8 @@ import com.seafile.seadroid2.framework.data.model.search.SearchModel; import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.GlideApp; +import com.seafile.seadroid2.framework.util.GlideOptions; +import com.seafile.seadroid2.framework.util.ThumbnailUtils; import com.seafile.seadroid2.framework.util.Utils; import com.seafile.seadroid2.ui.base.adapter.BaseMultiAdapter; import com.seafile.seadroid2.ui.repo.vh.AccountViewHolder; @@ -369,7 +376,7 @@ private void onBindDirents(DirentViewHolder holder, DirentModel model, @NonNull // holder.binding.getRoot().setBackground(AnimatedStateListDrawableCompatUtils.createDrawableCompat(getContext())); - if (repoEncrypted || !Utils.isViewableImage(model.name)) { + if (model.isDir() || repoEncrypted || (!Utils.isViewableImage(model.name) && !Utils.isVideoFile(model.name))) { holder.binding.itemIcon.setImageResource(model.getIcon()); } else { loadImage(model, holder.binding.itemIcon); @@ -473,7 +480,7 @@ private void onBindDirentsGrid(DirentGridViewHolder holder, DirentModel model, @ holder.binding.itemOutline.setVisibility(View.VISIBLE); } - if (repoEncrypted || !Utils.isViewableImage(model.name)) { + if (model.isDir() || repoEncrypted || (!Utils.isViewableImage(model.name) && !Utils.isVideoFile(model.name))) { holder.binding.itemIcon.setScaleType(ImageView.ScaleType.FIT_CENTER); holder.binding.itemIcon.setImageResource(model.getIcon()); } else { @@ -521,7 +528,7 @@ private void onBindDirentsGallery(DirentGalleryViewHolder holder, DirentModel mo } // holder.binding.getRoot().setBackground(AnimatedStateListDrawableCompatUtils.createDrawableCompat(getContext())); - if (repoEncrypted || !Utils.isViewableImage(model.name)) { + if (model.isDir() || repoEncrypted || (!Utils.isViewableImage(model.name) && !Utils.isVideoFile(model.name))) { holder.binding.itemIcon.setImageResource(model.getIcon()); } else { loadImage(model, holder.binding.itemIcon); @@ -551,7 +558,8 @@ private void onBindSearch(DirentViewHolder holder, SearchModel model) { // holder.binding.getRoot().setBackground(AnimatedStateListDrawableCompatUtils.createDrawableCompat(getContext())); - if (repoEncrypted || !Utils.isViewableImage(model.name)) { + + if (repoEncrypted || (!Utils.isViewableImage(model.name) && !Utils.isVideoFile(model.name))) { holder.binding.itemIcon.setImageResource(model.getIcon()); } else { DirentModel direntModel = new DirentModel(); @@ -570,39 +578,32 @@ private void onBindSearch(DirentViewHolder holder, SearchModel model) { } private void loadImage(DirentModel direntModel, ImageView imageView) { - - if (direntModel.name.toLowerCase().endsWith(".gif")) { - imageView.setImageResource(direntModel.getIcon()); - } else { - String url = convertThumbnailUrl(direntModel); - if (TextUtils.isEmpty(url)) { - GlideApp.with(getContext()).load(R.drawable.file_image) - .apply(GlideLoadConfig.getOptions()) - .into(imageView); - } else { - GlideApp.with(getContext()).load(url) - .apply(GlideLoadConfig.getOptions()) - .into(imageView); - } + String thumbnailUrl = convertThumbnailUrl(direntModel); + if (TextUtils.isEmpty(thumbnailUrl)) { + GlideApp.with(getContext()) + .load(direntModel.getIcon()) + .apply(GlideLoadConfig.getCacheableThumbnailOptions()) + .into(imageView); + return; } - } - private String convertThumbnailUrl(DirentModel direntModel) { - return convertThumbnailUrl(direntModel, 128); - } + String thumbKey = EncryptUtils.encryptMD5ToString(thumbnailUrl); - private String convertMiddleUrl(DirentModel direntModel) { - return convertThumbnailUrl(direntModel, 256); + GlideApp.with(getContext()) + .load(thumbnailUrl) + .signature(new ObjectKey(thumbKey)) + .apply(GlideLoadConfig.getCustomDrawableOptions(direntModel.getIcon())) + .into(imageView); } private String server_url; + private final boolean isLogin = SupportAccountManager.getInstance().isLogin(); private String getServerUrl() { if (!TextUtils.isEmpty(server_url)) { return server_url; } - boolean isLogin = SupportAccountManager.getInstance().isLogin(); if (!isLogin) { return null; } @@ -611,14 +612,12 @@ private String getServerUrl() { return server_url; } - private String convertThumbnailUrl(DirentModel direntModel, int size) { + private String convertThumbnailUrl(DirentModel direntModel) { String serverUrl = getServerUrl(); if (TextUtils.isEmpty(serverUrl)) { return null; } - - String newFilePath = EncodeUtils.urlEncode(direntModel.full_path); - return String.format(Locale.ROOT, "%sapi2/repos/%s/thumbnail/?p=%s&size=%d", serverUrl, direntModel.repo_id, newFilePath, size); + return ThumbnailUtils.convertThumbnailUrl(serverUrl, direntModel.repo_id, direntModel.full_path); } public void setOnActionMode(boolean on) { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickFragment.java index 5a1bdbce3..e6bec8389 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickFragment.java @@ -150,7 +150,11 @@ public void onCreate(@Nullable Bundle savedInstanceState) { @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = LayoutFastRvBinding.inflate(inflater, container, false); - binding.swipeRefreshLayout.setOnRefreshListener(() -> loadData(true)); + + binding.swipeRefreshLayout.setOnRefreshListener(() -> { + removeScrolledPosition(); + loadData(true); + }); return binding.getRoot(); } @@ -172,9 +176,19 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat @Override public void onFirstResume() { super.onFirstResume(); + + restoreNavContext(); + loadData(true); } + private void restoreNavContext() { + NavContext navContext = getNavContext(); + navContext.restoreNavContextFromSp(); + mainViewModel.getOnNavContextChangeListenerLiveData().setValue(true); + } + + @Override public void onOtherResume() { super.onOtherResume(); @@ -192,6 +206,15 @@ private void initRv() { .build(); binding.rv.addItemDecoration(decoration); + binding.rv.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + if (RecyclerView.SCROLL_STATE_IDLE == newState){ + saveScrollPosition(); + } + } + }); //layout manager binding.rv.setLayoutManager(getGridLayoutManager()); @@ -299,7 +322,6 @@ private void initAdapter() { binding.rv.setAdapter(adapter); } - private void initViewModel() { getViewModel().getRefreshLiveData().observe(getViewLifecycleOwner(), new Observer() { @Override @@ -713,7 +735,7 @@ private void switchRecyclerViewLayout(FileViewType newViewType) { } //If SPAN_COUNT is updated, then the data in the ScrollPosition is meaningless - removeScrollPositionExcludeRoot(); + removeScrolledPositionExcludeRoot(); lastViewType = newViewType; } @@ -811,7 +833,6 @@ public void loadData() { } public void loadData(boolean forceRefresh) { - removeCurrentScrollPosition(); if (forceRefresh) { long now = TimeUtils.getNowMills(); @@ -855,9 +876,9 @@ private void showEmptyTip() { if (FileViewType.GALLERY == type) { showErrorView(R.string.no_album_type_data); } else if (getNavContext().inRepo()) { - showErrorView(R.string.no_repo); - } else { showErrorView(R.string.dir_empty); + } else { + showErrorView(R.string.no_repo); } } @@ -877,6 +898,12 @@ private void showErrorView(int textRes) { private void showErrorView(String msg) { adapter.submitList(null); TextView tipView = TipsViews.getTipTextView(requireContext()); + tipView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + reloadData(); + } + }); tipView.setText(msg); adapter.setStateView(tipView); adapter.setStateViewEnable(true); @@ -884,8 +911,6 @@ private void showErrorView(String msg) { private void navTo(BaseModel model) { //save - saveScrollPosition(); - if (model instanceof RepoModel model1) { getNavContext().push(model1); @@ -928,7 +953,7 @@ public boolean backTo() { adapter.setOnActionMode(false); } else { // - removeCurrentScrollPosition(); + removeScrolledPosition(); // getNavContext().pop(); getViewModel().loadData(getNavContext(), false); @@ -986,8 +1011,9 @@ private void saveScrollPosition() { final int index = gridLayoutManager.findFirstVisibleItemPosition(); final ScrollState state = new ScrollState(index, top); + SLogs.d(state.toString()); - removeCurrentScrollPosition(); + removeScrolledPosition(); if (!getNavContext().inRepo()) { scrollPositions.put(KEY_REPO_SCROLL_POSITION, state); @@ -997,7 +1023,7 @@ private void saveScrollPosition() { } } - private void removeScrollPositionExcludeRoot() { + private void removeScrolledPositionExcludeRoot() { if (!scrollPositions.isEmpty()) { ScrollState rootState = scrollPositions.get(KEY_REPO_SCROLL_POSITION); scrollPositions.clear(); @@ -1005,7 +1031,7 @@ private void removeScrollPositionExcludeRoot() { } } - private void removeCurrentScrollPosition() { + private void removeScrolledPosition() { if (!getNavContext().inRepo()) { scrollPositions.remove(KEY_REPO_SCROLL_POSITION); } else { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoViewModel.java index e80886d16..337e89a97 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoViewModel.java @@ -240,6 +240,7 @@ public void accept(List direntModels) throws Exception { private void loadDirentsFromRemote(Account account, NavContext context) { if (!NetworkUtils.isConnected()) { getRefreshLiveData().setValue(false); + getSeafExceptionLiveData().setValue(SeafException.networkException); return; } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/repo/ScrollState.java b/app/src/main/java/com/seafile/seadroid2/ui/repo/ScrollState.java index 9951c5e6e..be6c0d0d4 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/repo/ScrollState.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/repo/ScrollState.java @@ -8,4 +8,12 @@ public ScrollState(int index, int top) { this.index = index; this.top = top; } + + @Override + public String toString() { + return "ScrollState{" + + "index=" + index + + ", top=" + top + + '}'; + } } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/sdoc/DocsCommentService.java b/app/src/main/java/com/seafile/seadroid2/ui/sdoc/DocsCommentService.java index 19f3df13d..f411fe28e 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/sdoc/DocsCommentService.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/sdoc/DocsCommentService.java @@ -38,7 +38,7 @@ public interface DocsCommentService { Single getMetadata(@Path("repo_id") String repoId); @GET("api/v2.1/repos/{repo_id}/metadata/record/") - Single getRecords(@Path("repo_id") String repoId, @Query("parent_dir") String parentDir, @Query("name") String name); + Single getRecords(@Path("repo_id") String repoId, @Query("parent_dir") String parentDir, @Query("name") String name, @Query("file_name") String fileName); // @GET("api/v1/docs/{uuid}/comment/") diff --git a/app/src/main/java/com/seafile/seadroid2/ui/sdoc/SDocViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/sdoc/SDocViewModel.java new file mode 100644 index 000000000..602210998 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/ui/sdoc/SDocViewModel.java @@ -0,0 +1,201 @@ +package com.seafile.seadroid2.ui.sdoc; + +import android.text.TextUtils; + +import androidx.lifecycle.MutableLiveData; + +import com.blankj.utilcode.util.CloneUtils; +import com.blankj.utilcode.util.CollectionUtils; +import com.seafile.seadroid2.account.Account; +import com.seafile.seadroid2.account.SupportAccountManager; +import com.seafile.seadroid2.framework.data.model.sdoc.FileDetailModel; +import com.seafile.seadroid2.framework.data.model.sdoc.FileProfileConfigModel; +import com.seafile.seadroid2.framework.data.model.sdoc.FileRecordWrapperModel; +import com.seafile.seadroid2.framework.data.model.sdoc.MetadataConfigModel; +import com.seafile.seadroid2.framework.data.model.sdoc.OutlineItemModel; +import com.seafile.seadroid2.framework.data.model.sdoc.SDocOutlineWrapperModel; +import com.seafile.seadroid2.framework.data.model.sdoc.SDocPageOptionsModel; +import com.seafile.seadroid2.framework.data.model.user.UserWrapperModel; +import com.seafile.seadroid2.framework.http.HttpIO; +import com.seafile.seadroid2.framework.util.SLogs; +import com.seafile.seadroid2.framework.util.StringUtils; +import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import io.reactivex.Single; +import io.reactivex.functions.BiFunction; +import io.reactivex.functions.Consumer; +import io.reactivex.functions.Function3; + +public class SDocViewModel extends BaseViewModel { + + private final MutableLiveData _fileProfileConfigLiveData = new MutableLiveData<>(); + private final MutableLiveData _fileRecordLiveData = new MutableLiveData<>(); + private final MutableLiveData> _sdocElementListLiveData = new MutableLiveData<>(); + + public MutableLiveData getFileDetailLiveData() { + return _fileProfileConfigLiveData; + } + + public MutableLiveData getSdocRecordLiveData() { + return _fileRecordLiveData; + } + + public MutableLiveData> getSdocElementLiveData() { + return _sdocElementListLiveData; + } + + public void loadFileDetail(String repoId, String path, boolean isMetadataEnable) { + + Single userSingle = HttpIO.getCurrentInstance().execute(DocsCommentService.class).getRelatedUsers(repoId); + + //Even if isMetadataEnable is enabled, you still need to check whether the enable field of MetadataConfigModel is available + Single metadataSingle = HttpIO.getCurrentInstance().execute(DocsCommentService.class).getMetadata(repoId); + Single detailSingle = HttpIO.getCurrentInstance().execute(DocsCommentService.class).getFileDetail(repoId, path); + + Single s; + if (isMetadataEnable) { + s = Single.zip(detailSingle, userSingle, metadataSingle, new Function3() { + @Override + public FileProfileConfigModel apply(FileDetailModel fileDetailModel, UserWrapperModel userWrapperModel, MetadataConfigModel metadataConfigModel) throws Exception { + FileProfileConfigModel configModel = new FileProfileConfigModel(); + configModel.setDetail(fileDetailModel); + configModel.setUsers(userWrapperModel); + configModel.setMetadataConfigModel(metadataConfigModel); + return configModel; + } + }); + } else { + s = Single.zip(detailSingle, userSingle, new BiFunction() { + @Override + public FileProfileConfigModel apply(FileDetailModel fileDetailModel, UserWrapperModel userWrapperModel) throws Exception { + FileProfileConfigModel configModel = new FileProfileConfigModel(); + configModel.setDetail(fileDetailModel); + configModel.setUsers(userWrapperModel); + return configModel; + } + }); + } + + addSingleDisposable(s, new Consumer() { + @Override + public void accept(FileProfileConfigModel fileProfileConfigModel) throws Exception { + getFileDetailLiveData().setValue(fileProfileConfigModel); + } + }); + } + + public void loadRecords(String repoId, String path) { + if (TextUtils.isEmpty(path) || TextUtils.equals("/", path)) { + return; + } + + String parent_dir; + String name; + + // 1. /a/b/c/t.txt + // 2. /a/t.txt + // 3. /t.txt + // 4. t.txt + // 5. / + if (path.contains("/")) { + parent_dir = path.substring(0, path.lastIndexOf("/")); + name = path.substring(path.lastIndexOf("/") + 1); + } else { + parent_dir = null; + name = path; + } + + if (TextUtils.isEmpty(parent_dir)) { + parent_dir = "/"; + } + + Single single = HttpIO.getCurrentInstance().execute(DocsCommentService.class).getRecords(repoId, parent_dir, name, name); + addSingleDisposable(single, new Consumer() { + @Override + public void accept(FileRecordWrapperModel fileRecordWrapperModel) throws Exception { + getSdocRecordLiveData().setValue(fileRecordWrapperModel); + } + }); + } + + public static final List _AllowedElementTypes = List.of("header1", "header2", "header3"); + + public void loadSdocElements(SDocPageOptionsModel pageOptionsModel) { + if (TextUtils.isEmpty(pageOptionsModel.seadocServerUrl)) { + return; + } + getRefreshLiveData().setValue(true); + + String sdocServerUrl = pageOptionsModel.seadocServerUrl; + if (!sdocServerUrl.endsWith("/")) { + sdocServerUrl = sdocServerUrl + "/"; + } + + Account curAccount = SupportAccountManager.getInstance().getCurrentAccount(); + Account partialAccount = CloneUtils.deepClone(curAccount, Account.class); + partialAccount.setToken(pageOptionsModel.seadocAccessToken); + partialAccount.setServer(sdocServerUrl); + + Single single = HttpIO.getInstanceByAccount(partialAccount).execute(DocsCommentService.class).getElements(pageOptionsModel.docUuid); + addSingleDisposable(single, new Consumer() { + @Override + public void accept(SDocOutlineWrapperModel wrapperModel) throws Exception { + + if (wrapperModel == null || wrapperModel.elements == null) { + getSdocElementLiveData().setValue(null); + return; + } + + List newList = wrapperModel.elements.stream().filter(new Predicate() { + @Override + public boolean test(OutlineItemModel sDocModel) { + if (!_AllowedElementTypes.contains(sDocModel.type)) { + return false; + } + + if (TextUtils.isEmpty(sDocModel.text) && CollectionUtils.isEmpty(sDocModel.children)) { + return false; + } + + return true; + } + }).map(new Function() { + @Override + public OutlineItemModel apply(OutlineItemModel sDocModel) { + if (!TextUtils.isEmpty(sDocModel.text)) { + return sDocModel; + } + + if (CollectionUtils.isEmpty(sDocModel.children)) { + return sDocModel; + } + + String text = ""; + for (OutlineItemModel child : sDocModel.children) { + if (!TextUtils.isEmpty(child.text)) { + String nt = StringUtils.trim(child.text, "\n").trim(); + text = text.concat(nt); + } + } + sDocModel.text = text; + return sDocModel; + } + }).collect(Collectors.toList()); + + getSdocElementLiveData().setValue(newList); + getRefreshLiveData().setValue(false); + } + }, new Consumer() { + @Override + public void accept(Throwable throwable) throws Exception { + SLogs.e(throwable); + getRefreshLiveData().setValue(false); + } + }); + } +} diff --git a/app/src/main/java/com/seafile/seadroid2/ui/sdoc/SDocWebViewActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/sdoc/SDocWebViewActivity.java index b992c2ff6..2b3430698 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/sdoc/SDocWebViewActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/sdoc/SDocWebViewActivity.java @@ -27,7 +27,6 @@ import com.seafile.seadroid2.R; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; -import com.seafile.seadroid2.annotation.Todo; import com.seafile.seadroid2.databinding.ActivitySeaWebviewProBinding; import com.seafile.seadroid2.databinding.ToolbarActionbarProgressBarBinding; import com.seafile.seadroid2.enums.WebViewPreviewType; @@ -42,11 +41,11 @@ import com.seafile.seadroid2.ui.docs_comment.DocsCommentsActivity; import com.seafile.seadroid2.ui.file_profile.FileProfileDialog; import com.seafile.seadroid2.ui.sdoc.outline.SDocOutlineDialog; +import com.seafile.seadroid2.view.webview.OnWebPageListener; import com.seafile.seadroid2.view.webview.PreloadWebView; import com.seafile.seadroid2.view.webview.SeaWebView; -@Todo -public class SDocWebViewActivity extends BaseActivityWithVM { +public class SDocWebViewActivity extends BaseActivityWithVM { private ActivitySeaWebviewProBinding binding; private ToolbarActionbarProgressBarBinding toolBinding; @@ -56,6 +55,7 @@ public class SDocWebViewActivity extends BaseActivityWithVM() { @Override public void onChanged(FileRecordWrapperModel fileRecordWrapperModel) { @@ -196,7 +202,6 @@ public void onChanged(FileRecordWrapperModel fileRecordWrapperModel) { }); } - private void showOutlineDialog() { readSDocOutlineList(new Consumer() { @Override @@ -229,12 +234,22 @@ private void showProfileDialog() { return; } - if (configModel.metadata.enabled) { - getViewModel().loadRecords(repoId, path); - } else { - FileProfileDialog dialog = FileProfileDialog.newInstance(configModel.detail, configModel.users.user_list); - dialog.show(getSupportFragmentManager(), FileProfileDialog.class.getSimpleName()); - } + readSDocPageOptionsData(new Consumer() { + @Override + public void accept(SDocPageOptionsModel model) { + if (model.enableMetadataManagement) { + if (configModel != null && configModel.metadataConfigModel != null && configModel.metadataConfigModel.enabled) { + getViewModel().loadRecords(repoId, path); + } else if (configModel != null) { + FileProfileDialog dialog = FileProfileDialog.newInstance(configModel.detail, configModel.users.user_list); + dialog.show(getSupportFragmentManager(), FileProfileDialog.class.getSimpleName()); + } + } else { + FileProfileDialog dialog = FileProfileDialog.newInstance(configModel.detail, configModel.users.user_list); + dialog.show(getSupportFragmentManager(), FileProfileDialog.class.getSimpleName()); + } + } + }); } private void showCommentsActivity() { @@ -247,6 +262,10 @@ public void accept(SDocPageOptionsModel model) { } private void readSDocPageOptionsData(Consumer continuation) { + if (pageOptionsData != null) { + continuation.accept(pageOptionsData); + return; + } String js = "(function() {" + " if (window.app && window.app.pageOptions) {" + @@ -260,9 +279,9 @@ private void readSDocPageOptionsData(Consumer continuation public void onReceiveValue(String value) { if (!TextUtils.isEmpty(value)) { value = StringUtils.deString(value).replace("\\", ""); - SDocPageOptionsModel configModel1 = GsonUtils.fromJson(value, SDocPageOptionsModel.class); - if (configModel1 != null) { - continuation.accept(configModel1); + pageOptionsData = GsonUtils.fromJson(value, SDocPageOptionsModel.class); + if (pageOptionsData != null) { + continuation.accept(pageOptionsData); } else { SLogs.e("read sodc page options data from web, an exception occurred in the parsing data"); SLogs.e(value); @@ -400,6 +419,16 @@ private void hideProgressBar() { } } + private void canLoadPageConfigData() { + readSDocPageOptionsData(new Consumer() { + @Override + public void accept(SDocPageOptionsModel model) { + getViewModel().loadFileDetail(repoId, path, model.enableMetadataManagement); + } + }); + + } + @Override protected void onPause() { super.onPause(); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/sdoc/outline/SDocOutlineRemoteDialog.java b/app/src/main/java/com/seafile/seadroid2/ui/sdoc/outline/SDocOutlineRemoteDialog.java index 7e6cd8196..2378d046d 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/sdoc/outline/SDocOutlineRemoteDialog.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/sdoc/outline/SDocOutlineRemoteDialog.java @@ -13,7 +13,6 @@ import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; -import com.blankj.utilcode.util.ToastUtils; import com.chad.library.adapter4.BaseQuickAdapter; import com.chad.library.adapter4.QuickAdapterHelper; import com.google.android.material.bottomsheet.BottomSheetDialog; @@ -22,12 +21,12 @@ import com.seafile.seadroid2.databinding.DialogSdocDirectoryBinding; import com.seafile.seadroid2.framework.data.model.sdoc.OutlineItemModel; import com.seafile.seadroid2.framework.data.model.sdoc.SDocPageOptionsModel; -import com.seafile.seadroid2.ui.sdoc.DocsCommentViewModel; +import com.seafile.seadroid2.ui.sdoc.SDocViewModel; import java.util.List; public class SDocOutlineRemoteDialog extends BottomSheetDialogFragment { - private DocsCommentViewModel viewModel; + private SDocViewModel viewModel; private SDocPageOptionsModel pageOptionsModel; @@ -51,7 +50,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { } pageOptionsModel = getArguments().getParcelable("pageOption"); - viewModel = new ViewModelProvider(this).get(DocsCommentViewModel.class); + viewModel = new ViewModelProvider(this).get(SDocViewModel.class); } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragment.java index a48934fc3..4c55a7d97 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragment.java @@ -301,13 +301,13 @@ private void doWorkInfoLiveData(WorkInfo workInfo) { } if (String.valueOf(TransferDataSource.ALBUM_BACKUP).equals(dataType)) { - if (TransferEvent.EVENT_CANCEL_OUT_OF_QUOTA.equals(progressEvent)) { + if (TransferEvent.EVENT_CANCEL_WITH_OUT_OF_QUOTA.equals(progressEvent)) { mCameraBackupState.setSummary(R.string.above_quota); } else if (TransferEvent.EVENT_TRANSFERRING.equals(progressEvent)) { viewModel.countAlbumBackupPendingList(requireContext()); } } else if (String.valueOf(TransferDataSource.FOLDER_BACKUP).equals(dataType)) { - if (TransferEvent.EVENT_CANCEL_OUT_OF_QUOTA.equals(progressEvent)) { + if (TransferEvent.EVENT_CANCEL_WITH_OUT_OF_QUOTA.equals(progressEvent)) { mFolderBackupState.setSummary(R.string.above_quota); } else if (TransferEvent.EVENT_TRANSFERRING.equals(progressEvent)) { viewModel.countFolderBackupPendingList(requireContext()); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/settings/TabSettingsFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/settings/TabSettingsFragment.java index 7c970136b..b34cada07 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/settings/TabSettingsFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/settings/TabSettingsFragment.java @@ -5,11 +5,9 @@ import android.content.DialogInterface; import android.content.Intent; -import android.os.Build; import android.os.Bundle; import android.text.Spanned; import android.text.TextUtils; -import android.view.View; import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultCallback; @@ -37,13 +35,10 @@ import com.seafile.seadroid2.bus.TransferBusHelper; import com.seafile.seadroid2.config.Constants; import com.seafile.seadroid2.enums.NetworkMode; -import com.seafile.seadroid2.enums.NightMode; import com.seafile.seadroid2.enums.TransferDataSource; -import com.seafile.seadroid2.framework.data.ServerInfo; import com.seafile.seadroid2.framework.datastore.StorageManager; import com.seafile.seadroid2.framework.datastore.sp_livedata.AlbumBackupSharePreferenceHelper; import com.seafile.seadroid2.framework.datastore.sp_livedata.FolderBackupSharePreferenceHelper; -import com.seafile.seadroid2.framework.helper.NightModeHelper; import com.seafile.seadroid2.framework.util.PermissionUtil; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; @@ -53,7 +48,6 @@ import com.seafile.seadroid2.framework.worker.upload.MediaBackupScannerWorker; import com.seafile.seadroid2.framework.worker.upload.UploadFolderFileAutomaticallyWorker; import com.seafile.seadroid2.framework.worker.upload.UploadMediaFileAutomaticallyWorker; -import com.seafile.seadroid2.gesturelock.LockPatternUtils; import com.seafile.seadroid2.preferences.RenameSharePreferenceFragmentCompat; import com.seafile.seadroid2.preferences.Settings; import com.seafile.seadroid2.ui.account.AccountsActivity; @@ -67,11 +61,9 @@ import com.seafile.seadroid2.ui.folder_backup.FolderBackupConfigActivity; import com.seafile.seadroid2.ui.folder_backup.FolderBackupSelectedPathActivity; import com.seafile.seadroid2.ui.folder_backup.RepoConfig; -import com.seafile.seadroid2.ui.gesture.CreateGesturePasswordActivity; import com.seafile.seadroid2.ui.main.MainActivity; import com.seafile.seadroid2.ui.selector.ObjSelectorActivity; import com.seafile.seadroid2.ui.webview.SeaWebViewActivity; -import com.seafile.seadroid2.widget.prefs.ButtonPreference; import java.util.ArrayList; import java.util.List; @@ -445,9 +437,10 @@ public void onChanged(Boolean aBoolean) { } else { mAlbumBackupSwitch.setChecked(false); AlbumBackupSharePreferenceHelper.writeRepoConfig(null); + switchAlbumBackupState(false); + dispatchAlbumBackupWork(false); } - switchAlbumBackupState(aBoolean); } }); @@ -557,14 +550,18 @@ private void checkScanWorkInfo(TransferDataSource dataSource, WorkInfo workInfo) Data outData = workInfo.getOutputData(); String outDataEvent = outData.getString(TransferWorker.KEY_DATA_EVENT); String outDataType = outData.getString(TransferWorker.KEY_DATA_TYPE); + + //scan end if (TextUtils.equals(String.valueOf(TransferDataSource.ALBUM_BACKUP), outDataType)) { if (TransferEvent.EVENT_SCAN_END.equals(outDataEvent)) { - mAlbumBackupState.setSummary(R.string.done); +// mAlbumBackupState.setSummary(R.string.done); + SLogs.e("album scan end"); return; } } else if (TextUtils.equals(String.valueOf(TransferDataSource.FOLDER_BACKUP), outDataType)) { if (TransferEvent.EVENT_SCAN_END.equals(outDataEvent)) { - mFolderBackupState.setSummary(R.string.done); +// mFolderBackupState.setSummary(R.string.done); + SLogs.e("folder scan end"); return; } } @@ -598,14 +595,18 @@ private void checkOutputData(WorkInfo workInfo) { if (TextUtils.equals(String.valueOf(TransferDataSource.ALBUM_BACKUP), outDataType)) { if (TransferEvent.EVENT_FINISH.equals(outDataEvent)) { mAlbumBackupState.setSummary(R.string.settings_cuc_finish_title); - } else if (TransferEvent.EVENT_CANCEL_OUT_OF_QUOTA.equals(outDataEvent)) { + } else if (TransferEvent.EVENT_CANCEL_WITH_OUT_OF_QUOTA.equals(outDataEvent)) { mAlbumBackupState.setSummary(R.string.above_quota); + } else if (TransferEvent.EVENT_CANCEL_WITH_BY_STOPPED.equals(outDataEvent)) { + mAlbumBackupState.setSummary(R.string.canceled); } } else if (TextUtils.equals(String.valueOf(TransferDataSource.FOLDER_BACKUP), outDataType)) { if (TransferEvent.EVENT_FINISH.equals(outDataEvent)) { mFolderBackupState.setSummary(R.string.folder_backup_waiting_state); - } else if (TransferEvent.EVENT_CANCEL_OUT_OF_QUOTA.equals(outDataEvent)) { + } else if (TransferEvent.EVENT_CANCEL_WITH_OUT_OF_QUOTA.equals(outDataEvent)) { mFolderBackupState.setSummary(R.string.above_quota); + } else if (TransferEvent.EVENT_CANCEL_WITH_BY_STOPPED.equals(outDataEvent)) { + mFolderBackupState.setSummary(R.string.canceled); } } else { checkProgressData(workInfo); @@ -696,6 +697,10 @@ private void switchAlbumBackupState(boolean isEnable) { mAlbumBackupState.setVisible(isEnable); mAlbumBackupAdvanced.setVisible(isEnable); + if (!isEnable) { + mAlbumBackupState.setSummary(null); + } + updateAlbumBackupSelectedRepoSummary(); } @@ -724,6 +729,10 @@ private void switchFolderBackupState(boolean isEnable) { mFolderBackupSelectFolder.setVisible(isEnable); mFolderBackupState.setVisible(isEnable); + if (!isEnable) { + mFolderBackupState.setSummary(null); + } + updateFolderBackupSelectedRepoAndFolderSummary(); } @@ -856,10 +865,12 @@ public void onActivityResult(ActivityResult o) { @Override public void onActivityResult(ActivityResult o) { if (o.getResultCode() == RESULT_OK) { + //The dispatch function needs to be put first dispatchAlbumBackupWork(true); - updateAlbumBackupSelectedRepoSummary(); + switchAlbumBackupState(true); } else { + //The dispatch function needs to be put first dispatchAlbumBackupWork(false); if (o.getData() != null) { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListAdapter.java b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListAdapter.java index 5a97437db..834e088bf 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListAdapter.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListAdapter.java @@ -97,8 +97,12 @@ private void onBindHolder(TransferItemViewHolder holder, FileTransferEntity enti } //target path - String targetPath = Utils.pathJoin(entity.repo_name, entity.getParent_path()); - holder.binding.transferTargetPath.setText(targetPath); + if (!TextUtils.isEmpty(entity.repo_name)) { + String targetPath = Utils.pathJoin(entity.repo_name, entity.getParent_path()); + holder.binding.transferTargetPath.setText(targetPath); + } else { + holder.binding.transferTargetPath.setText(entity.getParent_path()); + } //file name holder.binding.transferFileName.setText(entity.file_name); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListFragment.java index c8f764aeb..53080f0e6 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListFragment.java @@ -176,14 +176,6 @@ public void showBottomSheet(FileTransferEntity entity) { builder.removeMenu(R.id.upload); builder.removeMenu(R.id.download); - builder.removeMenu(R.id.pause); - - //not supported yet -// if (getTransferAction() == TransferAction.DOWNLOAD) { -// builder.removeMenu(R.id.upload); -// } else if (getTransferAction() == TransferAction.UPLOAD) { -// builder.removeMenu(R.id.download); -// } builder.show(getChildFragmentManager()); } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/UploadListFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/UploadListFragment.java index b7e323623..782a97af9 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/UploadListFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/UploadListFragment.java @@ -101,7 +101,7 @@ private void doWorkInfoLiveData(TransferDataSource dataSource, WorkInfo workInfo Data progressData = workInfo.getProgress(); String progressEvent = progressData.getString(TransferWorker.KEY_DATA_EVENT); - if (TransferEvent.EVENT_CANCEL_OUT_OF_QUOTA.equals(progressEvent)) { + if (TransferEvent.EVENT_CANCEL_WITH_OUT_OF_QUOTA.equals(progressEvent)) { refreshData(); } else if (TransferEvent.EVENT_TRANSFERRING.equals(progressEvent)) { @@ -113,7 +113,6 @@ private void doWorkInfoLiveData(TransferDataSource dataSource, WorkInfo workInfo SLogs.d("upload: " + fileName + ", percent:" + percent + ", total_size:" + totalSize + ", dataSource: " + dataSource); - if (TextUtils.equals(transferId, lastTransferId)) { notifyProgressById(transferId, transferredSize, percent, progressEvent); } else { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/GravitySnapHelper.java b/app/src/main/java/com/seafile/seadroid2/view/snap_recyclerview/GravitySnapHelper.java similarity index 98% rename from app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/GravitySnapHelper.java rename to app/src/main/java/com/seafile/seadroid2/view/snap_recyclerview/GravitySnapHelper.java index 7f27ee3a7..2c7d26d06 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview2/GravitySnapHelper.java +++ b/app/src/main/java/com/seafile/seadroid2/view/snap_recyclerview/GravitySnapHelper.java @@ -1,4 +1,4 @@ -package com.seafile.seadroid2.ui.media.image_preview2; +package com.seafile.seadroid2.view.snap_recyclerview; import android.util.DisplayMetrics; import android.view.Gravity; @@ -215,8 +215,8 @@ protected void onTargetFound(View targetView, // The associated RecyclerView has been removed so there is no action to take. return; } - int[] snapDistances = calculateDistanceToFinalSnap(recyclerView.getLayoutManager(), - targetView); + + int[] snapDistances = calculateDistanceToFinalSnap(recyclerView.getLayoutManager(), targetView); final int dx = snapDistances[0]; final int dy = snapDistances[1]; final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy))); @@ -468,18 +468,19 @@ private int getFlingDistance() { private boolean scrollTo(int position, boolean smooth) { if (recyclerView.getLayoutManager() != null) { if (smooth) { - RecyclerView.SmoothScroller smoothScroller - = createScroller(recyclerView.getLayoutManager()); + RecyclerView.SmoothScroller smoothScroller = createScroller(recyclerView.getLayoutManager()); if (smoothScroller != null) { smoothScroller.setTargetPosition(position); recyclerView.getLayoutManager().startSmoothScroll(smoothScroller); return true; } } else { - RecyclerView.ViewHolder viewHolder - = recyclerView.findViewHolderForAdapterPosition(position); +// recyclerView.getLayoutManager().scrollToPosition(position); +// return true; + RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(position); if (viewHolder != null) { - int[] distances = calculateDistanceToFinalSnap(recyclerView.getLayoutManager(), + int[] distances = calculateDistanceToFinalSnap( + recyclerView.getLayoutManager(), viewHolder.itemView); recyclerView.scrollBy(distances[0], distances[1]); return true; diff --git a/app/src/main/java/com/seafile/seadroid2/view/snap_recyclerview/GravitySnapRecyclerView.java b/app/src/main/java/com/seafile/seadroid2/view/snap_recyclerview/GravitySnapRecyclerView.java new file mode 100644 index 000000000..84dd154d1 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/view/snap_recyclerview/GravitySnapRecyclerView.java @@ -0,0 +1,150 @@ +package com.seafile.seadroid2.view.snap_recyclerview; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + +import com.seafile.seadroid2.R; + +/** + * An {@link OrientationAwareRecyclerView} that uses a default {@link GravitySnapHelper} + */ +public class GravitySnapRecyclerView extends OrientationAwareRecyclerView { + + @NonNull + final private GravitySnapHelper snapHelper; + + private boolean isSnappingEnabled = false; + + public GravitySnapRecyclerView(@NonNull Context context) { + this(context, null); + } + + public GravitySnapRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public GravitySnapRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + TypedArray typedArray = context.obtainStyledAttributes(attrs, + R.styleable.GravitySnapRecyclerView, defStyleAttr, 0); + int snapGravity = typedArray.getInt( + R.styleable.GravitySnapRecyclerView_snapGravity, 0); + switch (snapGravity) { + case 0: + snapHelper = new GravitySnapHelper(Gravity.START); + break; + case 1: + snapHelper = new GravitySnapHelper(Gravity.TOP); + break; + case 2: + snapHelper = new GravitySnapHelper(Gravity.END); + break; + case 3: + snapHelper = new GravitySnapHelper(Gravity.BOTTOM); + break; + case 4: + snapHelper = new GravitySnapHelper(Gravity.CENTER); + break; + default: + throw new IllegalArgumentException("Invalid gravity value. Use START " + + "| END | BOTTOM | TOP | CENTER constants"); + } + + snapHelper.setSnapToPadding(typedArray.getBoolean( + R.styleable.GravitySnapRecyclerView_snapToPadding, false)); + + snapHelper.setSnapLastItem(typedArray.getBoolean( + R.styleable.GravitySnapRecyclerView_snapLastItem, false)); + + snapHelper.setMaxFlingSizeFraction(typedArray.getFloat( + R.styleable.GravitySnapRecyclerView_snapMaxFlingSizeFraction, + GravitySnapHelper.FLING_SIZE_FRACTION_DISABLE)); + + snapHelper.setScrollMsPerInch(typedArray.getFloat( + R.styleable.GravitySnapRecyclerView_snapScrollMsPerInch, 100f)); + + enableSnapping(typedArray.getBoolean( + R.styleable.GravitySnapRecyclerView_snapEnabled, true)); + + typedArray.recycle(); + } + + @Override + public void smoothScrollToPosition(int position) { + if (!isSnappingEnabled || !snapHelper.smoothScrollToPosition(position)) { + super.smoothScrollToPosition(position); + } + } + + @Override + public void scrollToPosition(int position) { + if (!isSnappingEnabled || !snapHelper.scrollToPosition(position)) { + super.scrollToPosition(position); + } + } + + @NonNull + public GravitySnapHelper getSnapHelper() { + return snapHelper; + } + + public void enableSnapping(Boolean enable) { + if (enable) { + snapHelper.attachToRecyclerView(this); + } else { + snapHelper.attachToRecyclerView(null); + } + isSnappingEnabled = enable; + } + + public boolean isSnappingEnabled() { + return isSnappingEnabled; + } + + public int getCurrentSnappedPosition() { + return snapHelper.getCurrentSnappedPosition(); + } + + public void snapToNextPosition(Boolean smooth) { + snapTo(true, smooth); + } + + public void snapToPreviousPosition(Boolean smooth) { + snapTo(false, smooth); + } + + public void setSnapListener(@Nullable GravitySnapHelper.SnapListener listener) { + snapHelper.setSnapListener(listener); + } + + private void snapTo(Boolean next, Boolean smooth) { + final RecyclerView.LayoutManager lm = getLayoutManager(); + if (lm != null) { + final View snapView = snapHelper.findSnapView(lm, false); + if (snapView != null) { + final int pos = getChildAdapterPosition(snapView); + if (next) { + if (smooth) { + smoothScrollToPosition(pos + 1); + } else { + scrollToPosition(pos + 1); + } + } else if (pos > 0) { + if (smooth) { + smoothScrollToPosition(pos - 1); + } else { + scrollToPosition(pos - 1); + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/seafile/seadroid2/view/snap_recyclerview/OrientationAwareRecyclerView.java b/app/src/main/java/com/seafile/seadroid2/view/snap_recyclerview/OrientationAwareRecyclerView.java new file mode 100644 index 000000000..ba993d26b --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/view/snap_recyclerview/OrientationAwareRecyclerView.java @@ -0,0 +1,81 @@ +package com.seafile.seadroid2.view.snap_recyclerview; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + +/** + * A RecyclerView that only handles scroll events with the same orientation of its LayoutManager. + * Avoids situations where nested recyclerviews don't receive touch events properly: + */ +public class OrientationAwareRecyclerView extends RecyclerView { + + private float lastX = 0.0f; + private float lastY = 0.0f; + private boolean scrolling = false; + + public OrientationAwareRecyclerView(@NonNull Context context) { + this(context, null); + } + + public OrientationAwareRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public OrientationAwareRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + addOnScrollListener(new OnScrollListener() { + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + scrolling = newState != RecyclerView.SCROLL_STATE_IDLE; + } + }); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + final LayoutManager lm = getLayoutManager(); + if (lm == null) { + return super.onInterceptTouchEvent(e); + } + + boolean allowScroll = true; + + switch (e.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + lastX = e.getX(); + lastY = e.getY(); + // If we were scrolling, stop now by faking a touch release + if (scrolling) { + MotionEvent newEvent = MotionEvent.obtain(e); + newEvent.setAction(MotionEvent.ACTION_UP); + return super.onInterceptTouchEvent(newEvent); + } + break; + } + case MotionEvent.ACTION_MOVE: { + // We're moving, so check if we're trying + // to scroll vertically or horizontally so we don't intercept the wrong event. + float currentX = e.getX(); + float currentY = e.getY(); + float dx = Math.abs(currentX - lastX); + float dy = Math.abs(currentY - lastY); + allowScroll = dy > dx ? lm.canScrollVertically() : lm.canScrollHorizontally(); + break; + } + } + + if (!allowScroll) { + return false; + } + + return super.onInterceptTouchEvent(e); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/seafile/seadroid2/view/webview/OnWebPageListener.java b/app/src/main/java/com/seafile/seadroid2/view/webview/OnWebPageListener.java new file mode 100644 index 000000000..5348815f7 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/view/webview/OnWebPageListener.java @@ -0,0 +1,7 @@ +package com.seafile.seadroid2.view.webview; + +import android.webkit.WebView; + +public interface OnWebPageListener { + void onPageFinished(WebView view, String url); +} diff --git a/app/src/main/java/com/seafile/seadroid2/view/webview/SeaWebView.java b/app/src/main/java/com/seafile/seadroid2/view/webview/SeaWebView.java index c756bed2b..55bccf3db 100644 --- a/app/src/main/java/com/seafile/seadroid2/view/webview/SeaWebView.java +++ b/app/src/main/java/com/seafile/seadroid2/view/webview/SeaWebView.java @@ -6,6 +6,7 @@ import android.util.AttributeSet; import android.webkit.CookieManager; import android.webkit.WebSettings; +import android.webkit.WebView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -92,6 +93,11 @@ private void init() { registerCommonHandler(); } + public void setOnWebPageListener(OnWebPageListener onWebPageListener) { + mWebViewClient.setOnWebPageListener(onWebPageListener); + this.setWebViewClient(mWebViewClient); + } + public void load(String targetUrl) { mWebViewClient.go(targetUrl, this); } diff --git a/app/src/main/java/com/seafile/seadroid2/view/webview/SeaWebViewClient.java b/app/src/main/java/com/seafile/seadroid2/view/webview/SeaWebViewClient.java index b6da31b3f..fe7ae53bf 100644 --- a/app/src/main/java/com/seafile/seadroid2/view/webview/SeaWebViewClient.java +++ b/app/src/main/java/com/seafile/seadroid2/view/webview/SeaWebViewClient.java @@ -19,10 +19,25 @@ import java.util.Map; public class SeaWebViewClient extends BridgeWebViewClient { + + private OnWebPageListener onWebPageListener; + + public void setOnWebPageListener(OnWebPageListener onWebPageListener) { + this.onWebPageListener = onWebPageListener; + } + public SeaWebViewClient(BridgeWebView webView) { super(webView); } + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + if (onWebPageListener != null) { + onWebPageListener.onPageFinished(view, url); + } + } + @Override public boolean shouldOverrideUrlLoading(WebView wb, WebResourceRequest request) { String url = request.getUrl().toString(); diff --git a/app/src/main/res/drawable/baseline_delete_24.xml b/app/src/main/res/drawable/baseline_delete_24.xml index 8603e59d2..af9a48e10 100644 --- a/app/src/main/res/drawable/baseline_delete_24.xml +++ b/app/src/main/res/drawable/baseline_delete_24.xml @@ -4,6 +4,21 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:pathData="M20.792,6.462H3.208C3.093,6.462 3,6.555 3,6.669V7.985C3,8.099 3.093,8.192 3.208,8.192H20.792C20.907,8.192 21,8.099 21,7.985V6.669C21,6.555 20.907,6.462 20.792,6.462Z" + android:fillColor="#999999"/> + + + + + diff --git a/app/src/main/res/drawable/baseline_download_24.xml b/app/src/main/res/drawable/baseline_download_24.xml new file mode 100644 index 000000000..c9ca2f102 --- /dev/null +++ b/app/src/main/res/drawable/baseline_download_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_share_24.xml b/app/src/main/res/drawable/baseline_share_24.xml index ec5a0fac2..a3390defe 100644 --- a/app/src/main/res/drawable/baseline_share_24.xml +++ b/app/src/main/res/drawable/baseline_share_24.xml @@ -1,9 +1,21 @@ + android:width="18dp" + android:height="18dp" + android:viewportWidth="18" + android:viewportHeight="18"> + android:pathData="M17.308,2.423C17.308,1.085 16.223,0 14.884,0C13.546,0 12.461,1.085 12.461,2.423C12.461,3.761 13.546,4.846 14.884,4.846C16.223,4.846 17.308,3.761 17.308,2.423Z" + android:fillColor="#999999"/> + + + + diff --git a/app/src/main/res/drawable/baseline_star_filled_24.xml b/app/src/main/res/drawable/baseline_star_filled_24.xml deleted file mode 100644 index 40437ec7c..000000000 --- a/app/src/main/res/drawable/baseline_star_filled_24.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/baseline_star_outline_24.xml b/app/src/main/res/drawable/baseline_star_outline_24.xml deleted file mode 100644 index 2a5e48606..000000000 --- a/app/src/main/res/drawable/baseline_star_outline_24.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/baseline_starred_32.xml b/app/src/main/res/drawable/baseline_starred_32.xml deleted file mode 100644 index 6153d4543..000000000 --- a/app/src/main/res/drawable/baseline_starred_32.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/baseline_starred_filled_24.xml b/app/src/main/res/drawable/baseline_starred_filled_24.xml new file mode 100644 index 000000000..45b89e79b --- /dev/null +++ b/app/src/main/res/drawable/baseline_starred_filled_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/baseline_starred_filled_32.xml b/app/src/main/res/drawable/baseline_starred_filled_32.xml new file mode 100644 index 000000000..9efb9bdec --- /dev/null +++ b/app/src/main/res/drawable/baseline_starred_filled_32.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/baseline_starred_outline_24.xml b/app/src/main/res/drawable/baseline_starred_outline_24.xml new file mode 100644 index 000000000..b3d1e96ed --- /dev/null +++ b/app/src/main/res/drawable/baseline_starred_outline_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/shape_solid_grey100_radius_4.xml b/app/src/main/res/drawable/shape_solid_grey100_radius_4.xml new file mode 100644 index 000000000..ccd3eb776 --- /dev/null +++ b/app/src/main/res/drawable/shape_solid_grey100_radius_4.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_carousel_image_preview.xml b/app/src/main/res/layout/activity_carousel_image_preview.xml index bb2ada195..e12a5154a 100644 --- a/app/src/main/res/layout/activity_carousel_image_preview.xml +++ b/app/src/main/res/layout/activity_carousel_image_preview.xml @@ -21,7 +21,7 @@ style="@style/ToolBarStyle" android:layout_width="match_parent" android:layout_height="?actionBarSize" - android:background="@color/bar_background_translucent_color" + android:background="@color/bar_background_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -30,7 +30,7 @@ android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/bar_background_translucent_color" + android:background="@color/bar_background_color" android:clipToPadding="false" android:paddingTop="4dp" app:layout_constraintBottom_toTopOf="@+id/gallery_tool_bar" @@ -41,10 +41,11 @@ android:id="@+id/gallery_tool_bar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/bar_background_translucent_color" + android:background="@color/bar_background_color" android:gravity="center_horizontal" android:orientation="horizontal" - android:paddingTop="4dp" + android:paddingTop="8dp" + android:paddingBottom="8dp" android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -56,7 +57,7 @@ android:layout_height="36dp" android:foreground="?selectableItemBackground" android:scaleType="centerInside" - android:src="@drawable/action_download" + android:src="@drawable/baseline_download_24" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/gallery_delete_photo" app:layout_constraintStart_toStartOf="parent" @@ -82,7 +83,7 @@ android:layout_height="36dp" android:foreground="?selectableItemBackground" android:scaleType="centerInside" - android:src="@drawable/baseline_star_outline_24" + android:src="@drawable/baseline_starred_outline_24" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/gallery_share_photo" app:layout_constraintStart_toEndOf="@+id/gallery_delete_photo" diff --git a/app/src/main/res/layout/activity_image_preview.xml b/app/src/main/res/layout/activity_image_preview.xml index c1f5d7f55..53a7db8c7 100644 --- a/app/src/main/res/layout/activity_image_preview.xml +++ b/app/src/main/res/layout/activity_image_preview.xml @@ -28,7 +28,7 @@ android:background="@drawable/shape_stroke_radius_solid_translucent" android:foreground="?selectableItemBackground" android:scaleType="centerInside" - android:src="@drawable/action_download" + android:src="@drawable/baseline_download_24" app:shapeAppearance="@style/ShapeCircleStyle" app:tint="@color/white" /> @@ -40,7 +40,7 @@ android:background="@drawable/shape_stroke_radius_solid_translucent" android:foreground="?selectableItemBackground" android:scaleType="centerInside" - android:src="@drawable/action_delete" + android:src="@drawable/baseline_delete_24" app:shapeAppearance="@style/ShapeCircleStyle" app:tint="@color/white" /> @@ -52,7 +52,7 @@ android:background="@drawable/shape_stroke_radius_solid_translucent" android:foreground="?selectableItemBackground" android:scaleType="centerInside" - android:src="@drawable/baseline_star_outline_24" + android:src="@drawable/baseline_starred_outline_24" app:shapeAppearance="@style/ShapeCircleStyle" app:tint="@color/white" /> @@ -63,7 +63,7 @@ android:background="@drawable/shape_stroke_radius_solid_translucent" android:foreground="?selectableItemBackground" android:scaleType="centerInside" - android:src="@drawable/action_share" + android:src="@drawable/baseline_share_24" app:shapeAppearance="@style/ShapeCircleStyle" app:tint="@color/white" /> diff --git a/app/src/main/res/layout/gallery_activity_layout.xml b/app/src/main/res/layout/gallery_activity_layout.xml deleted file mode 100644 index a5a3f937f..000000000 --- a/app/src/main/res/layout/gallery_activity_layout.xml +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/menu/bottom_navigation_menu.xml b/app/src/main/res/menu/bottom_navigation_menu.xml index 726782915..b68ecbb92 100644 --- a/app/src/main/res/menu/bottom_navigation_menu.xml +++ b/app/src/main/res/menu/bottom_navigation_menu.xml @@ -8,7 +8,7 @@ - - \ No newline at end of file diff --git a/app/src/main/res/menu/bottom_sheet_unstarred.xml b/app/src/main/res/menu/bottom_sheet_unstarred.xml index a2c840bcb..82a8e0699 100644 --- a/app/src/main/res/menu/bottom_sheet_unstarred.xml +++ b/app/src/main/res/menu/bottom_sheet_unstarred.xml @@ -11,7 +11,7 @@ \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index b436f4ac2..6ae299478 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -71,4 +71,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 70fdab007..e3c2ccd5d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -683,6 +683,8 @@ Mark as resolved Are you sure you want to delete this item? + + Canceled diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 87a53e5fc..43be69259 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -201,6 +201,10 @@ 4dp +