From e0faf5f5a32fe28bc460ecd5f8402a258f857052 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Tue, 13 May 2025 14:04:29 +0600 Subject: [PATCH] feat: show size for open-source apps CleanAPK API response contains APK info in a dynamic property named "update_" where X is any integer. ApplicationDeserializer is modified to parse the property "apk_file_size" from the property "update_". The value contains a string such as 4.05 KiB, 1.23 MiB, etc. To make the size info consistent with existing UI for Google Play, which shows the size in KB and MB instead of KiB and MiB, size conversion is implemented in BinaryToDecimalSizeConverter, including unit tests. If there's no correct size information is found, the app details UI will not show any info. --- .../apps/data/application/data/Application.kt | 2 +- .../utils/BinaryToDecimalSizeConverter.kt | 69 +++++++++++++ .../data/cleanapk/ApplicationDeserializer.kt | 6 ++ .../BinaryToDecimalSizeConverterKtTest.kt | 96 +++++++++++++++++++ 4 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/foundation/e/apps/data/application/utils/BinaryToDecimalSizeConverter.kt create mode 100644 app/src/test/java/foundation/e/apps/data/application/utils/BinaryToDecimalSizeConverterKtTest.kt diff --git a/app/src/main/java/foundation/e/apps/data/application/data/Application.kt b/app/src/main/java/foundation/e/apps/data/application/data/Application.kt index 99e603411..b33249020 100644 --- a/app/src/main/java/foundation/e/apps/data/application/data/Application.kt +++ b/app/src/main/java/foundation/e/apps/data/application/data/Application.kt @@ -50,7 +50,7 @@ data class Application( var status: Status = Status.UNAVAILABLE, val shareUrl: String = String(), val originalSize: Long = 0, - val appSize: String = String(), + var appSize: String = String(), var source: Source = Source.PLAY_STORE, val price: String = String(), val isFree: Boolean = true, diff --git a/app/src/main/java/foundation/e/apps/data/application/utils/BinaryToDecimalSizeConverter.kt b/app/src/main/java/foundation/e/apps/data/application/utils/BinaryToDecimalSizeConverter.kt new file mode 100644 index 000000000..c4cda1e99 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/data/application/utils/BinaryToDecimalSizeConverter.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2025 e Foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package foundation.e.apps.data.application.utils + +import java.util.Locale + +@Suppress("MagicNumber") +fun convertBinaryToDecimal(input: String): String { + val binaryToDecimalMap = mapOf( + "KiB" to "KB", + "MiB" to "MB", + "GiB" to "GB", + "TiB" to "TB", + "PiB" to "PB", + "EiB" to "EB", + "ZiB" to "ZB", + "YiB" to "YB" + ) + + val regex = Regex("(-?\\d+(?:\\.\\d+)?)\\s*([KMGTPEZY]iB)") + val matchResult = regex.find(input) + + return if (matchResult != null) { + val value = matchResult.groupValues[1].toDouble() + + if (value < 0) { + return "" + } + + val binaryUnit = matchResult.groupValues[2] + val decimalUnit = binaryToDecimalMap[binaryUnit] + + if (decimalUnit != null) { + // Convert the value from binary to decimal using the correct factor + val decimalValue = when (binaryUnit) { + "KiB" -> value * 1.024 + "MiB" -> value * 1.048576 + "GiB" -> value * 1.073741824 + "TiB" -> value * 1.099511627776 + "PiB" -> value * 1.1258999068426 + "EiB" -> value * 1.15292150460685 + "ZiB" -> value * 1.1805916207174112 + "YiB" -> value * 1.2089258 + else -> value + } + String.format(Locale.getDefault(), "%.2f %s", decimalValue, decimalUnit) + } else { + "" // Unknown unit + } + } else { + "" // Invalid input format + } +} diff --git a/app/src/main/java/foundation/e/apps/data/cleanapk/ApplicationDeserializer.kt b/app/src/main/java/foundation/e/apps/data/cleanapk/ApplicationDeserializer.kt index b5c69ee73..9c0f2928c 100644 --- a/app/src/main/java/foundation/e/apps/data/cleanapk/ApplicationDeserializer.kt +++ b/app/src/main/java/foundation/e/apps/data/cleanapk/ApplicationDeserializer.kt @@ -22,6 +22,7 @@ import com.google.gson.Gson import com.google.gson.JsonDeserializationContext import com.google.gson.JsonDeserializer import com.google.gson.JsonElement +import foundation.e.apps.data.application.utils.convertBinaryToDecimal import foundation.e.apps.data.cleanapk.data.app.CleanApkApplication class ApplicationDeserializer : JsonDeserializer { @@ -38,6 +39,11 @@ class ApplicationDeserializer : JsonDeserializer { ?.asJsonObject?.get("update_on")?.asString ?: "" cleanApkApplication.app.updatedOn = lastUpdatedOn cleanApkApplication.app.latest_version_code = lastUpdateJson?.get("version_code")?.asInt ?: -1 + val appSizeInBinary = + (json?.asJsonObject?.get("app")?.asJsonObject?.get(lastUpdate)?.asJsonObject?.get("apk_file_size")?.asString + ?: "") + cleanApkApplication.app.appSize = convertBinaryToDecimal(appSizeInBinary) // KiB to KB, MiB to MB, etc. + return cleanApkApplication } } diff --git a/app/src/test/java/foundation/e/apps/data/application/utils/BinaryToDecimalSizeConverterKtTest.kt b/app/src/test/java/foundation/e/apps/data/application/utils/BinaryToDecimalSizeConverterKtTest.kt new file mode 100644 index 000000000..0f1edba78 --- /dev/null +++ b/app/src/test/java/foundation/e/apps/data/application/utils/BinaryToDecimalSizeConverterKtTest.kt @@ -0,0 +1,96 @@ +package foundation.e.apps.data.application.utils + +/* + * Copyright (C) 2025 e Foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +import junit.framework.TestCase.assertEquals +import org.junit.Test + + +class BinaryToDecimalSizeConverterTest { + + @Test + fun `test valid MiB to MB conversion`() { + assertEquals("1.05 MB", convertBinaryToDecimal("1 MiB")) + assertEquals("2.10 MB", convertBinaryToDecimal("2 MiB")) + } + + @Test + fun `test valid KiB to KB conversion`() { + assertEquals("1.02 KB", convertBinaryToDecimal("1 KiB")) + assertEquals("1048.58 KB", convertBinaryToDecimal("1024 KiB")) + } + + @Test + fun `test valid GiB to GB conversion`() { + assertEquals("1.07 GB", convertBinaryToDecimal("1 GiB")) + assertEquals("2.15 GB", convertBinaryToDecimal("2 GiB")) + } + + @Test + fun `test valid TiB to TB conversion`() { + assertEquals("1.10 TB", convertBinaryToDecimal("1 TiB")) + assertEquals("2.20 TB", convertBinaryToDecimal("2 TiB")) + } + + @Test + fun `test valid PiB to PB conversion`() { + assertEquals("1.13 PB", convertBinaryToDecimal("1 PiB")) + assertEquals("2.25 PB", convertBinaryToDecimal("2 PiB")) + } + + @Test + fun `test valid EiB to EB conversion`() { + assertEquals("1.15 EB", convertBinaryToDecimal("1 EiB")) + assertEquals("2.31 EB", convertBinaryToDecimal("2 EiB")) + } + + @Test + fun `test valid ZiB to ZB conversion`() { + assertEquals("1.18 ZB", convertBinaryToDecimal("1 ZiB")) + assertEquals("2.36 ZB", convertBinaryToDecimal("2 ZiB")) + } + + @Test + fun `test valid YiB to YB conversion`() { + assertEquals("1.21 YB", convertBinaryToDecimal("1 YiB")) + assertEquals("2.42 YB", convertBinaryToDecimal("2 YiB")) + } + + @Test + fun `test unknown unit`() { + assertEquals("", convertBinaryToDecimal("1 AiB")) // Unknown unit + assertEquals("", convertBinaryToDecimal("1 BiB")) // Unknown unit + } + + @Test + fun `test edge cases with zero and negative values`() { + assertEquals("0.00 MB", convertBinaryToDecimal("0 MiB")) + assertEquals("0.00 KB", convertBinaryToDecimal("0 KiB")) + assertEquals("0.00 GB", convertBinaryToDecimal("0 GiB")) + assertEquals("0.00 TB", convertBinaryToDecimal("0 TiB")) + assertEquals("0.00 PB", convertBinaryToDecimal("0 PiB")) + assertEquals("0.00 EB", convertBinaryToDecimal("0 EiB")) + assertEquals("0.00 ZB", convertBinaryToDecimal("0 ZiB")) + assertEquals("0.00 YB", convertBinaryToDecimal("0 YiB")) + + assertEquals("", convertBinaryToDecimal("-1 MiB")) // Negative value + assertEquals("", convertBinaryToDecimal("-1024 KiB")) // Negative value + } +} + -- GitLab