diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png index 11855bb257..ac96f0cce3 100644 Binary files a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png and b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png differ diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png index e3d65e9c51..cc50640110 100644 Binary files a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png and b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileAllShareTypes.png differ diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone.png index 3728ff64a5..365a99e5da 100644 Binary files a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone.png and b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileNone.png differ diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png index bdd7d17ab6..341b02c880 100644 Binary files a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png and b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesFileResharingNotAllowed.png differ diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt index 040765c04d..29c1e120ec 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt @@ -288,7 +288,6 @@ class FileDetailSharingFragmentIT : AbstractIT() { onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(isDisplayed())) onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(isDisplayed())) // click event onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) @@ -416,7 +415,6 @@ class FileDetailSharingFragmentIT : AbstractIT() { onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(isDisplayed())) onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(isDisplayed())) // click event onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) @@ -531,7 +529,6 @@ class FileDetailSharingFragmentIT : AbstractIT() { onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(not(isDisplayed()))) onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(not(isDisplayed()))) // click event onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) @@ -657,7 +654,6 @@ class FileDetailSharingFragmentIT : AbstractIT() { onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed())) onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(not(isDisplayed()))) onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed())) - onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(not(isDisplayed()))) // click event onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click()) diff --git a/app/src/main/java/com/owncloud/android/datamodel/SharesType.kt b/app/src/main/java/com/owncloud/android/datamodel/SharesType.kt new file mode 100644 index 0000000000..fb58a83bdb --- /dev/null +++ b/app/src/main/java/com/owncloud/android/datamodel/SharesType.kt @@ -0,0 +1,13 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Tobias Kaminsky + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.datamodel + +enum class SharesType { + INTERNAL, + EXTERNAL +} diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java b/app/src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java index 69109c62d6..4c9b95471f 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ShareViewHolder.java @@ -60,6 +60,11 @@ class ShareViewHolder extends RecyclerView.ViewHolder { float avatarRadiusDimension) { this.avatarRadiusDimension = avatarRadiusDimension; String name = share.getSharedWithDisplayName(); + + if ("".equals(name) && !"".equals(share.getShareWith())) { + name = share.getShareWith(); + } + binding.icon.setTag(null); switch (share.getShareType()) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java index 96c3904ac6..2b22b72050 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java @@ -14,7 +14,6 @@ package com.owncloud.android.ui.adapter; import android.annotation.SuppressLint; import android.graphics.drawable.Drawable; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -26,6 +25,7 @@ import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding; import com.owncloud.android.databinding.FileDetailsSharePublicLinkAddNewItemBinding; import com.owncloud.android.databinding.FileDetailsShareSecureFileDropAddNewItemBinding; import com.owncloud.android.databinding.FileDetailsShareShareItemBinding; +import com.owncloud.android.datamodel.SharesType; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.ui.activity.FileActivity; @@ -52,6 +52,8 @@ public class ShareeListAdapter extends RecyclerView.Adapter shares, @@ -59,7 +61,8 @@ public class ShareeListAdapter extends RecyclerView.Adapter sharesToAdd) { shares.addAll(sharesToAdd); @@ -227,7 +245,7 @@ public class ShareeListAdapter extends RecyclerView.Adapter(), - this, - userId, - user, - viewThemeUtils, - file.isEncrypted())); + internalShareeListAdapter = new ShareeListAdapter(fileActivity, + new ArrayList<>(), + this, + userId, + user, + viewThemeUtils, + file.isEncrypted(), + SharesType.INTERNAL); - binding.sharesList.setLayoutManager(new LinearLayoutManager(requireContext())); + binding.sharesListInternal.setAdapter(internalShareeListAdapter); + + binding.sharesListInternal.setLayoutManager(new LinearLayoutManager(requireContext())); + + externalShareeListAdapter = new ShareeListAdapter(fileActivity, + new ArrayList<>(), + this, + userId, + user, + viewThemeUtils, + file.isEncrypted(), + SharesType.EXTERNAL); + + binding.sharesListExternal.setAdapter(externalShareeListAdapter); + + binding.sharesListExternal.setLayoutManager(new LinearLayoutManager(requireContext())); binding.pickContactEmailBtn.setOnClickListener(v -> checkContactPermission()); @@ -221,13 +244,36 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda fileActivity.getComponentName()); viewThemeUtils.androidx.themeToolbarSearchView(binding.searchView); + viewThemeUtils.material.colorMaterialTextButton(binding.sharesListInternalShowAll); + binding.sharesListInternalShowAll.setOnClickListener(view -> { + internalShareeListAdapter.toggleShowAll(); - if (file.canReshare()) { + if (internalShareeListAdapter.isShowAll()) { + binding.sharesListInternalShowAll.setText(R.string.show_less); + } else { + binding.sharesListInternalShowAll.setText(R.string.show_all); + } + }); + + viewThemeUtils.material.colorMaterialTextButton(binding.sharesListExternalShowAll); + binding.sharesListExternalShowAll.setOnClickListener(view -> { + externalShareeListAdapter.toggleShowAll(); + + if (internalShareeListAdapter.isShowAll()) { + binding.sharesListExternalShowAll.setText(R.string.show_less); + } else { + binding.sharesListExternalShowAll.setText(R.string.show_all); + } + }); + + if (file.canReshare() && !FileDetailSharingFragmentHelper.isPublicShareDisabled(capabilities)) { if (file.isEncrypted() || (parentFile != null && parentFile.isEncrypted())) { 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()) { @@ -237,13 +283,20 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda } } } else { - binding.searchView.setQueryHint(getResources().getString(R.string.share_search)); + binding.createLink.setText(R.string.create_link); + binding.searchView.setQueryHint(getResources().getString(R.string.share_search_internal)); } + + binding.createLink.setOnClickListener(v -> createPublicShareLink()); + } else { binding.searchView.setQueryHint(getResources().getString(R.string.resharing_is_not_allowed)); + binding.createLink.setVisibility(View.GONE); + binding.externalSharesHeadline.setVisibility(View.GONE); binding.searchView.setInputType(InputType.TYPE_NULL); binding.pickContactEmailBtn.setVisibility(View.GONE); disableSearchView(binding.searchView); + binding.createLink.setOnClickListener(null); } checkShareViaUser(); @@ -453,45 +506,63 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda * Get public link from the DB to fill in the "Share link" section in the UI. Takes into account server capabilities * before reading database. */ + @SuppressFBWarnings("PSC") public void refreshSharesFromDB() { OCFile newFile = fileDataStorageManager.getFileById(file.getFileId()); if (newFile != null) { file = newFile; } - ShareeListAdapter adapter = (ShareeListAdapter) binding.sharesList.getAdapter(); - - if (adapter == null) { + if (internalShareeListAdapter == null) { DisplayUtils.showSnackMessage(getView(), getString(R.string.could_not_retrieve_shares)); return; } - adapter.getShares().clear(); + internalShareeListAdapter.getShares().clear(); // to show share with users/groups info List shares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(), user.getAccountName()); - adapter.addShares(shares); + List internalShares = new ArrayList<>(); + List externalShares = new ArrayList<>(); - if (FileDetailSharingFragmentHelper.isPublicShareDisabled(capabilities) || !file.canReshare()) { - return; + for (OCShare share : shares) { + if (share.getShareType() != null) { + switch (share.getShareType()) { + case PUBLIC_LINK: + case FEDERATED_GROUP: + case FEDERATED: + case EMAIL: + externalShares.add(share); + break; + + default: + internalShares.add(share); + break; + } + } } + + internalShareeListAdapter.addShares(internalShares); + + ViewExtensionsKt.setVisibleIf(binding.sharesListInternalShowAll, + internalShareeListAdapter.getShares().size() > 3 + ); + + externalShareeListAdapter.getShares().clear(); // Get public share List publicShares = fileDataStorageManager.getSharesByPathAndType(file.getRemotePath(), ShareType.PUBLIC_LINK, ""); - if (publicShares.isEmpty() && containsNoNewPublicShare(adapter.getShares()) && - (!file.isEncrypted() || capabilities.getEndToEndEncryption().isTrue())) { - final OCShare ocShare = new OCShare(); - ocShare.setShareType(ShareType.NEW_PUBLIC_LINK); - publicShares.add(ocShare); - } else { - adapter.removeNewPublicShare(); - } + externalShareeListAdapter.addShares(externalShares); - adapter.addShares(publicShares); + externalShareeListAdapter.addShares(publicShares); + + ViewExtensionsKt.setVisibleIf(binding.sharesListExternalShowAll, + externalShareeListAdapter.getShares().size() > 3 + ); } private void checkContactPermission() { @@ -545,16 +616,6 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda } } - private boolean containsNoNewPublicShare(List shares) { - for (OCShare share : shares) { - if (share.getShareType() == ShareType.NEW_PUBLIC_LINK) { - return false; - } - } - - return true; - } - @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); @@ -598,7 +659,7 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda @Override public void unShare(OCShare share) { unshareWith(share); - ShareeListAdapter adapter = (ShareeListAdapter) binding.sharesList.getAdapter(); + ShareeListAdapter adapter = (ShareeListAdapter) binding.sharesListInternal.getAdapter(); if (adapter == null) { DisplayUtils.showSnackMessage(getView(), getString(R.string.failed_update_ui)); return; diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java index 8a9d373d8a..be6779b957 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java @@ -54,7 +54,6 @@ public class FileDetailSharingMenuBottomSheetDialog extends BottomSheetDialog { viewThemeUtils.platform.themeDialog(binding.getRoot()); - viewThemeUtils.platform.colorImageView(binding.menuIconAddAnotherLink); viewThemeUtils.platform.colorImageView(binding.menuIconAdvancedPermissions); viewThemeUtils.platform.colorImageView(binding.menuIconSendLink); viewThemeUtils.platform.colorImageView(binding.menuIconUnshare); @@ -72,19 +71,15 @@ public class FileDetailSharingMenuBottomSheetDialog extends BottomSheetDialog { private void updateUI() { if (ocShare.getShareType() == ShareType.PUBLIC_LINK) { - binding.menuShareAddAnotherLink.setVisibility(View.VISIBLE); - if (MDMConfig.INSTANCE.sendFilesSupport(getContext())) { binding.menuShareSendLink.setVisibility(View.VISIBLE); } } else { - binding.menuShareAddAnotherLink.setVisibility(View.GONE); binding.menuShareSendLink.setVisibility(View.GONE); } if (SharingMenuHelper.isSecureFileDrop(ocShare)) { binding.menuShareAdvancedPermissions.setVisibility(View.GONE); - binding.menuShareAddAnotherLink.setVisibility(View.GONE); } } @@ -108,11 +103,6 @@ public class FileDetailSharingMenuBottomSheetDialog extends BottomSheetDialog { actions.sendLink(ocShare); dismiss(); }); - - binding.menuShareAddAnotherLink.setOnClickListener(v -> { - actions.addAnotherLink(ocShare); - dismiss(); - }); } @Override 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 be67c5cd56..adb3a65bf7 100644 --- a/app/src/main/res/layout/file_details_sharing_fragment.xml +++ b/app/src/main/res/layout/file_details_sharing_fragment.xml @@ -1,125 +1,193 @@ - - - - - - - - - - - - - - - + android:orientation="vertical"> - + - - + android:paddingRight="@dimen/standard_padding"> + android:text="@string/shared_with_you_by" + android:textSize="@dimen/two_line_primary_text_size" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + diff --git a/app/src/main/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml b/app/src/main/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml index 030f0e84c9..f0f1543e0f 100644 --- a/app/src/main/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml +++ b/app/src/main/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml @@ -147,38 +147,4 @@ - - - - - - - - diff --git a/app/src/main/res/values/setup.xml b/app/src/main/res/values/setup.xml index afed2dafe2..caf88b3f29 100644 --- a/app/src/main/res/values/setup.xml +++ b/app/src/main/res/values/setup.xml @@ -7,7 +7,7 @@ --> - false + true false diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 45b680ffa1..301ff722da 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -630,6 +630,7 @@ Unset Name, Federated Cloud ID or email address… + Add users and teams Secure share… %1$s (group) @@ -1088,7 +1089,6 @@ Conflict resolver dialog cannot be created QR code could not be read! Note icon - Add another link Add new public share link New name Share link (%1$s) @@ -1339,4 +1339,9 @@ Please manually check terms of service! Terms of service I agree to the above ToS + Show all + Show less + Internal shares + External shares + Create link diff --git a/app/src/test/java/com/owncloud/android/ui/adapter/ShareeListAdapterTest.kt b/app/src/test/java/com/owncloud/android/ui/adapter/ShareeListAdapterTest.kt index 87e5d090bb..3fb0b379bc 100644 --- a/app/src/test/java/com/owncloud/android/ui/adapter/ShareeListAdapterTest.kt +++ b/app/src/test/java/com/owncloud/android/ui/adapter/ShareeListAdapterTest.kt @@ -10,6 +10,7 @@ package com.owncloud.android.ui.adapter import android.content.Context import android.content.res.Resources import com.nextcloud.client.account.AnonymousUser +import com.owncloud.android.datamodel.SharesType import com.owncloud.android.lib.resources.shares.OCShare import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.ui.activity.FileActivity @@ -74,7 +75,8 @@ class ShareeListAdapterTest { user.accountName, user, viewThemeUtils, - false + false, + SharesType.INTERNAL ) sut.sortShares() diff --git a/scripts/androidScreenshotTest b/scripts/androidScreenshotTest index 5af0af5daa..66560fd909 100755 --- a/scripts/androidScreenshotTest +++ b/scripts/androidScreenshotTest @@ -78,7 +78,7 @@ fi if [[ $4 = "all" ]]; then scripts/runAllScreenshotCombinations "noCI" "$1" "-Pandroid.testInstrumentationRunnerArguments.class=$class$method" else - SHOT_TEST=true ./gradlew --offline gplayDebugExecuteScreenshotTests $record \ + SHOT_TEST=true ./gradlew gplayDebugExecuteScreenshotTests $record \ -Dorg.gradle.jvmargs="--add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/java.nio.channels=ALL-UNNAMED --add-exports java.base/sun.nio.ch=ALL-UNNAMED" \ -Pscreenshot=true \ -Pandroid.testInstrumentationRunnerArguments.annotation=com.owncloud.android.utils.ScreenshotTest \