Skip to content

Commit 82b2702

Browse files
committed
Allow to create encrypted folder directly from bottom sheet dialog from NC PR: nextcloud#10782
1 parent cf222d4 commit 82b2702

File tree

11 files changed

+135
-38
lines changed

11 files changed

+135
-38
lines changed

app/src/androidTest/java/com/owncloud/android/ui/dialog/DialogFragmentIT.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,11 @@ public void createFolder() {
342342

343343
}
344344

345+
@Override
346+
public void createEncryptedFolder() {
347+
348+
}
349+
345350
@Override
346351
public void uploadFromApp() {
347352

app/src/main/java/com/owncloud/android/files/FileMenuFilter.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ private void filterUnlock(List<Integer> toHide, boolean fileLockingEnabled) {
236236

237237
private void filterEncrypt(List<Integer> toHide, boolean endToEndEncryptionEnabled) {
238238
if (files.isEmpty() || !isSingleSelection() || isSingleFile() || isEncryptedFolder() || isGroupFolder()
239-
|| !endToEndEncryptionEnabled || !isEmptyFolder() || isShared()) {
239+
|| !endToEndEncryptionEnabled || !isEmptyFolder() || isShared() || isInSubFolder()) {
240240
toHide.add(R.id.action_encrypted);
241241
}
242242
}
@@ -574,4 +574,15 @@ private boolean isShared() {
574574
}
575575
return false;
576576
}
577+
578+
private boolean isInSubFolder() {
579+
OCFile folder = files.iterator().next();
580+
OCFile parent = storageManager.getFileById(folder.getParentId());
581+
582+
if (parent == null) {
583+
return false;
584+
}
585+
586+
return !OCFile.ROOT_PATH.equals(parent.getRemotePath());
587+
}
577588
}

app/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,31 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper
6060
private RemoteFile createdRemoteFolder;
6161
private User user;
6262
private Context context;
63+
private boolean encrypted;
6364

6465
/**
6566
* Constructor
6667
*/
67-
public CreateFolderOperation(String remotePath, User user, Context context, FileDataStorageManager storageManager) {
68+
public CreateFolderOperation(String remotePath,
69+
User user,
70+
Context context,
71+
FileDataStorageManager storageManager
72+
) {
73+
this(remotePath, false, user, context, storageManager);
74+
}
75+
76+
public CreateFolderOperation(String remotePath,
77+
boolean encrypted,
78+
User user,
79+
Context context,
80+
FileDataStorageManager storageManager
81+
) {
6882
super(storageManager);
6983

7084
this.remotePath = remotePath;
7185
this.user = user;
7286
this.context = context;
87+
this.encrypted = encrypted;
7388
}
7489

7590
@Override
@@ -105,7 +120,7 @@ protected RemoteOperationResult run(OwnCloudClient client) {
105120
}
106121
return new RemoteOperationResult(new IllegalStateException("E2E not supported"));
107122
} else {
108-
return normalCreate(client);
123+
return normalCreate(client, encrypted);
109124
}
110125
}
111126

@@ -473,7 +488,7 @@ private String createRandomFileName(DecryptedFolderMetadataFileV1 metadata) {
473488
return encryptedFileName;
474489
}
475490

