Skip to content

Commit b1213d7

Browse files
committed
Allow to create encrypted folder directly from bottom sheet dialog from NC PR: nextcloud#10782
1 parent 826ee7d commit b1213d7

File tree

11 files changed

+132
-35
lines changed

11 files changed

+132
-35
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
@@ -356,6 +356,11 @@ public void createFolder() {
356356

357357
}
358358

359+
@Override
360+
public void createEncryptedFolder() {
361+
362+
}
363+
359364
@Override
360365
public void uploadFromApp() {
361366

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

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

248248
private void filterEncrypt(List<Integer> toHide, boolean endToEndEncryptionEnabled) {
249249
if (files.isEmpty() || !isSingleSelection() || isSingleFile() || isEncryptedFolder() || isGroupFolder()
250-
|| !endToEndEncryptionEnabled || !isEmptyFolder() || isShared()) {
250+
|| !endToEndEncryptionEnabled || !isEmptyFolder() || isShared() || isInSubFolder()) {
251251
toHide.add(R.id.action_encrypted);
252252
}
253253
}
@@ -587,4 +587,15 @@ private boolean isShared() {
587587
}
588588
return false;
589589
}
590+
591+
private boolean isInSubFolder() {
592+
OCFile folder = files.iterator().next();
593+
OCFile parent = storageManager.getFileById(folder.getParentId());
594+
595+
if (parent == null) {
596+
return false;
597+
}
598+
599+
return !OCFile.ROOT_PATH.equals(parent.getRemotePath());
600+
}
590601
}

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

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,31 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper
7070
private RemoteFile createdRemoteFolder;
7171
private User user;
7272
private Context context;
73+
private boolean encrypted;
7374

7475
/**
7576
* Constructor
7677
*/
77-
public CreateFolderOperation(String remotePath, User user, Context context, FileDataStorageManager storageManager) {
78+
public CreateFolderOperation(String remotePath,
79+
User user,
80+
Context context,
81+
FileDataStorageManager storageManager
82+
) {
83+
this(remotePath, false, user, context, storageManager);
84+
}
85+
86+
public CreateFolderOperation(String remotePath,
87+
boolean encrypted,
88+
User user,
89+
Context context,
90+
FileDataStorageManager storageManager
91+
) {
7892
super(storageManager);
7993

8094
this.remotePath = remotePath;
8195
this.user = user;
8296
this.context = context;
97+
this.encrypted = encrypted;
8398
}
8499

85100
@Override
@@ -115,7 +130,7 @@ protected RemoteOperationResult run(OwnCloudClient client) {
115130
}
116131
return new RemoteOperationResult(new IllegalStateException("E2E not supported"));
117132
} else {
118-
return normalCreate(client);
133+
return normalCreate(client, encrypted);
119134
}
120135
}
121136

@@ -483,7 +498,7 @@ private String createRandomFileName(DecryptedFolderMetadataFileV1 metadata) {
483498
return encryptedFileName;
484499
}
485500

