diff --git a/app/src/main/java/com/seafile/seadroid2/annotation/Todo.java b/app/src/main/java/com/seafile/seadroid2/annotation/Todo.java new file mode 100644 index 000000000..d4ff3dcd2 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/annotation/Todo.java @@ -0,0 +1,17 @@ +package com.seafile.seadroid2.annotation; + + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * In this project, this feature is not yet complete, and will continue to be maintained in the future + */ + +@Target({TYPE, METHOD, CONSTRUCTOR, FIELD}) +public @interface Todo { +} 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 a0cab4654..590b8d29e 100644 --- a/app/src/main/java/com/seafile/seadroid2/context/NavContext.java +++ b/app/src/main/java/com/seafile/seadroid2/context/NavContext.java @@ -12,7 +12,9 @@ public class NavContext { private final Stack navStack = new Stack<>(); - // repoId = xxx, path = / + /** + * repoId = xxx, path = / + */ public boolean inRepoRoot() { return navStack.size() == 1; } @@ -97,27 +99,26 @@ public DirentModel getTopDirentModel() { return (DirentModel) navStack.peek(); } - /** - * Get the parents model of the current Dirent, maybe RepoModel - */ - public boolean isParentHasWritePermission() { - if (!inRepo()) { - //repo list page should not have permission verification - throw new IllegalArgumentException("Please check your code"); - } - - if (inRepoRoot()) { - return getRepoModel().hasWritePermission(); - } - - BaseModel bd = navStack.elementAt(navStack.size() - 1); - DirentModel d = (DirentModel) bd; - if (d == null) { - return false; - } - - return d.hasWritePermission(); - } +// /** +// * Get the parents model of the current Dirent, maybe RepoModel +// */ +// public boolean isParentHasWritePermission() { +// if (!inRepo()) { +// //repo list page should not have permission verification +// throw new IllegalArgumentException("Please check your code"); +// } +// +// if (inRepoRoot()) { +// return getRepoModel().hasWritePermission(); +// } +// +// BaseModel bd = navStack.elementAt(navStack.size() - 1); +// if (bd instanceof DirentModel d) { +// return d.hasWritePermission(); +// } else { +// return false; +// } +// } public RepoModel getRepoModel() { if (navStack.empty()) { diff --git a/app/src/main/java/com/seafile/seadroid2/enums/ActionModeCallbackType.java b/app/src/main/java/com/seafile/seadroid2/enums/ActionModeCallbackType.java new file mode 100644 index 000000000..91d496292 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/enums/ActionModeCallbackType.java @@ -0,0 +1,5 @@ +package com.seafile.seadroid2.enums; + +public enum ActionModeCallbackType { + CREATE, DESTORY, SELECT_ALL, SELECT_NONE +} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/PermissionDAO.java b/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/PermissionDAO.java index 5cbab7c71..66fe2de1a 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/PermissionDAO.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/PermissionDAO.java @@ -4,10 +4,7 @@ import androidx.room.Insert; import androidx.room.OnConflictStrategy; import androidx.room.Query; -import androidx.room.Update; -import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; -import com.seafile.seadroid2.framework.data.db.entities.PermissionEntity; import com.seafile.seadroid2.framework.data.db.entities.PermissionEntity; import java.util.List; @@ -18,16 +15,20 @@ @Dao public interface PermissionDAO { - @Query("select * from permissions where repo_id = :repoId and id = :id limit 1") - List getByIdSync(String repoId, int id); + @Query("select * from permissions where id = :id limit 1") + List getByIdSync(int id); + + @Query("select * from permissions where id IN (:ids)") + Single> getByIdsAsync(List ids); @Query("select * from permissions where repo_id = :repoId and id = :id limit 1") - Single> getByIdAsync(String repoId, int id); + Single> getWithAsync(String repoId, int id); @Query("select * from permissions where repo_id = :repoId") Single> getByRepoIdAsync(String repoId); + @Query("DELETE FROM permissions") void deleteAll(); diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/RepoDAO.java b/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/RepoDAO.java index c72996d4a..a30c00fd7 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/RepoDAO.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/RepoDAO.java @@ -24,6 +24,9 @@ public interface RepoDAO { @Query("select * from repos where repo_id = :repo_id limit 1") Single> getRepoById(String repo_id); + @Query("select * from repos where repo_id IN (:ids)") + Single> getRepoListByIds(List ids); + @Query("select * from repos where repo_id = :repo_id limit 1") List getByIdSync(String repo_id); diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/DirentModel.java b/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/DirentModel.java index b0f7ff205..fdcddfdf2 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/DirentModel.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/DirentModel.java @@ -154,6 +154,9 @@ public boolean hasDownloadPermission() { return true; } + /** + * is start with "custom-" ? + */ public boolean isCustomPermission() { return !TextUtils.isEmpty(permission) && permission.startsWith("custom-"); } diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/PermissionEntity.java b/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/PermissionEntity.java index 365037da8..81f830563 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/PermissionEntity.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/PermissionEntity.java @@ -1,9 +1,16 @@ package com.seafile.seadroid2.framework.data.db.entities; +import android.text.TextUtils; + import androidx.annotation.NonNull; import androidx.room.Entity; +import androidx.room.Ignore; import com.seafile.seadroid2.framework.data.model.BaseModel; +import com.seafile.seadroid2.framework.data.model.permission.PermissionWrapperModel; + +import java.util.HashMap; +import java.util.Set; @Entity(tableName = "permissions", primaryKeys = {"repo_id", "id"}) public class PermissionEntity extends BaseModel { @@ -24,5 +31,127 @@ public class PermissionEntity extends BaseModel { public boolean delete; public boolean modify; public boolean download_external_link; - //1001010 + + + /** + * key -> repo_id or dirent_id(uid) + */ + @Ignore + public HashMap cachedMap; + + public boolean hasId(String id) { + if (cachedMap == null) { + return false; + } + + return cachedMap.containsKey(id); + } + + public void cacheBaseModel(BaseModel model) { + if (cachedMap == null) { + cachedMap = new HashMap<>(); + return; + } + + if (model instanceof RepoModel m) { + cachedMap.put(m.repo_id, m); + } else if (model instanceof DirentModel m) { + cachedMap.put(m.uid, m); + } + } + + public void removeById(String id) { + if (cachedMap == null) { + return; + } + + cachedMap.remove(id); + } + + public void removeByModel(BaseModel model) { + if (cachedMap == null) { + return; + } + + if (model instanceof RepoModel m) { + cachedMap.remove(m.repo_id); + } else if (model instanceof DirentModel m) { + cachedMap.remove(m.uid); + } + } + + public boolean isEmptyIds() { + return cachedMap == null || cachedMap.isEmpty(); + } + + public PermissionEntity() { + } + + public PermissionEntity(@NonNull String repoId, @NonNull PermissionWrapperModel model) { + this.id = model.id; + this.name = model.name; + this.description = model.description; + + this.create = model.permission.create; + this.upload = model.permission.upload; + this.download = model.permission.download; + this.copy = model.permission.copy; + this.delete = model.permission.delete; + this.modify = model.permission.modify; + this.download_external_link = model.permission.download_external_link; + this.preview = model.permission.preview; + this.repo_id = repoId; + } + + public PermissionEntity(@NonNull String repoId, @NonNull String permission) { + if (TextUtils.isEmpty(permission)) { + throw new IllegalArgumentException("permission is null"); + } + + this.name = permission; + this.id = -1; + this.repo_id = repoId; + + if ("cloud-edit".equals(permission)) { + //用户可以通过浏览器在线查看和编辑,文件不能被下载。 + this.create = true; + this.upload = false; + this.download = false; + this.preview = true; + this.copy = true; + this.delete = true; + this.modify = true; + this.download_external_link = false; + + } else if ("preview".equals(permission)) { + //用户只能通过浏览器在线查看,文件不能被下载。 + this.create = false; + this.upload = false; + this.download = false; + this.preview = true; + this.copy = false; + this.delete = false; + this.modify = false; + this.download_external_link = false; + } else if ("r".equals(permission)) { + //用户可以查看、下载和同步文件 + this.create = false; + this.upload = false; + this.download = true; + this.preview = true; + this.copy = true; + this.delete = false; + this.modify = false; + this.download_external_link = false; + } else if ("rw".equals(permission)) { + this.create = true; + this.upload = true; + this.download = true; + this.preview = true; + this.copy = true; + this.delete = true; + this.modify = true; + this.download_external_link = true; + } + } } diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/RepoModel.java b/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/RepoModel.java index bc9784ebf..1cadcae4f 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/RepoModel.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/db/entities/RepoModel.java @@ -92,7 +92,7 @@ public int getIcon() { } /** - * You'll also need to check if it's a custom permission + * You should to check if it's a custom permission firstly */ public boolean hasWritePermission() { if (TextUtils.isEmpty(permission)) { diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/model/repo/RepoPermissionWrapper.java b/app/src/main/java/com/seafile/seadroid2/framework/data/model/repo/RepoPermissionWrapper.java new file mode 100644 index 000000000..0b30c0da0 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/model/repo/RepoPermissionWrapper.java @@ -0,0 +1,30 @@ +package com.seafile.seadroid2.framework.data.model.repo; + +import com.seafile.seadroid2.framework.data.db.entities.PermissionEntity; +import com.seafile.seadroid2.framework.data.db.entities.RepoModel; + +public class RepoPermissionWrapper { + public RepoModel repoModel; + public PermissionEntity permission; + + public RepoPermissionWrapper(RepoModel repoModel, PermissionEntity permission) { + this.repoModel = repoModel; + this.permission = permission; + } + + public PermissionEntity getPermission() { + return permission; + } + + public void setPermission(PermissionEntity permission) { + this.permission = permission; + } + + public void setRepoModel(RepoModel repoModel) { + this.repoModel = repoModel; + } + + public RepoModel getRepoModel() { + return repoModel; + } +} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/datastore/sp/SettingsManager.java b/app/src/main/java/com/seafile/seadroid2/framework/datastore/sp/SettingsManager.java index 47ebf8b2d..f26b92353 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/datastore/sp/SettingsManager.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/datastore/sp/SettingsManager.java @@ -104,8 +104,8 @@ public final class SettingsManager { //force refresh starred list state public static final String ON_FORCE_REFRESH_STARRED_LIST_KEY = "on_force_refresh_starred_list"; - public static void setForceRefreshStarredListState() { - Settings.getCommonPreferences().edit().putBoolean(ON_FORCE_REFRESH_STARRED_LIST_KEY, true).apply(); + public static void setForceRefreshStarredListState(boolean state) { + Settings.getCommonPreferences().edit().putBoolean(ON_FORCE_REFRESH_STARRED_LIST_KEY, state).apply(); } public static boolean getForceRefreshStarredListState() { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/bottomsheetmenu/BottomSheetMenuAdapter.java b/app/src/main/java/com/seafile/seadroid2/ui/bottomsheetmenu/BottomSheetMenuAdapter.java index 53c286cd5..6a5a99c8a 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/bottomsheetmenu/BottomSheetMenuAdapter.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/bottomsheetmenu/BottomSheetMenuAdapter.java @@ -35,11 +35,12 @@ protected void onBindViewHolder(@NonNull BottomSheetMenuAdapter.BottomSheetViewH holder.name.setEnabled(menuItem.isEnabled()); holder.itemView.setClickable(menuItem.isEnabled()); + int color; if (menuItem.isEnabled()) { - color = ContextCompat.getColor(getContext(), R.color.material_grey_600); + color = ContextCompat.getColor(getContext(), R.color.bottom_sheet_pop_enable_color); } else { - color = ContextCompat.getColor(getContext(), R.color.material_grey_400); + color = ContextCompat.getColor(getContext(), R.color.bottom_sheet_pop_disable_color); } holder.icon.setImageTintList(ColorStateList.valueOf(color)); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/bottomsheetmenu/BottomSheetMenuFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/bottomsheetmenu/BottomSheetMenuFragment.java index e561e1620..83ec86a10 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/bottomsheetmenu/BottomSheetMenuFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/bottomsheetmenu/BottomSheetMenuFragment.java @@ -193,5 +193,9 @@ public BottomSheetMenuFragment build() { public void show(FragmentManager fragmentManager) { build().show(fragmentManager, BottomSheetMenuFragment.class.getSimpleName()); } + + public void show(FragmentManager fragmentManager, String tag) { + build().show(fragmentManager, tag); + } } } 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 e0e86fc00..3cdc20e98 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 @@ -16,6 +16,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; import androidx.activity.OnBackPressedCallback; import androidx.activity.result.ActivityResult; @@ -43,14 +44,17 @@ import com.seafile.seadroid2.account.SupportAccountManager; import com.seafile.seadroid2.context.NavContext; import com.seafile.seadroid2.databinding.ActivityMainBinding; +import com.seafile.seadroid2.enums.ActionModeCallbackType; import com.seafile.seadroid2.enums.FileViewType; import com.seafile.seadroid2.enums.NightMode; import com.seafile.seadroid2.enums.SortBy; import com.seafile.seadroid2.framework.data.ServerInfo; +import com.seafile.seadroid2.framework.data.db.entities.DirentModel; import com.seafile.seadroid2.framework.data.db.entities.EncKeyCacheEntity; import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; import com.seafile.seadroid2.framework.data.db.entities.PermissionEntity; import com.seafile.seadroid2.framework.data.db.entities.RepoModel; +import com.seafile.seadroid2.framework.data.model.BaseModel; import com.seafile.seadroid2.framework.data.model.dirents.DirentFileModel; import com.seafile.seadroid2.framework.file_monitor.FileSyncService; import com.seafile.seadroid2.framework.helper.NightModeHelper; @@ -375,12 +379,14 @@ private void initViewPager() { @Override public void onPageSelected(int position) { super.onPageSelected(position); + onViewPageSelected(position); } }); } private void onViewPageSelected(int position) { + if (0 == position) { binding.navBottomView.setSelectedItemId(R.id.tabs_library); @@ -395,6 +401,9 @@ private void onViewPageSelected(int position) { return; } + if (menuBinding.search.isActionViewExpanded()) { + menuBinding.search.collapseActionView(); + } menuBinding.search.setVisible(false); menuBinding.sortGroup.setVisible(false); @@ -465,18 +474,23 @@ public void onChanged(NightMode nightMode) { } }); - mainViewModel.getOnActionModeLiveData().observe(this, new Observer() { + mainViewModel.getOnActionModeLiveData().observe(this, new Observer() { @Override - public void onChanged(Boolean aBoolean) { + public void onChanged(ActionModeCallbackType callbackType) { - onShowRepoActionMode(aBoolean); + onShowRepoActionMode(callbackType); } }); } - private void onShowRepoActionMode(boolean show) { - binding.pager.setUserInputEnabled(!show); + private void onShowRepoActionMode(ActionModeCallbackType type) { + if (type == ActionModeCallbackType.CREATE) { + binding.pager.setUserInputEnabled(false); + } else if (type == ActionModeCallbackType.DESTORY) { + binding.pager.setUserInputEnabled(true); + } + } private void refreshToolbarTitle() { @@ -633,6 +647,7 @@ public boolean onQueryTextChange(String newText) { return false; } }); + menuBinding.search.collapseActionView(); menuBinding.search.setActionView(searchView); } @@ -644,35 +659,21 @@ public boolean onPrepareOptionsMenu(Menu menu) { if (getNavContext().inRepo()) { menuBinding.createRepo.setVisible(false); - //check custom permission - String repoId = getNavContext().getRepoModel().repo_id; - mainViewModel.getRepoModelFromLocal(repoId, new Consumer>() { + checkCurrentPathHasWritePermission(new java.util.function.Consumer() { @Override - public void accept(Pair pair) throws Exception { - if (pair.getFirst().isCustomPermission()) { - PermissionEntity permission = pair.getSecond(); - if (permission != null) { - menuBinding.add.setEnabled(permission.create); - menuBinding.select.setEnabled(permission.modify); - } - } else if (getNavContext().isParentHasWritePermission()) { - menuBinding.add.setEnabled(true); - menuBinding.select.setEnabled(true); - } else { - menuBinding.add.setEnabled(false); - menuBinding.select.setEnabled(false); - } + public void accept(Boolean aBoolean) { + menuBinding.add.setEnabled(aBoolean); } }); - } else { menuBinding.createRepo.setVisible(true); menuBinding.add.setVisible(false); - menuBinding.select.setVisible(false); } + menuBinding.select.setVisible(true); menuBinding.sortGroup.setVisible(true); } else { + menuBinding.search.setVisible(false); menuBinding.sortGroup.setVisible(false); menuBinding.createRepo.setVisible(false); menuBinding.add.setVisible(false); @@ -701,7 +702,7 @@ public boolean onOptionsItemSelected(MenuItem item) { startActivity(newIntent); } else if (item.getItemId() == R.id.select) { if (binding.pager.getCurrentItem() == INDEX_LIBRARY_TAB) { - getReposFragment().startContextualActionMode(); + getReposFragment().startOrUpdateContextualActionBar(); } } else if (item.getItemId() == R.id.menu_action_view_list) { Settings.FILE_LIST_VIEW_TYPE.putValue(FileViewType.LIST); @@ -960,54 +961,102 @@ else if (which == 3) // take a photo }).show(); } - // - private void showNewDirDialog() { - if (!getNavContext().isParentHasWritePermission()) { - ToastUtils.showLong(R.string.library_read_only); + private void checkCurrentPathHasWritePermission(java.util.function.Consumer consumer) { + BaseModel baseModel = getNavContext().getTopModel(); + if (null == baseModel) { return; } - String rid = getNavContext().getRepoModel().repo_id; - String parentPath = getNavContext().getNavPath(); - NewDirFileDialogFragment dialogFragment = NewDirFileDialogFragment.newInstance(rid, parentPath, true); - dialogFragment.setRefreshListener(new OnRefreshDataListener() { + if (baseModel instanceof RepoModel m) { + if (!m.isCustomPermission()) { + consumer.accept(m.hasWritePermission()); + } else { + mainViewModel.getPermissionFromLocal(m.repo_id, m.getCustomPermissionNum(), new Consumer() { + @Override + public void accept(PermissionEntity entity) throws Exception { + consumer.accept(entity != null && entity.create); + } + }); + } + } else if (baseModel instanceof DirentModel m) { + if (!m.isCustomPermission()) { + consumer.accept(m.hasWritePermission()); + } else { + mainViewModel.getPermissionFromLocal(m.repo_id, m.getCustomPermissionNum(), new Consumer() { + @Override + public void accept(PermissionEntity entity) throws Exception { + consumer.accept(entity != null && entity.create); + } + }); + } + } + } + + // + private void showNewDirDialog() { + checkCurrentPathHasWritePermission(new java.util.function.Consumer() { @Override - public void onActionStatus(boolean isDone) { - if (isDone) { - mainViewModel.getOnForceRefreshRepoListLiveData().setValue(true); + public void accept(Boolean aBoolean) { + if (!aBoolean) { + ToastUtils.showLong(R.string.library_read_only); + return; } + + String rid = getNavContext().getRepoModel().repo_id; + String parentPath = getNavContext().getNavPath(); + NewDirFileDialogFragment dialogFragment = NewDirFileDialogFragment.newInstance(rid, parentPath, true); + dialogFragment.setRefreshListener(new OnRefreshDataListener() { + @Override + public void onActionStatus(boolean isDone) { + if (isDone) { + mainViewModel.getOnForceRefreshRepoListLiveData().setValue(true); + } + } + }); + dialogFragment.show(getSupportFragmentManager(), NewDirFileDialogFragment.class.getSimpleName()); } }); - dialogFragment.show(getSupportFragmentManager(), NewDirFileDialogFragment.class.getSimpleName()); } private void showNewFileDialog() { - if (!getNavContext().isParentHasWritePermission()) { - ToastUtils.showLong(R.string.library_read_only); - return; - } - - String rid = getNavContext().getRepoModel().repo_id; - String parentPath = getNavContext().getNavPath(); - NewDirFileDialogFragment dialogFragment = NewDirFileDialogFragment.newInstance(rid, parentPath, false); - dialogFragment.setRefreshListener(new OnRefreshDataListener() { + checkCurrentPathHasWritePermission(new java.util.function.Consumer() { @Override - public void onActionStatus(boolean isDone) { - if (isDone) { - mainViewModel.getOnForceRefreshRepoListLiveData().setValue(true); + public void accept(Boolean aBoolean) { + if (!aBoolean) { + ToastUtils.showLong(R.string.library_read_only); + return; } + + + String rid = getNavContext().getRepoModel().repo_id; + String parentPath = getNavContext().getNavPath(); + NewDirFileDialogFragment dialogFragment = NewDirFileDialogFragment.newInstance(rid, parentPath, false); + dialogFragment.setRefreshListener(new OnRefreshDataListener() { + @Override + public void onActionStatus(boolean isDone) { + if (isDone) { + mainViewModel.getOnForceRefreshRepoListLiveData().setValue(true); + } + } + }); + dialogFragment.show(getSupportFragmentManager(), NewDirFileDialogFragment.class.getSimpleName()); } }); - dialogFragment.show(getSupportFragmentManager(), NewDirFileDialogFragment.class.getSimpleName()); } private void pickFile() { - if (!getNavContext().isParentHasWritePermission()) { - ToastUtils.showLong(R.string.library_read_only); - return; - } + checkCurrentPathHasWritePermission(new java.util.function.Consumer() { + @Override + public void accept(Boolean aBoolean) { + if (!aBoolean) { + ToastUtils.showLong(R.string.library_read_only); + return; + } + + takeFile(false); + } + }); - takeFile(false); } 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 c2e2849fa..691070670 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 @@ -14,6 +14,7 @@ import com.seafile.seadroid2.R; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; +import com.seafile.seadroid2.enums.ActionModeCallbackType; import com.seafile.seadroid2.framework.data.db.entities.EncKeyCacheEntity; import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; import com.seafile.seadroid2.framework.data.db.entities.PermissionEntity; @@ -22,6 +23,8 @@ import com.seafile.seadroid2.enums.TransferDataSource; import com.seafile.seadroid2.enums.TransferResult; import com.seafile.seadroid2.enums.TransferStatus; +import com.seafile.seadroid2.framework.data.model.permission.PermissionListWrapperModel; +import com.seafile.seadroid2.framework.data.model.permission.PermissionWrapperModel; import com.seafile.seadroid2.framework.datastore.DataManager; import com.seafile.seadroid2.framework.util.Utils; import com.seafile.seadroid2.framework.worker.ExistingFileStrategy; @@ -51,9 +54,11 @@ import java.io.OutputStream; import java.nio.file.Files; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; +import io.reactivex.Completable; import io.reactivex.Single; import io.reactivex.SingleEmitter; import io.reactivex.SingleOnSubscribe; @@ -77,9 +82,9 @@ public class MainViewModel extends BaseViewModel { private final MutableLiveData OnNavChangeListenerLiveData = new MutableLiveData<>(); private final MutableLiveData _searchViewExpandedLiveData = new MutableLiveData<>(false); - private final MutableLiveData _onActionModeLiveData = new MutableLiveData<>(false); + private final MutableLiveData _onActionModeLiveData = new MutableLiveData<>(); - public MutableLiveData getOnActionModeLiveData() { + public MutableLiveData getOnActionModeLiveData() { return _onActionModeLiveData; } @@ -230,6 +235,77 @@ public void accept(Throwable throwable) throws Exception { }); } + public void getPermissionFromLocal(String repoId, int pNum, Consumer consumer) { + Single> pSingle = AppDatabase.getInstance().permissionDAO().getWithAsync(repoId, pNum); + Single s = pSingle.flatMap(new Function, SingleSource>() { + @Override + public SingleSource apply(List pList) throws Exception { + + if (CollectionUtils.isEmpty(pList)) { + return null; + } + + return Single.just(pList.get(0)); + } + }).flatMap(new Function>() { + @Override + public SingleSource apply(PermissionEntity entity) throws Exception { + Single> r = getLoadRepoPermissionFromRemoteSingle(repoId); + + return r.flatMap(new Function, SingleSource>() { + @Override + public SingleSource apply(List permissionEntities) throws Exception { + if (CollectionUtils.isEmpty(permissionEntities)) { + return null; + + } + Optional p = permissionEntities.stream().filter(f -> f.id == pNum).findFirst(); + if (p.isPresent()) { + return Single.just(p.get()); + } + return null; + } + }); + } + }); + + addSingleDisposable(s, new Consumer() { + @Override + public void accept(PermissionEntity entity) throws Exception { + if (consumer != null) { + consumer.accept(entity); + } + } + }); + } + + + private Single> getLoadRepoPermissionFromRemoteSingle(String repoId) { + Single single = HttpIO.getCurrentInstance().execute(RepoService.class).getCustomSharePermissions(repoId); + return single.flatMap(new Function>>() { + @Override + public SingleSource> apply(PermissionListWrapperModel wrapperModel) throws Exception { + + List list = CollectionUtils.newArrayList(); + + for (PermissionWrapperModel model : wrapperModel.permission_list) { + list.add(new PermissionEntity(repoId, model)); + } + + Completable insertCompletable = AppDatabase.getInstance().permissionDAO().insertAllAsync(list); + Single insertAllSingle = insertCompletable.toSingleDefault(0L); + return insertAllSingle.flatMap(new Function>>() { + @Override + public SingleSource> apply(Long aLong) throws Exception { + SLogs.d("The list has been inserted into the local database"); + return Single.just(list); + } + }); + } + }); + } + + public void getRepoModelFromLocal(String repoId, Consumer> consumer) { //from db Single> dbSingle = AppDatabase.getInstance().repoDao().getRepoById(repoId); @@ -251,7 +327,7 @@ public SingleSource> apply(List rep int pNum = repoModel.getCustomPermissionNum(); - Single> pSingle = AppDatabase.getInstance().permissionDAO().getByIdAsync(repoId, pNum); + Single> pSingle = AppDatabase.getInstance().permissionDAO().getWithAsync(repoId, pNum); return pSingle.flatMap(new Function, SingleSource>>() { @Override 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 96922d671..ab9894f4f 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 @@ -1,7 +1,9 @@ package com.seafile.seadroid2.ui.repo; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; @@ -45,6 +47,7 @@ import com.seafile.seadroid2.widget.AnimatedStateListDrawableCompatUtils; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.function.Predicate; @@ -137,7 +140,14 @@ public RepoViewHolder onCreate(@NonNull Context context, @NonNull ViewGroup view @Override public void onBind(@NonNull RepoViewHolder viewHolder, int i, @Nullable BaseModel baseModel) { - onBindRepos(viewHolder, (RepoModel) baseModel); + onBind(viewHolder, i, baseModel, Collections.emptyList()); + } + + @Override + public void onBind(@NonNull RepoViewHolder holder, int position, @Nullable BaseModel item, @NonNull List payloads) { + + onBindRepos(holder, (RepoModel) item, payloads); + } }).addItemType(AbsLayoutItemType.DIRENT_LIST, new OnMultiItem() { @NonNull @@ -149,7 +159,12 @@ public DirentViewHolder onCreate(@NonNull Context context, @NonNull ViewGroup vi @Override public void onBind(@NonNull DirentViewHolder viewHolder, int i, @Nullable BaseModel baseModel) { - onBindDirents(viewHolder, (DirentModel) baseModel); + onBind(viewHolder, i, baseModel, Collections.emptyList()); + } + + @Override + public void onBind(@NonNull DirentViewHolder holder, int position, @Nullable BaseModel item, @NonNull List payloads) { + onBindDirents(holder, (DirentModel) item, payloads); } }).addItemType(AbsLayoutItemType.DIRENT_GRID, new OnMultiItem() { @NonNull @@ -161,7 +176,12 @@ public DirentGridViewHolder onCreate(@NonNull Context context, @NonNull ViewGrou @Override public void onBind(@NonNull DirentGridViewHolder viewHolder, int i, @Nullable BaseModel baseModel) { - onBindDirentsGrid(viewHolder, (DirentModel) baseModel); + onBind(viewHolder, i, baseModel, Collections.emptyList()); + } + + @Override + public void onBind(@NonNull DirentGridViewHolder holder, int position, @Nullable BaseModel item, @NonNull List payloads) { + onBindDirentsGrid(holder, (DirentModel) item, payloads); } }).addItemType(AbsLayoutItemType.DIRENT_GALLERY, new OnMultiItem() { @NonNull @@ -173,7 +193,13 @@ public DirentGalleryViewHolder onCreate(@NonNull Context context, @NonNull ViewG @Override public void onBind(@NonNull DirentGalleryViewHolder viewHolder, int i, @Nullable BaseModel baseModel) { - onBindDirentsGallery(viewHolder, (DirentModel) baseModel); + onBind(viewHolder, i, baseModel, Collections.emptyList()); + } + + @Override + public void onBind(@NonNull DirentGalleryViewHolder holder, int position, @Nullable BaseModel item, @NonNull List payloads) { + onBindDirentsGallery(holder, (DirentModel) item, payloads); + } }).addItemType(AbsLayoutItemType.SEARCH, new OnMultiItem() { @NonNull @@ -196,7 +222,13 @@ public UnsupportedViewHolder onCreate(@NonNull Context context, @NonNull ViewGro } @Override - public void onBind(@NonNull UnsupportedViewHolder viewHolder, int i, @Nullable BaseModel baseModel) { + public void onBind(@NonNull UnsupportedViewHolder unsupportedViewHolder, int i, @Nullable BaseModel baseModel) { + + } + + @Override + public void onBind(@NonNull UnsupportedViewHolder holder, int position, @Nullable BaseModel item, @NonNull List payloads) { + super.onBind(holder, position, item, payloads); } }).onItemViewType(new OnItemViewTypeListener() { @Override @@ -268,10 +300,26 @@ private void onBindGroup(int position, GroupItemViewHolder holder, GroupItemMode // holder.binding.listSeparatorItemActionText.setRotation(model.is_checked ? 90 : 270); } - private void onBindRepos(RepoViewHolder holder, RepoModel model) { + private void onBindRepos(RepoViewHolder holder, RepoModel model, @NonNull List payloads) { + if (!CollectionUtils.isEmpty(payloads)) { + Bundle bundle = (Bundle) payloads.get(0); + boolean isChecked = bundle.getBoolean("is_check"); + + holder.binding.getRoot().setChecked(model.is_checked); + + if (isChecked) { + holder.binding.itemMultiSelect.setImageResource(R.drawable.ic_checkbox_checked); +// holder.binding.itemMultiSelect.setImageTintList(ColorStateList.valueOf(R.color.bar_disable_color)); + } else { + holder.binding.itemMultiSelect.setImageResource(R.drawable.ic_checkbox_unchecked); + } + return; + } + holder.binding.itemTitle.setText(model.repo_name); holder.binding.itemSubtitle.setText(model.getSubtitle()); holder.binding.itemIcon.setImageResource(model.getIcon()); + holder.binding.getRoot().setBackground(AnimatedStateListDrawableCompatUtils.createDrawableCompat(getContext())); if (selectType.ordinal() == RepoSelectType.ONLY_REPO.ordinal() || onActionMode) { holder.binding.getRoot().setChecked(model.is_checked); @@ -297,7 +345,21 @@ private void onBindRepos(RepoViewHolder holder, RepoModel model) { } } - private void onBindDirents(DirentViewHolder holder, DirentModel model) { + private void onBindDirents(DirentViewHolder holder, DirentModel model, @NonNull List payloads) { + if (!CollectionUtils.isEmpty(payloads)) { + Bundle bundle = (Bundle) payloads.get(0); + boolean isChecked = bundle.getBoolean("is_check"); + + holder.binding.getRoot().setChecked(model.is_checked); + + if (isChecked) { + holder.binding.itemMultiSelect.setImageResource(R.drawable.ic_checkbox_checked); + } else { + holder.binding.itemMultiSelect.setImageResource(R.drawable.ic_checkbox_unchecked); + } + return; + } + holder.binding.itemTitle.setText(model.name); holder.binding.itemSubtitle.setText(model.getSubtitle()); @@ -382,7 +444,21 @@ private void onBindDirents(DirentViewHolder holder, DirentModel model) { } } - private void onBindDirentsGrid(DirentGridViewHolder holder, DirentModel model) { + private void onBindDirentsGrid(DirentGridViewHolder holder, DirentModel model, @NonNull List payloads) { + if (!CollectionUtils.isEmpty(payloads)) { + Bundle bundle = (Bundle) payloads.get(0); + boolean isChecked = bundle.getBoolean("is_check"); + + holder.binding.getRoot().setChecked(model.is_checked); + + if (isChecked) { + holder.binding.itemMultiSelect.setImageResource(R.drawable.ic_checkbox_checked); + } else { + holder.binding.itemMultiSelect.setImageResource(R.drawable.ic_checkbox_unchecked); + } + return; + } + holder.binding.itemTitle.setText(model.name); holder.binding.getRoot().setBackground(AnimatedStateListDrawableCompatUtils.createDrawableCompat(getContext())); @@ -425,9 +501,20 @@ private void onBindDirentsGrid(DirentGridViewHolder holder, DirentModel model) { } } - private void onBindDirentsGallery(DirentGalleryViewHolder holder, DirentModel model) { -// holder.binding.itemTitle.setText(model.name); + private void onBindDirentsGallery(DirentGalleryViewHolder holder, DirentModel model, @NonNull List payloads) { + if (!CollectionUtils.isEmpty(payloads)) { + Bundle bundle = (Bundle) payloads.get(0); + boolean isChecked = bundle.getBoolean("is_check"); + + holder.binding.getRoot().setChecked(model.is_checked); + if (isChecked) { + holder.binding.itemMultiSelect.setImageResource(R.drawable.ic_checkbox_checked); + } else { + holder.binding.itemMultiSelect.setImageResource(R.drawable.ic_checkbox_unchecked); + } + return; + } holder.binding.getRoot().setBackground(AnimatedStateListDrawableCompatUtils.createDrawableCompat(getContext())); if (repoEncrypted || !Utils.isViewableImage(model.name)) { @@ -521,10 +608,21 @@ public boolean isOnActionMode() { public void setItemSelected(boolean itemSelected) { for (BaseModel item : getItems()) { + + if (item instanceof GroupItemModel) { + continue; + } else if (item instanceof Account) { + continue; + } else if (item instanceof SearchModel) { + continue; + } + item.is_checked = itemSelected; } - notifyItemRangeChanged(0, getItemCount()); + Bundle bundle = new Bundle(); + bundle.putBoolean("is_check", itemSelected); + notifyItemRangeChanged(0, getItemCount(), bundle); } public List getSelectedList() { 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 ea2051def..33fb7f094 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 @@ -44,7 +44,8 @@ import com.seafile.seadroid2.SeafException; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; -import com.seafile.seadroid2.framework.data.db.entities.PermissionEntity; +import com.seafile.seadroid2.enums.ActionModeCallbackType; +import com.seafile.seadroid2.framework.data.model.repo.RepoPermissionWrapper; import com.seafile.seadroid2.framework.datastore.sp.SettingsManager; import com.seafile.seadroid2.ui.bottomsheetmenu.BottomSheetMenuAdapter; import com.seafile.seadroid2.config.AbsLayoutItemType; @@ -95,13 +96,14 @@ import com.seafile.seadroid2.view.TipsViews; import java.io.File; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import java.util.stream.Stream; import io.reactivex.functions.Consumer; -import kotlin.Pair; public class RepoQuickFragment extends BaseFragmentWithVM { private static final String KEY_REPO_SCROLL_POSITION = "repo_scroll_position"; @@ -242,16 +244,25 @@ private void initAdapter() { adapter.setOnItemClickListener((baseQuickAdapter, view, i) -> { if (adapter.isOnActionMode()) { //toggle - toggleAdapterItemSelectedOnLongClick(i); + toggleAdapterItemSelectedState(i); //update bar title - updateContextualActionBar(); + startOrUpdateContextualActionBar(); BaseModel baseModel = adapter.getItems().get(i); + List selected = adapter.getSelectedList(); if (baseModel instanceof RepoModel m) { - getViewModel().inflateRepoMenuWithPermission(requireContext(), m, getDisableMenuIds(), getWillBeRemovedMenuIds(), isPermissionForce(m.repo_id)); + if (CollectionUtils.isEmpty(selected)) { + getViewModel().inflateRepoMenu(requireContext()); + } else { + getViewModel().inflateRepoMenuWithParams(requireContext(), m, getDisableMenuIds(), getWillBeRemovedMenuIds(), isPermissionForce(m.repo_id)); + } } else if (baseModel instanceof DirentModel m) { - getViewModel().inflateDirentMenuWithPermission(requireContext(), m, getDisableMenuIds(), getWillBeRemovedMenuIds(), isPermissionForce(m.repo_id)); + if (CollectionUtils.isEmpty(selected)) { + getViewModel().inflateDirentMenu(requireContext()); + } else { + getViewModel().inflateDirentMenuWithParams(requireContext(), CollectionUtils.newArrayList(m), m.is_checked, getDisableMenuIds(), getWillBeRemovedMenuIds(), isPermissionForce(m.repo_id)); + } } return; @@ -277,22 +288,13 @@ private void initAdapter() { return true; } - //start action mode - startContextualActionMode(); + adapter.setOnActionMode(true); - //toggle - toggleAdapterItemSelectedOnLongClick(i); + //toggle this item + toggleAdapterItemSelectedState(i); - //It's actually updating the title of the ActionBar - updateContextualActionBar(); - - BaseModel baseModel = adapter.getItems().get(i); - if (baseModel instanceof RepoModel m) { - getViewModel().inflateRepoMenuWithPermission(requireContext(), m, getDisableMenuIds(), getWillBeRemovedMenuIds(), isPermissionForce(m.repo_id)); - } else if (baseModel instanceof DirentModel m) { - getViewModel().inflateDirentMenuWithPermission(requireContext(), m, getDisableMenuIds(), getWillBeRemovedMenuIds(), isPermissionForce(m.repo_id)); + startOrUpdateContextualActionBar(); - } return true; }); @@ -328,7 +330,7 @@ public void onChanged(Boolean aBoolean) { closeActionMode(); - SettingsManager.setForceRefreshStarredListState(); + SettingsManager.setForceRefreshStarredListState(true); }); getViewModel().getObjListLiveData().observe(getViewLifecycleOwner(), repoModels -> { @@ -364,10 +366,10 @@ public void onChanged(String s) { } }); - mainViewModel.getOnActionModeLiveData().observe(getViewLifecycleOwner(), new Observer() { + mainViewModel.getOnActionModeLiveData().observe(getViewLifecycleOwner(), new Observer() { @Override - public void onChanged(Boolean aBoolean) { - onShowActionMode(aBoolean); + public void onChanged(ActionModeCallbackType callbackType) { + onShowActionMode(callbackType); } }); searchViewModel.getSearchListLiveData().observe(getViewLifecycleOwner(), new Observer>() { @@ -424,10 +426,32 @@ private List getDisableMenuIds() { } if (selectedList.size() == 1) { + BaseModel baseModel = selectedList.get(0); + if (baseModel instanceof RepoModel m) { + + } else if (baseModel instanceof DirentModel m) { + if (m.isDir()){ + return CollectionUtils.newArrayList(R.id.upload); + } + } + return null; } - return CollectionUtils.newArrayList(R.id.share, R.id.export, R.id.open, R.id.rename, R.id.upload); + long selectedRepoModelCount = selectedList.stream().filter(f -> f instanceof RepoModel).count(); + long selectedFolderCount = selectedList.stream() + .filter(f -> f instanceof DirentModel) + .map(m -> (DirentModel) m) + .filter(p -> p.isDir()) + .count(); + + if (selectedRepoModelCount > 0) { + return CollectionUtils.newArrayList(R.id.share, R.id.export, R.id.open, R.id.rename, R.id.upload, R.id.delete); + } else if (selectedFolderCount > 0) { + return CollectionUtils.newArrayList(R.id.share, R.id.export, R.id.open, R.id.rename, R.id.upload); + } else { + return CollectionUtils.newArrayList(R.id.share, R.id.export, R.id.open, R.id.rename); + } } private List getWillBeRemovedMenuIds() { @@ -485,8 +509,6 @@ private void showBottomSheetWindow(List localMenuItems) { return; } - View decorView = requireActivity().getWindow().getDecorView(); - FrameLayout content = decorView.findViewById(android.R.id.content); floatingView = getLayoutInflater().inflate(R.layout.layout_bottom_sheet_menu_view, null, false); @@ -515,6 +537,9 @@ public void onClick(@NonNull BaseQuickAdapter baseQuickAdapter, @No FrameLayout.LayoutParams p = new FrameLayout.LayoutParams(-1, -2); p.gravity = Gravity.BOTTOM; + + View decorView = requireActivity().getWindow().getDecorView(); + FrameLayout content = decorView.findViewById(android.R.id.content); content.addView(floatingView, p); } @@ -560,25 +585,69 @@ private void onBottomSheetItemClick(MenuItem item) { } } - private void onShowActionMode(boolean isShow) { - int p = Constants.DP.DP_32 * 4; - if (isShow) { + private void onShowActionMode(ActionModeCallbackType callbackType) { - } else { - p = 0; + if (callbackType == ActionModeCallbackType.CREATE) { + int p = Constants.DP.DP_32 * 4; + binding.rv.setPadding(0, 0, 0, p); + } else if (callbackType == ActionModeCallbackType.DESTORY) { + int p = 0; + binding.rv.setPadding(0, 0, 0, p); } - binding.rv.setPadding(0, 0, 0, p); - - if (isShow) { + if (callbackType == ActionModeCallbackType.CREATE) { if (!adapter.isOnActionMode()) { adapter.setOnActionMode(true); } - } else { + //select repo list + List models = adapter.getSelectedList(); + if (!getNavContext().inRepo()) { + if (CollectionUtils.isEmpty(models)) { + //click the select item of MenuItem + getViewModel().inflateRepoMenu(requireContext()); + } else { + //When press and hold to select some list item, only one can be selected + RepoModel repoModel = (RepoModel) models.get(0); + getViewModel().inflateRepoMenuWithParams(requireContext(), repoModel, getDisableMenuIds(), getWillBeRemovedMenuIds(), isPermissionForce(repoModel.repo_id)); + } + } else { + if (CollectionUtils.isEmpty(models)) { + getViewModel().inflateDirentMenu(requireContext()); + } else { + DirentModel direntModel = (DirentModel) models.get(0); + getViewModel().inflateDirentMenuWithParams(requireContext(), CollectionUtils.newArrayList(direntModel), true, getDisableMenuIds(), getWillBeRemovedMenuIds(), isPermissionForce(direntModel.repo_id)); + } + } + } else if (callbackType == ActionModeCallbackType.SELECT_ALL) { + // + List baseModels = adapter.getSelectedList(); + + if (!getNavContext().inRepo()) { + List repoModels = baseModels.stream().map(baseModel -> (RepoModel) baseModel).collect(Collectors.toList()); + getViewModel().inflateRepoMenuWithParams(requireContext(), repoModels, true, getDisableMenuIds(), getWillBeRemovedMenuIds(), false); + } else { + List direntModels = baseModels.stream().map(baseModel -> (DirentModel) baseModel).collect(Collectors.toList()); + getViewModel().inflateDirentMenuWithParams(requireContext(), direntModels, true, getDisableMenuIds(), getWillBeRemovedMenuIds(), false); + } + + + } else if (callbackType == ActionModeCallbackType.SELECT_NONE) { + //clear menu permission list + getViewModel().clearCachePermissionMap(); + + // + if (!getNavContext().inRepo()) { + getViewModel().inflateRepoMenu(requireContext()); + } else { + getViewModel().inflateDirentMenu(requireContext()); + } + } else if (callbackType == ActionModeCallbackType.DESTORY) { + removeFloatingView(); + closeActionMode(); + } else { removeFloatingView(); closeActionMode(); - } } @@ -841,10 +910,10 @@ private void navTo(BaseModel model) { getNavContext().switchToPath(getNavContext().getRepoModel(), searchModel.fullpath); loadData(isForce()); } else { - getViewModel().getRepoModelAndPermissionEntity(searchModel.repo_id, isPermissionForce(searchModel.repo_id), new Consumer>() { + getViewModel().getRepoModelAndPermissionEntity(searchModel.repo_id, isPermissionForce(searchModel.repo_id), new Consumer() { @Override - public void accept(Pair pair) throws Exception { - getNavContext().switchToPath(pair.getFirst(), searchModel.fullpath); + public void accept(RepoPermissionWrapper wrapper) throws Exception { + getNavContext().switchToPath(wrapper.repoModel, searchModel.fullpath); loadData(isForce()); } }); @@ -980,7 +1049,7 @@ public void onDestroy() { super.onDestroy(); } - private void toggleAdapterItemSelectedOnLongClick(int i) { + private void toggleAdapterItemSelectedState(int i) { BaseModel baseModel = adapter.getItems().get(i); if (baseModel instanceof RepoModel repoModel) { repoModel.is_checked = !repoModel.is_checked; @@ -1002,25 +1071,18 @@ public void closeActionMode() { } } - public void startContextualActionMode() { - //action mode on - if (actionMode == null) { - // start the actionMode - actionMode = activity.startSupportActionMode(new ActionModeCallback()); - } - } /** - * update state of contextual action bar (CAB) + * start or update state of contextual action bar (CAB) */ - public void updateContextualActionBar() { + public void startOrUpdateContextualActionBar() { if (actionMode == null) { // there are some selected items, start the actionMode actionMode = activity.startSupportActionMode(new ActionModeCallback()); - } else { - int count = adapter.getSelectedList().size(); - actionMode.setTitle(getResources().getQuantityString(R.plurals.transfer_list_items_selected, count, count)); } + + int count = adapter.getSelectedList().size(); + actionMode.setTitle(getResources().getQuantityString(R.plurals.transfer_list_items_selected, count, count)); } /** @@ -1038,7 +1100,7 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) { inflater.inflate(R.menu.repos_fragment_menu, menu); if (adapter == null) return true; - mainViewModel.getOnActionModeLiveData().setValue(true); + mainViewModel.getOnActionModeLiveData().setValue(ActionModeCallbackType.CREATE); // // to hidden "r" permissions files or folder // if (!getNavContext().isParentHasWritePermission()) { @@ -1078,23 +1140,18 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.action_mode_select_all) { adapter.setItemSelected(!allItemsSelected); - updateContextualActionBar(); + + startOrUpdateContextualActionBar(); + + if (!allItemsSelected) { + mainViewModel.getOnActionModeLiveData().setValue(ActionModeCallbackType.SELECT_ALL); + } else { + mainViewModel.getOnActionModeLiveData().setValue(ActionModeCallbackType.SELECT_NONE); + } allItemsSelected = !allItemsSelected; } -// else if (itemId == R.id.action_mode_delete) { -// deleteDirents(selectedDirents); -// } else if (itemId == R.id.action_mode_copy) { -// DirentModel dirent = selectedDirents.get(0); -// copyFiles(dirent.repo_id, dirent.repo_name, dirent.parent_dir, selectedDirents); -// } else if (itemId == R.id.action_mode_move) { -// DirentModel dirent = selectedDirents.get(0); -// moveFiles(dirent.repo_id, dirent.repo_name, dirent.parent_dir, selectedDirents); -// } else if (itemId == R.id.action_mode_download) { -// downloadDirents(selectedDirents); -// } else { -// return false; -// } + return true; } @@ -1103,7 +1160,7 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { public void onDestroyActionMode(ActionMode mode) { if (adapter == null) return; - mainViewModel.getOnActionModeLiveData().setValue(false); + mainViewModel.getOnActionModeLiveData().setValue(ActionModeCallbackType.DESTORY); } } @@ -1492,6 +1549,7 @@ private void addUploadTask(List dirents, boolean isUpdate) { } List direntModels = dirents.stream().map(m -> (DirentModel) m).collect(Collectors.toList()); + DirentModel dirent = direntModels.get(0); RepoModel targetedModel = getNavContext().getRepoModel(); 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 2afef9171..daca7b503 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 @@ -5,6 +5,7 @@ import android.view.MenuInflater; import android.view.MenuItem; +import androidx.annotation.NonNull; import androidx.lifecycle.MutableLiveData; import com.blankj.utilcode.util.CollectionUtils; @@ -14,6 +15,9 @@ import com.seafile.seadroid2.SeafException; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; +import com.seafile.seadroid2.annotation.Todo; +import com.seafile.seadroid2.annotation.Unstable; +import com.seafile.seadroid2.framework.data.model.repo.RepoPermissionWrapper; import com.seafile.seadroid2.ui.bottomsheetmenu.ActionMenu; import com.seafile.seadroid2.context.NavContext; import com.seafile.seadroid2.enums.FileViewType; @@ -41,9 +45,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; import io.reactivex.Completable; @@ -307,296 +309,386 @@ public MutableLiveData> getMenuItemListLiveData() { } - public void getRepoModelAndPermissionEntity(String repoId, boolean isForce, Consumer> consumer) { - Single> r = getRepoModelAndPermissionEntitySingle(repoId, isForce); - addSingleDisposable(r, new Consumer>() { + public void getRepoModelAndPermissionEntity(String repoId, boolean isForce, Consumer consumer) { + Single>> r = getRepoModelAndAllPermissionSingle(repoId, isForce); + addSingleDisposable(r, new Consumer>>() { @Override - public void accept(Pair repoModelPermissionEntityPair) throws Exception { + public void accept(Pair> pair) throws Exception { if (consumer != null) { - consumer.accept(repoModelPermissionEntityPair); + if (CollectionUtils.isEmpty(pair.getSecond())) { + consumer.accept(new RepoPermissionWrapper(pair.getFirst(), null)); + return; + } + + List list = pair.getSecond(); + + RepoModel repoModel = pair.getFirst(); + Optional permission = list.stream().filter(f -> f.id == repoModel.getCustomPermissionNum()).findFirst(); + + if (!permission.isPresent()) { + consumer.accept(new RepoPermissionWrapper(pair.getFirst(), null)); + return; + } + + consumer.accept(new RepoPermissionWrapper(pair.getFirst(), permission.get())); } } }); } /** - * get repoModel and permissionEntity from local, if not exist, get from remote. - * if isForce is true, get from remote directly and save to db + * do not use + *
  • get the list of libraries first.
  • + *
  • obtain the corresponding local permission data from the library list
  • + *
  • if the length of the local permission data list and the library list are the same, the data will be returned directly
  • + *
  • if not, send multiple requests to the server concurrently to obtain the permission list data
  • + *
  • merge the permission list data and the library list data to return
  • */ - private Single> getRepoModelAndPermissionEntitySingle(String repoId, boolean isForce) { - Single> dbSingle = AppDatabase.getInstance().repoDao().getRepoById(repoId); - return dbSingle.flatMap(new Function, SingleSource>>() { + @Unstable + @Todo + private Single> getMultipleRepoModelAndRelatePermissionDataSingle(List repoIds, boolean isForce) { + Single> dbSingle = AppDatabase.getInstance().repoDao().getRepoListByIds(repoIds); + return dbSingle.flatMap(new Function, SingleSource>>() { @Override - public SingleSource> apply(List repoModels) throws Exception { + public SingleSource> apply(List repoModels) throws Exception { if (CollectionUtils.isEmpty(repoModels)) { - return null; + return Single.error(new IllegalArgumentException("No RepoModels found for the given repoIds")); } - RepoModel repoModel = repoModels.get(0); - if (TextUtils.isEmpty(repoModel.permission)) { - //This issue doesn't actually happen, but it's still checked again from the remote check - return Single.just(new Pair<>(repoModel, null)); + if (isForce) { + // return empty list + List wrappers = repoModels.stream() + .map(model -> new RepoPermissionWrapper(model, null)) + .collect(Collectors.toList()); + return Single.just(wrappers); } - if (!repoModel.isCustomPermission()) { + return handleRepoPermissions(repoModels); + } + }).onErrorResumeNext(throwable -> { + // 错误处理,记录日志或返回默认值 + SLogs.e("Error in getRepoModelAndAllPermissionSingle2", throwable); + return Single.error(throwable); + }); + } - //Optionally - PermissionEntity permission = convertCustomPermission(repoModel.repo_id, repoModel.permission); + @Unstable + @Todo + private Single> handleRepoPermissions(List repoModels) { + List customPermissionIds = repoModels.stream() + .filter(RepoModel::isCustomPermission) + .map(RepoModel::getCustomPermissionNum) + .collect(Collectors.toList()); + + if (customPermissionIds.isEmpty()) { + // 如果没有自定义权限,直接返回默认权限 + List wrappers = repoModels.stream() + .map(repo -> new RepoPermissionWrapper(repo, + new PermissionEntity(repo.repo_id, repo.permission))) + .collect(Collectors.toList()); + return Single.just(wrappers); + } - return Single.just(new Pair<>(repoModel, permission)); - } + Single> pSingle = AppDatabase.getInstance().permissionDAO().getByIdsAsync(customPermissionIds); + return pSingle.map(permissionEntities -> mapPermissionsToWrappers(repoModels, permissionEntities, customPermissionIds)); + } - if (isForce) { + @Unstable + @Todo + private List mapPermissionsToWrappers(List repoModels, + List permissionEntities, + List customPermissionIds) { + boolean isValid = customPermissionIds.size() == permissionEntities.size(); + List wrappers = new ArrayList<>(); + + for (RepoModel model : repoModels) { + if (model.isCustomPermission()) { + Optional matchedPermission = permissionEntities.stream() + .filter(entity -> TextUtils.equals(entity.repo_id, model.repo_id)) + .findFirst(); + wrappers.add(new RepoPermissionWrapper(model, matchedPermission.orElse(null))); + } else { + wrappers.add(new RepoPermissionWrapper(model, + new PermissionEntity(model.repo_id, model.permission))); + } + } - //get permission from remote - return Single.just(new Pair<>(repoModel, null)); + // 如果权限不完整,需从远程加载 + if (!isValid) { + wrappers.forEach(wrapper -> { + if (wrapper.getPermission() == null) { + wrapper.setPermission(null); } + }); + } - int pNum = repoModel.getCustomPermissionNum(); - - //get special number permission from db - Single> pSingle = AppDatabase.getInstance().permissionDAO().getByIdAsync(repoId, pNum); + return wrappers; + } - return pSingle.flatMap(new Function, SingleSource>>() { + /** + * get the repoModel and repoMode‘s PermissionEntity from local, if not exist, get from remote. + * if isForce is true, get from remote directly and save to db + */ + private Single>> getRepoModelAndAllPermissionSingle(String repoId, boolean isForce) { + Single> dbSingle = AppDatabase.getInstance().repoDao().getRepoById(repoId); + return dbSingle.flatMap(new Function, SingleSource>>>() { @Override - public SingleSource> apply(List permissionEntities) throws Exception { + public SingleSource>> apply(List repoModels) throws Exception { + if (CollectionUtils.isEmpty(repoModels)) { + return null; + } + + RepoModel repoModel = repoModels.get(0); + if (TextUtils.isEmpty(repoModel.permission)) { + //This issue doesn't actually happen, but it's still checked again from the remote check + return Single.just(new Pair<>(repoModel, null)); + } - //no data in local db - if (CollectionUtils.isEmpty(permissionEntities)) { + if (isForce) { + //get permission from remote return Single.just(new Pair<>(repoModel, null)); } - //get first permission - return Single.just(new Pair<>(repoModel, permissionEntities.get(0))); + //get special number permission from db + Single> pSingle = AppDatabase.getInstance().permissionDAO().getByRepoIdAsync(repoId); + + return pSingle.flatMap((Function, SingleSource>>>) pList -> { + + //no data in local db + if (CollectionUtils.isEmpty(pList)) { + return Single.just(new Pair<>(repoModel, null)); + } + + //get first permission + return Single.just(new Pair<>(repoModel, pList)); + }); + } + }) + //from remote + .flatMap((Function>, SingleSource>>>) pair -> { + if (pair.getSecond() != null) { + return Single.just(pair); } - }); - } - }).flatMap(new Function, SingleSource>>() { - @Override - public SingleSource> apply(Pair pair) throws Exception { - if (pair.getSecond() != null) { - return Single.just(pair); - } - Single> permissionSingle = getLoadRepoPermissionFromRemoteSingle(repoId); - return permissionSingle.flatMap(new Function, SingleSource>>() { - @Override - public SingleSource> apply(List remotePermissionEntities) throws Exception { - if (CollectionUtils.isEmpty(remotePermissionEntities)) { + Single> permissionSingle = getLoadRepoPermissionFromRemoteSingle(repoId); + return permissionSingle.flatMap((Function, SingleSource>>>) remoteList -> { + if (CollectionUtils.isEmpty(remoteList)) { return Single.just(pair); } - Optional f = remotePermissionEntities.stream().filter(p -> p.id == pair.getFirst().getCustomPermissionNum()).findFirst(); - return Single.just(new Pair<>(pair.getFirst(), f.get())); - } + return Single.just(new Pair<>(pair.getFirst(), remoteList)); + }); }); - } - }); } - - public HashMap> getPermissionObjMap() { - return _permissionObjMap; - } + /** + *
    +     *     <"rw", PermissionEntity(permission,ids)>
    +     *     <"r", PermissionEntity(permission,ids)>
    +     *     <"custom-48", PermissionEntity(permission,ids)>
    +     *     <"custom-49", PermissionEntity(permission,ids)>
    +     * 
    + */ + private final HashMap _permissionMap = new HashMap<>(); public HashMap getPermissionStackMap() { return _permissionMap; } - // - private final HashMap> _permissionObjMap = new HashMap<>(); - private final HashMap _permissionMap = new HashMap<>(); - - public void inflateRepoMenuWithPermission(Context context, RepoModel repoModel, List disableMenuIds, List removedMenuIds, boolean isForce) { - if (!repoModel.is_checked) { - //remove permission - removeCachedPermissionMapData(repoModel.permission, repoModel.repo_id); - - List list = new ArrayList<>(getPermissionStackMap().values()); + public void inflateRepoMenu(Context context) { + getPermissionStackMap().clear(); + toParseMenu(context, R.menu.bottom_sheet_op_repo, null, CollectionUtils.newArrayList(R.id.unstar)); + } - int menuId = R.menu.bottom_sheet_op_repo; - List items = parseMenu(context, menuId, list, disableMenuIds, removedMenuIds); + /** + * @param repoModels + */ + public void inflateRepoMenuWithParams(Context context, List repoModels, boolean is_checked, List disableMenuIds, List removedMenuIds, boolean isForce) { + if (CollectionUtils.isEmpty(repoModels)) { + return; + } - getMenuItemListLiveData().setValue(items); + int menuId = R.menu.bottom_sheet_op_repo; + if (!is_checked) { + multipleRemoveCachedRepoPermissionMapData(repoModels); + toParseMenu(context, menuId, disableMenuIds, removedMenuIds); return; } + if (repoModels.size() == 1) { + inflateRepoMenuWithParams(context, repoModels.get(0), disableMenuIds, removedMenuIds, isForce); + } else { + List permissionEntities = CollectionUtils.newArrayList(); + for (RepoModel repoModel : repoModels) { + //NOTICE this is a special permission("r"), not a real permission + //because: currently, multiple repo lists cannot be deleted at the same time + //it will be fixed later + permissionEntities.add(new PermissionEntity(repoModel.repo_id, "r")); + } - Single> r = getRepoModelAndPermissionEntitySingle(repoModel.repo_id, isForce); + multipleCacheRepoPermissionMapData(repoModels, permissionEntities); - addSingleDisposable(r, new Consumer>() { - @Override - public void accept(Pair pair) throws Exception { - if (null == pair) { - getSeafExceptionLiveData().setValue(SeafException.unknownException); - return; - } + toParseMenu(context, menuId, disableMenuIds, removedMenuIds); + } + } - RepoModel repoModel = pair.getFirst(); - PermissionEntity entity; - if (!repoModel.isCustomPermission()) { - entity = convertCustomPermission(repoModel.repo_id, repoModel.permission); - } else { - entity = pair.getSecond(); - } + public void inflateRepoMenuWithParams(Context context, RepoModel repoModel, List disableMenuIds, List removedMenuIds, boolean isForce) { + int menuId = R.menu.bottom_sheet_op_repo; + + if (!repoModel.is_checked) { + //remove permission + removeCachedPermissionMapData(repoModel.permission, repoModel.repo_id); - cachePermissionMapData(repoModel.permission, repoModel.repo_id, entity); + toParseMenu(context, menuId, disableMenuIds, removedMenuIds); + return; + } + Single>> r = getRepoModelAndAllPermissionSingle(repoModel.repo_id, isForce); + addSingleDisposable(r, new Consumer>>() { + @Override + public void accept(Pair> pair) throws Exception { - List list = new ArrayList<>(getPermissionStackMap().values()); - int menuId = R.menu.bottom_sheet_op_repo; - List items = parseMenu(context, menuId, list, disableMenuIds, removedMenuIds); + multipleCacheRepoPermissionMapData(CollectionUtils.newArrayList(repoModel), pair.getSecond()); - getMenuItemListLiveData().setValue(items); + toParseMenu(context, menuId, disableMenuIds, removedMenuIds); } }); } + public void inflateDirentMenu(Context context) { + getPermissionStackMap().clear(); - public void inflateDirentMenuWithPermission(Context context, DirentModel direntModel, List disableMenuIds, List removedMenuIds, boolean isForce) { - int menuId = R.menu.bottom_sheet_op_dirent; - - - if (!direntModel.is_checked) { - //remove permission - removeCachedPermissionMapData(direntModel.permission, direntModel.uid); + toParseMenu(context, R.menu.bottom_sheet_op_dirent, null, CollectionUtils.newArrayList(R.id.unstar)); + } - List tempList1 = new ArrayList<>(getPermissionStackMap().values()); - List items = parseMenu(context, menuId, tempList1, disableMenuIds, removedMenuIds); - getMenuItemListLiveData().setValue(items); + public void inflateDirentMenuWithParams(Context context, List direntModels, boolean isChecked, List disableMenuIds, List removedMenuIds, boolean isForce) { + if (CollectionUtils.isEmpty(direntModels)) { return; } - if (!direntModel.isCustomPermission()) { - PermissionEntity entity = convertCustomPermission(direntModel.repo_id, direntModel.permission); - cachePermissionMapData(direntModel.permission, direntModel.uid, entity); + int menuId = R.menu.bottom_sheet_op_dirent; + + if (!isChecked) { + //remove permission + multipleRemoveCachedDirentPermissionMapData(direntModels); - List tempList = new ArrayList<>(getPermissionStackMap().values()); - List items = parseMenu(context, menuId, tempList, disableMenuIds, removedMenuIds); - getMenuItemListLiveData().setValue(items); + toParseMenu(context, menuId, disableMenuIds, removedMenuIds); return; } + String repo_id = direntModels.get(0).repo_id; - // - Single> r = getRepoModelAndPermissionEntitySingle(direntModel.repo_id, isForce); - addSingleDisposable(r, new Consumer>() { + Single>> r = getRepoModelAndAllPermissionSingle(repo_id, isForce); + addSingleDisposable(r, new Consumer>>() { @Override - public void accept(Pair pair) throws Exception { + public void accept(Pair> pair) throws Exception { RepoModel repoModel = pair.getFirst(); - PermissionEntity entity; - if (!repoModel.isCustomPermission()) { - entity = convertCustomPermission(repoModel.repo_id, repoModel.permission); - } else { - entity = pair.getSecond(); - } + List permissionList = pair.getSecond(); - cachePermissionMapData(direntModel.permission, direntModel.uid, entity); + multipleCacheDirentPermissionMapData(direntModels, permissionList); - List list = new ArrayList<>(getPermissionStackMap().values()); - List items = parseMenu(context, menuId, list, disableMenuIds, removedMenuIds); - getMenuItemListLiveData().setValue(items); + toParseMenu(context, menuId, disableMenuIds, removedMenuIds); } }); } public void clearCachePermissionMap() { - getPermissionObjMap().clear(); getPermissionStackMap().clear(); } + private void multipleRemoveCachedDirentPermissionMapData(List direntModels) { + if (CollectionUtils.isEmpty(direntModels)) { + return; + } + + for (DirentModel direntModel : direntModels) { + removeCachedPermissionMapData(direntModel.permission, direntModel.uid); + } + } + + private void multipleRemoveCachedRepoPermissionMapData(List repoModels) { + if (CollectionUtils.isEmpty(repoModels)) { + return; + } + for (RepoModel repoModel : repoModels) { + removeCachedPermissionMapData(repoModel.permission, repoModel.repo_id); + } + } + private void removeCachedPermissionMapData(String permission, String id) { - if (!getPermissionObjMap().containsKey(permission)) { + if (!getPermissionStackMap().containsKey(permission)) { return; } - Set set = getPermissionObjMap().get(permission); - if (set == null) { + PermissionEntity entity = getPermissionStackMap().get(permission); + if (entity == null) { return; } - if (!set.contains(id)) { + if (!entity.hasId(id)) { return; } - set.remove(id); + entity.removeById(id); // - if (set.isEmpty()) { + if (entity.isEmptyIds()) { getPermissionStackMap().remove(permission); } } - private void cachePermissionMapData(String permission, String id, PermissionEntity entity) { - if (!getPermissionStackMap().containsKey(permission)) { - getPermissionStackMap().put(permission, entity); + private void multipleCacheDirentPermissionMapData(List models, List entities) { + if (CollectionUtils.isEmpty(models)) { + return; + } + + for (DirentModel model : models) { + if (!model.isCustomPermission()) { + cachePermissionMapData(model.permission, model, new PermissionEntity(model.repo_id, model.permission)); + } else { + entities.stream().filter(f -> f.id == model.getCustomPermissionNum()).findFirst().ifPresent(entity -> cachePermissionMapData(model.permission, model, entity)); + } } + } - if (getPermissionObjMap().containsKey(permission)) { - Objects.requireNonNull(getPermissionObjMap().get(permission)).add(id); + private void multipleCacheRepoPermissionMapData(List models, List entities) { + if (CollectionUtils.isEmpty(models)) { + return; + } + + for (RepoModel model : models) { + if (!model.isCustomPermission()) { + cachePermissionMapData(model.permission, model, new PermissionEntity(model.repo_id, model.permission)); + } else { + entities.stream().filter(f -> f.id == model.getCustomPermissionNum()).findFirst().ifPresent(entity -> cachePermissionMapData(model.permission, model, entity)); + } + } + } + + private void cachePermissionMapData(String permission, BaseModel baseModel, @NonNull PermissionEntity entity) { + if (!getPermissionStackMap().containsKey(permission)) { + entity.cacheBaseModel(baseModel); } else { - getPermissionObjMap().put(permission, CollectionUtils.newHashSet(id)); - } - } - - private PermissionEntity convertCustomPermission(String repoId, String p) { - - PermissionEntity permission = new PermissionEntity(); - permission.name = p; - permission.id = -1; - permission.repo_id = repoId; - - if ("cloud-edit".equals(p)) { - //用户可以通过浏览器在线查看和编辑,文件不能被下载。 - permission.create = true; - permission.upload = false; - permission.download = false; - permission.preview = true; - permission.copy = true; - permission.delete = true; - permission.modify = true; - permission.download_external_link = false; - - } else if ("preview".equals(p)) { - //用户只能通过浏览器在线查看,文件不能被下载。 - permission.create = false; - permission.upload = false; - permission.download = false; - permission.preview = true; - permission.copy = false; - permission.delete = false; - permission.modify = false; - permission.download_external_link = false; - } else if ("r".equals(p)) { - //用户可以查看、下载和同步文件 - permission.create = false; - permission.upload = false; - permission.download = true; - permission.preview = true; - permission.copy = true; - permission.delete = false; - permission.modify = false; - permission.download_external_link = false; - } else if ("rw".equals(p)) { - permission.create = true; - permission.upload = true; - permission.download = true; - permission.preview = true; - permission.copy = true; - permission.delete = true; - permission.modify = true; - permission.download_external_link = true; - } - - return permission; - } - - private List parseMenu(Context context, int menuId, List list, List disableMenuIds, List removedMenuIds) { + PermissionEntity entity1 = getPermissionStackMap().get(permission); + assert entity1 != null; + entity1.cacheBaseModel(baseModel); + } + + getPermissionStackMap().put(permission, entity); + } + + private void toParseMenu(Context context, int menuId, List disableMenuIds, List removedMenuIds) { + List permissionList = new ArrayList<>(getPermissionStackMap().values()); + List items = parseMenu(context, menuId, permissionList, disableMenuIds, removedMenuIds); + getMenuItemListLiveData().setValue(items); + } + + private List parseMenu(Context context, int menuId, List permissionList, List disableMenuIds, List removedMenuIds) { List items = inflateMenu(context, menuId); //if no permission list, disable all menu - if (CollectionUtils.isEmpty(list)) { + if (CollectionUtils.isEmpty(permissionList)) { for (MenuItem item : items) { item.setEnabled(false); } @@ -619,31 +711,31 @@ private List parseMenu(Context context, int menuId, List !f.modify).count(); + long l = permissionList.stream().filter(f -> !f.modify).count(); item.setEnabled(!(l > 0)); } else if (item.getItemId() == R.id.move) { - long l = list.stream().filter(f -> !f.modify).count(); + long l = permissionList.stream().filter(f -> !f.modify).count(); item.setEnabled(!(l > 0)); } else if (item.getItemId() == R.id.copy) { - long l = list.stream().filter(f -> !f.copy).count(); + long l = permissionList.stream().filter(f -> !f.copy).count(); item.setEnabled(!(l > 0)); } else if (item.getItemId() == R.id.delete) { - long l = list.stream().filter(f -> !f.delete).count(); + long l = permissionList.stream().filter(f -> !f.delete).count(); item.setEnabled(!(l > 0)); } else if (item.getItemId() == R.id.upload) { - long l = list.stream().filter(f -> !f.upload).count(); + long l = permissionList.stream().filter(f -> !f.upload).count(); item.setEnabled(!(l > 0)); } else if (item.getItemId() == R.id.download) { - long l = list.stream().filter(f -> !f.download).count(); + long l = permissionList.stream().filter(f -> !f.download).count(); item.setEnabled(!(l > 0)); } else if (item.getItemId() == R.id.share) { - long l = list.stream().filter(f -> !f.download_external_link).count(); + long l = permissionList.stream().filter(f -> !f.download_external_link).count(); item.setEnabled(!(l > 0)); } else if (item.getItemId() == R.id.export) { - long l = list.stream().filter(f -> !f.download).count(); + long l = permissionList.stream().filter(f -> !f.download).count(); item.setEnabled(!(l > 0)); } else if (item.getItemId() == R.id.open) { - long l = list.stream().filter(f -> !f.download).count(); + long l = permissionList.stream().filter(f -> !f.download).count(); item.setEnabled(!(l > 0)); } @@ -670,22 +762,7 @@ public SingleSource> apply(PermissionListWrapperModel wra List list = CollectionUtils.newArrayList(); for (PermissionWrapperModel model : wrapperModel.permission_list) { - PermissionEntity permission = new PermissionEntity(); - permission.id = model.id; - permission.name = model.name; - permission.description = model.description; - - permission.create = model.permission.create; - permission.upload = model.permission.upload; - permission.download = model.permission.download; - permission.copy = model.permission.copy; - permission.delete = model.permission.delete; - permission.modify = model.permission.modify; - permission.download_external_link = model.permission.download_external_link; - permission.preview = model.permission.preview; - permission.repo_id = repoId; - - list.add(permission); + list.add(new PermissionEntity(repoId, model)); } Completable insertCompletable = AppDatabase.getInstance().permissionDAO().insertAllAsync(list); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorActivity.java index 81aa7a744..0bd581ed2 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorActivity.java @@ -21,13 +21,14 @@ import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.config.AbsLayoutItemType; import com.seafile.seadroid2.context.NavContext; +import com.seafile.seadroid2.databinding.ActivitySelectorObjBinding; import com.seafile.seadroid2.enums.FileViewType; import com.seafile.seadroid2.enums.RepoSelectType; import com.seafile.seadroid2.framework.data.db.entities.DirentModel; import com.seafile.seadroid2.framework.data.db.entities.EncKeyCacheEntity; +import com.seafile.seadroid2.framework.data.db.entities.PermissionEntity; import com.seafile.seadroid2.framework.data.db.entities.RepoModel; import com.seafile.seadroid2.framework.data.model.BaseModel; -import com.seafile.seadroid2.databinding.ActivitySelectorObjBinding; import com.seafile.seadroid2.ui.base.BaseActivity; import com.seafile.seadroid2.ui.dialog_fragment.NewDirFileDialogFragment; import com.seafile.seadroid2.ui.dialog_fragment.PasswordDialogFragment; @@ -271,29 +272,43 @@ public void onResultData(RepoModel uRepoModel) { dialogFragment.show(getSupportFragmentManager(), PasswordDialogFragment.class.getSimpleName()); } + private void checkCurrentPathHasWritePermission(java.util.function.Consumer consumer) { + DirentModel direntModel = mNavContext.getTopDirentModel(); + if (!direntModel.isCustomPermission()) { + consumer.accept(direntModel.hasWritePermission()); + } else { + viewModel.getPermissionFromLocal(direntModel.repo_id, direntModel.getCustomPermissionNum(), new Consumer() { + @Override + public void accept(PermissionEntity entity) throws Exception { + consumer.accept(entity != null && entity.create); + } + }); + } + } + + private void showNewDirDialog() { if (!mNavContext.inRepo()) { ToastUtils.showLong(R.string.choose_a_library); return; } - - if (!mNavContext.isParentHasWritePermission()) { - ToastUtils.showLong(R.string.library_read_only); - return; - } - - String rid = mNavContext.getRepoModel().repo_id; - String parentPath = mNavContext.getNavPath(); - NewDirFileDialogFragment dialogFragment = NewDirFileDialogFragment.newInstance(rid, parentPath, true); - dialogFragment.setRefreshListener(new OnRefreshDataListener() { + checkCurrentPathHasWritePermission(new java.util.function.Consumer() { @Override - public void onActionStatus(boolean isDone) { - if (isDone) { - loadData(); - } + public void accept(Boolean aBoolean) { + String rid = mNavContext.getRepoModel().repo_id; + String parentPath = mNavContext.getNavPath(); + NewDirFileDialogFragment dialogFragment = NewDirFileDialogFragment.newInstance(rid, parentPath, true); + dialogFragment.setRefreshListener(new OnRefreshDataListener() { + @Override + public void onActionStatus(boolean isDone) { + if (isDone) { + loadData(); + } + } + }); + dialogFragment.show(getSupportFragmentManager(), NewDirFileDialogFragment.class.getSimpleName()); } }); - dialogFragment.show(getSupportFragmentManager(), NewDirFileDialogFragment.class.getSimpleName()); } private void loadData() { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorViewModel.java index 7c9edde62..d1e4cd03b 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorViewModel.java @@ -10,6 +10,9 @@ 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.EncKeyCacheEntity; +import com.seafile.seadroid2.framework.data.db.entities.PermissionEntity; +import com.seafile.seadroid2.framework.data.model.permission.PermissionListWrapperModel; +import com.seafile.seadroid2.framework.data.model.permission.PermissionWrapperModel; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.context.NavContext; import com.seafile.seadroid2.framework.data.model.BaseModel; @@ -22,9 +25,13 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import io.reactivex.Completable; import io.reactivex.Single; +import io.reactivex.SingleSource; import io.reactivex.functions.Consumer; +import io.reactivex.functions.Function; import kotlin.Pair; public class ObjSelectorViewModel extends BaseViewModel { @@ -114,4 +121,76 @@ public void accept(Throwable throwable) throws Exception { } }); } + + + public void getPermissionFromLocal(String repoId, int pNum, Consumer consumer) { + Single> pSingle = AppDatabase.getInstance().permissionDAO().getWithAsync(repoId, pNum); + Single s = pSingle.flatMap(new Function, SingleSource>() { + @Override + public SingleSource apply(List pList) throws Exception { + + if (CollectionUtils.isEmpty(pList)) { + return null; + } + + return Single.just(pList.get(0)); + } + }).flatMap(new Function>() { + @Override + public SingleSource apply(PermissionEntity entity) throws Exception { + Single> r = getLoadRepoPermissionFromRemoteSingle(repoId); + + return r.flatMap(new Function, SingleSource>() { + @Override + public SingleSource apply(List permissionEntities) throws Exception { + if (CollectionUtils.isEmpty(permissionEntities)) { + return null; + + } + Optional p = permissionEntities.stream().filter(f -> f.id == pNum).findFirst(); + if (p.isPresent()) { + return Single.just(p.get()); + } + return null; + } + }); + } + }); + + addSingleDisposable(s, new Consumer() { + @Override + public void accept(PermissionEntity entity) throws Exception { + if (consumer != null) { + consumer.accept(entity); + } + } + }); + } + + + private Single> getLoadRepoPermissionFromRemoteSingle(String repoId) { + Single single = HttpIO.getCurrentInstance().execute(RepoService.class).getCustomSharePermissions(repoId); + return single.flatMap(new Function>>() { + @Override + public SingleSource> apply(PermissionListWrapperModel wrapperModel) throws Exception { + + List list = CollectionUtils.newArrayList(); + + for (PermissionWrapperModel model : wrapperModel.permission_list) { + list.add(new PermissionEntity(repoId, model)); + } + + Completable insertCompletable = AppDatabase.getInstance().permissionDAO().insertAllAsync(list); + Single insertAllSingle = insertCompletable.toSingleDefault(0L); + return insertAllSingle.flatMap(new Function>>() { + @Override + public SingleSource> apply(Long aLong) throws Exception { + SLogs.d("The list has been inserted into the local database"); + return Single.just(list); + } + }); + } + }); + } + } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/star/StarredQuickFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/star/StarredQuickFragment.java index bb7c51d0b..d51578abf 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/star/StarredQuickFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/star/StarredQuickFragment.java @@ -107,10 +107,18 @@ public void onFirstResume() { public void onOtherResume() { super.onOtherResume(); + if (isForce()) { + reload(); + } + } + + private boolean isForce() { boolean isForce = SettingsManager.getForceRefreshStarredListState(); if (isForce) { - reload(); + SettingsManager.setForceRefreshStarredListState(false); } + + return isForce; } private void initAdapter() { @@ -206,7 +214,7 @@ public void onMenuClick(MenuItem menuItem) { builder.removeMenu(R.id.nav_to); } - builder.show(getChildFragmentManager()); + builder.show(getChildFragmentManager(), StarredQuickFragment.class.getSimpleName()); } private void navTo(StarredModel starredModel) { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferActivity.java index 4bcf3df8e..a393ca992 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferActivity.java @@ -29,8 +29,6 @@ public class TransferActivity extends BaseActivity implements Toolbar.OnMenuItem private TransferListLayoutBinding binding; private final List fragments = new ArrayList<>(); - private Menu overFlowMenu = null; - @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); @@ -156,16 +154,6 @@ public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) { } - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_MENU: - if (overFlowMenu != null) { - overFlowMenu.performIdentifierAction(R.id.transfer_overflow_menu, 0); - } - } - return super.onKeyUp(keyCode, event); - } @Override public boolean onCreateOptionsMenu(Menu menu) { diff --git a/app/src/main/res/drawable/baseline_activities_24.xml b/app/src/main/res/drawable/baseline_activities_24.xml deleted file mode 100644 index d1eea04d0..000000000 --- a/app/src/main/res/drawable/baseline_activities_24.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/baseline_activities_32.xml b/app/src/main/res/drawable/baseline_activities_32.xml new file mode 100644 index 000000000..ecbaf9600 --- /dev/null +++ b/app/src/main/res/drawable/baseline_activities_32.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/baseline_activities_bold_24.xml b/app/src/main/res/drawable/baseline_activities_bold_24.xml deleted file mode 100644 index d61da030c..000000000 --- a/app/src/main/res/drawable/baseline_activities_bold_24.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/baseline_repo_bold_32.xml b/app/src/main/res/drawable/baseline_repo_bold_32.xml new file mode 100644 index 000000000..a09df8cb4 --- /dev/null +++ b/app/src/main/res/drawable/baseline_repo_bold_32.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/baseline_settings_32.xml b/app/src/main/res/drawable/baseline_settings_32.xml new file mode 100644 index 000000000..1092217d1 --- /dev/null +++ b/app/src/main/res/drawable/baseline_settings_32.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/baseline_starred_32.xml b/app/src/main/res/drawable/baseline_starred_32.xml new file mode 100644 index 000000000..6153d4543 --- /dev/null +++ b/app/src/main/res/drawable/baseline_starred_32.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_setup_32.xml b/app/src/main/res/drawable/ic_setup_32.xml deleted file mode 100644 index 0df7a9a07..000000000 --- a/app/src/main/res/drawable/ic_setup_32.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/seekbar_for_webview_style.xml b/app/src/main/res/drawable/seekbar_for_webview_style.xml index 5501769bc..945f226a7 100644 --- a/app/src/main/res/drawable/seekbar_for_webview_style.xml +++ b/app/src/main/res/drawable/seekbar_for_webview_style.xml @@ -7,8 +7,8 @@ + android:endColor="@color/toolbar_progress_bar_background_color" + android:startColor="@color/toolbar_progress_bar_background_color" /> @@ -20,8 +20,8 @@ + android:endColor="@color/toolbar_progress_bar_second_color" + android:startColor="@color/toolbar_progress_bar_second_color" /> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 6ad01c3a6..f00160374 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -11,7 +11,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - @@ -57,22 +57,10 @@ - - - diff --git a/app/src/main/res/layout/item_repo.xml b/app/src/main/res/layout/item_repo.xml index 643f611fc..1caa7b160 100644 --- a/app/src/main/res/layout/item_repo.xml +++ b/app/src/main/res/layout/item_repo.xml @@ -12,6 +12,7 @@ android:id="@+id/item_icon" android:layout_width="@dimen/rv_item_icon_width" android:layout_height="@dimen/rv_item_icon_width" + android:contentDescription="@null" android:padding="12dp" android:src="@drawable/baseline_repo_24" app:layout_constraintBottom_toBottomOf="parent" @@ -51,9 +52,9 @@ android:id="@+id/action_container" android:layout_width="wrap_content" android:layout_height="match_parent" - app:layout_constraintStart_toEndOf="@+id/text_container" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/text_container" app:layout_constraintTop_toTopOf="parent"> @@ -12,11 +12,8 @@ android:id="@+id/item_icon" android:layout_width="@dimen/lv_icon_width" android:layout_height="@dimen/lv_icon_height" - android:layout_centerVertical="true" - android:layout_marginVertical="4dp" android:contentDescription="@null" android:padding="12dp" - android:scaleType="centerCrop" android:src="@drawable/baseline_repo_24" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -60,6 +57,7 @@ android:contentDescription="@string/file_action_more" android:foreground="@drawable/selection_control_ripple" android:padding="8dp" + android:scaleType="center" android:src="@drawable/baseline_more_vert_24" android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" diff --git a/app/src/main/res/layout/layout_bottom_sheet_menu_view.xml b/app/src/main/res/layout/layout_bottom_sheet_menu_view.xml index 215702e73..8cfd6ac4f 100644 --- a/app/src/main/res/layout/layout_bottom_sheet_menu_view.xml +++ b/app/src/main/res/layout/layout_bottom_sheet_menu_view.xml @@ -5,7 +5,7 @@ android:id="@+id/bottom_drawer_2" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/white" + android:background="@color/bar_background_color" android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/bottom_navigation_menu.xml b/app/src/main/res/menu/bottom_navigation_menu.xml index f4ca6c31b..0e7dc36ab 100644 --- a/app/src/main/res/menu/bottom_navigation_menu.xml +++ b/app/src/main/res/menu/bottom_navigation_menu.xml @@ -3,21 +3,21 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> \ No newline at end of file diff --git a/app/src/main/res/menu/browser_menu.xml b/app/src/main/res/menu/browser_menu.xml index 73f7c5fcd..f65bf1524 100644 --- a/app/src/main/res/menu/browser_menu.xml +++ b/app/src/main/res/menu/browser_menu.xml @@ -7,12 +7,14 @@ android:icon="@drawable/baseline_search_24" android:title="@string/search_menu_item" app:actionViewClass="androidx.appcompat.widget.SearchView" + app:iconTint="@color/bar_icon_tint_color" app:showAsAction="ifRoom|collapseActionView" /> - - - - - + android:id="@+id/cancel_transfer_tasks" + android:title="@string/transfer_list_cancel_all" + android:visible="true" + app:showAsAction="never" /> + + \ No newline at end of file diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 1360d9ec6..10d12c3db 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -2,7 +2,6 @@ #FFFFFF #000000 - #FFF2F2F2 #6E6E6E #FF0000 #BDBDBD @@ -57,14 +56,24 @@ @color/material_grey_919 @color/material_grey_911 - @color/material_grey_50 - @color/material_grey_50 - @color/material_grey_400 + @color/material_grey_200 + @color/material_grey_200 - @color/material_grey_700 - @color/material_grey_919 + + @color/material_grey_500 + + @color/bar_title_color + @color/material_grey_700 + + + @color/material_grey_700 + @color/material_grey_919 @color/material_grey_919 + @color/blue_500 + @color/white + + @color/material_grey_824 @@ -73,9 +82,6 @@ #9a9a9a - @color/blue_500 - @color/white - #191919 #f2f2f2 diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index cb9c14401..0688e981d 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -2,14 +2,15 @@ - @@ -63,9 +66,9 @@ 10dip -10dip wrap_content - @color/white - @color/white + @color/bar_background_color + @color/bar_background_color