From b79b17513dfd20a450f8a6d4870a82def96e8073 Mon Sep 17 00:00:00 2001 From: joragua Date: Thu, 6 Nov 2025 14:25:28 +0100 Subject: [PATCH 1/6] feat: improve rounding precision for space quota values --- .../com/owncloud/android/utils/DisplayUtils.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/owncloudApp/src/main/java/com/owncloud/android/utils/DisplayUtils.java index c0746bc7736..accc6f77780 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/owncloudApp/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -4,8 +4,10 @@ * @author Bartek Przybylski * @author David A. Velasco * @author David González Verdugo + * @author Jorge Aguado Recio + * * Copyright (C) 2011 Bartek Przybylski - * Copyright (C) 2020 ownCloud GmbH. + * Copyright (C) 2025 ownCloud GmbH. *

* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -35,6 +37,8 @@ import java.io.File; import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; import java.net.IDN; import java.text.DateFormat; import java.util.Calendar; @@ -257,7 +261,11 @@ public static int getDrawerHeaderHeight(int displayCutout, Resources resources) } public static String formatFromBytesToGb(long bytes) { - BigDecimal valueInGB = new BigDecimal(bytes).divide(BigDecimal.valueOf(1_000_000_000L)).stripTrailingZeros(); - return valueInGB.toPlainString(); + BigDecimal valueInGB = new BigDecimal(bytes).divide(BigDecimal.valueOf(1_000_000_000L)); + if (valueInGB.compareTo(BigDecimal.ONE) >= 0) { + return valueInGB.setScale(1, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString(); + } else { + return valueInGB.round(new MathContext(1, RoundingMode.HALF_UP)).stripTrailingZeros().toPlainString(); + } } } From 9eeddffcfb47c66ff7b343efa80233cce7e7af15 Mon Sep 17 00:00:00 2001 From: joragua Date: Fri, 7 Nov 2025 14:52:33 +0100 Subject: [PATCH 2/6] refactor: rename quota label to make it more generic --- .../spaces/createspace/CreateSpaceDialogFragment.kt | 2 +- owncloudApp/src/main/res/layout/create_space_dialog.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt index f0243ebe00b..52f7d2123a6 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt @@ -65,7 +65,7 @@ class CreateSpaceDialogFragment : DialogFragment() { updateUI() createSpaceDialogQuotaNoRestrictionLabel.isVisible = !isChecked createSpaceDialogQuotaLayout.isVisible = isChecked - createSpaceDialogQuotaGbLabel.isVisible = isChecked + createSpaceDialogQuotaUnitLabel.isVisible = isChecked } if (isEditMode) { diff --git a/owncloudApp/src/main/res/layout/create_space_dialog.xml b/owncloudApp/src/main/res/layout/create_space_dialog.xml index 68ab2000638..c81d1282f77 100644 --- a/owncloudApp/src/main/res/layout/create_space_dialog.xml +++ b/owncloudApp/src/main/res/layout/create_space_dialog.xml @@ -169,7 +169,7 @@ Date: Mon, 10 Nov 2025 09:19:53 +0100 Subject: [PATCH 3/6] feat: convert space quota value to the nearest appropriate unit --- .../createspace/CreateSpaceDialogFragment.kt | 28 +++++++++++++++---- .../owncloud/android/utils/DisplayUtils.java | 23 +++++++++++---- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt index 52f7d2123a6..e7c2e044dc8 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt @@ -78,7 +78,9 @@ class CreateSpaceDialogFragment : DialogFragment() { val totalQuota = it.quota?.total ?: 0L if (totalQuota != 0L) { createSpaceDialogQuotaSwitch.isChecked = true - createSpaceDialogQuotaValue.setText(DisplayUtils.formatFromBytesToGb(totalQuota)) + val formattedQuota = DisplayUtils.formatFromBytes(totalQuota) + createSpaceDialogQuotaValue.setText(formattedQuota.first) + createSpaceDialogQuotaUnitLabel.text = formattedQuota.second } } @@ -91,7 +93,8 @@ class CreateSpaceDialogFragment : DialogFragment() { createSpaceButton.setOnClickListener { val spaceName = createSpaceDialogNameValue.text.toString() val spaceSubtitle = createSpaceDialogSubtitleValue.text.toString() - val spaceQuota = if (createSpaceDialogQuotaSwitch.isChecked) convertToBytes(createSpaceDialogQuotaValue.text.toString()) else 0L + val spaceQuota = if (createSpaceDialogQuotaSwitch.isChecked) convertToBytes(createSpaceDialogQuotaValue.text.toString(), + createSpaceDialogQuotaUnitLabel.text.toString()) else 0L if (isEditMode) { currentSpace?.let { @@ -152,9 +155,18 @@ class CreateSpaceDialogFragment : DialogFragment() { } } - private fun convertToBytes(spaceQuota: String): Long { - val quotaNumber = spaceQuota.toDoubleOrNull() ?: return 0L - return (quotaNumber * 1_000_000_000L).toLong() + private fun convertToBytes(spaceQuotaValue: String, spaceQuotaUnit: String): Long { + val quotaNumber = spaceQuotaValue.toDoubleOrNull() ?: return 0L + val multiplier = when (spaceQuotaUnit) { + DisplayUtils.sizeSuffixes[0] -> B_MULTIPLIER + DisplayUtils.sizeSuffixes[1] -> KB_MULTIPLIER + DisplayUtils.sizeSuffixes[2] -> MB_MULTIPLIER + DisplayUtils.sizeSuffixes[3] -> GB_MULTIPLIER + DisplayUtils.sizeSuffixes[4] -> TB_MULTIPLIER + DisplayUtils.sizeSuffixes[5] -> PB_MULTIPLIER + else -> B_MULTIPLIER + } + return (quotaNumber * multiplier).toLong() } interface CreateSpaceListener { @@ -169,6 +181,12 @@ class CreateSpaceDialogFragment : DialogFragment() { private const val FORBIDDEN_CHARACTERS = """[/\\.:?*"'><|]""" private const val MIN_SPACE_QUOTA_GB = 0.0 private const val MAX_SPACE_QUOTA_GB = 1_000_000.0 + private const val B_MULTIPLIER = 1L + private const val KB_MULTIPLIER = 1_000L + private const val MB_MULTIPLIER = 1_000_000L + private const val GB_MULTIPLIER = 1_000_000_000L + private const val TB_MULTIPLIER = 1_000_000_000_000L + private const val PB_MULTIPLIER = 1_000_000_000_000_000L fun newInstance( isEditMode: Boolean, diff --git a/owncloudApp/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/owncloudApp/src/main/java/com/owncloud/android/utils/DisplayUtils.java index accc6f77780..cab6b427bee 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/owncloudApp/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -29,6 +29,7 @@ import android.content.res.Resources; import android.text.format.DateUtils; import android.util.DisplayMetrics; +import android.util.Pair; import androidx.core.content.ContextCompat; import com.google.android.material.snackbar.Snackbar; @@ -53,7 +54,7 @@ public class DisplayUtils { private static final String OWNCLOUD_APP_NAME = "ownCloud"; - private static final String[] sizeSuffixes = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; + public static final String[] sizeSuffixes = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; private static final int[] sizeScales = {0, 0, 1, 1, 1, 2, 2, 2, 2}; private static Map mimeType2HumanReadable; @@ -260,12 +261,22 @@ public static int getDrawerHeaderHeight(int displayCutout, Resources resources) } } - public static String formatFromBytesToGb(long bytes) { - BigDecimal valueInGB = new BigDecimal(bytes).divide(BigDecimal.valueOf(1_000_000_000L)); - if (valueInGB.compareTo(BigDecimal.ONE) >= 0) { - return valueInGB.setScale(1, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString(); + public static Pair formatFromBytes(long bytes) { + BigDecimal value = new BigDecimal(bytes); + BigDecimal baseUnit = new BigDecimal(1000L); + int unitIndex = 0; + + while (value.compareTo(baseUnit) >= 0 && unitIndex < sizeSuffixes.length - 1) { + value = value.divide(baseUnit); + unitIndex++; + } + + if (value.compareTo(BigDecimal.ONE) >= 0) { + value = value.setScale(1, RoundingMode.HALF_UP); } else { - return valueInGB.round(new MathContext(1, RoundingMode.HALF_UP)).stripTrailingZeros().toPlainString(); + value = value.round(new MathContext(1, RoundingMode.HALF_UP)); } + + return new Pair<>(value.stripTrailingZeros().toPlainString(), sizeSuffixes[unitIndex]); } } From 3dcc9e94239e29b655ffe21bb9efd026563eefff Mon Sep 17 00:00:00 2001 From: joragua Date: Mon, 10 Nov 2025 09:28:13 +0100 Subject: [PATCH 4/6] chore: add calens file --- changelog/unreleased/4687 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog/unreleased/4687 b/changelog/unreleased/4687 index 185ac43ed86..b81803da360 100644 --- a/changelog/unreleased/4687 +++ b/changelog/unreleased/4687 @@ -5,5 +5,7 @@ with the required permissions when the three-dot menu button is tapped. https://github.com/owncloud/android/issues/4607 https://github.com/owncloud/android/issues/4688 +https://github.com/owncloud/android/issues/4695 https://github.com/owncloud/android/pull/4687 https://github.com/owncloud/android/pull/4694 +https://github.com/owncloud/android/pull/4713 From d9826f4829266afb04f93bba7402465c8b78fe10 Mon Sep 17 00:00:00 2001 From: joragua Date: Mon, 10 Nov 2025 13:13:13 +0100 Subject: [PATCH 5/6] fix: handle quota validation on big unit conversions --- .../spaces/createspace/CreateSpaceDialogFragment.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt index e7c2e044dc8..8b39551412e 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/createspace/CreateSpaceDialogFragment.kt @@ -130,15 +130,16 @@ class CreateSpaceDialogFragment : DialogFragment() { return when { spaceQuota.isEmpty() -> getString(R.string.create_space_dialog_quota_empty_error) - spaceQuota.toDouble() == MIN_SPACE_QUOTA_GB -> getString(R.string.create_space_dialog_quota_zero_error) - spaceQuota.toDouble() > MAX_SPACE_QUOTA_GB -> getString(R.string.create_space_dialog_quota_too_large_error) + spaceQuota.toDouble() == MIN_SPACE_QUOTA_LIMIT -> getString(R.string.create_space_dialog_quota_zero_error) + spaceQuota.toDouble() > MAX_SPACE_QUOTA_LIMIT -> getString(R.string.create_space_dialog_quota_too_large_error) else -> null } } private fun updateUI() { val nameError = validateName(binding.createSpaceDialogNameValue.text.toString()) - val quotaError = validateQuota(binding.createSpaceDialogQuotaValue.text.toString()) + val quotaValue = convertToBytes(binding.createSpaceDialogQuotaValue.text.toString(), binding.createSpaceDialogQuotaUnitLabel.text.toString()) + val quotaError = validateQuota(quotaValue.toString()) val noErrors = nameError == null && quotaError == null val colorButton = if (noErrors) { @@ -179,8 +180,8 @@ class CreateSpaceDialogFragment : DialogFragment() { private const val ARG_CAN_EDIT_SPACE_QUOTA = "CAN_EDIT_SPACE_QUOTA" private const val ARG_CURRENT_SPACE = "CURRENT_SPACE" private const val FORBIDDEN_CHARACTERS = """[/\\.:?*"'><|]""" - private const val MIN_SPACE_QUOTA_GB = 0.0 - private const val MAX_SPACE_QUOTA_GB = 1_000_000.0 + private const val MIN_SPACE_QUOTA_LIMIT = 0.0 + private const val MAX_SPACE_QUOTA_LIMIT = 1_000_000_000_000_000.0 private const val B_MULTIPLIER = 1L private const val KB_MULTIPLIER = 1_000L private const val MB_MULTIPLIER = 1_000_000L From 0e1c16074f1d6703784e18aa8975ad54d2bc85a4 Mon Sep 17 00:00:00 2001 From: ownClouders Date: Mon, 10 Nov 2025 13:32:10 +0000 Subject: [PATCH 6/6] docs: calens changelog updated --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e63b6d6fa3..d851e519b21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -124,8 +124,10 @@ ownCloud admins and users. https://github.com/owncloud/android/issues/4607 https://github.com/owncloud/android/issues/4688 + https://github.com/owncloud/android/issues/4695 https://github.com/owncloud/android/pull/4687 https://github.com/owncloud/android/pull/4694 + https://github.com/owncloud/android/pull/4713 * Enhancement - Disable/Remove a space: [#4611](https://github.com/owncloud/android/issues/4611)