486-
private RemoteOperationResult normalCreate(OwnCloudClient client) {
501+
private RemoteOperationResult normalCreate(OwnCloudClient client, boolean encrypted) {
487502
RemoteOperationResult result = new CreateFolderRemoteOperation(remotePath, true).execute(client);
488503

489504
if (result.isSuccess()) {
@@ -492,6 +507,21 @@ private RemoteOperationResult normalCreate(OwnCloudClient client) {
492507

493508
createdRemoteFolder = (RemoteFile) remoteFolderOperationResult.getData().get(0);
494509
saveFolderInDB();
510+
511+
if (encrypted) {
512+
final OCFile folder = getStorageManager().getFileByDecryptedRemotePath(remotePath);
513+
514+
final RemoteOperationResult remoteOperationResult =
515+
new ToggleEncryptionRemoteOperation(folder.getLocalId(),
516+
remotePath,
517+
true)
518+
.execute(client);
519+
520+
if (remoteOperationResult.isSuccess()) {
521+
folder.setEncrypted(true);
522+
getStorageManager().saveFile(folder);
523+
}
524+
}
495525
} else {
496526
Log_OC.e(TAG, remotePath + " hasn't been created");
497527
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public class OperationsService extends Service {
9595
public static final String EXTRA_ACCOUNT = "ACCOUNT";
9696
public static final String EXTRA_SERVER_URL = "SERVER_URL";
9797
public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
98+
public static final String EXTRA_ENCRYPTED = "ENCRYPTED";
9899
public static final String EXTRA_NEWNAME = "NEWNAME";
99100
public static final String EXTRA_REMOVE_ONLY_LOCAL = "REMOVE_LOCAL_COPY";
100101
public static final String EXTRA_SYNC_FILE_CONTENTS = "SYNC_FILE_CONTENTS";
@@ -696,7 +697,9 @@ private Pair<Target, RemoteOperation> newOperation(Intent operationIntent) {
696697

697698
case ACTION_CREATE_FOLDER:
698699
remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
700+
boolean encrypted = operationIntent.getBooleanExtra(EXTRA_ENCRYPTED, false);
699701
operation = new CreateFolderOperation(remotePath,
702+
encrypted,
700703
user,
701704
getApplicationContext(),
702705
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
@@ -332,6 +332,10 @@ public int getItemCount() {
332332

333333
@Nullable
334334
public OCFile getItem(int position) {
335+
if (position == -1) {
336+
return null;
337+
}
338+
335339
int newPosition = position;
336340

337341
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
@@ -63,8 +63,9 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList
6363
@JvmField
6464
@Inject
6565
var keyboardUtils: KeyboardUtils? = null
66-
private var mParentFolder: OCFile? = null
66+
private var parentFolder: OCFile? = null
6767
private var positiveButton: MaterialButton? = null
68+
private var encrypted = false
6869

6970
private lateinit var binding: EditBoxDialogBinding
7071

@@ -93,7 +94,8 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList
9394

9495
@Suppress("EmptyFunctionBlock")
9596
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
96-
mParentFolder = arguments?.getParcelableArgument(ARG_PARENT_FOLDER, OCFile::class.java)
97+
parentFolder = arguments?.getParcelableArgument(ARG_PARENT_FOLDER, OCFile::class.java)
98+
encrypted = arguments?.getBoolean(ARG_ENCRYPTED) ?: false
9799

98100
// Inflate the layout for the dialog
99101
val inflater = requireActivity().layoutInflater
@@ -177,26 +179,32 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList
177179
DisplayUtils.showSnackMessage(requireActivity(), R.string.filename_forbidden_charaters_from_server)
178180
return
179181
}
180-
val path = mParentFolder!!.decryptedRemotePath + newFolderName + OCFile.PATH_SEPARATOR
181-
(requireActivity() as ComponentsGetter).fileOperationsHelper.createFolder(path)
182+
val path = parentFolder!!.decryptedRemotePath + newFolderName + OCFile.PATH_SEPARATOR
183+
(requireActivity() as ComponentsGetter).fileOperationsHelper.createFolder(path, encrypted)
182184
}
183185
}
184186

185187
companion object {
186188
private const val ARG_PARENT_FOLDER = "PARENT_FOLDER"
189+
private const val ARG_ENCRYPTED = "ENCRYPTED"
187190
const val CREATE_FOLDER_FRAGMENT = "CREATE_FOLDER_FRAGMENT"
188191

192+
@JvmStatic
193+
fun newInstance(parentFolder: OCFile?): CreateFolderDialogFragment {
194+
return newInstance(parentFolder, false)
195+
}
189196
/**
190197
* Public factory method to create new CreateFolderDialogFragment instances.
191198
*
192199
* @param parentFolder Folder to create
193200
* @return Dialog ready to show.
194201
*/
195202
@JvmStatic
196-
fun newInstance(parentFolder: OCFile?): CreateFolderDialogFragment {
203+
fun newInstance(parentFolder: OCFile?, encrypted: Boolean): CreateFolderDialogFragment {
197204
val frag = CreateFolderDialogFragment()
198205
val args = Bundle()
199206
args.putParcelable(ARG_PARENT_FOLDER, parentFolder)
207+
args.putBoolean(ARG_ENCRYPTED, encrypted)
200208
frag.arguments = args
201209
return frag
202210
}

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
@@ -23,8 +23,12 @@ package com.owncloud.android.ui.events
2323
* Event for set folder as encrypted/decrypted
2424
*/
2525
class EncryptionEvent(
26+
@JvmField
2627
val localId: Long,
28+
@JvmField
2729
val remoteId: String,
30+
@JvmField
2831
val remotePath: String,
32+
@JvmField
2933
val shouldBeEncrypted: Boolean
3034
)

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
@@ -32,6 +32,11 @@ public interface OCFileListBottomSheetActions {
3232
*/
3333
void createFolder();
3434

35+
/**
36+
* creates an encrypted folder within the actual folder
37+
*/
38+
void createEncryptedFolder();
39+
3540
/**
3641
* offers a file upload with the Android OS file picker to the current folder.
3742
*/

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
@@ -99,9 +99,8 @@ protected void onCreate(Bundle savedInstanceState) {
9999
binding.addToCloud.setText(getContext().getResources().getString(R.string.add_to_cloud,
100100
themeUtils.getDefaultDisplayNameForRootFolder(getContext())));
101101

102-
OCCapability capability = fileActivity.getCapabilities();
103-
if (capability != null &&
104-
capability.getRichDocuments().isTrue() &&
102+
OCCapability capability = fileActivity.getStorageManager().getCapability(user.getAccountName());
103+
if (capability.getRichDocuments().isTrue() &&
105104
capability.getRichDocumentsDirectEditing().isTrue() &&
106105
capability.getRichDocumentsTemplatesAvailable().isTrue() &&
107106
!file.isEncrypted()) {
@@ -149,6 +148,12 @@ protected void onCreate(Bundle savedInstanceState) {
149148
binding.menuDirectCameraUpload.setVisibility(View.GONE);
150149
}
151150

151+
if (capability.getEndToEndEncryption().isTrue() && OCFile.ROOT_PATH.equals(file.getRemotePath())) {
152+
binding.menuEncryptedMkdir.setVisibility(View.VISIBLE);
153+
} else {
154+
binding.menuEncryptedMkdir.setVisibility(View.GONE);
155+
}
156+
152157
// create rich workspace
153158
if (editorUtils.isEditorAvailable(user,
154159
MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN) &&
@@ -183,6 +188,11 @@ private void setupClickListener() {
183188
dismiss();
184189
});
185190

191+
binding.menuEncryptedMkdir.setOnClickListener(v -> {
192+
actions.createEncryptedFolder();
193+
dismiss();
194+
});
195+
186196
binding.menuUploadFromApp.setOnClickListener(v -> {
187197
actions.uploadFromApp();
188198
dismiss();

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

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,14 @@ public void createFolder() {
517517
.show(getActivity().getSupportFragmentManager(), DIALOG_CREATE_FOLDER);
518518
}
519519

520+
@Override
521+
public void createEncryptedFolder() {
522+
if (checkEncryptionIsSetup(null)) {
523+
CreateFolderDialogFragment.newInstance(mFile, true)
524+
.show(getActivity().getSupportFragmentManager(), DIALOG_CREATE_FOLDER);
525+
}
526+
}
527+
520528
@Override
521529
public void uploadFromApp() {
522530
Intent action = new Intent(Intent.ACTION_GET_CONTENT);
@@ -1133,10 +1141,11 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
11331141
int position = data.getIntExtra(SetupEncryptionDialogFragment.ARG_POSITION, -1);
11341142
OCFile file = mAdapter.getItem(position);
11351143

1136-
if (file != null) {
1137-
mContainerActivity.getFileOperationsHelper().toggleEncryption(file, true);
1138-
mAdapter.setEncryptionAttributeForItemID(file.getRemoteId(), true);
1144+
if (file == null) {
1145+
return;
11391146
}
1147+
mContainerActivity.getFileOperationsHelper().toggleEncryption(file, true);
1148+
mAdapter.setEncryptionAttributeForItemID(file.getRemoteId(), true);
11401149

11411150
// update state and view of this fragment
11421151
searchFragment = false;
@@ -1693,49 +1702,52 @@ protected RemoteOperation getSearchRemoteOperation(final User currentUser, final
16931702

16941703
@Subscribe(threadMode = ThreadMode.BACKGROUND)
16951704
public void onMessageEvent(EncryptionEvent event) {
1705+
if (checkEncryptionIsSetup(event.remoteId)) {
1706+
encryptFolder(event.localId, event.remoteId, event.remotePath, event.shouldBeEncrypted);
1707+
}
1708+
}
1709+
1710+
private boolean checkEncryptionIsSetup(@Nullable String remoteId) {
16961711
final User user = accountManager.getUser();
16971712

16981713
// check if keys are stored
16991714
String publicKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PUBLIC_KEY);
17001715
String privateKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PRIVATE_KEY);
17011716

1702-
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
1703-
OCFile file = storageManager.getFileByRemoteId(event.getRemoteId());
1704-
17051717
if (publicKey.isEmpty() || privateKey.isEmpty()) {
17061718
Log_OC.d(TAG, "no public key for " + user.getAccountName());
17071719

1720+
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
17081721
int position = -1;
1709-
if (file != null) {
1710-
position = mAdapter.getItemPosition(file);
1722+
if (remoteId != null) {
1723+
OCFile file = storageManager.getFileByRemoteId(remoteId);
1724+
if (file != null) {
1725+
position = mAdapter.getItemPosition(file);
1726+
}
17111727
}
17121728
SetupEncryptionDialogFragment dialog = SetupEncryptionDialogFragment.newInstance(user, position);
17131729
dialog.setTargetFragment(this, SETUP_ENCRYPTION_REQUEST_CODE);
17141730
dialog.show(getParentFragmentManager(), SETUP_ENCRYPTION_DIALOG_TAG);
1731+
1732+
return false;
17151733
} else {
1716-
// TODO E2E: if encryption fails, to not set it as encrypted!
1717-
encryptFolder(file,
1718-
event.getLocalId(),
1719-
event.getRemoteId(),
1720-
event.getRemotePath(),
1721-
event.getShouldBeEncrypted(),
1722-
publicKey,
1723-
privateKey,
1724-
storageManager);
1734+
return true;
17251735
}
17261736
}
17271737

1728-
private void encryptFolder(OCFile folder,
1729-
long localId,
1738+
private void encryptFolder(long localId,
17301739
String remoteId,
17311740
String remotePath,
1732-
boolean shouldBeEncrypted,
1733-
String publicKeyString,
1734-
String privateKeyString,
1735-
FileDataStorageManager storageManager) {
1741+
boolean shouldBeEncrypted) {
17361742
try {
17371743
Log_OC.d(TAG, "encrypt folder " + folder.getRemoteId());
17381744
User user = accountManager.getUser();
1745+
String publicKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PUBLIC_KEY);
1746+
String privateKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PRIVATE_KEY);
1747+
1748+
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
1749+
OCFile folder = storageManager.getFileByRemoteId(remoteId);
1750+
17391751
OwnCloudClient client = clientFactory.create(user);
17401752
RemoteOperationResult remoteOperationResult = new ToggleEncryptionRemoteOperation(localId,
17411753
remotePath,

0 commit comments

Comments
 (0)