From 1c60a82b0f06d6b804047d0f17fa5f46d8b56a4e Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 11 Mar 2026 18:33:01 +0100 Subject: [PATCH 1/5] fix(share): secure file drop Signed-off-by: alperozturk96 --- .../ui/fragment/FileDetailSharingFragment.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 48a1ac642937..58dfba1f0888 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -613,9 +613,21 @@ public void refreshSharesFromDB() { } private void addExternalAndPublicShares(List externalShares) { - final var publicShares = fileDataStorageManager.getSharesByPathAndType(file.getRemotePath(), ShareType.PUBLIC_LINK, ""); + final var publicShares = fileDataStorageManager.getSharesByPathAndType( + file.getRemotePath(), ShareType.PUBLIC_LINK, ""); externalShareeListAdapter.removeAll(); final var shares = OCShareExtensionsKt.mergeDistinctByToken(externalShares, publicShares); + + boolean hasNoSecureFileDrop = shares.stream() + .noneMatch(s -> s.getShareType() == ShareType.PUBLIC_LINK + || s.getShareType() == ShareType.NEW_PUBLIC_LINK); + + if (file.isEncrypted() && hasNoSecureFileDrop) { + OCShare placeholder = new OCShare(); + placeholder.setShareType(ShareType.NEW_PUBLIC_LINK); + shares.add(placeholder); + } + externalShareeListAdapter.addShares(shares); } From 518668608d0382f2d2caa9db0cd2edf112b12de7 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 18 Mar 2026 15:42:27 +0100 Subject: [PATCH 2/5] align wording style with web Signed-off-by: alperozturk96 --- .../quickPermission/QuickPermissionType.kt | 2 +- .../ui/adapter/NewSecureFileDropViewHolder.kt | 15 ++++- .../android/ui/adapter/ShareeListAdapter.kt | 2 +- .../fragment/FileDetailSharingFragment.java | 10 ++-- ...ls_share_secure_file_drop_add_new_item.xml | 55 +++++-------------- .../layout/file_details_sharing_fragment.xml | 15 +++++ app/src/main/res/values/strings.xml | 6 +- 7 files changed, 53 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermissionType.kt b/app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermissionType.kt index d867fb69e0c5..83819443a209 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermissionType.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/quickPermission/QuickPermissionType.kt @@ -18,7 +18,7 @@ enum class QuickPermissionType(val iconId: Int, val textId: Int) { VIEW_ONLY(R.drawable.ic_eye, R.string.share_permission_view_only), CAN_EDIT(R.drawable.ic_edit, R.string.share_permission_can_edit), FILE_REQUEST(R.drawable.ic_file_request, R.string.share_permission_file_request), - SECURE_FILE_DROP(R.drawable.ic_file_request, R.string.share_permission_secure_file_drop), + SECURE_FILE_DROP(R.drawable.ic_file_request, R.string.create_end_to_end_encrypted_share), CUSTOM_PERMISSIONS(R.drawable.ic_custom_permissions, R.string.share_custom_permission); fun getText(context: Context): String = context.getString(textId) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/NewSecureFileDropViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/NewSecureFileDropViewHolder.kt index 0d69f2ca28a8..0e2746288561 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/NewSecureFileDropViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/NewSecureFileDropViewHolder.kt @@ -10,15 +10,24 @@ package com.owncloud.android.ui.adapter import android.view.View import androidx.recyclerview.widget.RecyclerView import com.owncloud.android.databinding.FileDetailsShareSecureFileDropAddNewItemBinding +import com.owncloud.android.utils.theme.ViewThemeUtils -internal class NewSecureFileDropViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { +class NewSecureFileDropViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private var binding: FileDetailsShareSecureFileDropAddNewItemBinding? = null + private var viewThemeUtils: ViewThemeUtils? = null - constructor(binding: FileDetailsShareSecureFileDropAddNewItemBinding) : this(binding.root) { + constructor( + binding: FileDetailsShareSecureFileDropAddNewItemBinding, + viewThemeUtils: ViewThemeUtils + ) : this(binding.root) { this.binding = binding + this.viewThemeUtils = viewThemeUtils } fun bind(listener: ShareeListAdapterListener) { - binding!!.addNewSecureFileDrop.setOnClickListener { v: View? -> listener.createSecureFileDrop() } + binding?.addPublicShare?.let { + it.setOnClickListener { listener.createSecureFileDrop() } + viewThemeUtils?.material?.colorMaterialButtonPrimaryTonal(it) + } } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.kt index f171602f625c..ef449afabfac 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.kt @@ -77,7 +77,7 @@ class ShareeListAdapter( shareType == ShareType.NEW_PUBLIC_LINK -> { if (encrypted) { val binding = FileDetailsShareSecureFileDropAddNewItemBinding.inflate(inflater, parent, false) - NewSecureFileDropViewHolder(binding) + NewSecureFileDropViewHolder(binding, viewThemeUtils) } else { val binding = FileDetailsSharePublicLinkAddNewItemBinding.inflate(inflater, parent, false) NewLinkShareViewHolder(binding) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 58dfba1f0888..456a16132a9d 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -321,6 +321,10 @@ private void setupView() { if (file.canReshare() && !FileDetailSharingFragmentHelper.isPublicShareDisabled(capabilities)) { if (file.isEncrypted() || (parentFile != null && parentFile.isEncrypted())) { + binding.internalShareHeadline.setText(getResources().getString(R.string.internal_share_headline_end_to_end_encrypted)); + binding.internalShareDescription.setVisibility(View.VISIBLE); + binding.externalSharesHeadline.setText(getResources().getString(R.string.create_end_to_end_encrypted_share_title)); + if (file.getE2eCounter() == -1) { // V1 cannot share binding.searchContainer.setVisibility(View.GONE); @@ -618,11 +622,7 @@ private void addExternalAndPublicShares(List externalShares) { externalShareeListAdapter.removeAll(); final var shares = OCShareExtensionsKt.mergeDistinctByToken(externalShares, publicShares); - boolean hasNoSecureFileDrop = shares.stream() - .noneMatch(s -> s.getShareType() == ShareType.PUBLIC_LINK - || s.getShareType() == ShareType.NEW_PUBLIC_LINK); - - if (file.isEncrypted() && hasNoSecureFileDrop) { + if (file.isEncrypted()) { OCShare placeholder = new OCShare(); placeholder.setShareType(ShareType.NEW_PUBLIC_LINK); shares.add(placeholder); diff --git a/app/src/main/res/layout/file_details_share_secure_file_drop_add_new_item.xml b/app/src/main/res/layout/file_details_share_secure_file_drop_add_new_item.xml index 420633b1987b..2ab14998d1f7 100644 --- a/app/src/main/res/layout/file_details_share_secure_file_drop_add_new_item.xml +++ b/app/src/main/res/layout/file_details_share_secure_file_drop_add_new_item.xml @@ -2,43 +2,18 @@ - - - - - - - - + ~ SPDX-FileCopyrightText: 2026 Alper Ozturk + ~ SPDX-License-Identifier: AGPL-3.0-or-later + --> + diff --git a/app/src/main/res/layout/file_details_sharing_fragment.xml b/app/src/main/res/layout/file_details_sharing_fragment.xml index 4ee05b28248a..14a056da8f5c 100644 --- a/app/src/main/res/layout/file_details_sharing_fragment.xml +++ b/app/src/main/res/layout/file_details_sharing_fragment.xml @@ -144,6 +144,7 @@ android:text="@string/send_copy_to" /> + + + File request View only Can edit - Secure file drop - + End-to-end encrypted link shares + Link share + End-to-end encrypted shares + Share recipients always have access to the full encrypted folder. It is only possible to share with accounts that have already setup end-to-end encryption. Choose what to sync Free up space %1$s is %2$s, but there is only %3$s available on device. From 180a8b1cab87bd49db459afbf8a43f3decd0e85b Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 25 Mar 2026 09:29:48 +0100 Subject: [PATCH 3/5] use latest e2ee counter Signed-off-by: alperozturk96 --- .../fragment/FileDetailSharingFragment.java | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 456a16132a9d..f90b420ba79d 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -51,13 +51,16 @@ import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.SharesType; +import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFolderMetadataFile; import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.NextcloudVersion; import com.owncloud.android.lib.resources.status.OCCapability; +import com.owncloud.android.operations.RefreshFolderOperation; import com.owncloud.android.providers.UsersAndGroupsSearchConfig; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; @@ -325,20 +328,22 @@ private void setupView() { binding.internalShareDescription.setVisibility(View.VISIBLE); binding.externalSharesHeadline.setText(getResources().getString(R.string.create_end_to_end_encrypted_share_title)); - if (file.getE2eCounter() == -1) { - // V1 cannot share - binding.searchContainer.setVisibility(View.GONE); - binding.createLink.setVisibility(View.GONE); - } else { - binding.createLink.setText(R.string.add_new_secure_file_drop); - binding.searchView.setQueryHint(getResources().getString(R.string.secure_share_search)); - - if (file.isSharedViaLink()) { - binding.searchView.setQueryHint(getResources().getString(R.string.share_not_allowed_when_file_drop)); - binding.searchView.setInputType(InputType.TYPE_NULL); - disableSearchView(binding.searchView); - } - } + fetchE2EECounter(() -> { + if (file.getE2eCounter() == -1) { + // V1 cannot share + binding.searchContainer.setVisibility(View.GONE); + binding.createLink.setVisibility(View.GONE); + } else { + binding.createLink.setText(R.string.add_new_secure_file_drop); + binding.searchView.setQueryHint(getResources().getString(R.string.secure_share_search)); + + if (file.isSharedViaLink()) { + binding.searchView.setQueryHint(getResources().getString(R.string.share_not_allowed_when_file_drop)); + binding.searchView.setInputType(InputType.TYPE_NULL); + disableSearchView(binding.searchView); + } + } + }); } else { binding.createLink.setText(R.string.create_link); binding.searchView.setQueryHint(getResources().getString(R.string.share_search_internal)); @@ -367,6 +372,23 @@ private void setupView() { ); } + private void fetchE2EECounter(Runnable onComplete) { + new Thread(() -> { + try { + OwnCloudClient client = clientFactory.create(user); + Object metadata = RefreshFolderOperation.getDecryptedFolderMetadata(true, file, client, user, requireContext()); + if (metadata instanceof DecryptedFolderMetadataFile decryptedMetadata) { + file.setE2eCounter(decryptedMetadata.getMetadata().getCounter()); + fileDataStorageManager.saveFile(file); + } + } catch (Exception e) { + Log_OC.e(TAG, "Error refreshing E2E counter: " + e.getMessage()); + } + + requireActivity().runOnUiThread(onComplete); + }).start(); + } + private void checkShareViaUser() { if (!MDMConfig.INSTANCE.shareViaUser(requireContext())) { binding.searchContainer.setVisibility(View.GONE); @@ -617,17 +639,9 @@ public void refreshSharesFromDB() { } private void addExternalAndPublicShares(List externalShares) { - final var publicShares = fileDataStorageManager.getSharesByPathAndType( - file.getRemotePath(), ShareType.PUBLIC_LINK, ""); + final var publicShares = fileDataStorageManager.getSharesByPathAndType(file.getRemotePath(), ShareType.PUBLIC_LINK, ""); externalShareeListAdapter.removeAll(); final var shares = OCShareExtensionsKt.mergeDistinctByToken(externalShares, publicShares); - - if (file.isEncrypted()) { - OCShare placeholder = new OCShare(); - placeholder.setShareType(ShareType.NEW_PUBLIC_LINK); - shares.add(placeholder); - } - externalShareeListAdapter.addShares(shares); } From 5201a4b8998a0063aa741c091bb3193bf8be44f5 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 25 Mar 2026 11:15:07 +0100 Subject: [PATCH 4/5] fix search view enability Signed-off-by: alperozturk96 --- .../ui/fragment/FileDetailSharingFragment.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index f90b420ba79d..711d2b0c2f44 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -340,7 +340,7 @@ private void setupView() { if (file.isSharedViaLink()) { binding.searchView.setQueryHint(getResources().getString(R.string.share_not_allowed_when_file_drop)); binding.searchView.setInputType(InputType.TYPE_NULL); - disableSearchView(binding.searchView); + toggleSearchViewEnable(binding.searchView, false); } } }); @@ -357,7 +357,7 @@ private void setupView() { binding.externalSharesHeadline.setVisibility(View.GONE); binding.searchView.setInputType(InputType.TYPE_NULL); binding.pickContactEmailBtn.setVisibility(View.GONE); - disableSearchView(binding.searchView); + toggleSearchViewEnable(binding.searchView, false); binding.createLink.setOnClickListener(null); } @@ -395,12 +395,11 @@ private void checkShareViaUser() { } } - private void disableSearchView(View view) { - view.setEnabled(false); - + private void toggleSearchViewEnable(View view, boolean enable) { + view.setEnabled(enable); if (view instanceof ViewGroup viewGroup) { for (int i = 0; i < viewGroup.getChildCount(); i++) { - disableSearchView(viewGroup.getChildAt(i)); + toggleSearchViewEnable(viewGroup.getChildAt(i), enable); } } } @@ -744,6 +743,7 @@ public void unShare(OCShare share) { if (binding.sharesListInternal.getAdapter() instanceof ShareeListAdapter adapter) { adapter.remove(share); if (entity != null && adapter.isAdapterEmpty()) { + toggleSearchViewEnable(binding.searchView, true); entity.setSharedWithSharee(0); fileDataStorageManager.updateFileEntity(entity); } From 5b6e5cf303a6460f86ba975dc983382d6080dce8 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 25 Mar 2026 11:23:30 +0100 Subject: [PATCH 5/5] fix search view enability Signed-off-by: alperozturk96 --- .../android/ui/fragment/FileDetailSharingFragment.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 711d2b0c2f44..834278b3b231 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -288,7 +288,16 @@ public void onStop() { searchConfig.reset(); } + private void resetSearchView() { + toggleSearchViewEnable(binding.searchView, true); + binding.searchView.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + binding.searchView.setQueryHint(null); + binding.searchView.setQuery("", false); + binding.pickContactEmailBtn.setVisibility(View.VISIBLE); + } + private void setupView() { + resetSearchView(); setShareWithYou(); OCFile parentFile = fileDataStorageManager.getFileById(file.getParentId()); @@ -743,7 +752,6 @@ public void unShare(OCShare share) { if (binding.sharesListInternal.getAdapter() instanceof ShareeListAdapter adapter) { adapter.remove(share); if (entity != null && adapter.isAdapterEmpty()) { - toggleSearchViewEnable(binding.searchView, true); entity.setSharedWithSharee(0); fileDataStorageManager.updateFileEntity(entity); }