476-
private RemoteOperationResult normalCreate(OwnCloudClient client) {
491+
private RemoteOperationResult normalCreate(OwnCloudClient client, boolean encrypted) {
477492
RemoteOperationResult result = new CreateFolderRemoteOperation(remotePath, true).execute(client);
478493

479494
if (result.isSuccess()) {
@@ -482,6 +497,21 @@ private RemoteOperationResult normalCreate(OwnCloudClient client) {
482497

483498
createdRemoteFolder = (RemoteFile) remoteFolderOperationResult.getData().get(0);
484499
saveFolderInDB();
500+
501+
if (encrypted) {
502+
final OCFile folder = getStorageManager().getFileByDecryptedRemotePath(remotePath);
503+
504+
final RemoteOperationResult remoteOperationResult =
505+
new ToggleEncryptionRemoteOperation(folder.getLocalId(),
506+
remotePath,
507+
true)
508+
.execute(client);
509+
510+
if (remoteOperationResult.isSuccess()) {
511+
folder.setEncrypted(true);
512+
getStorageManager().saveFile(folder);
513+
}
514+
}
485515
} else {
486516
Log_OC.e(TAG, remotePath + " hasn't been created");
487517
}

app/src/main/java/com/owncloud/android/services/OperationsService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public class OperationsService extends Service {
8484
public static final String EXTRA_ACCOUNT = "ACCOUNT";
8585
public static final String EXTRA_SERVER_URL = "SERVER_URL";
8686
public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
87+
public static final String EXTRA_ENCRYPTED = "ENCRYPTED";
8788
public static final String EXTRA_NEWNAME = "NEWNAME";
8889
public static final String EXTRA_REMOVE_ONLY_LOCAL = "REMOVE_LOCAL_COPY";
8990
public static final String EXTRA_SYNC_FILE_CONTENTS = "SYNC_FILE_CONTENTS";
@@ -685,7 +686,9 @@ private Pair<Target, RemoteOperation> newOperation(Intent operationIntent) {
685686

686687
case ACTION_CREATE_FOLDER:
687688
remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
689+
boolean encrypted = operationIntent.getBooleanExtra(EXTRA_ENCRYPTED, false);
688690
operation = new CreateFolderOperation(remotePath,
691+
encrypted,
689692
user,
690693
getApplicationContext(),
691694
fileDataStorageManager);

app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,10 @@ public int getItemCount() {
323323

324324
@Nullable
325325
public OCFile getItem(int position) {
326+
if (position == -1) {
327+
return null;
328+
}
329+
326330
int newPosition = position;
327331

328332
if (shouldShowHeader() && position > 0) {

app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList
5454
@JvmField
5555
@Inject
5656
var keyboardUtils: KeyboardUtils? = null
57-
private var mParentFolder: OCFile? = null
57+
private var parentFolder: OCFile? = null
5858
private var positiveButton: MaterialButton? = null
59+
private var encrypted = false
5960

6061
private lateinit var binding: EditBoxDialogBinding
6162

@@ -84,7 +85,8 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList
8485

8586
@Suppress("EmptyFunctionBlock")
8687
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
87-
mParentFolder = arguments?.getParcelableArgument(ARG_PARENT_FOLDER, OCFile::class.java)
88+
parentFolder = arguments?.getParcelableArgument(ARG_PARENT_FOLDER, OCFile::class.java)
89+
encrypted = arguments?.getBoolean(ARG_ENCRYPTED) ?: false
8890

8991
// Inflate the layout for the dialog
9092
val inflater = requireActivity().layoutInflater
@@ -168,26 +170,32 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList
168170
DisplayUtils.showSnackMessage(requireActivity(), R.string.filename_forbidden_charaters_from_server)
169171
return
170172
}
171-
val path = mParentFolder!!.decryptedRemotePath + newFolderName + OCFile.PATH_SEPARATOR
172-
(requireActivity() as ComponentsGetter).fileOperationsHelper.createFolder(path)
173+
val path = parentFolder!!.decryptedRemotePath + newFolderName + OCFile.PATH_SEPARATOR
174+
(requireActivity() as ComponentsGetter).fileOperationsHelper.createFolder(path, encrypted)
173175
}
174176
}
175177

176178
companion object {
177179
private const val ARG_PARENT_FOLDER = "PARENT_FOLDER"
180+
private const val ARG_ENCRYPTED = "ENCRYPTED"
178181
const val CREATE_FOLDER_FRAGMENT = "CREATE_FOLDER_FRAGMENT"
179182

183+
@JvmStatic
184+
fun newInstance(parentFolder: OCFile?): CreateFolderDialogFragment {
185+
return newInstance(parentFolder, false)
186+
}
180187
/**
181188
* Public factory method to create new CreateFolderDialogFragment instances.
182189
*
183190
* @param parentFolder Folder to create
184191
* @return Dialog ready to show.
185192
*/
186193
@JvmStatic
187-
fun newInstance(parentFolder: OCFile?): CreateFolderDialogFragment {
194+
fun newInstance(parentFolder: OCFile?, encrypted: Boolean): CreateFolderDialogFragment {
188195
val frag = CreateFolderDialogFragment()
189196
val args = Bundle()
190197
args.putParcelable(ARG_PARENT_FOLDER, parentFolder)
198+
args.putBoolean(ARG_ENCRYPTED, encrypted)
191199
frag.arguments = args
192200
return frag
193201
}

app/src/main/java/com/owncloud/android/ui/events/EncryptionEvent.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@ package com.owncloud.android.ui.events
1010
* Event for set folder as encrypted/decrypted
1111
*/
1212
class EncryptionEvent(
13+
@JvmField
1314
val localId: Long,
15+
@JvmField
1416
val remoteId: String,
17+
@JvmField
1518
val remotePath: String,
19+
@JvmField
1620
val shouldBeEncrypted: Boolean
1721
)

app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetActions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ public interface OCFileListBottomSheetActions {
1818
*/
1919
void createFolder();
2020

21+
/**
22+
* creates an encrypted folder within the actual folder
23+
*/
24+
void createEncryptedFolder();
25+
2126
/**
2227
* offers a file upload with the Android OS file picker to the current folder.
2328
*/

app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,8 @@ protected void onCreate(Bundle savedInstanceState) {
8686
binding.addToCloud.setText(getContext().getResources().getString(R.string.add_to_cloud,
8787
themeUtils.getDefaultDisplayNameForRootFolder(getContext())));
8888

89-
OCCapability capability = fileActivity.getCapabilities();
90-
if (capability != null &&
91-
capability.getRichDocuments().isTrue() &&
89+
OCCapability capability = fileActivity.getStorageManager().getCapability(user.getAccountName());
90+
if (capability.getRichDocuments().isTrue() &&
9291
capability.getRichDocumentsDirectEditing().isTrue() &&
9392
capability.getRichDocumentsTemplatesAvailable().isTrue() &&
9493
!file.isEncrypted()) {
@@ -136,6 +135,12 @@ protected void onCreate(Bundle savedInstanceState) {
136135
binding.menuDirectCameraUpload.setVisibility(View.GONE);
137136
}
138137

138+
if (capability.getEndToEndEncryption().isTrue() && OCFile.ROOT_PATH.equals(file.getRemotePath())) {
139+
binding.menuEncryptedMkdir.setVisibility(View.VISIBLE);
140+
} else {
141+
binding.menuEncryptedMkdir.setVisibility(View.GONE);
142+
}
143+
139144
// create rich workspace
140145
if (editorUtils.isEditorAvailable(user,
141146
MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN) &&
@@ -170,6 +175,11 @@ private void setupClickListener() {
170175
dismiss();
171176
});
172177

178+
binding.menuEncryptedMkdir.setOnClickListener(v -> {
179+
actions.createEncryptedFolder();
180+
dismiss();
181+
});
182+
173183
binding.menuUploadFromApp.setOnClickListener(v -> {
174184
actions.uploadFromApp();
175185
dismiss();

app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,14 @@ public void createFolder() {
509509
.show(getActivity().getSupportFragmentManager(), DIALOG_CREATE_FOLDER);
510510
}
511511

512+
@Override
513+
public void createEncryptedFolder() {
514+
if (checkEncryptionIsSetup(null)) {
515+
CreateFolderDialogFragment.newInstance(mFile, true)
516+
.show(getActivity().getSupportFragmentManager(), DIALOG_CREATE_FOLDER);
517+
}
518+
}
519+
512520
@Override
513521
public void uploadFromApp() {
514522
Intent action = new Intent(Intent.ACTION_GET_CONTENT);
@@ -1136,10 +1144,11 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
11361144
int position = data.getIntExtra(SetupEncryptionDialogFragment.ARG_POSITION, -1);
11371145
OCFile file = mAdapter.getItem(position);
11381146

1139-
if (file != null) {
1140-
mContainerActivity.getFileOperationsHelper().toggleEncryption(file, true);
1141-
mAdapter.setEncryptionAttributeForItemID(file.getRemoteId(), true);
1147+
if (file == null) {
1148+
return;
11421149
}
1150+
mContainerActivity.getFileOperationsHelper().toggleEncryption(file, true);
1151+
mAdapter.setEncryptionAttributeForItemID(file.getRemoteId(), true);
11431152

11441153
// update state and view of this fragment
11451154
searchFragment = false;
@@ -1719,49 +1728,52 @@ protected RemoteOperation getSearchRemoteOperation(final User currentUser, final
17191728

17201729
@Subscribe(threadMode = ThreadMode.BACKGROUND)
17211730
public void onMessageEvent(EncryptionEvent event) {
1731+
if (checkEncryptionIsSetup(event.remoteId)) {
1732+
encryptFolder(event.localId, event.remoteId, event.remotePath, event.shouldBeEncrypted);
1733+
}
1734+
}
1735+
1736+
private boolean checkEncryptionIsSetup(@Nullable String remoteId) {
17221737
final User user = accountManager.getUser();
17231738

17241739
// check if keys are stored
17251740
String publicKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PUBLIC_KEY);
17261741
String privateKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PRIVATE_KEY);
17271742

1728-
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
1729-
OCFile file = storageManager.getFileByRemoteId(event.getRemoteId());
1730-
17311743
if (publicKey.isEmpty() || privateKey.isEmpty()) {
17321744
Log_OC.d(TAG, "no public key for " + user.getAccountName());
17331745

1746+
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
17341747
int position = -1;
1735-
if (file != null) {
1736-
position = mAdapter.getItemPosition(file);
1748+
if (remoteId != null) {
1749+
OCFile file = storageManager.getFileByRemoteId(remoteId);
1750+
if (file != null) {
1751+
position = mAdapter.getItemPosition(file);
1752+
}
17371753
}
17381754
SetupEncryptionDialogFragment dialog = SetupEncryptionDialogFragment.newInstance(user, position);
17391755
dialog.setTargetFragment(this, SETUP_ENCRYPTION_REQUEST_CODE);
17401756
dialog.show(getParentFragmentManager(), SETUP_ENCRYPTION_DIALOG_TAG);
1757+
1758+
return false;
17411759
} else {
1742-
// TODO E2E: if encryption fails, to not set it as encrypted!
1743-
encryptFolder(file,
1744-
event.getLocalId(),
1745-
event.getRemoteId(),
1746-
event.getRemotePath(),
1747-
event.getShouldBeEncrypted(),
1748-
publicKey,
1749-
privateKey,
1750-
storageManager);
1760+
return true;
17511761
}
17521762
}
17531763

1754-
private void encryptFolder(OCFile folder,
1755-
long localId,
1764+
private void encryptFolder(long localId,
17561765
String remoteId,
17571766
String remotePath,
1758-
boolean shouldBeEncrypted,
1759-
String publicKeyString,
1760-
String privateKeyString,
1761-
FileDataStorageManager storageManager) {
1767+
boolean shouldBeEncrypted) {
17621768
try {
1763-
Log_OC.d(TAG, "encrypt folder " + folder.getRemoteId());
1769+
Log_OC.d(TAG, "encrypt folder " + remoteId);
17641770
User user = accountManager.getUser();
1771+
String publicKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PUBLIC_KEY);
1772+
String privateKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PRIVATE_KEY);
1773+
1774+
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
1775+
OCFile folder = storageManager.getFileByRemoteId(remoteId);
1776+
17651777
OwnCloudClient client = clientFactory.create(user);
17661778
RemoteOperationResult remoteOperationResult = new ToggleEncryptionRemoteOperation(localId,
17671779
remotePath,
@@ -1778,8 +1790,8 @@ private void encryptFolder(OCFile folder,
17781790
// Update metadata
17791791
Pair<Boolean, DecryptedFolderMetadataFile> metadataPair = EncryptionUtils.retrieveMetadata(folder,
17801792
client,
1781-
privateKeyString,
1782-
publicKeyString,
1793+
privateKey,
1794+
publicKey,
17831795
storageManager,
17841796
user,
17851797
requireContext(),

0 commit comments

Comments
 (0)