From b19195b1d2b434986a99fdf1d80909908c9c3769 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 22 Sep 2026 17:04:41 +0200 Subject: [PATCH 1/3] refactor(display-util): size conversion Signed-off-by: alperozturk96 --- .../AutoUploadLocalDeletionWorker.kt | 4 +- .../player/ui/audio/AudioFileFragment.kt | 3 +- .../ui/ChooseStorageLocationDialogFragment.kt | 6 +- .../nextcloud/ui/fileInfo/ImageDetailInfo.kt | 4 +- .../main/java/com/nextcloud/utils/ByteUnit.kt | 20 +++ .../nextcloud/utils/HumanReadableFormatter.kt | 40 ++++++ .../utils/extensions/FileExtensions.kt | 6 +- .../adapter/ActivityAndVersionListAdapter.kt | 4 +- .../android/ui/activity/DrawerActivity.java | 7 +- .../android/ui/activity/ShareActivity.kt | 6 +- .../adapter/InternalTwoWaySyncViewHolder.kt | 3 +- .../android/ui/adapter/OCFileListAdapter.java | 5 +- .../ui/adapter/ReceiveExternalFilesAdapter.kt | 3 +- .../ui/adapter/TrashbinListAdapter.java | 3 +- .../localFileList/LocalFileListItemBinder.kt | 3 +- .../adapter/uploadList/UploadListAdapter.kt | 3 +- .../SyncFileNotEnoughSpaceDialogFragment.kt | 6 +- .../conflict/ConflictResolveDialogFactory.kt | 3 +- .../ui/fragment/FileDetailFragment.java | 5 +- .../owncloud/android/utils/DisplayUtils.java | 50 +------ .../utils/HumanReadableFormatterTest.kt | 122 ++++++++++++++++++ 21 files changed, 226 insertions(+), 80 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/utils/ByteUnit.kt create mode 100644 app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt create mode 100644 app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadLocalDeletionWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadLocalDeletionWorker.kt index 778d290ce594..b409020deb10 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadLocalDeletionWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadLocalDeletionWorker.kt @@ -13,6 +13,7 @@ import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.jobs.notification.WorkerNotificationManager +import com.nextcloud.utils.HumanReadableFormatter import com.owncloud.android.R import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.SyncedFolderProvider @@ -20,7 +21,6 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.operations.upload.DeleteUploadedFileOperation import com.owncloud.android.ui.notifications.NotificationUtils -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.FileUtil import com.owncloud.android.utils.theme.ViewThemeUtils import java.io.File @@ -122,7 +122,7 @@ class AutoUploadLocalDeletionWorker( ): Notification { val notificationContent = context.getString( R.string.autoupload_delete_uploaded_notif_ended_content, - DisplayUtils.bytesToHumanReadable(spaceFreed), + HumanReadableFormatter.bytesToHumanReadable(spaceFreed), context.resources.getQuantityString( R.plurals.autoupload_delete_uploaded_notif_ended_content_files, filesRemoved.toInt(), diff --git a/app/src/main/java/com/nextcloud/client/player/ui/audio/AudioFileFragment.kt b/app/src/main/java/com/nextcloud/client/player/ui/audio/AudioFileFragment.kt index 2eaa40b2b5fe..267a554dadac 100644 --- a/app/src/main/java/com/nextcloud/client/player/ui/audio/AudioFileFragment.kt +++ b/app/src/main/java/com/nextcloud/client/player/ui/audio/AudioFileFragment.kt @@ -20,6 +20,7 @@ import com.nextcloud.client.player.model.state.PlaybackItemMetadata import com.nextcloud.client.player.model.state.PlaybackState import com.nextcloud.client.player.util.PlayerUtil.getPlaybackFile import com.nextcloud.client.player.util.PlayerUtil.putPlaybackFile +import com.nextcloud.utils.HumanReadableFormatter import com.owncloud.android.R import com.owncloud.android.databinding.PlayerAudioFileFragmentBinding import com.owncloud.android.utils.DisplayUtils @@ -132,7 +133,7 @@ open class AudioFileFragment : } private fun PlaybackFile.getDetailsText(): String = listOfNotNull( - contentLength.takeIf { it > 0 }?.let { DisplayUtils.bytesToHumanReadable(it) }, + contentLength.takeIf { it > 0 }?.let { HumanReadableFormatter.bytesToHumanReadable(it) }, lastModified.takeIf { it > 0 }?.let(::getLastModifiedText) ).joinToString(DETAILS_SEPARATOR) diff --git a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt index 3361e5dab7c9..0c2e41857b18 100644 --- a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt @@ -18,6 +18,7 @@ import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.nextcloud.client.di.Injectable import com.nextcloud.client.preferences.AppPreferencesImpl +import com.nextcloud.utils.HumanReadableFormatter import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.databinding.DialogDataStorageLocationBinding @@ -27,7 +28,6 @@ import com.owncloud.android.datastorage.StoragePoint.PrivacyType import com.owncloud.android.datastorage.StoragePoint.StorageType import com.owncloud.android.ui.dialog.extensions.themeButtons import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.ViewThemeUtils import java.io.File import javax.inject.Inject @@ -121,8 +121,8 @@ class ChooseStorageLocationDialogFragment : return String.format( getString(R.string.file_migration_free_space), typeString, - DisplayUtils.bytesToHumanReadable(usedSpace), - DisplayUtils.bytesToHumanReadable(totalSpace) + HumanReadableFormatter.bytesToHumanReadable(usedSpace), + HumanReadableFormatter.bytesToHumanReadable(totalSpace) ) } ?: typeString } diff --git a/app/src/main/java/com/nextcloud/ui/fileInfo/ImageDetailInfo.kt b/app/src/main/java/com/nextcloud/ui/fileInfo/ImageDetailInfo.kt index b23bc1eb1a8f..1102143337b6 100644 --- a/app/src/main/java/com/nextcloud/ui/fileInfo/ImageDetailInfo.kt +++ b/app/src/main/java/com/nextcloud/ui/fileInfo/ImageDetailInfo.kt @@ -20,13 +20,13 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.NominatimClient import com.nextcloud.client.utils.IntentUtil import com.nextcloud.ui.fileInfo.model.ImageMetadata +import com.nextcloud.utils.HumanReadableFormatter import com.nextcloud.utils.extensions.getSmallThumbnail import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.databinding.FileInfoFragmentBinding import com.owncloud.android.datamodel.OCFile import com.owncloud.android.utils.BitmapUtils -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.ViewThemeUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -181,7 +181,7 @@ class ImageDetailInfo(private val fragment: FileInfoFragment, private val viewTh } fun gatherMetadata(file: OCFile): ImageMetadata { - val fileSize = DisplayUtils.bytesToHumanReadable(file.fileLength) + val fileSize = HumanReadableFormatter.bytesToHumanReadable(file.fileLength) val timestamp = maxOf(file.modificationTimestamp, file.creationTimestamp) return if (file.isDown) { gatherLocalMetadata(file, fileSize, timestamp) diff --git a/app/src/main/java/com/nextcloud/utils/ByteUnit.kt b/app/src/main/java/com/nextcloud/utils/ByteUnit.kt new file mode 100644 index 000000000000..447a3489e986 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/ByteUnit.kt @@ -0,0 +1,20 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +enum class ByteUnit(val suffix: String, val decimals: Int) { + BYTES("B", 0), + KILOBYTES("KB", 0), + MEGABYTES("MB", 1), + GIGABYTES("GB", 1), + TERABYTES("TB", 1), + PETABYTES("PB", 2), + EXABYTES("EB", 2), + ZETTABYTES("ZB", 2), + YOTTABYTES("YB", 2) +} diff --git a/app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt b/app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt new file mode 100644 index 000000000000..5c1e246b18b6 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt @@ -0,0 +1,40 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import com.owncloud.android.MainApp +import com.owncloud.android.R +import java.text.DateFormat +import java.util.Date +import java.util.Locale + +object HumanReadableFormatter { + + private const val BYTE_SIZE_DIVIDER = 1024.0 + + @JvmStatic + fun bytesToHumanReadable(bytes: Long): String { + if (bytes < 0) { + return MainApp.string(R.string.common_pending) + } + + var size = bytes.toDouble() + var unitIndex = 0 + while (size > BYTE_SIZE_DIVIDER && unitIndex < ByteUnit.entries.lastIndex) { + size /= BYTE_SIZE_DIVIDER + unitIndex++ + } + + val unit = ByteUnit.entries[unitIndex] + return "${String.format(Locale.ROOT, "%.${unit.decimals}f", size)} ${unit.suffix}" + } + + @JvmStatic + fun unixTimeToHumanReadable(milliseconds: Long): String = + DateFormat.getDateTimeInstance().format(Date(milliseconds)) +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt index 138af0d0060c..437a572d8ba3 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt @@ -9,18 +9,18 @@ package com.nextcloud.utils.extensions import android.graphics.Bitmap import androidx.exifinterface.media.ExifInterface +import com.nextcloud.utils.HumanReadableFormatter import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.ThumbnailsCacheManager import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.files.model.ServerFileInterface -import com.owncloud.android.utils.DisplayUtils import java.io.File import java.nio.file.Path private const val TAG = "FileExtensions" fun OCFile?.logFileSize(tag: String) { - val size = DisplayUtils.bytesToHumanReadable(this?.fileLength ?: -1) + val size = HumanReadableFormatter.bytesToHumanReadable(this?.fileLength ?: -1) val rawByte = this?.fileLength ?: -1 Log_OC.d(tag, "onSaveInstanceState: $size, raw byte $rawByte") } @@ -48,7 +48,7 @@ fun File?.getSmallThumbnail(): Bitmap? = ThumbnailsCacheManager.getBitmapFromDis fun File?.getSmallThumbnailKey(): String = ThumbnailsCacheManager.PREFIX_THUMBNAIL + hashCode() fun File?.logFileSize(tag: String) { - val size = DisplayUtils.bytesToHumanReadable(this?.length() ?: -1) + val size = HumanReadableFormatter.bytesToHumanReadable(this?.length() ?: -1) val rawByte = this?.length() ?: -1 Log_OC.d(tag, "onSaveInstanceState: $size, raw byte $rawByte") } diff --git a/app/src/main/java/com/owncloud/android/ui/activities/adapter/ActivityAndVersionListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/activities/adapter/ActivityAndVersionListAdapter.kt index cd2c24c65ce3..bc385267ed93 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/adapter/ActivityAndVersionListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/activities/adapter/ActivityAndVersionListAdapter.kt @@ -14,12 +14,12 @@ import androidx.fragment.app.FragmentActivity import androidx.recyclerview.widget.RecyclerView import com.nextcloud.client.account.CurrentAccountProvider import com.nextcloud.common.NextcloudClient +import com.nextcloud.utils.HumanReadableFormatter import com.owncloud.android.databinding.VersionListItemBinding import com.owncloud.android.lib.resources.activities.model.Activity import com.owncloud.android.lib.resources.files.model.FileVersion import com.owncloud.android.ui.interfaces.ActivityListInterface import com.owncloud.android.ui.interfaces.VersionListInterface -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.ViewThemeUtils import java.util.Date @@ -71,7 +71,7 @@ class ActivityAndVersionListAdapter( override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (holder is VersionViewHolder) { val fileVersion = values[position] as FileVersion - holder.binding.size.text = DisplayUtils.bytesToHumanReadable(fileVersion.fileLength) + holder.binding.size.text = HumanReadableFormatter.bytesToHumanReadable(fileVersion.fileLength) holder.binding.time.text = DateFormat.format("HH:mm", Date(fileVersion.modifiedTimestamp).time) holder.binding.restore.setOnClickListener { versionListInterface.onRestoreClicked(fileVersion) } } else { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 5430460c5000..6d0d1c5ffd54 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -65,6 +65,7 @@ import com.nextcloud.ui.composeActivity.ComposeActivity; import com.nextcloud.ui.composeActivity.ComposeDestination; import com.nextcloud.utils.GlideHelper; +import com.nextcloud.utils.HumanReadableFormatter; import com.nextcloud.utils.LinkHelper; import com.nextcloud.utils.SnackbarUtil; import com.nextcloud.utils.extensions.ActivityExtensionsKt; @@ -940,12 +941,12 @@ private void setQuotaInformation(long usedSpace, long totalSpace, int relative, if (GetUserInfoRemoteOperation.SPACE_UNLIMITED == quotaValue) { mQuotaTextPercentage.setText(String.format( getString(R.string.drawer_quota_unlimited), - DisplayUtils.bytesToHumanReadable(usedSpace))); + HumanReadableFormatter.bytesToHumanReadable(usedSpace))); } else { mQuotaTextPercentage.setText(String.format( getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(usedSpace), - DisplayUtils.bytesToHumanReadable(totalSpace))); + HumanReadableFormatter.bytesToHumanReadable(usedSpace), + HumanReadableFormatter.bytesToHumanReadable(totalSpace))); } mQuotaProgressBar.setProgress(relative); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.kt index b60f3997eb6d..a8e8337fd21b 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.kt @@ -18,6 +18,7 @@ import androidx.lifecycle.lifecycleScope import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.User import com.nextcloud.client.di.Injectable +import com.nextcloud.utils.HumanReadableFormatter import com.owncloud.android.R import com.owncloud.android.databinding.ShareActivityBinding import com.owncloud.android.datamodel.OCFile @@ -32,7 +33,6 @@ import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.operations.GetSharesForFileOperation import com.owncloud.android.ui.fragment.FileDetailSharingFragment import com.owncloud.android.ui.fragment.FileDetailsSharingProcessFragment -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.MimeTypeUtil import com.nextcloud.utils.thumbnail.ThumbnailGenerator import kotlinx.coroutines.Dispatchers @@ -122,7 +122,7 @@ class ShareActivity : with(binding) { shareFileName.text = getString(R.string.share_file, file.fileName) viewThemeUtils.platform.colorViewBackground(shareHeaderDivider) - shareFileSize.text = DisplayUtils.bytesToHumanReadable(file.fileLength) + shareFileSize.text = HumanReadableFormatter.bytesToHumanReadable(file.fileLength) } } @@ -150,7 +150,7 @@ class ShareActivity : val length = (result.data.first() as? RemoteFile)?.length ?: return@launch file.fileLength = length withContext(Dispatchers.Main) { - binding.shareFileSize.text = DisplayUtils.bytesToHumanReadable(length) + binding.shareFileSize.text = HumanReadableFormatter.bytesToHumanReadable(length) } } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/InternalTwoWaySyncViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/InternalTwoWaySyncViewHolder.kt index a35f362bdec6..b283e9f7a7f0 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/InternalTwoWaySyncViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/InternalTwoWaySyncViewHolder.kt @@ -10,6 +10,7 @@ package com.owncloud.android.ui.adapter import android.content.Context import android.view.View import androidx.recyclerview.widget.RecyclerView +import com.nextcloud.utils.HumanReadableFormatter import com.owncloud.android.R import com.owncloud.android.databinding.InternalTwoWaySyncViewHolderBinding import com.owncloud.android.datamodel.FileDataStorageManager @@ -25,7 +26,7 @@ class InternalTwoWaySyncViewHolder(val binding: InternalTwoWaySyncViewHolderBind internalTwoWaySyncAdapter: InternalTwoWaySyncAdapter ) { binding.run { - size.text = DisplayUtils.bytesToHumanReadable(folder.fileLength) + size.text = HumanReadableFormatter.bytesToHumanReadable(folder.fileLength) name.text = folder.decryptedFileName if (folder.internalFolderSyncResult.isEmpty()) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index b65ecf532d4e..5684c47059e3 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -34,6 +34,7 @@ import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.model.OfflineOperationType; +import com.nextcloud.utils.HumanReadableFormatter; import com.nextcloud.utils.e2ee.E2EVersionHelper; import com.nextcloud.utils.extensions.ImageViewExtensionsKt; import com.nextcloud.utils.extensions.ViewExtensionsKt; @@ -741,7 +742,7 @@ private void prepareFileSize(ListItemViewHolder holder, OCFile file, long size) private String getFileSizeText(OCFile file, long size) { if (!file.isOfflineOperation()) { - return DisplayUtils.bytesToHumanReadable(size); + return HumanReadableFormatter.bytesToHumanReadable(size); } OfflineOperationEntity entity = mStorageManager.getOfflineEntityFromOCFile(file); @@ -1044,7 +1045,7 @@ public String getPopupText(@NonNull View view, int position) { Date date = new Date(milliseconds); return dateFormat.format(date); case SIZE: - return DisplayUtils.bytesToHumanReadable(file.getFileLength()); + return HumanReadableFormatter.bytesToHumanReadable(file.getFileLength()); default: Log_OC.d(TAG, "getPopupText: Unsupported sort order: " + sortOrder.getType()); return ""; diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt index cbac02172939..9127a3b1160a 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt @@ -15,6 +15,7 @@ import android.view.ViewGroup import android.widget.ImageView import androidx.recyclerview.widget.RecyclerView import com.nextcloud.client.account.User +import com.nextcloud.utils.HumanReadableFormatter import com.owncloud.android.databinding.UploaderListItemLayoutBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile @@ -83,7 +84,7 @@ class ReceiveExternalFilesAdapter( viewHolder.binding.lastMod.text = DisplayUtils.getRelativeTimestamp(context, file.modificationTimestamp) if (!file.isFolder) { - viewHolder.binding.fileSize.text = DisplayUtils.bytesToHumanReadable(file.fileLength) + viewHolder.binding.fileSize.text = HumanReadableFormatter.bytesToHumanReadable(file.fileLength) } viewHolder.binding.fileSize.visibility = if (file.isFolder) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java index 3d1595be438f..8c1e3be3577e 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java @@ -20,6 +20,7 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole; import com.nextcloud.client.account.User; import com.nextcloud.client.preferences.AppPreferences; +import com.nextcloud.utils.HumanReadableFormatter; import com.nextcloud.utils.extensions.FileExtensionsKt; import com.nextcloud.utils.extensions.ViewExtensionsKt; import com.nextcloud.utils.thumbnail.VideoOverlayGenerator; @@ -137,7 +138,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi trashbinFileViewHolder.binding.Filename.setText(file.getFileName()); // fileSize - trashbinFileViewHolder.binding.fileSize.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength())); + trashbinFileViewHolder.binding.fileSize.setText(HumanReadableFormatter.bytesToHumanReadable(file.getFileLength())); // originalLocation String location; diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/localFileList/LocalFileListItemBinder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/localFileList/LocalFileListItemBinder.kt index e52e0790d957..a0613a903fab 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/localFileList/LocalFileListItemBinder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/localFileList/LocalFileListItemBinder.kt @@ -10,6 +10,7 @@ import android.app.Activity import android.view.View import androidx.core.content.ContextCompat import com.nextcloud.android.common.ui.theme.utils.ColorRole +import com.nextcloud.utils.HumanReadableFormatter import com.owncloud.android.R import com.owncloud.android.ui.interfaces.LocalFileListFragmentInterface import com.owncloud.android.utils.DisplayUtils @@ -76,7 +77,7 @@ internal class LocalFileListItemBinder( } else { holder.fileSize.visibility = View.VISIBLE holder.fileSeparator.visibility = View.VISIBLE - holder.fileSize.text = DisplayUtils.bytesToHumanReadable(file.length()) + holder.fileSize.text = HumanReadableFormatter.bytesToHumanReadable(file.length()) } holder.lastModification.text = DisplayUtils.getRelativeTimestamp(activity, file.lastModified()) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt index 49eaa86821b8..cec954de620d 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt @@ -24,6 +24,7 @@ import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.network.ConnectivityService +import com.nextcloud.utils.HumanReadableFormatter import com.nextcloud.utils.SnackbarUtil import com.nextcloud.utils.extensions.getStatusText import com.nextcloud.utils.extensions.isLastResultConflictError @@ -276,7 +277,7 @@ class UploadListAdapter( fileSizeFormat = "%s, " } - val fileSizeInBytes = DisplayUtils.bytesToHumanReadable(item.fileSize) + val fileSizeInBytes = HumanReadableFormatter.bytesToHumanReadable(item.fileSize) val uploadFileSize = String.format(fileSizeFormat, fileSizeInBytes) holder.binding.uploadFileSize.text = uploadFileSize } else { diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragment.kt index cca15c8cdcd4..80daea3cdf71 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragment.kt @@ -11,11 +11,11 @@ import android.content.Intent import android.os.Bundle import android.os.storage.StorageManager import androidx.activity.result.contract.ActivityResultContracts +import com.nextcloud.utils.HumanReadableFormatter import com.nextcloud.utils.extensions.getParcelableArgument import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile import com.owncloud.android.ui.dialog.ConfirmationDialogFragment.ConfirmationDialogFragmentListener -import com.owncloud.android.utils.DisplayUtils class SyncFileNotEnoughSpaceDialogFragment : ConfirmationDialogFragment(), @@ -66,8 +66,8 @@ class SyncFileNotEnoughSpaceDialogFragment : ARG_MESSAGE_ARGUMENTS, arrayOf( file.fileName, - DisplayUtils.bytesToHumanReadable(file.fileLength), - DisplayUtils.bytesToHumanReadable(availableDeviceSpace) + HumanReadableFormatter.bytesToHumanReadable(file.fileLength), + HumanReadableFormatter.bytesToHumanReadable(availableDeviceSpace) ) ) putParcelable(ARG_PASSED_FILE, file) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/conflict/ConflictResolveDialogFactory.kt b/app/src/main/java/com/owncloud/android/ui/dialog/conflict/ConflictResolveDialogFactory.kt index 9a6fd6a32a98..07a980c1f929 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/conflict/ConflictResolveDialogFactory.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/conflict/ConflictResolveDialogFactory.kt @@ -12,6 +12,7 @@ import android.os.Bundle import androidx.annotation.StringRes import com.nextcloud.client.account.User import com.nextcloud.client.database.entity.OfflineOperationEntity +import com.nextcloud.utils.HumanReadableFormatter import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile import com.owncloud.android.ui.dialog.conflict.model.ConflictDialogData @@ -75,7 +76,7 @@ object ConflictResolveDialogFactory { ConflictFileData( title = getString(titleId), timestamp = DisplayUtils.getRelativeTimestamp(this, timestamp).toString(), - fileSize = DisplayUtils.bytesToHumanReadable(fileLength) + fileSize = HumanReadableFormatter.bytesToHumanReadable(fileLength) ) private fun createDialog(type: ConflictDialogType, putFiles: Bundle.() -> Unit): ConflictsResolveDialog = diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index e5d3e9e686b0..1cbfe42e9fad 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -35,6 +35,7 @@ import com.nextcloud.ui.fileactions.FileAction; import com.nextcloud.ui.fileactions.FileActionsBottomSheet; import com.nextcloud.ui.tags.TagManagementBottomSheet; +import com.nextcloud.utils.HumanReadableFormatter; import com.nextcloud.utils.MenuUtils; import com.nextcloud.utils.SnackbarUtil; import com.nextcloud.utils.extensions.BundleExtensionsKt; @@ -609,7 +610,7 @@ public void updateFileDetails(boolean transferring, boolean refresh) { } else { binding.filename.setVisibility(View.GONE); } - binding.size.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength())); + binding.size.setText(HumanReadableFormatter.bytesToHumanReadable(file.getFileLength())); boolean showDetailedTimestamp = preferences.isShowDetailedTimestampEnabled(); setFileModificationTimestamp(file, showDetailedTimestamp); @@ -670,7 +671,7 @@ public void onDownloadProgress(FileDownloadProgressEvent event) { private void setFileModificationTimestamp(OCFile file, boolean showDetailedTimestamp) { if (showDetailedTimestamp) { - binding.lastModificationTimestamp.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp())); + binding.lastModificationTimestamp.setText(HumanReadableFormatter.unixTimeToHumanReadable(file.getModificationTimestamp())); } else { binding.lastModificationTimestamp.setText(DisplayUtils.getRelativeTimestamp(getContext(), file.getModificationTimestamp())); diff --git a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java index 43dc3b796afd..5dccf48c6c53 100644 --- a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -37,6 +37,7 @@ import android.view.View; import com.nextcloud.client.account.User; +import com.nextcloud.utils.HumanReadableFormatter; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.ThumbnailsCacheManager; @@ -52,13 +53,10 @@ import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.net.IDN; import java.nio.charset.Charset; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.Date; import java.util.Locale; import java.util.TimeZone; @@ -84,14 +82,9 @@ public final class DisplayUtils { private static final String TAG = DisplayUtils.class.getSimpleName(); - private 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 final String HTTP_PROTOCOL = "http://"; private static final String HTTPS_PROTOCOL = "https://"; private static final String TWITTER_HANDLE_PREFIX = "@"; - private static final int BYTE_SIZE_DIVIDER = 1024; - private static final double BYTE_SIZE_DIVIDER_DOUBLE = 1024.0; private static final int DATE_TIME_PARTS_SIZE = 2; public static final String MONTH_YEAR_PATTERN = "MMMM yyyy"; public static final String MONTH_PATTERN = "MMMM"; @@ -101,45 +94,6 @@ private DisplayUtils() { // utility class -> private constructor } - /** - * Converts the file size in bytes to human readable output. - *
    - *
  • appends a size suffix, e.g. B, KB, MB etc.
  • - *
  • rounds the size based on the suffix to 0,1 or 2 decimals
  • - *
- * - * @param bytes Input file size - * @return something readable like "12 MB", {@link com.owncloud.android.R.string#common_pending} for negative - * byte values - */ - public static String bytesToHumanReadable(long bytes) { - if (bytes < 0) { - return MainApp.string(R.string.common_pending); - } else { - double result = bytes; - int suffixIndex = 0; - while (result > BYTE_SIZE_DIVIDER && suffixIndex < sizeSuffixes.length) { - result /= BYTE_SIZE_DIVIDER_DOUBLE; - suffixIndex++; - } - - return new BigDecimal(String.valueOf(result)).setScale( - sizeScales[suffixIndex], RoundingMode.HALF_UP) + " " + sizeSuffixes[suffixIndex]; - } - } - - /** - * Converts Unix time to human readable format - * - * @param milliseconds that have passed since 01/01/1970 - * @return The human readable time for the users locale - */ - public static String unixTimeToHumanReadable(long milliseconds) { - Date date = new Date(milliseconds); - DateFormat df = DateFormat.getDateTimeInstance(); - return df.format(date); - } - /** * beautifies a given URL by removing any http/https protocol prefix. * @@ -270,7 +224,7 @@ public static CharSequence getRelativeDateTimeString(Context c, // in Future if (!showFuture && time > System.currentTimeMillis()) { - return DisplayUtils.unixTimeToHumanReadable(time); + return HumanReadableFormatter.unixTimeToHumanReadable(time); } // < 60 seconds -> seconds ago long diff = System.currentTimeMillis() - time; diff --git a/app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt b/app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt new file mode 100644 index 000000000000..de164d295c90 --- /dev/null +++ b/app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt @@ -0,0 +1,122 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import com.owncloud.android.MainApp +import com.owncloud.android.R +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Before +import org.junit.Test +import org.mockito.MockedStatic +import org.mockito.Mockito +import java.text.DateFormat +import java.util.Date +import java.util.Locale +import java.util.TimeZone + +class HumanReadableFormatterTest { + + private val pendingLabel = "Pending" + + private lateinit var mainApp: MockedStatic + private lateinit var defaultLocale: Locale + private lateinit var defaultTimeZone: TimeZone + + @Before + fun setUp() { + mainApp = Mockito.mockStatic(MainApp::class.java) + mainApp.`when` { MainApp.string(R.string.common_pending) }.thenReturn(pendingLabel) + + defaultLocale = Locale.getDefault() + defaultTimeZone = TimeZone.getDefault() + Locale.setDefault(Locale.US) + TimeZone.setDefault(TimeZone.getTimeZone("UTC")) + } + + @After + fun tearDown() { + mainApp.close() + Locale.setDefault(defaultLocale) + TimeZone.setDefault(defaultTimeZone) + } + + @Test + fun `sizes below one kilobyte are shown as whole bytes`() { + assertEquals("0 B", HumanReadableFormatter.bytesToHumanReadable(0)) + assertEquals("1 B", HumanReadableFormatter.bytesToHumanReadable(1)) + assertEquals("512 B", HumanReadableFormatter.bytesToHumanReadable(512)) + } + + @Test + fun `a unit is kept as long as the value fits into it`() { + assertEquals("1024 B", HumanReadableFormatter.bytesToHumanReadable(1024)) + assertEquals("1024 KB", HumanReadableFormatter.bytesToHumanReadable(1024 * 1024)) + assertEquals("1024.0 GB", HumanReadableFormatter.bytesToHumanReadable(1024L * 1024 * 1024 * 1024)) + } + + @Test + fun `kilobytes are rounded to whole numbers`() { + assertEquals("1 KB", HumanReadableFormatter.bytesToHumanReadable(1025)) + assertEquals("2 KB", HumanReadableFormatter.bytesToHumanReadable(1536)) + } + + @Test + fun `megabytes up to terabytes keep one decimal`() { + assertEquals("1.0 MB", HumanReadableFormatter.bytesToHumanReadable(1024L * 1024 + 1)) + assertEquals("1.5 GB", HumanReadableFormatter.bytesToHumanReadable(1610612736)) + assertEquals("5.0 GB", HumanReadableFormatter.bytesToHumanReadable(5L * 1024 * 1024 * 1024)) + } + + @Test + fun `petabytes and above keep two decimals`() { + assertEquals("1.00 PB", HumanReadableFormatter.bytesToHumanReadable(1024L * 1024 * 1024 * 1024 * 1024 + 1)) + } + + @Test + fun `the largest possible size stays within the known units`() { + assertEquals("8.00 EB", HumanReadableFormatter.bytesToHumanReadable(Long.MAX_VALUE)) + } + + @Test + fun `a size is written with a decimal point in every locale`() { + Locale.setDefault(Locale.GERMANY) + assertEquals("1.5 GB", HumanReadableFormatter.bytesToHumanReadable(1610612736)) + + Locale.setDefault(Locale.forLanguageTag("ar-EG")) + assertEquals("1.5 GB", HumanReadableFormatter.bytesToHumanReadable(1610612736)) + } + + @Test + fun `a negative size is pending`() { + assertEquals(pendingLabel, HumanReadableFormatter.bytesToHumanReadable(-1)) + assertEquals(pendingLabel, HumanReadableFormatter.bytesToHumanReadable(Long.MIN_VALUE)) + } + + @Test + fun `a timestamp keeps its date and time when read back`() { + val timestamp = 1790000000000 + val formatted = HumanReadableFormatter.unixTimeToHumanReadable(timestamp) + + val parsed = DateFormat.getDateTimeInstance().parse(formatted) + + assertEquals(Date(timestamp - timestamp % 1000), parsed) + } + + @Test + fun `a timestamp is formatted for the current locale`() { + val timestamp = 1790000000000 + + val american = HumanReadableFormatter.unixTimeToHumanReadable(timestamp) + Locale.setDefault(Locale.GERMANY) + val german = HumanReadableFormatter.unixTimeToHumanReadable(timestamp) + + assertNotEquals(american, german) + } +} From 6b6a4611de7f92691038a85be1f5f95dd1370821 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 23 Sep 2026 09:23:03 +0200 Subject: [PATCH 2/3] make it better Signed-off-by: alperozturk96 --- .../main/java/com/nextcloud/utils/ByteUnit.kt | 4 +--- .../nextcloud/utils/HumanReadableFormatter.kt | 13 ++++--------- .../utils/HumanReadableFormatterTest.kt | 16 ++++++++++------ 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/ByteUnit.kt b/app/src/main/java/com/nextcloud/utils/ByteUnit.kt index 447a3489e986..500ae027858b 100644 --- a/app/src/main/java/com/nextcloud/utils/ByteUnit.kt +++ b/app/src/main/java/com/nextcloud/utils/ByteUnit.kt @@ -14,7 +14,5 @@ enum class ByteUnit(val suffix: String, val decimals: Int) { GIGABYTES("GB", 1), TERABYTES("TB", 1), PETABYTES("PB", 2), - EXABYTES("EB", 2), - ZETTABYTES("ZB", 2), - YOTTABYTES("YB", 2) + EXABYTES("EB", 2) } diff --git a/app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt b/app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt index 5c1e246b18b6..722149c741e8 100644 --- a/app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt +++ b/app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt @@ -15,22 +15,17 @@ import java.util.Locale object HumanReadableFormatter { - private const val BYTE_SIZE_DIVIDER = 1024.0 - @JvmStatic fun bytesToHumanReadable(bytes: Long): String { if (bytes < 0) { return MainApp.string(R.string.common_pending) } - var size = bytes.toDouble() - var unitIndex = 0 - while (size > BYTE_SIZE_DIVIDER && unitIndex < ByteUnit.entries.lastIndex) { - size /= BYTE_SIZE_DIVIDER - unitIndex++ - } - + val unitIndex = ((63 - java.lang.Long.numberOfLeadingZeros(bytes)) / 10) + .coerceIn(0, ByteUnit.entries.lastIndex) val unit = ByteUnit.entries[unitIndex] + val size = bytes.toDouble() / (1L shl (10 * unitIndex)) + return "${String.format(Locale.ROOT, "%.${unit.decimals}f", size)} ${unit.suffix}" } diff --git a/app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt b/app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt index de164d295c90..d0700a04360a 100644 --- a/app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt +++ b/app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt @@ -52,13 +52,17 @@ class HumanReadableFormatterTest { assertEquals("0 B", HumanReadableFormatter.bytesToHumanReadable(0)) assertEquals("1 B", HumanReadableFormatter.bytesToHumanReadable(1)) assertEquals("512 B", HumanReadableFormatter.bytesToHumanReadable(512)) + assertEquals("1023 B", HumanReadableFormatter.bytesToHumanReadable(1023)) } @Test - fun `a unit is kept as long as the value fits into it`() { - assertEquals("1024 B", HumanReadableFormatter.bytesToHumanReadable(1024)) - assertEquals("1024 KB", HumanReadableFormatter.bytesToHumanReadable(1024 * 1024)) - assertEquals("1024.0 GB", HumanReadableFormatter.bytesToHumanReadable(1024L * 1024 * 1024 * 1024)) + fun `each unit starts at exactly 1024 of the previous one`() { + assertEquals("1 KB", HumanReadableFormatter.bytesToHumanReadable(1L shl 10)) + assertEquals("1.0 MB", HumanReadableFormatter.bytesToHumanReadable(1L shl 20)) + assertEquals("1.0 GB", HumanReadableFormatter.bytesToHumanReadable(1L shl 30)) + assertEquals("1.0 TB", HumanReadableFormatter.bytesToHumanReadable(1L shl 40)) + assertEquals("1.00 PB", HumanReadableFormatter.bytesToHumanReadable(1L shl 50)) + assertEquals("1.00 EB", HumanReadableFormatter.bytesToHumanReadable(1L shl 60)) } @Test @@ -76,7 +80,7 @@ class HumanReadableFormatterTest { @Test fun `petabytes and above keep two decimals`() { - assertEquals("1.00 PB", HumanReadableFormatter.bytesToHumanReadable(1024L * 1024 * 1024 * 1024 * 1024 + 1)) + assertEquals("1.00 PB", HumanReadableFormatter.bytesToHumanReadable((1L shl 50) + 1)) } @Test @@ -106,7 +110,7 @@ class HumanReadableFormatterTest { val parsed = DateFormat.getDateTimeInstance().parse(formatted) - assertEquals(Date(timestamp - timestamp % 1000), parsed) + assertEquals(Date(timestamp), parsed) } @Test From 2b1f0e93748eb6b2bd3eb1c2070e730e15c86d75 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 23 Sep 2026 09:36:43 +0200 Subject: [PATCH 3/3] rename functions Signed-off-by: alperozturk96 --- .../AutoUploadLocalDeletionWorker.kt | 2 +- .../player/ui/audio/AudioFileFragment.kt | 2 +- .../ui/ChooseStorageLocationDialogFragment.kt | 4 +- .../nextcloud/ui/fileInfo/ImageDetailInfo.kt | 2 +- .../nextcloud/utils/HumanReadableFormatter.kt | 17 ++++--- .../utils/extensions/FileExtensions.kt | 4 +- .../adapter/ActivityAndVersionListAdapter.kt | 2 +- .../android/ui/activity/DrawerActivity.java | 7 ++- .../android/ui/activity/ShareActivity.kt | 4 +- .../adapter/InternalTwoWaySyncViewHolder.kt | 2 +- .../android/ui/adapter/OCFileListAdapter.java | 4 +- .../ui/adapter/ReceiveExternalFilesAdapter.kt | 3 +- .../ui/adapter/TrashbinListAdapter.java | 2 +- .../localFileList/LocalFileListItemBinder.kt | 2 +- .../adapter/uploadList/UploadListAdapter.kt | 2 +- .../SyncFileNotEnoughSpaceDialogFragment.kt | 4 +- .../conflict/ConflictResolveDialogFactory.kt | 2 +- .../ui/fragment/FileDetailFragment.java | 4 +- .../owncloud/android/utils/DisplayUtils.java | 2 +- .../utils/HumanReadableFormatterTest.kt | 48 +++++++++---------- 20 files changed, 60 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadLocalDeletionWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadLocalDeletionWorker.kt index b409020deb10..2d2844f01cb8 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadLocalDeletionWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadLocalDeletionWorker.kt @@ -122,7 +122,7 @@ class AutoUploadLocalDeletionWorker( ): Notification { val notificationContent = context.getString( R.string.autoupload_delete_uploaded_notif_ended_content, - HumanReadableFormatter.bytesToHumanReadable(spaceFreed), + HumanReadableFormatter.formatBytes(spaceFreed), context.resources.getQuantityString( R.plurals.autoupload_delete_uploaded_notif_ended_content_files, filesRemoved.toInt(), diff --git a/app/src/main/java/com/nextcloud/client/player/ui/audio/AudioFileFragment.kt b/app/src/main/java/com/nextcloud/client/player/ui/audio/AudioFileFragment.kt index 267a554dadac..65c49a651623 100644 --- a/app/src/main/java/com/nextcloud/client/player/ui/audio/AudioFileFragment.kt +++ b/app/src/main/java/com/nextcloud/client/player/ui/audio/AudioFileFragment.kt @@ -133,7 +133,7 @@ open class AudioFileFragment : } private fun PlaybackFile.getDetailsText(): String = listOfNotNull( - contentLength.takeIf { it > 0 }?.let { HumanReadableFormatter.bytesToHumanReadable(it) }, + contentLength.takeIf { it > 0 }?.let { HumanReadableFormatter.formatBytes(it) }, lastModified.takeIf { it > 0 }?.let(::getLastModifiedText) ).joinToString(DETAILS_SEPARATOR) diff --git a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt index 0c2e41857b18..bab326a269dd 100644 --- a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt @@ -121,8 +121,8 @@ class ChooseStorageLocationDialogFragment : return String.format( getString(R.string.file_migration_free_space), typeString, - HumanReadableFormatter.bytesToHumanReadable(usedSpace), - HumanReadableFormatter.bytesToHumanReadable(totalSpace) + HumanReadableFormatter.formatBytes(usedSpace), + HumanReadableFormatter.formatBytes(totalSpace) ) } ?: typeString } diff --git a/app/src/main/java/com/nextcloud/ui/fileInfo/ImageDetailInfo.kt b/app/src/main/java/com/nextcloud/ui/fileInfo/ImageDetailInfo.kt index 1102143337b6..11eced485f55 100644 --- a/app/src/main/java/com/nextcloud/ui/fileInfo/ImageDetailInfo.kt +++ b/app/src/main/java/com/nextcloud/ui/fileInfo/ImageDetailInfo.kt @@ -181,7 +181,7 @@ class ImageDetailInfo(private val fragment: FileInfoFragment, private val viewTh } fun gatherMetadata(file: OCFile): ImageMetadata { - val fileSize = HumanReadableFormatter.bytesToHumanReadable(file.fileLength) + val fileSize = HumanReadableFormatter.formatBytes(file.fileLength) val timestamp = maxOf(file.modificationTimestamp, file.creationTimestamp) return if (file.isDown) { gatherLocalMetadata(file, fileSize, timestamp) diff --git a/app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt b/app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt index 722149c741e8..c61822f19725 100644 --- a/app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt +++ b/app/src/main/java/com/nextcloud/utils/HumanReadableFormatter.kt @@ -15,21 +15,24 @@ import java.util.Locale object HumanReadableFormatter { + private const val BITS_PER_UNIT = 10 + @JvmStatic - fun bytesToHumanReadable(bytes: Long): String { + fun formatBytes(bytes: Long): String { if (bytes < 0) { return MainApp.string(R.string.common_pending) } - val unitIndex = ((63 - java.lang.Long.numberOfLeadingZeros(bytes)) / 10) - .coerceIn(0, ByteUnit.entries.lastIndex) + val highestSetBit = Long.SIZE_BITS - 1 - bytes.countLeadingZeroBits() + val unitIndex = (highestSetBit / BITS_PER_UNIT).coerceIn(0, ByteUnit.entries.lastIndex) val unit = ByteUnit.entries[unitIndex] - val size = bytes.toDouble() / (1L shl (10 * unitIndex)) + val size = bytes.toDouble() / (1L shl (BITS_PER_UNIT * unitIndex)) - return "${String.format(Locale.ROOT, "%.${unit.decimals}f", size)} ${unit.suffix}" + val pattern = "%.${unit.decimals}f" + val formattedSize = String.format(Locale.ROOT, pattern, size) + return "$formattedSize ${unit.suffix}" } @JvmStatic - fun unixTimeToHumanReadable(milliseconds: Long): String = - DateFormat.getDateTimeInstance().format(Date(milliseconds)) + fun formatDateTime(milliseconds: Long): String = DateFormat.getDateTimeInstance().format(Date(milliseconds)) } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt index 437a572d8ba3..21ee4888cecd 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/FileExtensions.kt @@ -20,7 +20,7 @@ import java.nio.file.Path private const val TAG = "FileExtensions" fun OCFile?.logFileSize(tag: String) { - val size = HumanReadableFormatter.bytesToHumanReadable(this?.fileLength ?: -1) + val size = HumanReadableFormatter.formatBytes(this?.fileLength ?: -1) val rawByte = this?.fileLength ?: -1 Log_OC.d(tag, "onSaveInstanceState: $size, raw byte $rawByte") } @@ -48,7 +48,7 @@ fun File?.getSmallThumbnail(): Bitmap? = ThumbnailsCacheManager.getBitmapFromDis fun File?.getSmallThumbnailKey(): String = ThumbnailsCacheManager.PREFIX_THUMBNAIL + hashCode() fun File?.logFileSize(tag: String) { - val size = HumanReadableFormatter.bytesToHumanReadable(this?.length() ?: -1) + val size = HumanReadableFormatter.formatBytes(this?.length() ?: -1) val rawByte = this?.length() ?: -1 Log_OC.d(tag, "onSaveInstanceState: $size, raw byte $rawByte") } diff --git a/app/src/main/java/com/owncloud/android/ui/activities/adapter/ActivityAndVersionListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/activities/adapter/ActivityAndVersionListAdapter.kt index bc385267ed93..e0e531f1f337 100644 --- a/app/src/main/java/com/owncloud/android/ui/activities/adapter/ActivityAndVersionListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/activities/adapter/ActivityAndVersionListAdapter.kt @@ -71,7 +71,7 @@ class ActivityAndVersionListAdapter( override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (holder is VersionViewHolder) { val fileVersion = values[position] as FileVersion - holder.binding.size.text = HumanReadableFormatter.bytesToHumanReadable(fileVersion.fileLength) + holder.binding.size.text = HumanReadableFormatter.formatBytes(fileVersion.fileLength) holder.binding.time.text = DateFormat.format("HH:mm", Date(fileVersion.modifiedTimestamp).time) holder.binding.restore.setOnClickListener { versionListInterface.onRestoreClicked(fileVersion) } } else { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 6d0d1c5ffd54..56c21cea670a 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -103,7 +103,6 @@ import com.owncloud.android.ui.fragment.albums.AlbumsFragment; import com.owncloud.android.ui.navigation.NavigatorActivity; import com.owncloud.android.ui.navigation.NavigatorScreen; -import com.owncloud.android.ui.trashbin.TrashbinFragment; import com.owncloud.android.utils.BitmapUtils; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.DrawableUtil; @@ -941,12 +940,12 @@ private void setQuotaInformation(long usedSpace, long totalSpace, int relative, if (GetUserInfoRemoteOperation.SPACE_UNLIMITED == quotaValue) { mQuotaTextPercentage.setText(String.format( getString(R.string.drawer_quota_unlimited), - HumanReadableFormatter.bytesToHumanReadable(usedSpace))); + HumanReadableFormatter.formatBytes(usedSpace))); } else { mQuotaTextPercentage.setText(String.format( getString(R.string.drawer_quota), - HumanReadableFormatter.bytesToHumanReadable(usedSpace), - HumanReadableFormatter.bytesToHumanReadable(totalSpace))); + HumanReadableFormatter.formatBytes(usedSpace), + HumanReadableFormatter.formatBytes(totalSpace))); } mQuotaProgressBar.setProgress(relative); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.kt index a8e8337fd21b..a0df0673cf4c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ShareActivity.kt @@ -122,7 +122,7 @@ class ShareActivity : with(binding) { shareFileName.text = getString(R.string.share_file, file.fileName) viewThemeUtils.platform.colorViewBackground(shareHeaderDivider) - shareFileSize.text = HumanReadableFormatter.bytesToHumanReadable(file.fileLength) + shareFileSize.text = HumanReadableFormatter.formatBytes(file.fileLength) } } @@ -150,7 +150,7 @@ class ShareActivity : val length = (result.data.first() as? RemoteFile)?.length ?: return@launch file.fileLength = length withContext(Dispatchers.Main) { - binding.shareFileSize.text = HumanReadableFormatter.bytesToHumanReadable(length) + binding.shareFileSize.text = HumanReadableFormatter.formatBytes(length) } } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/InternalTwoWaySyncViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/InternalTwoWaySyncViewHolder.kt index b283e9f7a7f0..a0de99f64027 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/InternalTwoWaySyncViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/InternalTwoWaySyncViewHolder.kt @@ -26,7 +26,7 @@ class InternalTwoWaySyncViewHolder(val binding: InternalTwoWaySyncViewHolderBind internalTwoWaySyncAdapter: InternalTwoWaySyncAdapter ) { binding.run { - size.text = HumanReadableFormatter.bytesToHumanReadable(folder.fileLength) + size.text = HumanReadableFormatter.formatBytes(folder.fileLength) name.text = folder.decryptedFileName if (folder.internalFolderSyncResult.isEmpty()) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 5684c47059e3..bce25acf11e8 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -742,7 +742,7 @@ private void prepareFileSize(ListItemViewHolder holder, OCFile file, long size) private String getFileSizeText(OCFile file, long size) { if (!file.isOfflineOperation()) { - return HumanReadableFormatter.bytesToHumanReadable(size); + return HumanReadableFormatter.formatBytes(size); } OfflineOperationEntity entity = mStorageManager.getOfflineEntityFromOCFile(file); @@ -1045,7 +1045,7 @@ public String getPopupText(@NonNull View view, int position) { Date date = new Date(milliseconds); return dateFormat.format(date); case SIZE: - return HumanReadableFormatter.bytesToHumanReadable(file.getFileLength()); + return HumanReadableFormatter.formatBytes(file.getFileLength()); default: Log_OC.d(TAG, "getPopupText: Unsupported sort order: " + sortOrder.getType()); return ""; diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt index 9127a3b1160a..e01ca8d26e25 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt @@ -28,7 +28,6 @@ import com.owncloud.android.datamodel.ThumbnailsCacheManager.ThumbnailGeneration import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.MimeTypeUtil import com.owncloud.android.utils.theme.ViewThemeUtils -import java.util.Objects @Suppress("LongParameterList") class ReceiveExternalFilesAdapter( @@ -84,7 +83,7 @@ class ReceiveExternalFilesAdapter( viewHolder.binding.lastMod.text = DisplayUtils.getRelativeTimestamp(context, file.modificationTimestamp) if (!file.isFolder) { - viewHolder.binding.fileSize.text = HumanReadableFormatter.bytesToHumanReadable(file.fileLength) + viewHolder.binding.fileSize.text = HumanReadableFormatter.formatBytes(file.fileLength) } viewHolder.binding.fileSize.visibility = if (file.isFolder) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java index 8c1e3be3577e..83e24b76791e 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java @@ -138,7 +138,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi trashbinFileViewHolder.binding.Filename.setText(file.getFileName()); // fileSize - trashbinFileViewHolder.binding.fileSize.setText(HumanReadableFormatter.bytesToHumanReadable(file.getFileLength())); + trashbinFileViewHolder.binding.fileSize.setText(HumanReadableFormatter.formatBytes(file.getFileLength())); // originalLocation String location; diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/localFileList/LocalFileListItemBinder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/localFileList/LocalFileListItemBinder.kt index a0613a903fab..929e7d5b1479 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/localFileList/LocalFileListItemBinder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/localFileList/LocalFileListItemBinder.kt @@ -77,7 +77,7 @@ internal class LocalFileListItemBinder( } else { holder.fileSize.visibility = View.VISIBLE holder.fileSeparator.visibility = View.VISIBLE - holder.fileSize.text = HumanReadableFormatter.bytesToHumanReadable(file.length()) + holder.fileSize.text = HumanReadableFormatter.formatBytes(file.length()) } holder.lastModification.text = DisplayUtils.getRelativeTimestamp(activity, file.lastModified()) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt index cec954de620d..ed41ac446d93 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt @@ -277,7 +277,7 @@ class UploadListAdapter( fileSizeFormat = "%s, " } - val fileSizeInBytes = HumanReadableFormatter.bytesToHumanReadable(item.fileSize) + val fileSizeInBytes = HumanReadableFormatter.formatBytes(item.fileSize) val uploadFileSize = String.format(fileSizeFormat, fileSizeInBytes) holder.binding.uploadFileSize.text = uploadFileSize } else { diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragment.kt index 80daea3cdf71..2344680c594f 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SyncFileNotEnoughSpaceDialogFragment.kt @@ -66,8 +66,8 @@ class SyncFileNotEnoughSpaceDialogFragment : ARG_MESSAGE_ARGUMENTS, arrayOf( file.fileName, - HumanReadableFormatter.bytesToHumanReadable(file.fileLength), - HumanReadableFormatter.bytesToHumanReadable(availableDeviceSpace) + HumanReadableFormatter.formatBytes(file.fileLength), + HumanReadableFormatter.formatBytes(availableDeviceSpace) ) ) putParcelable(ARG_PASSED_FILE, file) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/conflict/ConflictResolveDialogFactory.kt b/app/src/main/java/com/owncloud/android/ui/dialog/conflict/ConflictResolveDialogFactory.kt index 07a980c1f929..9f5f8e4d94ab 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/conflict/ConflictResolveDialogFactory.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/conflict/ConflictResolveDialogFactory.kt @@ -76,7 +76,7 @@ object ConflictResolveDialogFactory { ConflictFileData( title = getString(titleId), timestamp = DisplayUtils.getRelativeTimestamp(this, timestamp).toString(), - fileSize = HumanReadableFormatter.bytesToHumanReadable(fileLength) + fileSize = HumanReadableFormatter.formatBytes(fileLength) ) private fun createDialog(type: ConflictDialogType, putFiles: Bundle.() -> Unit): ConflictsResolveDialog = diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index 1cbfe42e9fad..734d02d76557 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -610,7 +610,7 @@ public void updateFileDetails(boolean transferring, boolean refresh) { } else { binding.filename.setVisibility(View.GONE); } - binding.size.setText(HumanReadableFormatter.bytesToHumanReadable(file.getFileLength())); + binding.size.setText(HumanReadableFormatter.formatBytes(file.getFileLength())); boolean showDetailedTimestamp = preferences.isShowDetailedTimestampEnabled(); setFileModificationTimestamp(file, showDetailedTimestamp); @@ -671,7 +671,7 @@ public void onDownloadProgress(FileDownloadProgressEvent event) { private void setFileModificationTimestamp(OCFile file, boolean showDetailedTimestamp) { if (showDetailedTimestamp) { - binding.lastModificationTimestamp.setText(HumanReadableFormatter.unixTimeToHumanReadable(file.getModificationTimestamp())); + binding.lastModificationTimestamp.setText(HumanReadableFormatter.formatDateTime(file.getModificationTimestamp())); } else { binding.lastModificationTimestamp.setText(DisplayUtils.getRelativeTimestamp(getContext(), file.getModificationTimestamp())); diff --git a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java index 5dccf48c6c53..433ab2124305 100644 --- a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -224,7 +224,7 @@ public static CharSequence getRelativeDateTimeString(Context c, // in Future if (!showFuture && time > System.currentTimeMillis()) { - return HumanReadableFormatter.unixTimeToHumanReadable(time); + return HumanReadableFormatter.formatDateTime(time); } // < 60 seconds -> seconds ago long diff = System.currentTimeMillis() - time; diff --git a/app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt b/app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt index d0700a04360a..6c9cf3d04aec 100644 --- a/app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt +++ b/app/src/test/java/com/nextcloud/utils/HumanReadableFormatterTest.kt @@ -49,64 +49,64 @@ class HumanReadableFormatterTest { @Test fun `sizes below one kilobyte are shown as whole bytes`() { - assertEquals("0 B", HumanReadableFormatter.bytesToHumanReadable(0)) - assertEquals("1 B", HumanReadableFormatter.bytesToHumanReadable(1)) - assertEquals("512 B", HumanReadableFormatter.bytesToHumanReadable(512)) - assertEquals("1023 B", HumanReadableFormatter.bytesToHumanReadable(1023)) + assertEquals("0 B", HumanReadableFormatter.formatBytes(0)) + assertEquals("1 B", HumanReadableFormatter.formatBytes(1)) + assertEquals("512 B", HumanReadableFormatter.formatBytes(512)) + assertEquals("1023 B", HumanReadableFormatter.formatBytes(1023)) } @Test fun `each unit starts at exactly 1024 of the previous one`() { - assertEquals("1 KB", HumanReadableFormatter.bytesToHumanReadable(1L shl 10)) - assertEquals("1.0 MB", HumanReadableFormatter.bytesToHumanReadable(1L shl 20)) - assertEquals("1.0 GB", HumanReadableFormatter.bytesToHumanReadable(1L shl 30)) - assertEquals("1.0 TB", HumanReadableFormatter.bytesToHumanReadable(1L shl 40)) - assertEquals("1.00 PB", HumanReadableFormatter.bytesToHumanReadable(1L shl 50)) - assertEquals("1.00 EB", HumanReadableFormatter.bytesToHumanReadable(1L shl 60)) + assertEquals("1 KB", HumanReadableFormatter.formatBytes(1L shl 10)) + assertEquals("1.0 MB", HumanReadableFormatter.formatBytes(1L shl 20)) + assertEquals("1.0 GB", HumanReadableFormatter.formatBytes(1L shl 30)) + assertEquals("1.0 TB", HumanReadableFormatter.formatBytes(1L shl 40)) + assertEquals("1.00 PB", HumanReadableFormatter.formatBytes(1L shl 50)) + assertEquals("1.00 EB", HumanReadableFormatter.formatBytes(1L shl 60)) } @Test fun `kilobytes are rounded to whole numbers`() { - assertEquals("1 KB", HumanReadableFormatter.bytesToHumanReadable(1025)) - assertEquals("2 KB", HumanReadableFormatter.bytesToHumanReadable(1536)) + assertEquals("1 KB", HumanReadableFormatter.formatBytes(1025)) + assertEquals("2 KB", HumanReadableFormatter.formatBytes(1536)) } @Test fun `megabytes up to terabytes keep one decimal`() { - assertEquals("1.0 MB", HumanReadableFormatter.bytesToHumanReadable(1024L * 1024 + 1)) - assertEquals("1.5 GB", HumanReadableFormatter.bytesToHumanReadable(1610612736)) - assertEquals("5.0 GB", HumanReadableFormatter.bytesToHumanReadable(5L * 1024 * 1024 * 1024)) + assertEquals("1.0 MB", HumanReadableFormatter.formatBytes(1024L * 1024 + 1)) + assertEquals("1.5 GB", HumanReadableFormatter.formatBytes(1610612736)) + assertEquals("5.0 GB", HumanReadableFormatter.formatBytes(5L * 1024 * 1024 * 1024)) } @Test fun `petabytes and above keep two decimals`() { - assertEquals("1.00 PB", HumanReadableFormatter.bytesToHumanReadable((1L shl 50) + 1)) + assertEquals("1.00 PB", HumanReadableFormatter.formatBytes((1L shl 50) + 1)) } @Test fun `the largest possible size stays within the known units`() { - assertEquals("8.00 EB", HumanReadableFormatter.bytesToHumanReadable(Long.MAX_VALUE)) + assertEquals("8.00 EB", HumanReadableFormatter.formatBytes(Long.MAX_VALUE)) } @Test fun `a size is written with a decimal point in every locale`() { Locale.setDefault(Locale.GERMANY) - assertEquals("1.5 GB", HumanReadableFormatter.bytesToHumanReadable(1610612736)) + assertEquals("1.5 GB", HumanReadableFormatter.formatBytes(1610612736)) Locale.setDefault(Locale.forLanguageTag("ar-EG")) - assertEquals("1.5 GB", HumanReadableFormatter.bytesToHumanReadable(1610612736)) + assertEquals("1.5 GB", HumanReadableFormatter.formatBytes(1610612736)) } @Test fun `a negative size is pending`() { - assertEquals(pendingLabel, HumanReadableFormatter.bytesToHumanReadable(-1)) - assertEquals(pendingLabel, HumanReadableFormatter.bytesToHumanReadable(Long.MIN_VALUE)) + assertEquals(pendingLabel, HumanReadableFormatter.formatBytes(-1)) + assertEquals(pendingLabel, HumanReadableFormatter.formatBytes(Long.MIN_VALUE)) } @Test fun `a timestamp keeps its date and time when read back`() { val timestamp = 1790000000000 - val formatted = HumanReadableFormatter.unixTimeToHumanReadable(timestamp) + val formatted = HumanReadableFormatter.formatDateTime(timestamp) val parsed = DateFormat.getDateTimeInstance().parse(formatted) @@ -117,9 +117,9 @@ class HumanReadableFormatterTest { fun `a timestamp is formatted for the current locale`() { val timestamp = 1790000000000 - val american = HumanReadableFormatter.unixTimeToHumanReadable(timestamp) + val american = HumanReadableFormatter.formatDateTime(timestamp) Locale.setDefault(Locale.GERMANY) - val german = HumanReadableFormatter.unixTimeToHumanReadable(timestamp) + val german = HumanReadableFormatter.formatDateTime(timestamp) assertNotEquals(american, german) }