From 7b38627947e35a4bb655b8cf3bf2eff5495abc84 Mon Sep 17 00:00:00 2001 From: A117870935 Date: Tue, 27 Jun 2023 23:07:46 +0530 Subject: [PATCH] Allow to create encrypted folder directly from bottom sheet dialog from NC PR: https://github.com/nextcloud/android/pull/10782 --- .../android/ui/dialog/DialogFragmentIT.java | 5 ++ .../android/files/FileMenuFilter.java | 13 ++++- .../operations/CreateFolderOperation.java | 36 +++++++++++- .../android/services/OperationsService.java | 3 + .../android/ui/adapter/OCFileListAdapter.java | 4 ++ .../ui/dialog/CreateFolderDialogFragment.kt | 18 ++++-- .../OCFileListBottomSheetActions.java | 5 ++ .../fragment/OCFileListBottomSheetDialog.java | 16 +++++- .../ui/fragment/OCFileListFragment.java | 55 ++++++++++++------- .../ui/helpers/FileOperationsHelper.java | 5 ++ 10 files changed, 128 insertions(+), 32 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java b/app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java index beefce3cda72..484ece9e49d4 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java +++ b/app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java @@ -357,6 +357,11 @@ public void createFolder() { } + @Override + public void createEncryptedFolder() { + + } + @Override public void uploadFromApp() { diff --git a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java index ecf266cc045c..0bb8a8fcda7b 100644 --- a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java +++ b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java @@ -243,7 +243,7 @@ private void filterUnlock(List toHide, boolean fileLockingEnabled) { private void filterEncrypt(List toHide, boolean endToEndEncryptionEnabled) { if (files.isEmpty() || !isSingleSelection() || isSingleFile() || isEncryptedFolder() || isGroupFolder() - || !endToEndEncryptionEnabled || !isEmptyFolder() || isShared()) { + || !endToEndEncryptionEnabled || !isEmptyFolder() || isShared() || isInSubFolder()) { toHide.add(R.id.action_encrypted); } } @@ -566,4 +566,15 @@ private boolean isShared() { } return false; } + + private boolean isInSubFolder() { + OCFile folder = files.iterator().next(); + OCFile parent = storageManager.getFileById(folder.getParentId()); + + if (parent == null) { + return false; + } + + return !OCFile.ROOT_PATH.equals(parent.getRemotePath()); + } } diff --git a/app/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java index f5d2f1f43a5f..78885279e79e 100644 --- a/app/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java @@ -65,16 +65,31 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper private RemoteFile createdRemoteFolder; private User user; private Context context; + private boolean encrypted; /** * Constructor */ - public CreateFolderOperation(String remotePath, User user, Context context, FileDataStorageManager storageManager) { + public CreateFolderOperation(String remotePath, + User user, + Context context, + FileDataStorageManager storageManager + ) { + this(remotePath, false, user, context, storageManager); + } + + public CreateFolderOperation(String remotePath, + boolean encrypted, + User user, + Context context, + FileDataStorageManager storageManager + ) { super(storageManager); this.remotePath = remotePath; this.user = user; this.context = context; + this.encrypted = encrypted; } @Override @@ -102,7 +117,7 @@ protected RemoteOperationResult run(OwnCloudClient client) { if (encryptedAncestor) { return encryptedCreate(parent, client); } else { - return normalCreate(client); + return normalCreate(client, encrypted); } } @@ -288,7 +303,7 @@ private String createRandomFileName(DecryptedFolderMetadata metadata) { return encryptedFileName; } - private RemoteOperationResult normalCreate(OwnCloudClient client) { + private RemoteOperationResult normalCreate(OwnCloudClient client, boolean encrypted) { RemoteOperationResult result = new CreateFolderRemoteOperation(remotePath, true).execute(client); if (result.isSuccess()) { @@ -297,6 +312,21 @@ private RemoteOperationResult normalCreate(OwnCloudClient client) { createdRemoteFolder = (RemoteFile) remoteFolderOperationResult.getData().get(0); saveFolderInDB(); + + if (encrypted) { + final OCFile folder = getStorageManager().getFileByDecryptedRemotePath(remotePath); + + final RemoteOperationResult remoteOperationResult = + new ToggleEncryptionRemoteOperation(folder.getLocalId(), + remotePath, + true) + .execute(client); + + if (remoteOperationResult.isSuccess()) { + folder.setEncrypted(true); + getStorageManager().saveFile(folder); + } + } } else { Log_OC.e(TAG, remotePath + " hasn't been created"); } diff --git a/app/src/main/java/com/owncloud/android/services/OperationsService.java b/app/src/main/java/com/owncloud/android/services/OperationsService.java index ce6ca4697eb2..2c6687b564ae 100644 --- a/app/src/main/java/com/owncloud/android/services/OperationsService.java +++ b/app/src/main/java/com/owncloud/android/services/OperationsService.java @@ -93,6 +93,7 @@ public class OperationsService extends Service { public static final String EXTRA_ACCOUNT = "ACCOUNT"; public static final String EXTRA_SERVER_URL = "SERVER_URL"; public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH"; + public static final String EXTRA_ENCRYPTED = "ENCRYPTED"; public static final String EXTRA_NEWNAME = "NEWNAME"; public static final String EXTRA_REMOVE_ONLY_LOCAL = "REMOVE_LOCAL_COPY"; public static final String EXTRA_SYNC_FILE_CONTENTS = "SYNC_FILE_CONTENTS"; @@ -685,7 +686,9 @@ private Pair newOperation(Intent operationIntent) { case ACTION_CREATE_FOLDER: remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH); + boolean encrypted = operationIntent.getBooleanExtra(EXTRA_ENCRYPTED, false); operation = new CreateFolderOperation(remotePath, + encrypted, user, getApplicationContext(), fileDataStorageManager); diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 92478acdcaf0..dcc1e25e2960 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -570,6 +570,10 @@ private String generateFooterText(int filesCount, int foldersCount) { public @Nullable OCFile getItem(int position) { + if (position == -1) { + return null; + } + int newPosition = position; if (shouldShowHeader() && position > 0) { diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt index 70c4e0c6b987..826e8d41ebf7 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt @@ -62,8 +62,9 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList @JvmField @Inject var keyboardUtils: KeyboardUtils? = null - private var mParentFolder: OCFile? = null + private var parentFolder: OCFile? = null private var positiveButton: MaterialButton? = null + private var encrypted = false private lateinit var binding: EditBoxDialogBinding @@ -92,7 +93,8 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList @Suppress("EmptyFunctionBlock") override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - mParentFolder = arguments?.getParcelable(ARG_PARENT_FOLDER) + parentFolder = arguments?.getParcelable(ARG_PARENT_FOLDER) + encrypted = arguments?.getBoolean(ARG_ENCRYPTED) ?: false // Inflate the layout for the dialog val inflater = requireActivity().layoutInflater @@ -176,15 +178,20 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList DisplayUtils.showSnackMessage(requireActivity(), R.string.filename_forbidden_charaters_from_server) return } - val path = mParentFolder!!.decryptedRemotePath + newFolderName + OCFile.PATH_SEPARATOR - (requireActivity() as ComponentsGetter).fileOperationsHelper.createFolder(path) + val path = parentFolder!!.decryptedRemotePath + newFolderName + OCFile.PATH_SEPARATOR + (requireActivity() as ComponentsGetter).fileOperationsHelper.createFolder(path, encrypted) } } companion object { private const val ARG_PARENT_FOLDER = "PARENT_FOLDER" + private const val ARG_ENCRYPTED = "ENCRYPTED" const val CREATE_FOLDER_FRAGMENT = "CREATE_FOLDER_FRAGMENT" + @JvmStatic + fun newInstance(parentFolder: OCFile?): CreateFolderDialogFragment { + return newInstance(parentFolder, false) + } /** * Public factory method to create new CreateFolderDialogFragment instances. * @@ -192,10 +199,11 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList * @return Dialog ready to show. */ @JvmStatic - fun newInstance(parentFolder: OCFile?): CreateFolderDialogFragment { + fun newInstance(parentFolder: OCFile?, encrypted: Boolean): CreateFolderDialogFragment { val frag = CreateFolderDialogFragment() val args = Bundle() args.putParcelable(ARG_PARENT_FOLDER, parentFolder) + args.putBoolean(ARG_ENCRYPTED, encrypted) frag.arguments = args return frag } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetActions.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetActions.java index 97b3f9f3278b..baa0e46d9c70 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetActions.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetActions.java @@ -32,6 +32,11 @@ public interface OCFileListBottomSheetActions { */ void createFolder(); + /** + * creates an encrypted folder within the actual folder + */ + void createEncryptedFolder(); + /** * offers a file upload with the Android OS file picker to the current folder. */ diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java index 95701987ab50..6db65cedfe16 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java @@ -99,9 +99,8 @@ protected void onCreate(Bundle savedInstanceState) { binding.addToCloud.setText(getContext().getResources().getString(R.string.add_to_cloud, themeUtils.getDefaultDisplayNameForRootFolder(getContext()))); - OCCapability capability = fileActivity.getCapabilities(); - if (capability != null && - capability.getRichDocuments().isTrue() && + OCCapability capability = fileActivity.getStorageManager().getCapability(user.getAccountName()); + if (capability.getRichDocuments().isTrue() && capability.getRichDocumentsDirectEditing().isTrue() && capability.getRichDocumentsTemplatesAvailable().isTrue() && !file.isEncrypted()) { @@ -149,6 +148,12 @@ protected void onCreate(Bundle savedInstanceState) { binding.menuDirectCameraUpload.setVisibility(View.GONE); } + if (capability.getEndToEndEncryption().isTrue() && OCFile.ROOT_PATH.equals(file.getRemotePath())) { + binding.menuEncryptedMkdir.setVisibility(View.VISIBLE); + } else { + binding.menuEncryptedMkdir.setVisibility(View.GONE); + } + // create rich workspace if (editorUtils.isEditorAvailable(user, MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN) && @@ -183,6 +188,11 @@ private void setupClickListener() { dismiss(); }); + binding.menuEncryptedMkdir.setOnClickListener(v -> { + actions.createEncryptedFolder(); + dismiss(); + }); + binding.menuUploadFromApp.setOnClickListener(v -> { actions.uploadFromApp(); dismiss(); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index 420cabc849d0..4872c7eabaa5 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -511,6 +511,14 @@ public void createFolder() { .show(getActivity().getSupportFragmentManager(), DIALOG_CREATE_FOLDER); } + @Override + public void createEncryptedFolder() { + if (checkEncryptionIsSetup(null)) { + CreateFolderDialogFragment.newInstance(mFile, true) + .show(getActivity().getSupportFragmentManager(), DIALOG_CREATE_FOLDER); + } + } + @Override public void uploadFromApp() { Intent action = new Intent(Intent.ACTION_GET_CONTENT); @@ -1125,10 +1133,11 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { int position = data.getIntExtra(SetupEncryptionDialogFragment.ARG_POSITION, -1); OCFile file = mAdapter.getItem(position); - if (file != null) { - mContainerActivity.getFileOperationsHelper().toggleEncryption(file, true); - mAdapter.setEncryptionAttributeForItemID(file.getRemoteId(), true); + if (file == null) { + return; } + mContainerActivity.getFileOperationsHelper().toggleEncryption(file, true); + mAdapter.setEncryptionAttributeForItemID(file.getRemoteId(), true); // update state and view of this fragment searchFragment = false; @@ -1685,45 +1694,51 @@ protected RemoteOperation getSearchRemoteOperation(final User currentUser, final @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onMessageEvent(EncryptionEvent event) { + if (checkEncryptionIsSetup(event.remoteId)) { + encryptFolder(event.localId, event.remoteId, event.remotePath, event.shouldBeEncrypted); + } + } + + private boolean checkEncryptionIsSetup(@Nullable String remoteId) { final User user = accountManager.getUser(); // check if keys are stored String publicKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PUBLIC_KEY); String privateKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PRIVATE_KEY); - FileDataStorageManager storageManager = mContainerActivity.getStorageManager(); - OCFile file = storageManager.getFileByRemoteId(event.getRemoteId()); - if (publicKey.isEmpty() || privateKey.isEmpty()) { Log_OC.d(TAG, "no public key for " + user.getAccountName()); + FileDataStorageManager storageManager = mContainerActivity.getStorageManager(); int position = -1; - if (file != null) { - position = mAdapter.getItemPosition(file); + if (remoteId != null) { + OCFile file = storageManager.getFileByRemoteId(remoteId); + if (file != null) { + position = mAdapter.getItemPosition(file); + } } SetupEncryptionDialogFragment dialog = SetupEncryptionDialogFragment.newInstance(user, position); dialog.setTargetFragment(this, SETUP_ENCRYPTION_REQUEST_CODE); dialog.show(getParentFragmentManager(), SETUP_ENCRYPTION_DIALOG_TAG); + + return false; } else { - encryptFolder(file, - event.getLocalId(), - event.getRemoteId(), - event.getRemotePath(), - event.getShouldBeEncrypted(), - publicKey, - privateKey); + return true; } } - private void encryptFolder(OCFile folder, - long localId, + private void encryptFolder(long localId, String remoteId, String remotePath, - boolean shouldBeEncrypted, - String publicKey, - String privateKey) { + boolean shouldBeEncrypted) { try { User user = accountManager.getUser(); + String publicKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PUBLIC_KEY); + String privateKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PRIVATE_KEY); + + FileDataStorageManager storageManager = mContainerActivity.getStorageManager(); + OCFile folder = storageManager.getFileByRemoteId(remoteId); + OwnCloudClient client = clientFactory.create(user); RemoteOperationResult remoteOperationResult = new ToggleEncryptionRemoteOperation(localId, remotePath, diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 0e66daa7a429..e67df017e223 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -971,11 +971,16 @@ public void removeFiles(Collection files, boolean onlyLocalCopy, boolean public void createFolder(String remotePath) { + createFolder(remotePath, false); + } + + public void createFolder(String remotePath, boolean encrypted) { // Create Folder Intent service = new Intent(fileActivity, OperationsService.class); service.setAction(OperationsService.ACTION_CREATE_FOLDER); service.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount()); service.putExtra(OperationsService.EXTRA_REMOTE_PATH, remotePath); + service.putExtra(OperationsService.EXTRA_ENCRYPTED, encrypted); mWaitingForOpId = fileActivity.getOperationsServiceBinder().queueNewOperation(service); fileActivity.showLoadingDialog(fileActivity.getString(R.string.wait_a_moment));