From b5475af644b44620e58216e9a376584d626917a5 Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 6 Feb 2026 09:31:20 -0500 Subject: [PATCH 1/7] refactor: simplify LinkShareViewHolder.setSubline() Changes: - Remove redundant fileDownloadLimit null check (already handled by remainingDownloadLimit() extension function) - Remove unnecessary intermediate variable - Add KDoc documenting the method's behavior No functional changes - simplifies logic while maintaining identical behavior. Signed-off-by: Josh --- .../android/ui/adapter/LinkShareViewHolder.kt | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt index 3ebe6b08d380..415cf2e5cb87 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt @@ -98,26 +98,33 @@ internal class LinkShareViewHolder(itemView: View) : RecyclerView.ViewHolder(ite } } - private fun setSubline(binding: FileDetailsShareLinkShareItemBinding?, context: Context?, publicShare: OCShare) { + /** + * Displays download limit information when available, hides subline otherwise. + * Clears text on hide to prevent RecyclerView item reuse issues. + */ + private fun setSubline( + binding: FileDetailsShareLinkShareItemBinding?, + context: Context?, + publicShare: OCShare + ) { if (binding == null || context == null) { return } - val downloadLimit = publicShare.fileDownloadLimit - if (downloadLimit != null) { - val remaining = publicShare.remainingDownloadLimit() ?: return - val text = context.resources.getQuantityString( + val remaining = publicShare.remainingDownloadLimit() + + if (remaining != null) { + val binding.subline.text = context.resources.getQuantityString( R.plurals.share_download_limit_description, remaining, remaining ) - - binding.subline.text = text binding.subline.visibility = View.VISIBLE - return + } else { + // No download limit set at all + binding.subline.text = "" + binding.subline.visibility = View.GONE } - - binding.subline.visibility = View.GONE } private fun setPermissionName( From b795037013be98072f7dce8202c005a11cb700a0 Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 6 Feb 2026 09:35:47 -0500 Subject: [PATCH 2/7] chore: add zero string handling to share download limit description Signed-off-by: Josh --- app/src/main/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b4cac075161e..1d4324a4b4fc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1452,6 +1452,7 @@ Unable to set download limit. Please check capabilities. Download limit + No downloads remaining %1$d download remaining %1$d downloads remaining From 7b6dfb4e2a5d5e3dccd9973d61e259ca61ed4957 Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 6 Feb 2026 21:38:58 -0500 Subject: [PATCH 3/7] refactor: Use NumberFormat for parsing download limits (consistency) And also guard against <0 while permitting 0 Signed-off-by: Josh --- .../FileDetailsSharingProcessFragment.kt | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt index d97dfc911c40..6eee36d232fc 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt @@ -42,6 +42,7 @@ import com.owncloud.android.utils.ClipboardUtil import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.CapabilityUtils import com.owncloud.android.utils.theme.ViewThemeUtils +import java.text.NumberFormat import java.text.SimpleDateFormat import java.util.Date import java.util.Locale @@ -491,22 +492,24 @@ class FileDetailsSharingProcessFragment : } } + /** + * Pre-fills download limit controls with current remaining count for existing share. + * Clamps negative values to 0. Returns early if no limit is set. + */ private fun updateFileDownloadLimitView() { if (!canSetDownloadLimit()) { return } - // user can set download limit thus no need to rely on current limit to show download limit - showFileDownloadLimitInput(true) + showFileDownloadLimitInput(true) // only other option binding.shareProcessSetDownloadLimitSwitch.visibility = View.VISIBLE binding.shareProcessSetDownloadLimitInput.visibility = View.VISIBLE - val currentLimit = share?.remainingDownloadLimit() ?: return - if (currentLimit > 0) { - binding.shareProcessSetDownloadLimitSwitch.isChecked = true - binding.shareProcessSetDownloadLimitInput.setText( - "%d".format(Locale.getDefault(), currentLimit) - ) + val currentLimit = share?.remainingDownloadLimit()?.coerceAtLeast(0) ?: return + + binding.shareProcessSetDownloadLimitSwitch.isChecked = true + binding.shareProcessSetDownloadLimitInput.setText( + NumberFormat.getInstance(Locale.getDefault()).format(currentLimit) } } From 80c6e711b10110e4e303414466c8a7ef5177af7c Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 6 Feb 2026 21:53:33 -0500 Subject: [PATCH 4/7] fix: handle locale-formatted numbers in download limit input Replace toInt() with NumberFormat.parse() to handle locale-specific grouping separators (e.g., "1,000" or "1.000"). Add validation for empty and negative inputs with inline error messages. Signed-off-by: Josh --- .../FileDetailsSharingProcessFragment.kt | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt index 6eee36d232fc..59e0819554b9 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt @@ -847,16 +847,50 @@ class FileDetailsSharingProcessFragment : } } + /** + * Validates and applies the download limit from the input field. + * + * Parses the user's input using locale-aware number formatting to handle + * grouping separators (e.g., "1,000" in US or "1.000" in Germany). + * Shows inline error messages for invalid input. + * + * @see [updateFileDownloadLimitView] for how the field is populated + */ private fun setDownloadLimit() { + if (!binding.shareProcessSetDownloadLimitSwitch.isChecked) { + // User wants to remove the download limit + fileOperationsHelper?.updateFilesDownloadLimit(share, 0) + return + } + val downloadLimitInput = binding.shareProcessSetDownloadLimitInput.text.toString().trim() - val downloadLimit = - if (binding.shareProcessSetDownloadLimitSwitch.isChecked && downloadLimitInput.isNotEmpty()) { - downloadLimitInput.toInt() - } else { - 0 - } - fileOperationsHelper?.updateFilesDownloadLimit(share, downloadLimit) + if (downloadLimitInput.isEmpty()) { + binding.shareProcessSetDownloadLimitInput.error = getString(R.string.share_download_limit_required) + return + } + + // Parse with locale awareness to match formatting in updateFileDownloadLimitView + val downloadLimit = try { + NumberFormat.getInstance(Locale.getDefault()) + .parse(downloadLimitInput)?.toInt() + } catch (e: ParseException) { + null + } + + when { + downloadLimit == null -> { + binding.shareProcessSetDownloadLimitInput.error = getString(R.string.share_download_limit_invalid) + return + } + downloadLimit <= 0 -> { + binding.shareProcessSetDownloadLimitInput.error = getString(R.string.share_download_limit_must_be_positive) + return + } + else -> { + fileOperationsHelper?.updateFilesDownloadLimit(share, downloadLimit) + } + } } private fun createShare(noteText: String) { From 500e9706dea6cb0aba697bfca6c43ab02a196561 Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 6 Feb 2026 21:54:05 -0500 Subject: [PATCH 5/7] chore: import ParseException Signed-off-by: Josh --- .../android/ui/fragment/FileDetailsSharingProcessFragment.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt index 59e0819554b9..be099b4e35df 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt @@ -43,6 +43,7 @@ import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.CapabilityUtils import com.owncloud.android.utils.theme.ViewThemeUtils import java.text.NumberFormat +import java.text.ParseException import java.text.SimpleDateFormat import java.util.Date import java.util.Locale From d9cd93944f682e29c06b86f6d70d4dffcb5d82db Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 6 Feb 2026 21:56:16 -0500 Subject: [PATCH 6/7] chore: add download limit error handling string resources Signed-off-by: Josh --- app/src/main/res/values/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1d4324a4b4fc..656da2b0de7e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1456,6 +1456,9 @@ %1$d download remaining %1$d downloads remaining + Download limit is required + Please enter a valid number + Download limit must be greater than 0 Please manually check terms of service! Terms of service I agree to the above ToS From 2df78db3db67397bc75ea7c51de1e6ccb0c50450 Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 6 Feb 2026 22:01:13 -0500 Subject: [PATCH 7/7] refactor: further tidy setSubline Signed-off-by: Josh --- .../android/ui/adapter/LinkShareViewHolder.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt index 415cf2e5cb87..5409a793d298 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt @@ -111,19 +111,19 @@ internal class LinkShareViewHolder(itemView: View) : RecyclerView.ViewHolder(ite return } - val remaining = publicShare.remainingDownloadLimit() + val remaining = publicShare.remainingDownloadLimit()?.coerceAtLeast(0) - if (remaining != null) { + if (remaining == null) { + // No download limit set at all + binding.subline.text = "" + binding.subline.visibility = View.GONE + } else { val binding.subline.text = context.resources.getQuantityString( R.plurals.share_download_limit_description, remaining, remaining ) binding.subline.visibility = View.VISIBLE - } else { - // No download limit set at all - binding.subline.text = "" - binding.subline.visibility = View.GONE } }