From f9dd0c00d0e73237eda6f8a7df91b45f5d1fff8e Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Thu, 6 Feb 2025 13:33:09 +0100 Subject: [PATCH 01/21] refactor(DynamicDashboard): Rename AppQuota in Product Quota --- .../myksuite/ui/screens/MyKSuiteDashboardScreen.kt | 2 +- .../myksuite/ui/screens/components/AppStorageQuotas.kt | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index 931fa57b..456e2506 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -136,7 +136,7 @@ private fun SubscriptionInfoCard( MyKSuiteChip(tier = MyKSuiteTier.Free) } PaddedDivider(paddedModifier) - AppStorageQuotas(paddedModifier) + ProductsStorageQuotas(paddedModifier) PaddedDivider(paddedModifier) ExpendableActionItem(iconRes = R.drawable.ic_envelope, textRes = R.string.myKSuiteDashboardFreeMailLabel) ExpendableActionItem( diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/AppStorageQuotas.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/AppStorageQuotas.kt index e76eba79..cddc8aab 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/AppStorageQuotas.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/AppStorageQuotas.kt @@ -36,14 +36,14 @@ import com.infomaniak.core.myksuite.ui.theme.MyKSuiteTheme import com.infomaniak.core.myksuite.ui.theme.Typography @Composable -internal fun AppStorageQuotas(modifier: Modifier) { +internal fun ProductsStorageQuotas(modifier: Modifier) { Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(Margin.Medium)) { - KSuiteAppWithQuotas.entries.forEach { AppStorageQuota(app = it) } + KSuiteProductsWithQuotas.entries.forEach { ProductStorageQuota(app = it) } } } @Composable -private fun AppStorageQuota(modifier: Modifier = Modifier, app: KSuiteAppWithQuotas) { +private fun ProductStorageQuota(modifier: Modifier = Modifier, app: KSuiteProductsWithQuotas) { val localColors = LocalMyKSuiteColors.current Column(modifier) { Row(verticalAlignment = Alignment.CenterVertically) { @@ -75,7 +75,7 @@ private fun AppStorageQuota(modifier: Modifier = Modifier, app: KSuiteAppWithQuo } } -private enum class KSuiteAppWithQuotas(val displayName: String, val color: @Composable () -> Color) { +private enum class KSuiteProductsWithQuotas(val displayName: String, val color: @Composable () -> Color) { Mail("Mail", { LocalMyKSuiteColors.current.mail }), Drive("kDrive", { LocalMyKSuiteColors.current.drive }), } @@ -86,7 +86,7 @@ private enum class KSuiteAppWithQuotas(val displayName: String, val color: @Comp private fun Preview() { MyKSuiteTheme { Surface { - AppStorageQuotas(Modifier.padding(Margin.Medium)) + ProductsStorageQuotas(Modifier.padding(Margin.Medium)) } } } From d84167cff8e38d85d652d2f8f5cde0b7e0808f8b Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Thu, 6 Feb 2025 13:38:27 +0100 Subject: [PATCH 02/21] feat(DynamicDashboard): Accept dynamic data in UI --- .../core/myksuite/ui/network/ApiRoutes.kt | 2 +- .../ui/screens/MyKSuiteDashboardScreen.kt | 36 +++++++++++-- .../components/LimitedFunctionnalities.kt | 6 +-- ...rageQuotas.kt => ProductsStorageQuotas.kt} | 52 +++++++++++++++---- .../ui/views/MyKSuiteDashboardFragment.kt | 3 +- .../res/navigation/my_ksuite_navigation.xml | 3 ++ 6 files changed, 81 insertions(+), 21 deletions(-) rename MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/{AppStorageQuotas.kt => ProductsStorageQuotas.kt} (58%) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt index 1d892f11..f24054f8 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt @@ -21,5 +21,5 @@ object ApiRoutes { private const val BASE_URL = "https://api.staging-myksuite.dev.infomaniak.ch" - val myKSuiteData = "$BASE_URL/1/my_ksuite/current?with=*" + fun myKSuiteData() = "$BASE_URL/1/my_ksuite/current?with=*" } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index 456e2506..786d1f20 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -43,7 +43,8 @@ import com.infomaniak.core.myksuite.ui.theme.Typography fun MyKSuiteDashboardScreen( userName: String, avatarUri: String = "", - dailySendingLimit: String, + dailySendingLimit: () -> String, + kSuiteProductsWithQuotas: () -> List, onClose: () -> Unit = {}, ) { MyKSuiteTheme { @@ -66,7 +67,13 @@ fun MyKSuiteDashboardScreen( ) Column(Modifier.padding(paddingValues), verticalArrangement = Arrangement.spacedBy(Margin.Large)) { val paddedModifier = Modifier.padding(horizontal = Margin.Medium) - SubscriptionInfoCard(paddedModifier, avatarUri, userName, dailySendingLimit) + SubscriptionInfoCard( + paddedModifier = paddedModifier, + avatarUri = avatarUri, + userName = userName, + dailySendingLimit = dailySendingLimit, + kSuiteProductsWithQuotas = kSuiteProductsWithQuotas, + ) // TODO: Add this line when we'll have In-app payments // MyKSuitePlusPromotionCard(paddedModifier) {} } @@ -108,7 +115,8 @@ private fun SubscriptionInfoCard( paddedModifier: Modifier, avatarUri: String, userName: String, - dailySendingLimit: String, + dailySendingLimit: () -> String, + kSuiteProductsWithQuotas: () -> List, ) { val localColors = LocalMyKSuiteColors.current @@ -136,7 +144,7 @@ private fun SubscriptionInfoCard( MyKSuiteChip(tier = MyKSuiteTier.Free) } PaddedDivider(paddedModifier) - ProductsStorageQuotas(paddedModifier) + ProductsStorageQuotas(paddedModifier, kSuiteProductsWithQuotas) PaddedDivider(paddedModifier) ExpendableActionItem(iconRes = R.drawable.ic_envelope, textRes = R.string.myKSuiteDashboardFreeMailLabel) ExpendableActionItem( @@ -215,6 +223,24 @@ private fun MyKSuitePlusPromotionCard(modifier: Modifier = Modifier, onButtonCli @Composable private fun Preview() { Surface(Modifier.fillMaxSize(), color = Color.White) { - MyKSuiteDashboardScreen(userName = "Toto", avatarUri = "", dailySendingLimit = "500") + MyKSuiteDashboardScreen( + userName = "Toto", + avatarUri = "", + dailySendingLimit = { "500" }, + kSuiteProductsWithQuotas = { + listOf( + KSuiteProductsWithQuotas.Mail( + usedSize = { "0.2 Go" }, + maxSize = { "20 Go" }, + progress = { 0.01f } + ), + KSuiteProductsWithQuotas.Drive( + usedSize = { "6 Go" }, + maxSize = { "15 Go" }, + progress = { 0.4f }, + ), + ) + }, + ) } } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/LimitedFunctionnalities.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/LimitedFunctionnalities.kt index 37b3adcd..0a41510c 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/LimitedFunctionnalities.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/LimitedFunctionnalities.kt @@ -34,7 +34,7 @@ import com.infomaniak.core.myksuite.ui.theme.MyKSuiteTheme import com.infomaniak.core.myksuite.ui.theme.Typography @Composable -internal fun LimitedFunctionalities(paddedModifier: Modifier, dailySendingLimit: String) { +internal fun LimitedFunctionalities(paddedModifier: Modifier, dailySendingLimit: () -> String) { Column( modifier = paddedModifier.padding(top = Margin.Mini), verticalArrangement = Arrangement.spacedBy(Margin.Mini), @@ -44,7 +44,7 @@ internal fun LimitedFunctionalities(paddedModifier: Modifier, dailySendingLimit: LimitedFunctionalityLabel(modifier = Modifier.weight(1.0f), R.string.myKSuiteDashboardFunctionalityLimit) Text( modifier = Modifier.padding(start = Margin.Mini), - text = dailySendingLimit, + text = dailySendingLimit(), style = Typography.bodySmallMedium, ) } @@ -68,7 +68,7 @@ private fun LimitedFunctionalityLabel(modifier: Modifier = Modifier, @StringRes private fun Preview() { MyKSuiteTheme { Surface { - LimitedFunctionalities(Modifier.padding(horizontal = Margin.Medium), dailySendingLimit = "500") + LimitedFunctionalities(Modifier.padding(horizontal = Margin.Medium), dailySendingLimit = { "500" }) } } } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/AppStorageQuotas.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt similarity index 58% rename from MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/AppStorageQuotas.kt rename to MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt index cddc8aab..043d053f 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/AppStorageQuotas.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt @@ -18,6 +18,7 @@ package com.infomaniak.core.myksuite.ui.screens.components import android.content.res.Configuration +import android.os.Parcelable import androidx.compose.foundation.layout.* import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.Surface @@ -34,27 +35,28 @@ import com.infomaniak.core.myksuite.ui.theme.LocalMyKSuiteColors import com.infomaniak.core.myksuite.ui.theme.Margin import com.infomaniak.core.myksuite.ui.theme.MyKSuiteTheme import com.infomaniak.core.myksuite.ui.theme.Typography +import kotlinx.parcelize.Parcelize @Composable -internal fun ProductsStorageQuotas(modifier: Modifier) { +internal fun ProductsStorageQuotas(modifier: Modifier, kSuiteProductsWithQuotas: () -> List) { Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(Margin.Medium)) { - KSuiteProductsWithQuotas.entries.forEach { ProductStorageQuota(app = it) } + kSuiteProductsWithQuotas().forEach { ProductStorageQuota(product = it) } } } @Composable -private fun ProductStorageQuota(modifier: Modifier = Modifier, app: KSuiteProductsWithQuotas) { +private fun ProductStorageQuota(modifier: Modifier = Modifier, product: KSuiteProductsWithQuotas) { val localColors = LocalMyKSuiteColors.current Column(modifier) { Row(verticalAlignment = Alignment.CenterVertically) { Text( - text = app.displayName, + text = product.displayName, style = Typography.bodyRegular, color = localColors.primaryTextColor, ) WeightOneSpacer(minWidth = Margin.Medium) Text( - text = "0.2 Go / 20 Go", // TODO: Use real data + text = "${product.usedSize()} / ${product.maxSize()}", style = Typography.bodySmallRegular, color = localColors.secondaryTextColor, ) @@ -65,19 +67,31 @@ private fun ProductStorageQuota(modifier: Modifier = Modifier, app: KSuiteProduc modifier = Modifier .height(progressIndicatorHeight) .fillMaxWidth(), - color = app.color(), + color = product.color(), trackColor = localColors.chipBackground, strokeCap = StrokeCap.Round, gapSize = -progressIndicatorHeight, - progress = { 0.5f }, // TODO: Use real values + progress = product.progress, drawStopIndicator = {}, ) } } -private enum class KSuiteProductsWithQuotas(val displayName: String, val color: @Composable () -> Color) { - Mail("Mail", { LocalMyKSuiteColors.current.mail }), - Drive("kDrive", { LocalMyKSuiteColors.current.drive }), +@Parcelize +sealed class KSuiteProductsWithQuotas( + internal val displayName: String, + internal val color: @Composable () -> Color, + open val usedSize: () -> String, + open val maxSize: () -> String, + open val progress: () -> Float, +) : Parcelable { + + class Mail(override val usedSize: () -> String, override val maxSize: () -> String, override val progress: () -> Float) : + KSuiteProductsWithQuotas("Mail", { LocalMyKSuiteColors.current.mail }, usedSize, maxSize, progress) + + data class Drive(override val usedSize: () -> String, override val maxSize: () -> String, override val progress: () -> Float) : + KSuiteProductsWithQuotas("kDrive", { LocalMyKSuiteColors.current.drive }, usedSize, maxSize, progress) + } @Preview(name = "(1) Light") @@ -86,7 +100,23 @@ private enum class KSuiteProductsWithQuotas(val displayName: String, val color: private fun Preview() { MyKSuiteTheme { Surface { - ProductsStorageQuotas(Modifier.padding(Margin.Medium)) + ProductsStorageQuotas( + modifier = Modifier.padding(Margin.Medium), + kSuiteProductsWithQuotas = { + listOf( + KSuiteProductsWithQuotas.Mail( + usedSize = { "0.2 Go" }, + maxSize = { "20 Go" }, + progress = { 0.01f } + ), + KSuiteProductsWithQuotas.Drive( + usedSize = { "6 Go" }, + maxSize = { "15 Go" }, + progress = { 0.4f }, + ), + ) + }, + ) } } } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt index 2ade4da3..e2445786 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt @@ -41,7 +41,8 @@ open class MyKSuiteDashboardFragment : Fragment() { MyKSuiteDashboardScreen( userName = userName, avatarUri = avatarUri, - dailySendingLimit = dailySendLimit, + dailySendingLimit = { dailySendLimit }, + kSuiteProductsWithQuotas = { kSuiteAppsWithQuotas.toList() }, onClose = onClose, ) } diff --git a/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml b/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml index de744452..253c350d 100644 --- a/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml +++ b/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml @@ -33,6 +33,9 @@ + Date: Fri, 7 Feb 2025 11:48:08 +0100 Subject: [PATCH 03/21] refactor(DynamicDashboard): Replace userName by ksuite email --- .../myksuite/ui/screens/MyKSuiteDashboardScreen.kt | 10 +++++----- .../myksuite/ui/views/MyKSuiteDashboardFragment.kt | 2 +- .../src/main/res/navigation/my_ksuite_navigation.xml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index 786d1f20..eeb33a36 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -41,7 +41,7 @@ import com.infomaniak.core.myksuite.ui.theme.Typography @Composable fun MyKSuiteDashboardScreen( - userName: String, + email: String, avatarUri: String = "", dailySendingLimit: () -> String, kSuiteProductsWithQuotas: () -> List, @@ -69,8 +69,8 @@ fun MyKSuiteDashboardScreen( val paddedModifier = Modifier.padding(horizontal = Margin.Medium) SubscriptionInfoCard( paddedModifier = paddedModifier, + email = email, avatarUri = avatarUri, - userName = userName, dailySendingLimit = dailySendingLimit, kSuiteProductsWithQuotas = kSuiteProductsWithQuotas, ) @@ -113,8 +113,8 @@ private fun TopAppBar(onClose: () -> Unit) { @Composable private fun SubscriptionInfoCard( paddedModifier: Modifier, + email: String, avatarUri: String, - userName: String, dailySendingLimit: () -> String, kSuiteProductsWithQuotas: () -> List, ) { @@ -137,7 +137,7 @@ private fun SubscriptionInfoCard( modifier = Modifier.weight(1.0f), style = Typography.bodyRegular, color = localColors.primaryTextColor, - text = userName, + text = email, maxLines = 1, overflow = TextOverflow.Ellipsis, ) @@ -224,7 +224,7 @@ private fun MyKSuitePlusPromotionCard(modifier: Modifier = Modifier, onButtonCli private fun Preview() { Surface(Modifier.fillMaxSize(), color = Color.White) { MyKSuiteDashboardScreen( - userName = "Toto", + email = "Toto", avatarUri = "", dailySendingLimit = { "500" }, kSuiteProductsWithQuotas = { diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt index e2445786..59c0f1ef 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt @@ -39,7 +39,7 @@ open class MyKSuiteDashboardFragment : Fragment() { val onClose: () -> Unit = { this@MyKSuiteDashboardFragment.findNavController().popBackStack() } with(navigationArgs) { MyKSuiteDashboardScreen( - userName = userName, + email = email, avatarUri = avatarUri, dailySendingLimit = { dailySendLimit }, kSuiteProductsWithQuotas = { kSuiteAppsWithQuotas.toList() }, diff --git a/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml b/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml index 253c350d..cc82d45f 100644 --- a/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml +++ b/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml @@ -25,7 +25,7 @@ android:name="com.infomaniak.core.myksuite.ui.views.MyKSuiteDashboardFragment" android:label="MyKSuiteDashboardFragment"> Date: Wed, 12 Feb 2025 15:00:40 +0100 Subject: [PATCH 04/21] chore(MyKSuiteApiRoute): Change ApiRoute for the Prod --- .../com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt index f24054f8..398b53c7 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt @@ -19,7 +19,8 @@ package com.infomaniak.core.myksuite.ui.network object ApiRoutes { - private const val BASE_URL = "https://api.staging-myksuite.dev.infomaniak.ch" + private const val PREPROD_BASE_URL = "https://api.staging-myksuite.dev.infomaniak.ch" + private const val BASE_URL = "https://api.infomaniak.com" - fun myKSuiteData() = "$BASE_URL/1/my_ksuite/current?with=*" + fun myKSuiteData() = "$BASE_URL/1/my_ksuite/current?with=drive,mail,pack,can_trial,has_auto_renew" } From 213c6dde24d59a9a3a59b7032ab736a0b5b0fa79 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Wed, 12 Feb 2025 16:57:04 +0100 Subject: [PATCH 05/21] fix(MyKSuiteDynamicDashboard): Fix OOM error due to callbacks in the serialization --- .../ui/screens/MyKSuiteDashboardScreen.kt | 12 +++---- .../components/ProductsStorageQuotas.kt | 36 ++++++++----------- gradle/core.versions.toml | 2 +- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index eeb33a36..c47cd115 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -230,14 +230,14 @@ private fun Preview() { kSuiteProductsWithQuotas = { listOf( KSuiteProductsWithQuotas.Mail( - usedSize = { "0.2 Go" }, - maxSize = { "20 Go" }, - progress = { 0.01f } + usedSize = "0.2 Go", + maxSize = "20 Go", + progress = 0.01f, ), KSuiteProductsWithQuotas.Drive( - usedSize = { "6 Go" }, - maxSize = { "15 Go" }, - progress = { 0.4f }, + usedSize = "6 Go", + maxSize = "15 Go", + progress = 0.4f, ), ) }, diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt index 043d053f..be3bd5e0 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt @@ -26,7 +26,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -56,7 +55,7 @@ private fun ProductStorageQuota(modifier: Modifier = Modifier, product: KSuitePr ) WeightOneSpacer(minWidth = Margin.Medium) Text( - text = "${product.usedSize()} / ${product.maxSize()}", + text = "${product.usedSize} / ${product.maxSize}", style = Typography.bodySmallRegular, color = localColors.secondaryTextColor, ) @@ -67,11 +66,11 @@ private fun ProductStorageQuota(modifier: Modifier = Modifier, product: KSuitePr modifier = Modifier .height(progressIndicatorHeight) .fillMaxWidth(), - color = product.color(), + color = product.getColor(), trackColor = localColors.chipBackground, strokeCap = StrokeCap.Round, gapSize = -progressIndicatorHeight, - progress = product.progress, + progress = { product.progress }, drawStopIndicator = {}, ) } @@ -80,18 +79,19 @@ private fun ProductStorageQuota(modifier: Modifier = Modifier, product: KSuitePr @Parcelize sealed class KSuiteProductsWithQuotas( internal val displayName: String, - internal val color: @Composable () -> Color, - open val usedSize: () -> String, - open val maxSize: () -> String, - open val progress: () -> Float, + open val usedSize: String, + open val maxSize: String, + open val progress: Float, ) : Parcelable { - class Mail(override val usedSize: () -> String, override val maxSize: () -> String, override val progress: () -> Float) : - KSuiteProductsWithQuotas("Mail", { LocalMyKSuiteColors.current.mail }, usedSize, maxSize, progress) + class Mail(override val usedSize: String, override val maxSize: String, override val progress: Float) : + KSuiteProductsWithQuotas("Mail", usedSize, maxSize, progress) - data class Drive(override val usedSize: () -> String, override val maxSize: () -> String, override val progress: () -> Float) : - KSuiteProductsWithQuotas("kDrive", { LocalMyKSuiteColors.current.drive }, usedSize, maxSize, progress) + data class Drive(override val usedSize: String, override val maxSize: String, override val progress: Float) : + KSuiteProductsWithQuotas("kDrive", usedSize, maxSize, progress) + @Composable + fun getColor() = if (this is Mail) LocalMyKSuiteColors.current.mail else LocalMyKSuiteColors.current.drive } @Preview(name = "(1) Light") @@ -104,16 +104,8 @@ private fun Preview() { modifier = Modifier.padding(Margin.Medium), kSuiteProductsWithQuotas = { listOf( - KSuiteProductsWithQuotas.Mail( - usedSize = { "0.2 Go" }, - maxSize = { "20 Go" }, - progress = { 0.01f } - ), - KSuiteProductsWithQuotas.Drive( - usedSize = { "6 Go" }, - maxSize = { "15 Go" }, - progress = { 0.4f }, - ), + KSuiteProductsWithQuotas.Mail(usedSize = "0.2 Go", maxSize = "20 Go", progress = 0.01f), + KSuiteProductsWithQuotas.Drive(usedSize = "6 Go", maxSize = "15 Go", progress = 0.4f), ) }, ) diff --git a/gradle/core.versions.toml b/gradle/core.versions.toml index 60455827..57815c6b 100644 --- a/gradle/core.versions.toml +++ b/gradle/core.versions.toml @@ -2,7 +2,7 @@ androidxCore = "1.13.1" # Doesn't build when bumped to 1.15.0 (Waiting SDK 35) coil = "3.0.2" coilNetworkOkhttp = "3.0.4" -composeBom = "2024.12.01" +composeBom = "2025.01.01" integrity = "1.4.0" junit = "4.13.2" junitAndroidx = "1.2.1" From 2e853303ec91b01cafff9193d6b5e072351ed8df Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Mon, 17 Feb 2025 14:35:29 +0100 Subject: [PATCH 06/21] fix(ProductName): Fix the display name of the my kSuite products --- MykSuite/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MykSuite/src/main/res/values/strings.xml b/MykSuite/src/main/res/values/strings.xml index b45cf564..e4cb5d52 100644 --- a/MykSuite/src/main/res/values/strings.xml +++ b/MykSuite/src/main/res/values/strings.xml @@ -17,8 +17,8 @@ --> - My kSuite - My kSuite + + my kSuite + my kSuite+ Manage my offer To manage or cancel your subscription, log on to the kSuite web interface. From d2c20d43f28a993bc319efd9fdbc2d19accae990 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Mon, 17 Feb 2025 15:59:49 +0100 Subject: [PATCH 07/21] feat(DynamicDashboard): Display unlimited mail quotas --- .../ui/screens/MyKSuiteDashboardScreen.kt | 16 +++-- .../components/ProductsStorageQuotas.kt | 60 +++++++++++++------ .../ui/views/MyKSuiteDashboardFragment.kt | 3 +- .../res/navigation/my_ksuite_navigation.xml | 3 + gradle/core.versions.toml | 2 +- 5 files changed, 59 insertions(+), 25 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index c47cd115..feb873b5 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -41,8 +41,9 @@ import com.infomaniak.core.myksuite.ui.theme.Typography @Composable fun MyKSuiteDashboardScreen( + myKSuiteTier: () -> MyKSuiteTier, email: String, - avatarUri: String = "", + avatarUri: () -> String = { "" }, dailySendingLimit: () -> String, kSuiteProductsWithQuotas: () -> List, onClose: () -> Unit = {}, @@ -69,6 +70,7 @@ fun MyKSuiteDashboardScreen( val paddedModifier = Modifier.padding(horizontal = Margin.Medium) SubscriptionInfoCard( paddedModifier = paddedModifier, + myKSuiteTier = myKSuiteTier, email = email, avatarUri = avatarUri, dailySendingLimit = dailySendingLimit, @@ -113,8 +115,9 @@ private fun TopAppBar(onClose: () -> Unit) { @Composable private fun SubscriptionInfoCard( paddedModifier: Modifier, + myKSuiteTier: () -> MyKSuiteTier, email: String, - avatarUri: String, + avatarUri: () -> String, dailySendingLimit: () -> String, kSuiteProductsWithQuotas: () -> List, ) { @@ -132,7 +135,7 @@ private fun SubscriptionInfoCard( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(Margin.Mini), ) { - UserAvatar(avatarUri) + UserAvatar(avatarUri()) Text( modifier = Modifier.weight(1.0f), style = Typography.bodyRegular, @@ -141,10 +144,10 @@ private fun SubscriptionInfoCard( maxLines = 1, overflow = TextOverflow.Ellipsis, ) - MyKSuiteChip(tier = MyKSuiteTier.Free) + MyKSuiteChip(tier = myKSuiteTier()) } PaddedDivider(paddedModifier) - ProductsStorageQuotas(paddedModifier, kSuiteProductsWithQuotas) + ProductsStorageQuotas(paddedModifier, myKSuiteTier, kSuiteProductsWithQuotas) PaddedDivider(paddedModifier) ExpendableActionItem(iconRes = R.drawable.ic_envelope, textRes = R.string.myKSuiteDashboardFreeMailLabel) ExpendableActionItem( @@ -224,8 +227,9 @@ private fun MyKSuitePlusPromotionCard(modifier: Modifier = Modifier, onButtonCli private fun Preview() { Surface(Modifier.fillMaxSize(), color = Color.White) { MyKSuiteDashboardScreen( + myKSuiteTier = { MyKSuiteTier.Plus }, email = "Toto", - avatarUri = "", + avatarUri = { "" }, dailySendingLimit = { "500" }, kSuiteProductsWithQuotas = { listOf( diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt index be3bd5e0..c8e707a1 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt @@ -27,8 +27,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.infomaniak.core.myksuite.R +import com.infomaniak.core.myksuite.ui.components.MyKSuiteTier import com.infomaniak.core.myksuite.ui.components.WeightOneSpacer import com.infomaniak.core.myksuite.ui.theme.LocalMyKSuiteColors import com.infomaniak.core.myksuite.ui.theme.Margin @@ -37,15 +40,25 @@ import com.infomaniak.core.myksuite.ui.theme.Typography import kotlinx.parcelize.Parcelize @Composable -internal fun ProductsStorageQuotas(modifier: Modifier, kSuiteProductsWithQuotas: () -> List) { +internal fun ProductsStorageQuotas( + modifier: Modifier, + myKSuiteTier: () -> MyKSuiteTier, + kSuiteProductsWithQuotas: () -> List, +) { Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(Margin.Medium)) { - kSuiteProductsWithQuotas().forEach { ProductStorageQuota(product = it) } + kSuiteProductsWithQuotas().forEach { ProductStorageQuota(myKSuiteTier = myKSuiteTier, product = it) } } } @Composable -private fun ProductStorageQuota(modifier: Modifier = Modifier, product: KSuiteProductsWithQuotas) { +private fun ProductStorageQuota( + modifier: Modifier = Modifier, + myKSuiteTier: () -> MyKSuiteTier, + product: KSuiteProductsWithQuotas, +) { val localColors = LocalMyKSuiteColors.current + val isUnlimitedMail = myKSuiteTier() == MyKSuiteTier.Plus && product is KSuiteProductsWithQuotas.Mail + Column(modifier) { Row(verticalAlignment = Alignment.CenterVertically) { Text( @@ -55,24 +68,36 @@ private fun ProductStorageQuota(modifier: Modifier = Modifier, product: KSuitePr ) WeightOneSpacer(minWidth = Margin.Medium) Text( - text = "${product.usedSize} / ${product.maxSize}", + text = computeQuotasString(isUnlimitedMail, product), style = Typography.bodySmallRegular, color = localColors.secondaryTextColor, ) } - Spacer(Modifier.height(Margin.Mini)) - val progressIndicatorHeight = 14.dp - LinearProgressIndicator( - modifier = Modifier - .height(progressIndicatorHeight) - .fillMaxWidth(), - color = product.getColor(), - trackColor = localColors.chipBackground, - strokeCap = StrokeCap.Round, - gapSize = -progressIndicatorHeight, - progress = { product.progress }, - drawStopIndicator = {}, - ) + + if (!isUnlimitedMail) { + Spacer(Modifier.height(Margin.Mini)) + val progressIndicatorHeight = 14.dp + LinearProgressIndicator( + modifier = Modifier + .height(progressIndicatorHeight) + .fillMaxWidth(), + color = product.getColor(), + trackColor = localColors.chipBackground, + strokeCap = StrokeCap.Round, + gapSize = -progressIndicatorHeight, + progress = { product.progress }, + drawStopIndicator = {}, + ) + } + } +} + +@Composable +private fun computeQuotasString(isUnlimitedMail: Boolean, product: KSuiteProductsWithQuotas): String { + return if (isUnlimitedMail) { + stringResource(R.string.myKSuiteDashboardDataUnlimited) + } else { + "${product.usedSize} / ${product.maxSize}" } } @@ -102,6 +127,7 @@ private fun Preview() { Surface { ProductsStorageQuotas( modifier = Modifier.padding(Margin.Medium), + myKSuiteTier = { MyKSuiteTier.Plus }, kSuiteProductsWithQuotas = { listOf( KSuiteProductsWithQuotas.Mail(usedSize = "0.2 Go", maxSize = "20 Go", progress = 0.01f), diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt index 59c0f1ef..ab9aa9a8 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt @@ -39,8 +39,9 @@ open class MyKSuiteDashboardFragment : Fragment() { val onClose: () -> Unit = { this@MyKSuiteDashboardFragment.findNavController().popBackStack() } with(navigationArgs) { MyKSuiteDashboardScreen( + myKSuiteTier = { myKSuiteTier }, email = email, - avatarUri = avatarUri, + avatarUri = { avatarUri }, dailySendingLimit = { dailySendLimit }, kSuiteProductsWithQuotas = { kSuiteAppsWithQuotas.toList() }, onClose = onClose, diff --git a/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml b/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml index cc82d45f..c15f2fb9 100644 --- a/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml +++ b/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml @@ -24,6 +24,9 @@ android:id="@+id/myKSuiteDashboardFragment" android:name="com.infomaniak.core.myksuite.ui.views.MyKSuiteDashboardFragment" android:label="MyKSuiteDashboardFragment"> + diff --git a/gradle/core.versions.toml b/gradle/core.versions.toml index 57815c6b..12bdd558 100644 --- a/gradle/core.versions.toml +++ b/gradle/core.versions.toml @@ -1,5 +1,5 @@ [versions] -androidxCore = "1.13.1" # Doesn't build when bumped to 1.15.0 (Waiting SDK 35) +androidxCore = "1.13.1" # Doesn't build when bumped to 1.15.0 (Waiting SDK 35) coil = "3.0.2" coilNetworkOkhttp = "3.0.4" composeBom = "2025.01.01" From ebaf6816d21e303f2bc85141373d28a64fd2f844 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Mon, 17 Feb 2025 16:47:17 +0100 Subject: [PATCH 08/21] feat(DynamicDashboard): Display Trial expiry --- MykSuite/build.gradle.kts | 2 + .../core/myksuite/ui/data/MyKSuiteData.kt | 3 + .../ui/screens/MyKSuiteDashboardScreen.kt | 30 +++++++-- .../screens/components/ExpandableItemView.kt | 2 +- .../components/MyKSuitePlusTextItem.kt | 63 +++++++++++++++++++ .../ui/views/MyKSuiteDashboardFragment.kt | 1 + .../res/navigation/my_ksuite_navigation.xml | 4 ++ 7 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt diff --git a/MykSuite/build.gradle.kts b/MykSuite/build.gradle.kts index 5070ae57..eacd22c2 100644 --- a/MykSuite/build.gradle.kts +++ b/MykSuite/build.gradle.kts @@ -48,6 +48,8 @@ android { dependencies { + implementation(project(":Core")) + implementation(core.androidx.core.ktx) implementation(core.material) implementation(core.navigation.fragment.ktx) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteData.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteData.kt index 9d50bb6e..64faf244 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteData.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteData.kt @@ -24,6 +24,7 @@ import androidx.room.PrimaryKey import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient +import java.util.Date @Serializable @Entity @@ -62,4 +63,6 @@ data class MyKSuiteData( val isMyKSuitePlus get() = kSuitePack.type == KSuitePack.KSuitePackType.MY_KSUITE_PLUS || kSuitePack.type == KSuitePack.KSuitePackType.MY_KSUITE_PLUS_DRIVE_SOLO + + inline val trialExpiryDate get() = trialExpiryAt?.let { Date(it * 1000) } } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index feb873b5..1bcb71f3 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -33,11 +33,14 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.infomaniak.core.FORMAT_DATE_SIMPLE +import com.infomaniak.core.format import com.infomaniak.core.myksuite.R import com.infomaniak.core.myksuite.ui.components.* import com.infomaniak.core.myksuite.ui.screens.components.* import com.infomaniak.core.myksuite.ui.theme.* import com.infomaniak.core.myksuite.ui.theme.Typography +import java.util.Date @Composable fun MyKSuiteDashboardScreen( @@ -46,6 +49,7 @@ fun MyKSuiteDashboardScreen( avatarUri: () -> String = { "" }, dailySendingLimit: () -> String, kSuiteProductsWithQuotas: () -> List, + trialExpiryDate: () -> Date?, onClose: () -> Unit = {}, ) { MyKSuiteTheme { @@ -75,6 +79,7 @@ fun MyKSuiteDashboardScreen( avatarUri = avatarUri, dailySendingLimit = dailySendingLimit, kSuiteProductsWithQuotas = kSuiteProductsWithQuotas, + trialExpiryDate = trialExpiryDate, ) // TODO: Add this line when we'll have In-app payments // MyKSuitePlusPromotionCard(paddedModifier) {} @@ -120,6 +125,7 @@ private fun SubscriptionInfoCard( avatarUri: () -> String, dailySendingLimit: () -> String, kSuiteProductsWithQuotas: () -> List, + trialExpiryDate: () -> Date?, ) { val localColors = LocalMyKSuiteColors.current @@ -149,12 +155,23 @@ private fun SubscriptionInfoCard( PaddedDivider(paddedModifier) ProductsStorageQuotas(paddedModifier, myKSuiteTier, kSuiteProductsWithQuotas) PaddedDivider(paddedModifier) - ExpendableActionItem(iconRes = R.drawable.ic_envelope, textRes = R.string.myKSuiteDashboardFreeMailLabel) - ExpendableActionItem( - iconRes = R.drawable.ic_padlock, - textRes = R.string.myKSuiteDashboardLimitedFunctionalityLabel, - expendedView = { LimitedFunctionalities(paddedModifier, dailySendingLimit) }, - ) + + if (myKSuiteTier() == MyKSuiteTier.Free) { + ExpendableActionItem(iconRes = R.drawable.ic_envelope, textRes = R.string.myKSuiteDashboardFreeMailLabel) + ExpendableActionItem( + iconRes = R.drawable.ic_padlock, + textRes = R.string.myKSuiteDashboardLimitedFunctionalityLabel, + expendedView = { LimitedFunctionalities(paddedModifier, dailySendingLimit) }, + ) + } else { + trialExpiryDate()?.let { expiryDate -> + MyKSuitePlusTextItem( + modifier = paddedModifier, + title = stringResource(R.string.myKSuiteDashboardTrialPeriod), + value = stringResource(R.string.myKSuiteDashboardUntil, expiryDate.format(FORMAT_DATE_SIMPLE)), + ) + } + } Spacer(Modifier.height(Margin.Medium)) } } @@ -245,6 +262,7 @@ private fun Preview() { ), ) }, + trialExpiryDate = { Date() } ) } } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ExpandableItemView.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ExpandableItemView.kt index e775b2fe..f7e9d50b 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ExpandableItemView.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ExpandableItemView.kt @@ -51,7 +51,7 @@ internal fun ExpendableActionItem( Column { Row( modifier = Modifier - .heightIn(40.dp) + .heightIn(min = 40.dp) .fillMaxWidth() .then( // TODO add onClickLabel for accessibility if (expendedView == null) Modifier else Modifier.clickable { isExpanded = !isExpanded } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt new file mode 100644 index 00000000..1c6c65e2 --- /dev/null +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt @@ -0,0 +1,63 @@ +/* + * Infomaniak Core - Android + * Copyright (C) 2025 Infomaniak Network SA + * + * 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 com.infomaniak.core.myksuite.ui.screens.components + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.infomaniak.core.FORMAT_DATE_SIMPLE +import com.infomaniak.core.format +import com.infomaniak.core.myksuite.R +import com.infomaniak.core.myksuite.ui.theme.LocalMyKSuiteColors +import com.infomaniak.core.myksuite.ui.theme.Margin +import com.infomaniak.core.myksuite.ui.theme.MyKSuiteTheme +import java.util.Date + +@Composable +fun MyKSuitePlusTextItem(modifier: Modifier = Modifier, title: String, value: String) { + Row( + modifier = modifier + .heightIn(min = 40.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text(text = title) + Text(text = value, color = LocalMyKSuiteColors.current.secondaryTextColor) + } +} + +@Composable +@Preview +private fun Preview() { + MyKSuiteTheme { + Surface { + MyKSuitePlusTextItem( + modifier = Modifier.padding(horizontal = Margin.Medium), + title = stringResource(R.string.myKSuiteDashboardTrialPeriod), + value = stringResource(R.string.myKSuiteDashboardUntil, Date().format(FORMAT_DATE_SIMPLE)), + ) + } + } +} diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt index ab9aa9a8..4efa0107 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt @@ -44,6 +44,7 @@ open class MyKSuiteDashboardFragment : Fragment() { avatarUri = { avatarUri }, dailySendingLimit = { dailySendLimit }, kSuiteProductsWithQuotas = { kSuiteAppsWithQuotas.toList() }, + trialExpiryDate = { trialExpiryDate }, onClose = onClose, ) } diff --git a/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml b/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml index c15f2fb9..75996b8f 100644 --- a/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml +++ b/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml @@ -39,6 +39,10 @@ + Date: Tue, 18 Feb 2025 10:29:23 +0100 Subject: [PATCH 09/21] feat(DynamicDashboard): Add TextWithIcon component --- .../myksuite/ui/components/TextWithIcon.kt | 116 ++++++++++++++++++ .../src/main/res/drawable/ic_circle_i.xml | 47 +++++++ 2 files changed, 163 insertions(+) create mode 100644 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt create mode 100644 MykSuite/src/main/res/drawable/ic_circle_i.xml diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt new file mode 100644 index 00000000..2a521ba5 --- /dev/null +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt @@ -0,0 +1,116 @@ +/* + * Infomaniak Core - Android + * Copyright (C) 2025 Infomaniak Network SA + * + * 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 com.infomaniak.core.myksuite.ui.components + +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.drawscope.translate +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.datasource.LoremIpsum +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.infomaniak.core.myksuite.R +import com.infomaniak.core.myksuite.ui.theme.LocalMyKSuiteColors +import com.infomaniak.core.myksuite.ui.theme.Margin +import com.infomaniak.core.myksuite.ui.theme.MyKSuiteTheme +import com.infomaniak.core.myksuite.ui.theme.Typography + +/** This component allows to put an icon on any line of the text, thanks to the [iconLine] parameter + * + * IMPORTANT: You need to define the passed modifier's startPadding as at least the sum of [icon]'s horizontal dp size and + * [iconRightPadding] to display correctly the icon. + */ +@Composable +internal fun TextWithIcon( + modifier: Modifier, + text: String, + icon: ImageVector, + color: Color = Color.Unspecified, + style: TextStyle, + iconRightPadding: Dp = 0.dp, + iconLine: Int = 0, + iconTint: Color = Color.Black, +) { + val painter = rememberVectorPainter(image = icon) + var lineTop = 0f + var lineBottom = 0f + var lineLeft = 0f + with(LocalDensity.current) { + val imageSize = Size(icon.defaultWidth.toPx(), icon.defaultHeight.toPx()) + val rightPadding = iconRightPadding.toPx() + Text( + text = text, + color = color, + style = style, + onTextLayout = { layoutResult -> + val nbLines = layoutResult.lineCount + if (nbLines > iconLine) { + lineTop = layoutResult.getLineTop(iconLine) + lineBottom = layoutResult.getLineBottom(iconLine) + lineLeft = layoutResult.getLineLeft(iconLine) + } + }, + modifier = modifier.drawBehind { + with(painter) { + translate( + left = lineLeft - imageSize.width - rightPadding, + top = lineTop + (lineBottom - lineTop) / 2 - imageSize.height / 2, + ) { + draw(painter.intrinsicSize, colorFilter = ColorFilter.tint(iconTint)) + } + } + } + ) + } +} + +@Preview +@Composable +private fun Preview() { + + fun getLoremText(words: Int) = LoremIpsum(words).values.joinToString(separator = " ") + + MyKSuiteTheme { + val localColors = LocalMyKSuiteColors.current + Surface { + TextWithIcon( + // The icon's horizontal size is 16dp, the iconRightPadding is 12dp, so here by passing 32dp as the Text padding + // it will result in a 32 - (16 + 12) = 4dp start padding for the icon + modifier = Modifier.padding(start = Margin.Huge), + text = getLoremText(35), icon = ImageVector.vectorResource(R.drawable.ic_circle_i), + color = localColors.primaryTextColor, + style = Typography.bodyRegular, + iconRightPadding = Margin.Small, + iconLine = 0, + iconTint = localColors.iconColor, + ) + } + } +} diff --git a/MykSuite/src/main/res/drawable/ic_circle_i.xml b/MykSuite/src/main/res/drawable/ic_circle_i.xml new file mode 100644 index 00000000..0001fa05 --- /dev/null +++ b/MykSuite/src/main/res/drawable/ic_circle_i.xml @@ -0,0 +1,47 @@ + + + + + + + From cafaafbd5db815ce96e195de9651a3d330d7c384 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Tue, 18 Feb 2025 10:31:23 +0100 Subject: [PATCH 10/21] feat(DynamicDashboard): Add Information block --- .../ui/screens/MyKSuiteDashboardScreen.kt | 7 ++ .../ui/screens/components/InformationBlock.kt | 82 +++++++++++++++++++ .../core/myksuite/ui/theme/ColorsDark.kt | 1 + .../core/myksuite/ui/theme/ColorsLight.kt | 1 + .../core/myksuite/ui/theme/Dimens.kt | 2 + .../core/myksuite/ui/theme/Theme.kt | 1 + 6 files changed, 94 insertions(+) create mode 100644 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index 1bcb71f3..1de30437 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -171,6 +171,13 @@ private fun SubscriptionInfoCard( value = stringResource(R.string.myKSuiteDashboardUntil, expiryDate.format(FORMAT_DATE_SIMPLE)), ) } + Spacer(Modifier.height(Margin.Medium)) + InformationBlock( + modifier = paddedModifier, + text = stringResource(R.string.myKSuiteManageSubscriptionDescription), + buttonText = stringResource(R.string.myKSuiteManageSubscriptionButton), + onClick = {} // TODO + ) } Spacer(Modifier.height(Margin.Medium)) } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt new file mode 100644 index 00000000..50b9c6d4 --- /dev/null +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt @@ -0,0 +1,82 @@ +/* + * Infomaniak Core - Android + * Copyright (C) 2025 Infomaniak Network SA + * + * 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 com.infomaniak.core.myksuite.ui.screens.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.datasource.LoremIpsum +import androidx.compose.ui.unit.dp +import com.infomaniak.core.myksuite.R +import com.infomaniak.core.myksuite.ui.components.TextWithIcon +import com.infomaniak.core.myksuite.ui.theme.* + +@Composable +fun InformationBlock(modifier: Modifier = Modifier, text: String, buttonText: String, onClick: () -> Unit) { + val localColors = LocalMyKSuiteColors.current + + Column( + modifier + .background(color = localColors.informationBlockBackground, shape = RoundedCornerShape(Dimens.smallCornerRadius)) + .padding(horizontal = Margin.Medium) + .padding(top = Margin.Medium), + ) { + val icon = ImageVector.vectorResource(R.drawable.ic_circle_i) + TextWithIcon( + text = text, + icon = icon, + iconTint = localColors.iconColor, + modifier = Modifier + .fillMaxWidth() + .padding(start = 28.dp), + style = Typography.bodyRegular, + iconRightPadding = Margin.Small, + ) + TextButton( + modifier = Modifier.padding(start = Margin.Medium), + onClick = onClick, + colors = ButtonDefaults.textButtonColors(contentColor = localColors.primaryButton), + ) { + Text(text = buttonText, style = Typography.bodyMedium) + } + } +} + +@Preview +@Composable +private fun Preview() { + + fun getLoremText(words: Int) = LoremIpsum(words).values.joinToString(separator = " ") + + MyKSuiteTheme { + Surface { + InformationBlock(text = getLoremText(35), buttonText = getLoremText(1)) {} + } + } +} diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsDark.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsDark.kt index f903df22..7446f745 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsDark.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsDark.kt @@ -48,6 +48,7 @@ internal val MyKSuiteDarkColors = MyKSuiteColors( background = Color(bat), secondaryBackground = Color(orca), topAppBarBackground = Color(orca), + informationBlockBackground = Color(orca), // TODO put real color when Design explicit it chipBackground = Color(orca), drive = Color(drive), mail = Color(mail), diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsLight.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsLight.kt index 143dd09a..5b87d74a 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsLight.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsLight.kt @@ -55,6 +55,7 @@ internal val MyKSuiteLightColors = MyKSuiteColors( background = Color(white), secondaryBackground = Color(white), topAppBarBackground = Color(sky), + informationBlockBackground = Color(polar_bear), chipBackground = Color(rabbit), drive = Color(drive), mail = Color(mail), diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Dimens.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Dimens.kt index 1b184898..1611b4ba 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Dimens.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Dimens.kt @@ -28,6 +28,8 @@ object Dimens { /** 24 dp */ val iconSize = 24.dp /** 16 dp */ + val smallCornerRadius = 8.dp + /** 16 dp */ val largeCornerRadius = 16.dp /** 5 dp */ val cardElevation = 5.dp diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Theme.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Theme.kt index fb1648b8..3e7ceb88 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Theme.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Theme.kt @@ -49,6 +49,7 @@ internal data class MyKSuiteColors( val background: Color = Color.Unspecified, val secondaryBackground: Color = Color.Unspecified, val topAppBarBackground: Color = Color.Unspecified, + val informationBlockBackground: Color = Color.Unspecified, val chipBackground: Color = Color.Unspecified, val drive: Color = Color.Unspecified, val mail: Color = Color.Unspecified, From 7e4791332150360ab6a0f4d7db3bae51a860cfbf Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Tue, 18 Feb 2025 11:50:21 +0100 Subject: [PATCH 11/21] feat(DynamicDashboard): Add the advantages card --- .../ui/screens/MyKSuiteDashboardScreen.kt | 29 +++++++++++++++++-- .../ui/screens/MyKSuiteUpgradeBottomSheet.kt | 18 +++++++++--- .../ui/screens/components/UpgradeFeature.kt | 23 ++++++++++----- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index 1de30437..6d5eb92a 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -81,8 +81,13 @@ fun MyKSuiteDashboardScreen( kSuiteProductsWithQuotas = kSuiteProductsWithQuotas, trialExpiryDate = trialExpiryDate, ) - // TODO: Add this line when we'll have In-app payments - // MyKSuitePlusPromotionCard(paddedModifier) {} + + if (myKSuiteTier() == MyKSuiteTier.Free) { + // TODO: Add this line when we'll have In-app payments + // MyKSuitePlusPromotionCard(paddedModifier) {} + } else { + AdvantagesCard(paddedModifier) + } } } } @@ -245,6 +250,26 @@ private fun MyKSuitePlusPromotionCard(modifier: Modifier = Modifier, onButtonCli } } +@Composable +private fun AdvantagesCard(modifier: Modifier = Modifier) { + val localColors = LocalMyKSuiteColors.current + Card( + modifier = modifier, + shape = RoundedCornerShape(Dimens.largeCornerRadius), + elevation = CardDefaults.elevatedCardElevation(defaultElevation = Dimens.cardElevation), + colors = CardDefaults.elevatedCardColors(containerColor = localColors.background), + ) { + Column(modifier = Modifier.padding(Margin.Medium), verticalArrangement = Arrangement.spacedBy(Margin.Large)) { + Text(stringResource(R.string.myKSuiteUpgradeBenefitsTitle), color = localColors.secondaryTextColor) + val upgradeFeatureModifier = Modifier.fillMaxWidth() + val iconSize = Dimens.smallIconSize + UpgradeFeature(upgradeFeatureModifier, MyKSuiteUpgradeFeatures.DriveStorageFeature, iconSize) + UpgradeFeature(upgradeFeatureModifier, MyKSuiteUpgradeFeatures.MailUnlimitedFeature, iconSize) + UpgradeFeature(upgradeFeatureModifier, MyKSuiteUpgradeFeatures.MoreFeatures, iconSize) + } + } +} + @Preview(name = "(1) Light") @Preview(name = "(2) Dark", uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL) @Composable diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteUpgradeBottomSheet.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteUpgradeBottomSheet.kt index afc42170..89f443c4 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteUpgradeBottomSheet.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteUpgradeBottomSheet.kt @@ -112,13 +112,23 @@ private fun UpgradeBottomSheetContent(app: KSuiteApp, onButtonClicked: () -> Uni } @Composable -private fun ColumnScope.UpgradeFeatures(app: KSuiteApp, modifier: Modifier) { - app.features.forEach { UpgradeFeature(it, modifier) } - UpgradeFeature(MyKSuiteUpgradeFeatures.MoreFeatures, modifier) +private fun UpgradeFeatures(app: KSuiteApp, modifier: Modifier) { + val upgradeFeatureModifier = Modifier.fillMaxWidth() + val textColor = LocalMyKSuiteColors.current.secondaryTextColor + + Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(Margin.Medium)) { + app.features.forEach { UpgradeFeature(modifier = upgradeFeatureModifier, customFeature = it, textColor = textColor) } + UpgradeFeature( + modifier = upgradeFeatureModifier, + customFeature = MyKSuiteUpgradeFeatures.MoreFeatures, + textColor = textColor, + ) + } } @Parcelize -enum class KSuiteApp(internal val features: List, internal val buttonStyle: MyKSuiteButtonType) : Parcelable { +enum class KSuiteApp(internal val features: List, internal val buttonStyle: MyKSuiteButtonType) : + Parcelable { Mail( features = listOf(MyKSuiteUpgradeFeatures.MailUnlimitedFeature, MyKSuiteUpgradeFeatures.MailOtherFeature), buttonStyle = MyKSuiteButtonType.Mail, diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/UpgradeFeature.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/UpgradeFeature.kt index 747a2aca..cbc158fd 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/UpgradeFeature.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/UpgradeFeature.kt @@ -25,34 +25,41 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import com.infomaniak.core.myksuite.ui.screens.MyKSuiteUpgradeFeatures import com.infomaniak.core.myksuite.ui.screens.MyKSuiteUpgradeFeatures.* import com.infomaniak.core.myksuite.ui.theme.* @Composable -internal fun ColumnScope.UpgradeFeature(customFeature: MyKSuiteUpgradeFeatures, modifier: Modifier = Modifier) { +internal fun ColumnScope.UpgradeFeature( + modifier: Modifier = Modifier, + customFeature: MyKSuiteUpgradeFeatures, + iconSize: Dp = Dimens.iconSize, + textColor: Color = Color.Unspecified, +) { val localColors = LocalMyKSuiteColors.current Row( modifier = modifier - .padding(vertical = Margin.Mini) .align(Alignment.Start), verticalAlignment = Alignment.CenterVertically, ) { Icon( - modifier = Modifier.size(Dimens.iconSize), + modifier = Modifier.size(iconSize), imageVector = ImageVector.vectorResource(customFeature.icon), contentDescription = null, tint = localColors.iconColor, ) Spacer(Modifier.width(Margin.Mini)) Text( + modifier = modifier, text = stringResource(customFeature.title), style = Typography.bodyRegular, - color = localColors.secondaryTextColor, + color = textColor, ) } } @@ -63,10 +70,10 @@ internal fun ColumnScope.UpgradeFeature(customFeature: MyKSuiteUpgradeFeatures, private fun Preview() { MyKSuiteTheme { Surface { - Column { - UpgradeFeature(DriveStorageFeature) - UpgradeFeature(DriveDropboxFeature) - UpgradeFeature(MoreFeatures) + Column(verticalArrangement = Arrangement.spacedBy(Margin.Mini)) { + UpgradeFeature(customFeature = DriveStorageFeature) + UpgradeFeature(customFeature = DriveDropboxFeature) + UpgradeFeature(customFeature = MoreFeatures) } } } From 0803d4f6f96cbd7bb4ea43fcd9e35de4334ab123 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Tue, 18 Feb 2025 13:21:09 +0100 Subject: [PATCH 12/21] feat(DynamicDashboard): Open the web manager when clicking on "manage my offer" --- .../com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt | 3 +++ .../core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt | 6 +++++- .../kotlin/com/infomaniak/core/extensions/ContextExt.kt | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt index 398b53c7..f2881b92 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt @@ -19,8 +19,11 @@ package com.infomaniak.core.myksuite.ui.network object ApiRoutes { + const val MANAGER_URL = "https://manager.infomaniak.com/v3/ng/home" + private const val PREPROD_BASE_URL = "https://api.staging-myksuite.dev.infomaniak.ch" private const val BASE_URL = "https://api.infomaniak.com" fun myKSuiteData() = "$BASE_URL/1/my_ksuite/current?with=drive,mail,pack,can_trial,has_auto_renew" + } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index 6d5eb92a..b4bc948f 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -28,15 +28,18 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.infomaniak.core.FORMAT_DATE_SIMPLE +import com.infomaniak.core.extensions.openUrl import com.infomaniak.core.format import com.infomaniak.core.myksuite.R import com.infomaniak.core.myksuite.ui.components.* +import com.infomaniak.core.myksuite.ui.network.ApiRoutes import com.infomaniak.core.myksuite.ui.screens.components.* import com.infomaniak.core.myksuite.ui.theme.* import com.infomaniak.core.myksuite.ui.theme.Typography @@ -132,6 +135,7 @@ private fun SubscriptionInfoCard( kSuiteProductsWithQuotas: () -> List, trialExpiryDate: () -> Date?, ) { + val context = LocalContext.current val localColors = LocalMyKSuiteColors.current Card( @@ -181,7 +185,7 @@ private fun SubscriptionInfoCard( modifier = paddedModifier, text = stringResource(R.string.myKSuiteManageSubscriptionDescription), buttonText = stringResource(R.string.myKSuiteManageSubscriptionButton), - onClick = {} // TODO + onClick = { context.openUrl(ApiRoutes.MANAGER_URL) } ) } Spacer(Modifier.height(Margin.Medium)) diff --git a/src/main/kotlin/com/infomaniak/core/extensions/ContextExt.kt b/src/main/kotlin/com/infomaniak/core/extensions/ContextExt.kt index 71abfa9e..0164e636 100644 --- a/src/main/kotlin/com/infomaniak/core/extensions/ContextExt.kt +++ b/src/main/kotlin/com/infomaniak/core/extensions/ContextExt.kt @@ -20,7 +20,9 @@ package com.infomaniak.core.extensions import android.app.Activity import android.content.Context import android.content.ContextWrapper +import android.content.Intent import android.content.pm.PackageManager +import android.net.Uri import androidx.core.content.ContextCompat tailrec fun Context.findActivity(): Activity? = when (this) { @@ -34,3 +36,7 @@ fun Context.hasPermissions(permissions: Array): Boolean { ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED } } + +fun Context.openUrl(url: String) { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) +} From 4f56d18ac19ea9f370b36a38451b7f30b1a472a2 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Tue, 18 Feb 2025 13:21:25 +0100 Subject: [PATCH 13/21] chore(DynamicDashboard): Clean code --- .../com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt | 2 +- .../core/myksuite/ui/screens/components/InformationBlock.kt | 2 +- .../core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt index 2a521ba5..3ceff71f 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt @@ -101,7 +101,7 @@ private fun Preview() { val localColors = LocalMyKSuiteColors.current Surface { TextWithIcon( - // The icon's horizontal size is 16dp, the iconRightPadding is 12dp, so here by passing 32dp as the Text padding + // The icon's horizontal size is 16dp, the iconRightPadding is 12dp. So here by passing 32dp as the Text padding, // it will result in a 32 - (16 + 12) = 4dp start padding for the icon modifier = Modifier.padding(start = Margin.Huge), text = getLoremText(35), icon = ImageVector.vectorResource(R.drawable.ic_circle_i), diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt index 50b9c6d4..7f0d5318 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt @@ -38,7 +38,7 @@ import com.infomaniak.core.myksuite.ui.components.TextWithIcon import com.infomaniak.core.myksuite.ui.theme.* @Composable -fun InformationBlock(modifier: Modifier = Modifier, text: String, buttonText: String, onClick: () -> Unit) { +internal fun InformationBlock(modifier: Modifier = Modifier, text: String, buttonText: String, onClick: () -> Unit) { val localColors = LocalMyKSuiteColors.current Column( diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt index 1c6c65e2..7e0b0bb3 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt @@ -35,7 +35,7 @@ import com.infomaniak.core.myksuite.ui.theme.MyKSuiteTheme import java.util.Date @Composable -fun MyKSuitePlusTextItem(modifier: Modifier = Modifier, title: String, value: String) { +internal fun MyKSuitePlusTextItem(modifier: Modifier = Modifier, title: String, value: String) { Row( modifier = modifier .heightIn(min = 40.dp) From 6c39567c846cdb9237b2dfb9030aae7b6d53bb45 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Tue, 18 Feb 2025 14:50:24 +0100 Subject: [PATCH 14/21] feat(DynamicDashboard): Add resetContent method to update the dashboard view --- .../ui/views/MyKSuiteDashboardFragment.kt | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt index 4efa0107..697e9932 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt @@ -26,17 +26,22 @@ import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs +import com.infomaniak.core.myksuite.ui.components.MyKSuiteTier +import com.infomaniak.core.myksuite.ui.data.MyKSuiteData import com.infomaniak.core.myksuite.ui.screens.MyKSuiteDashboardScreen +import com.infomaniak.core.myksuite.ui.screens.components.KSuiteProductsWithQuotas open class MyKSuiteDashboardFragment : Fragment() { private val navigationArgs: MyKSuiteDashboardFragmentArgs by navArgs() + private lateinit var composeView: ComposeView + + private val onClose: () -> Unit by lazy { { this@MyKSuiteDashboardFragment.findNavController().popBackStack() } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return ComposeView(requireContext()).apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { - val onClose: () -> Unit = { this@MyKSuiteDashboardFragment.findNavController().popBackStack() } with(navigationArgs) { MyKSuiteDashboardScreen( myKSuiteTier = { myKSuiteTier }, @@ -48,6 +53,23 @@ open class MyKSuiteDashboardFragment : Fragment() { onClose = onClose, ) } + composeView = this + } + } + } + + protected fun resetContent(myKSuiteData: MyKSuiteData, avatarUri: String, products: List) { + composeView.setContent { + with(myKSuiteData) { + MyKSuiteDashboardScreen( + myKSuiteTier = { if (isMyKSuitePlus) MyKSuiteTier.Plus else MyKSuiteTier.Free }, + email = mail.email, + avatarUri = { avatarUri }, + dailySendingLimit = { mail.dailyLimitSent.toString() }, + kSuiteProductsWithQuotas = { products }, + trialExpiryDate = { trialExpiryDate }, + onClose = onClose, + ) } } } From 9bf04150497deaf6808f4a6cfb84e1fc5ccf55f1 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Tue, 18 Feb 2025 16:32:17 +0100 Subject: [PATCH 15/21] refactor(DynamicDashboard): Extract dashboard parameter to a dataclass --- .../ui/screens/MyKSuiteDashboardScreen.kt | 105 ++++++++---------- .../components/ProductsStorageQuotas.kt | 8 +- .../ui/views/MyKSuiteDashboardFragment.kt | 46 ++++---- 3 files changed, 79 insertions(+), 80 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index b4bc948f..e9e30b37 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -46,15 +46,7 @@ import com.infomaniak.core.myksuite.ui.theme.Typography import java.util.Date @Composable -fun MyKSuiteDashboardScreen( - myKSuiteTier: () -> MyKSuiteTier, - email: String, - avatarUri: () -> String = { "" }, - dailySendingLimit: () -> String, - kSuiteProductsWithQuotas: () -> List, - trialExpiryDate: () -> Date?, - onClose: () -> Unit = {}, -) { +fun MyKSuiteDashboardScreen(dashboardScreenData: () -> MyKSuiteDashboardScreenData, onClose: () -> Unit = {}) { MyKSuiteTheme { Scaffold( topBar = { TopAppBar(onClose) }, @@ -75,17 +67,9 @@ fun MyKSuiteDashboardScreen( ) Column(Modifier.padding(paddingValues), verticalArrangement = Arrangement.spacedBy(Margin.Large)) { val paddedModifier = Modifier.padding(horizontal = Margin.Medium) - SubscriptionInfoCard( - paddedModifier = paddedModifier, - myKSuiteTier = myKSuiteTier, - email = email, - avatarUri = avatarUri, - dailySendingLimit = dailySendingLimit, - kSuiteProductsWithQuotas = kSuiteProductsWithQuotas, - trialExpiryDate = trialExpiryDate, - ) + SubscriptionInfoCard(paddedModifier = paddedModifier, dashboardScreenData = dashboardScreenData) - if (myKSuiteTier() == MyKSuiteTier.Free) { + if (dashboardScreenData().myKSuiteTier == MyKSuiteTier.Free) { // TODO: Add this line when we'll have In-app payments // MyKSuitePlusPromotionCard(paddedModifier) {} } else { @@ -126,15 +110,7 @@ private fun TopAppBar(onClose: () -> Unit) { } @Composable -private fun SubscriptionInfoCard( - paddedModifier: Modifier, - myKSuiteTier: () -> MyKSuiteTier, - email: String, - avatarUri: () -> String, - dailySendingLimit: () -> String, - kSuiteProductsWithQuotas: () -> List, - trialExpiryDate: () -> Date?, -) { +private fun SubscriptionInfoCard(paddedModifier: Modifier, dashboardScreenData: () -> MyKSuiteDashboardScreenData) { val context = LocalContext.current val localColors = LocalMyKSuiteColors.current @@ -150,30 +126,37 @@ private fun SubscriptionInfoCard( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(Margin.Mini), ) { - UserAvatar(avatarUri()) + UserAvatar(dashboardScreenData().avatarUri) Text( modifier = Modifier.weight(1.0f), style = Typography.bodyRegular, color = localColors.primaryTextColor, - text = email, + text = dashboardScreenData().email, maxLines = 1, overflow = TextOverflow.Ellipsis, ) - MyKSuiteChip(tier = myKSuiteTier()) + MyKSuiteChip(tier = dashboardScreenData().myKSuiteTier) } PaddedDivider(paddedModifier) - ProductsStorageQuotas(paddedModifier, myKSuiteTier, kSuiteProductsWithQuotas) + ProductsStorageQuotas( + paddedModifier, + dashboardScreenData().myKSuiteTier, + { dashboardScreenData().kSuiteProductsWithQuotas }) PaddedDivider(paddedModifier) - if (myKSuiteTier() == MyKSuiteTier.Free) { + if (dashboardScreenData().myKSuiteTier == MyKSuiteTier.Free) { ExpendableActionItem(iconRes = R.drawable.ic_envelope, textRes = R.string.myKSuiteDashboardFreeMailLabel) ExpendableActionItem( iconRes = R.drawable.ic_padlock, textRes = R.string.myKSuiteDashboardLimitedFunctionalityLabel, - expendedView = { LimitedFunctionalities(paddedModifier, dailySendingLimit) }, + expendedView = { + LimitedFunctionalities( + paddedModifier, + dailySendingLimit = { dashboardScreenData().dailySendingLimit }) + }, ) } else { - trialExpiryDate()?.let { expiryDate -> + dashboardScreenData().trialExpiryDate?.let { expiryDate -> MyKSuitePlusTextItem( modifier = paddedModifier, title = stringResource(R.string.myKSuiteDashboardTrialPeriod), @@ -274,31 +257,41 @@ private fun AdvantagesCard(modifier: Modifier = Modifier) { } } +data class MyKSuiteDashboardScreenData( + val myKSuiteTier: MyKSuiteTier, + val email: String, + val dailySendingLimit: String, + val kSuiteProductsWithQuotas: List, + val trialExpiryDate: Date?, + val avatarUri: String = "", +) + @Preview(name = "(1) Light") @Preview(name = "(2) Dark", uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL) @Composable private fun Preview() { + val dashboardScreenData = MyKSuiteDashboardScreenData( + myKSuiteTier = MyKSuiteTier.Plus, + email = "Toto", + avatarUri = "", + dailySendingLimit = "500", + kSuiteProductsWithQuotas = + listOf( + KSuiteProductsWithQuotas.Mail( + usedSize = "0.2 Go", + maxSize = "20 Go", + progress = 0.01f, + ), + KSuiteProductsWithQuotas.Drive( + usedSize = "6 Go", + maxSize = "15 Go", + progress = 0.4f, + ), + ), + trialExpiryDate = Date(), + ) + Surface(Modifier.fillMaxSize(), color = Color.White) { - MyKSuiteDashboardScreen( - myKSuiteTier = { MyKSuiteTier.Plus }, - email = "Toto", - avatarUri = { "" }, - dailySendingLimit = { "500" }, - kSuiteProductsWithQuotas = { - listOf( - KSuiteProductsWithQuotas.Mail( - usedSize = "0.2 Go", - maxSize = "20 Go", - progress = 0.01f, - ), - KSuiteProductsWithQuotas.Drive( - usedSize = "6 Go", - maxSize = "15 Go", - progress = 0.4f, - ), - ) - }, - trialExpiryDate = { Date() } - ) + MyKSuiteDashboardScreen(dashboardScreenData = { dashboardScreenData }) } } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt index c8e707a1..ca4791d3 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt @@ -42,7 +42,7 @@ import kotlinx.parcelize.Parcelize @Composable internal fun ProductsStorageQuotas( modifier: Modifier, - myKSuiteTier: () -> MyKSuiteTier, + myKSuiteTier: MyKSuiteTier, kSuiteProductsWithQuotas: () -> List, ) { Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(Margin.Medium)) { @@ -53,11 +53,11 @@ internal fun ProductsStorageQuotas( @Composable private fun ProductStorageQuota( modifier: Modifier = Modifier, - myKSuiteTier: () -> MyKSuiteTier, + myKSuiteTier: MyKSuiteTier, product: KSuiteProductsWithQuotas, ) { val localColors = LocalMyKSuiteColors.current - val isUnlimitedMail = myKSuiteTier() == MyKSuiteTier.Plus && product is KSuiteProductsWithQuotas.Mail + val isUnlimitedMail = myKSuiteTier == MyKSuiteTier.Plus && product is KSuiteProductsWithQuotas.Mail Column(modifier) { Row(verticalAlignment = Alignment.CenterVertically) { @@ -127,7 +127,7 @@ private fun Preview() { Surface { ProductsStorageQuotas( modifier = Modifier.padding(Margin.Medium), - myKSuiteTier = { MyKSuiteTier.Plus }, + myKSuiteTier = MyKSuiteTier.Plus, kSuiteProductsWithQuotas = { listOf( KSuiteProductsWithQuotas.Mail(usedSize = "0.2 Go", maxSize = "20 Go", progress = 0.01f), diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt index 697e9932..da2b4300 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt @@ -29,6 +29,7 @@ import androidx.navigation.fragment.navArgs import com.infomaniak.core.myksuite.ui.components.MyKSuiteTier import com.infomaniak.core.myksuite.ui.data.MyKSuiteData import com.infomaniak.core.myksuite.ui.screens.MyKSuiteDashboardScreen +import com.infomaniak.core.myksuite.ui.screens.MyKSuiteDashboardScreenData import com.infomaniak.core.myksuite.ui.screens.components.KSuiteProductsWithQuotas open class MyKSuiteDashboardFragment : Fragment() { @@ -39,35 +40,40 @@ open class MyKSuiteDashboardFragment : Fragment() { private val onClose: () -> Unit by lazy { { this@MyKSuiteDashboardFragment.findNavController().popBackStack() } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + val dashboardScreenData = with(navigationArgs) { + MyKSuiteDashboardScreenData( + myKSuiteTier = myKSuiteTier, + email = email, + avatarUri = avatarUri, + dailySendingLimit = dailySendLimit, + kSuiteProductsWithQuotas = kSuiteAppsWithQuotas.toList(), + trialExpiryDate = trialExpiryDate, + ) + } + return ComposeView(requireContext()).apply { + composeView = this setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { - with(navigationArgs) { - MyKSuiteDashboardScreen( - myKSuiteTier = { myKSuiteTier }, - email = email, - avatarUri = { avatarUri }, - dailySendingLimit = { dailySendLimit }, - kSuiteProductsWithQuotas = { kSuiteAppsWithQuotas.toList() }, - trialExpiryDate = { trialExpiryDate }, - onClose = onClose, - ) - } - composeView = this + MyKSuiteDashboardScreen({ dashboardScreenData }, onClose) } } } protected fun resetContent(myKSuiteData: MyKSuiteData, avatarUri: String, products: List) { - composeView.setContent { - with(myKSuiteData) { + with(myKSuiteData) { + composeView.setContent { MyKSuiteDashboardScreen( - myKSuiteTier = { if (isMyKSuitePlus) MyKSuiteTier.Plus else MyKSuiteTier.Free }, - email = mail.email, - avatarUri = { avatarUri }, - dailySendingLimit = { mail.dailyLimitSent.toString() }, - kSuiteProductsWithQuotas = { products }, - trialExpiryDate = { trialExpiryDate }, + dashboardScreenData = { + MyKSuiteDashboardScreenData( + myKSuiteTier = if (isMyKSuitePlus) MyKSuiteTier.Plus else MyKSuiteTier.Free, + email = mail.email, + avatarUri = avatarUri, + dailySendingLimit = mail.dailyLimitSent.toString(), + kSuiteProductsWithQuotas = products, + trialExpiryDate = trialExpiryDate, + ) + }, onClose = onClose, ) } From b3fbbdbb717c9854d8efa3f5cf15ff460ae31151 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Tue, 18 Feb 2025 16:48:06 +0100 Subject: [PATCH 16/21] refactor(DynamicDashboard): Directly pass the DashboardData class to navArgs --- .../ui/screens/MyKSuiteDashboardScreen.kt | 8 ++-- .../ui/views/MyKSuiteDashboardFragment.kt | 39 ++++--------------- .../res/navigation/my_ksuite_navigation.xml | 20 +--------- 3 files changed, 14 insertions(+), 53 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index e9e30b37..8e466218 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -18,6 +18,7 @@ package com.infomaniak.core.myksuite.ui.screens import android.content.res.Configuration +import android.os.Parcelable import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape @@ -43,6 +44,7 @@ import com.infomaniak.core.myksuite.ui.network.ApiRoutes import com.infomaniak.core.myksuite.ui.screens.components.* import com.infomaniak.core.myksuite.ui.theme.* import com.infomaniak.core.myksuite.ui.theme.Typography +import kotlinx.parcelize.Parcelize import java.util.Date @Composable @@ -257,6 +259,7 @@ private fun AdvantagesCard(modifier: Modifier = Modifier) { } } +@Parcelize data class MyKSuiteDashboardScreenData( val myKSuiteTier: MyKSuiteTier, val email: String, @@ -264,7 +267,7 @@ data class MyKSuiteDashboardScreenData( val kSuiteProductsWithQuotas: List, val trialExpiryDate: Date?, val avatarUri: String = "", -) +) : Parcelable @Preview(name = "(1) Light") @Preview(name = "(2) Dark", uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL) @@ -275,8 +278,7 @@ private fun Preview() { email = "Toto", avatarUri = "", dailySendingLimit = "500", - kSuiteProductsWithQuotas = - listOf( + kSuiteProductsWithQuotas = listOf( KSuiteProductsWithQuotas.Mail( usedSize = "0.2 Go", maxSize = "20 Go", diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt index da2b4300..24af017f 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt @@ -26,11 +26,8 @@ import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import com.infomaniak.core.myksuite.ui.components.MyKSuiteTier -import com.infomaniak.core.myksuite.ui.data.MyKSuiteData import com.infomaniak.core.myksuite.ui.screens.MyKSuiteDashboardScreen import com.infomaniak.core.myksuite.ui.screens.MyKSuiteDashboardScreenData -import com.infomaniak.core.myksuite.ui.screens.components.KSuiteProductsWithQuotas open class MyKSuiteDashboardFragment : Fragment() { @@ -40,43 +37,21 @@ open class MyKSuiteDashboardFragment : Fragment() { private val onClose: () -> Unit by lazy { { this@MyKSuiteDashboardFragment.findNavController().popBackStack() } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - val dashboardScreenData = with(navigationArgs) { - MyKSuiteDashboardScreenData( - myKSuiteTier = myKSuiteTier, - email = email, - avatarUri = avatarUri, - dailySendingLimit = dailySendLimit, - kSuiteProductsWithQuotas = kSuiteAppsWithQuotas.toList(), - trialExpiryDate = trialExpiryDate, - ) - } - return ComposeView(requireContext()).apply { composeView = this setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { - MyKSuiteDashboardScreen({ dashboardScreenData }, onClose) + MyKSuiteDashboardScreen({ navigationArgs.dashboardData }, onClose) } } } - protected fun resetContent(myKSuiteData: MyKSuiteData, avatarUri: String, products: List) { - with(myKSuiteData) { - composeView.setContent { - MyKSuiteDashboardScreen( - dashboardScreenData = { - MyKSuiteDashboardScreenData( - myKSuiteTier = if (isMyKSuitePlus) MyKSuiteTier.Plus else MyKSuiteTier.Free, - email = mail.email, - avatarUri = avatarUri, - dailySendingLimit = mail.dailyLimitSent.toString(), - kSuiteProductsWithQuotas = products, - trialExpiryDate = trialExpiryDate, - ) - }, - onClose = onClose, - ) - } + protected fun resetContent(dashboardData: MyKSuiteDashboardScreenData) { + composeView.setContent { + MyKSuiteDashboardScreen( + dashboardScreenData = { dashboardData }, + onClose = onClose, + ) } } } diff --git a/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml b/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml index 75996b8f..56e8da83 100644 --- a/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml +++ b/MykSuite/src/main/res/navigation/my_ksuite_navigation.xml @@ -25,24 +25,8 @@ android:name="com.infomaniak.core.myksuite.ui.views.MyKSuiteDashboardFragment" android:label="MyKSuiteDashboardFragment"> - - - - - + android:name="dashboardData" + app:argType="com.infomaniak.core.myksuite.ui.screens.MyKSuiteDashboardScreenData" /> Date: Wed, 19 Feb 2025 07:43:57 +0100 Subject: [PATCH 17/21] chore(dynamicDashboard): Clean code --- .../myksuite/ui/components/TextWithIcon.kt | 4 ++- .../ui/screens/MyKSuiteDashboardScreen.kt | 27 ++++++++++++------- .../ui/screens/MyKSuiteUpgradeBottomSheet.kt | 6 +++-- .../screens/components/ExpandableItemView.kt | 19 +++++++------ .../ui/screens/components/InformationBlock.kt | 13 ++++----- .../components/LimitedFunctionnalities.kt | 4 +-- .../components/MyKSuitePlusTextItem.kt | 4 +-- .../core/myksuite/ui/theme/ColorsDark.kt | 2 +- .../core/myksuite/ui/theme/Dimens.kt | 4 ++- .../ui/views/MyKSuiteDashboardFragment.kt | 7 +---- 10 files changed, 49 insertions(+), 41 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt index 3ceff71f..49050aa5 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt @@ -46,6 +46,8 @@ import com.infomaniak.core.myksuite.ui.theme.Typography * * IMPORTANT: You need to define the passed modifier's startPadding as at least the sum of [icon]'s horizontal dp size and * [iconRightPadding] to display correctly the icon. + * + * This code comes from [here](https://stackoverflow.com/questions/70708056/how-to-centrally-align-icon-to-first-line-of-a-text-component-in-compose/71312465#71312465) */ @Composable internal fun TextWithIcon( @@ -83,7 +85,7 @@ internal fun TextWithIcon( left = lineLeft - imageSize.width - rightPadding, top = lineTop + (lineBottom - lineTop) / 2 - imageSize.height / 2, ) { - draw(painter.intrinsicSize, colorFilter = ColorFilter.tint(iconTint)) + draw(size = intrinsicSize, colorFilter = ColorFilter.tint(iconTint)) } } } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index 8e466218..b6e3e4fa 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -141,20 +141,22 @@ private fun SubscriptionInfoCard(paddedModifier: Modifier, dashboardScreenData: } PaddedDivider(paddedModifier) ProductsStorageQuotas( - paddedModifier, - dashboardScreenData().myKSuiteTier, - { dashboardScreenData().kSuiteProductsWithQuotas }) + modifier = paddedModifier, + myKSuiteTier = dashboardScreenData().myKSuiteTier, + kSuiteProductsWithQuotas = { dashboardScreenData().kSuiteProductsWithQuotas }, + ) PaddedDivider(paddedModifier) if (dashboardScreenData().myKSuiteTier == MyKSuiteTier.Free) { - ExpendableActionItem(iconRes = R.drawable.ic_envelope, textRes = R.string.myKSuiteDashboardFreeMailLabel) - ExpendableActionItem( + ExpandableActionItem(iconRes = R.drawable.ic_envelope, textRes = R.string.myKSuiteDashboardFreeMailLabel) + ExpandableActionItem( iconRes = R.drawable.ic_padlock, textRes = R.string.myKSuiteDashboardLimitedFunctionalityLabel, - expendedView = { + expandedView = { LimitedFunctionalities( - paddedModifier, - dailySendingLimit = { dashboardScreenData().dailySendingLimit }) + modifier = paddedModifier, + dailySendingLimit = { dashboardScreenData().dailySendingLimit }, + ) }, ) } else { @@ -170,7 +172,7 @@ private fun SubscriptionInfoCard(paddedModifier: Modifier, dashboardScreenData: modifier = paddedModifier, text = stringResource(R.string.myKSuiteManageSubscriptionDescription), buttonText = stringResource(R.string.myKSuiteManageSubscriptionButton), - onClick = { context.openUrl(ApiRoutes.MANAGER_URL) } + onClick = { context.openUrl(ApiRoutes.MANAGER_URL) }, ) } Spacer(Modifier.height(Margin.Medium)) @@ -249,7 +251,12 @@ private fun AdvantagesCard(modifier: Modifier = Modifier) { colors = CardDefaults.elevatedCardColors(containerColor = localColors.background), ) { Column(modifier = Modifier.padding(Margin.Medium), verticalArrangement = Arrangement.spacedBy(Margin.Large)) { - Text(stringResource(R.string.myKSuiteUpgradeBenefitsTitle), color = localColors.secondaryTextColor) + Text( + text = stringResource(R.string.myKSuiteUpgradeBenefitsTitle), + color = localColors.secondaryTextColor, + style = Typography.bodySmallRegular, + ) + val upgradeFeatureModifier = Modifier.fillMaxWidth() val iconSize = Dimens.smallIconSize UpgradeFeature(upgradeFeatureModifier, MyKSuiteUpgradeFeatures.DriveStorageFeature, iconSize) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteUpgradeBottomSheet.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteUpgradeBottomSheet.kt index 89f443c4..4db40b39 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteUpgradeBottomSheet.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteUpgradeBottomSheet.kt @@ -127,8 +127,10 @@ private fun UpgradeFeatures(app: KSuiteApp, modifier: Modifier) { } @Parcelize -enum class KSuiteApp(internal val features: List, internal val buttonStyle: MyKSuiteButtonType) : - Parcelable { +enum class KSuiteApp( + internal val features: List, + internal val buttonStyle: MyKSuiteButtonType, +) : Parcelable { Mail( features = listOf(MyKSuiteUpgradeFeatures.MailUnlimitedFeature, MyKSuiteUpgradeFeatures.MailOtherFeature), buttonStyle = MyKSuiteButtonType.Mail, diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ExpandableItemView.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ExpandableItemView.kt index f7e9d50b..b70e66ee 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ExpandableItemView.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ExpandableItemView.kt @@ -33,15 +33,14 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import com.infomaniak.core.myksuite.R import com.infomaniak.core.myksuite.ui.theme.* @Composable -internal fun ExpendableActionItem( +internal fun ExpandableActionItem( @DrawableRes iconRes: Int, @StringRes textRes: Int, - expendedView: (@Composable () -> Unit)? = null, + expandedView: (@Composable () -> Unit)? = null, ) { var isExpanded by remember { mutableStateOf(false) } @@ -51,10 +50,10 @@ internal fun ExpendableActionItem( Column { Row( modifier = Modifier - .heightIn(min = 40.dp) + .heightIn(min = Dimens.textItemMinHeight) .fillMaxWidth() .then( // TODO add onClickLabel for accessibility - if (expendedView == null) Modifier else Modifier.clickable { isExpanded = !isExpanded } + if (expandedView == null) Modifier else Modifier.clickable { isExpanded = !isExpanded } ) .padding(horizontal = Margin.Medium), verticalAlignment = Alignment.CenterVertically, @@ -72,7 +71,7 @@ internal fun ExpendableActionItem( style = Typography.bodyRegular, color = localColors.primaryTextColor, ) - expendedView?.let { + expandedView?.let { val icon = ImageVector.vectorResource(if (isExpanded) R.drawable.ic_chevron_up else R.drawable.ic_chevron_down) Icon( imageVector = icon, @@ -81,7 +80,7 @@ internal fun ExpendableActionItem( ) } } - AnimatedVisibility(isExpanded) { expendedView?.invoke() } + AnimatedVisibility(isExpanded) { expandedView?.invoke() } } } @@ -92,11 +91,11 @@ private fun Preview() { MyKSuiteTheme { Surface { Column { - ExpendableActionItem(R.drawable.ic_padlock, R.string.myKSuiteDashboardLimitedFunctionalityLabel) - ExpendableActionItem( + ExpandableActionItem(R.drawable.ic_padlock, R.string.myKSuiteDashboardLimitedFunctionalityLabel) + ExpandableActionItem( iconRes = R.drawable.ic_padlock, textRes = R.string.myKSuiteDashboardLimitedFunctionalityLabel, - expendedView = { Text("") } + expandedView = { Text("") } ) } } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt index 7f0d5318..793d8e83 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt @@ -17,6 +17,7 @@ */ package com.infomaniak.core.myksuite.ui.screens.components +import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -44,17 +45,15 @@ internal fun InformationBlock(modifier: Modifier = Modifier, text: String, butto Column( modifier .background(color = localColors.informationBlockBackground, shape = RoundedCornerShape(Dimens.smallCornerRadius)) - .padding(horizontal = Margin.Medium) - .padding(top = Margin.Medium), + .padding(start = Margin.Medium, top = Margin.Medium, end = Margin.Medium), ) { - val icon = ImageVector.vectorResource(R.drawable.ic_circle_i) TextWithIcon( text = text, - icon = icon, + icon = ImageVector.vectorResource(R.drawable.ic_circle_i), iconTint = localColors.iconColor, modifier = Modifier .fillMaxWidth() - .padding(start = 28.dp), + .padding(start = 28.dp), // Icon size + iconRightPadding style = Typography.bodyRegular, iconRightPadding = Margin.Small, ) @@ -68,7 +67,9 @@ internal fun InformationBlock(modifier: Modifier = Modifier, text: String, butto } } -@Preview + +@Preview(name = "(1) Light") +@Preview(name = "(2) Dark", uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL) @Composable private fun Preview() { diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/LimitedFunctionnalities.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/LimitedFunctionnalities.kt index 0a41510c..4bdea849 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/LimitedFunctionnalities.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/LimitedFunctionnalities.kt @@ -34,9 +34,9 @@ import com.infomaniak.core.myksuite.ui.theme.MyKSuiteTheme import com.infomaniak.core.myksuite.ui.theme.Typography @Composable -internal fun LimitedFunctionalities(paddedModifier: Modifier, dailySendingLimit: () -> String) { +internal fun LimitedFunctionalities(modifier: Modifier, dailySendingLimit: () -> String) { Column( - modifier = paddedModifier.padding(top = Margin.Mini), + modifier = modifier.padding(top = Margin.Mini), verticalArrangement = Arrangement.spacedBy(Margin.Mini), ) { LimitedFunctionalityLabel(textRes = R.string.myKSuiteDashboardFunctionalityMailAndDrive) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt index 7e0b0bb3..bf208ef4 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt @@ -25,10 +25,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import com.infomaniak.core.FORMAT_DATE_SIMPLE import com.infomaniak.core.format import com.infomaniak.core.myksuite.R +import com.infomaniak.core.myksuite.ui.theme.Dimens import com.infomaniak.core.myksuite.ui.theme.LocalMyKSuiteColors import com.infomaniak.core.myksuite.ui.theme.Margin import com.infomaniak.core.myksuite.ui.theme.MyKSuiteTheme @@ -38,7 +38,7 @@ import java.util.Date internal fun MyKSuitePlusTextItem(modifier: Modifier = Modifier, title: String, value: String) { Row( modifier = modifier - .heightIn(min = 40.dp) + .heightIn(min = Dimens.textItemMinHeight) .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsDark.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsDark.kt index 7446f745..ca98fee8 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsDark.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsDark.kt @@ -48,7 +48,7 @@ internal val MyKSuiteDarkColors = MyKSuiteColors( background = Color(bat), secondaryBackground = Color(orca), topAppBarBackground = Color(orca), - informationBlockBackground = Color(orca), // TODO put real color when Design explicit it + informationBlockBackground = Color(orca), chipBackground = Color(orca), drive = Color(drive), mail = Color(mail), diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Dimens.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Dimens.kt index 1611b4ba..65c7bcaf 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Dimens.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Dimens.kt @@ -27,10 +27,12 @@ object Dimens { val smallIconSize = 16.dp /** 24 dp */ val iconSize = 24.dp - /** 16 dp */ + /** 8 dp */ val smallCornerRadius = 8.dp /** 16 dp */ val largeCornerRadius = 16.dp /** 5 dp */ val cardElevation = 5.dp + /** 40 dp */ + val textItemMinHeight = 40.dp } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt index 24af017f..f790a30f 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt @@ -47,11 +47,6 @@ open class MyKSuiteDashboardFragment : Fragment() { } protected fun resetContent(dashboardData: MyKSuiteDashboardScreenData) { - composeView.setContent { - MyKSuiteDashboardScreen( - dashboardScreenData = { dashboardData }, - onClose = onClose, - ) - } + composeView.setContent { MyKSuiteDashboardScreen(dashboardScreenData = { dashboardData }, onClose = onClose) } } } From 226411b402c1b818547d54a475dfd04a8eefcfc4 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Wed, 19 Feb 2025 10:44:43 +0100 Subject: [PATCH 18/21] refactor(DynamicDashboard): Make the startPadding's calculation direclty in the TextWithIcon component --- .../myksuite/ui/components/TextWithIcon.kt | 27 ++++++++----------- .../ui/screens/components/InformationBlock.kt | 5 +--- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt index 49050aa5..8062e84e 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt @@ -43,15 +43,11 @@ import com.infomaniak.core.myksuite.ui.theme.MyKSuiteTheme import com.infomaniak.core.myksuite.ui.theme.Typography /** This component allows to put an icon on any line of the text, thanks to the [iconLine] parameter - * - * IMPORTANT: You need to define the passed modifier's startPadding as at least the sum of [icon]'s horizontal dp size and - * [iconRightPadding] to display correctly the icon. - * * This code comes from [here](https://stackoverflow.com/questions/70708056/how-to-centrally-align-icon-to-first-line-of-a-text-component-in-compose/71312465#71312465) */ @Composable internal fun TextWithIcon( - modifier: Modifier, + modifier: Modifier = Modifier, text: String, icon: ImageVector, color: Color = Color.Unspecified, @@ -79,16 +75,18 @@ internal fun TextWithIcon( lineLeft = layoutResult.getLineLeft(iconLine) } }, - modifier = modifier.drawBehind { - with(painter) { - translate( - left = lineLeft - imageSize.width - rightPadding, - top = lineTop + (lineBottom - lineTop) / 2 - imageSize.height / 2, - ) { - draw(size = intrinsicSize, colorFilter = ColorFilter.tint(iconTint)) + modifier = modifier + .padding(start = icon.defaultWidth + iconRightPadding) + .drawBehind { + with(painter) { + translate( + left = lineLeft - imageSize.width - rightPadding, + top = lineTop + (lineBottom - lineTop) / 2 - imageSize.height / 2, + ) { + draw(size = intrinsicSize, colorFilter = ColorFilter.tint(iconTint)) + } } } - } ) } } @@ -103,9 +101,6 @@ private fun Preview() { val localColors = LocalMyKSuiteColors.current Surface { TextWithIcon( - // The icon's horizontal size is 16dp, the iconRightPadding is 12dp. So here by passing 32dp as the Text padding, - // it will result in a 32 - (16 + 12) = 4dp start padding for the icon - modifier = Modifier.padding(start = Margin.Huge), text = getLoremText(35), icon = ImageVector.vectorResource(R.drawable.ic_circle_i), color = localColors.primaryTextColor, style = Typography.bodyRegular, diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt index 793d8e83..eaeb088b 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt @@ -33,7 +33,6 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.datasource.LoremIpsum -import androidx.compose.ui.unit.dp import com.infomaniak.core.myksuite.R import com.infomaniak.core.myksuite.ui.components.TextWithIcon import com.infomaniak.core.myksuite.ui.theme.* @@ -51,9 +50,7 @@ internal fun InformationBlock(modifier: Modifier = Modifier, text: String, butto text = text, icon = ImageVector.vectorResource(R.drawable.ic_circle_i), iconTint = localColors.iconColor, - modifier = Modifier - .fillMaxWidth() - .padding(start = 28.dp), // Icon size + iconRightPadding + modifier = Modifier.fillMaxWidth(), style = Typography.bodyRegular, iconRightPadding = Margin.Small, ) From 3fca939557514ad9d209f90508f49cf6dbc00030 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Wed, 19 Feb 2025 11:20:54 +0100 Subject: [PATCH 19/21] feat(DynamicDashboard): Add the card border for the advantages card --- .../ui/screens/MyKSuiteDashboardScreen.kt | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index b6e3e4fa..6c2f7c8e 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -69,7 +69,7 @@ fun MyKSuiteDashboardScreen(dashboardScreenData: () -> MyKSuiteDashboardScreenDa ) Column(Modifier.padding(paddingValues), verticalArrangement = Arrangement.spacedBy(Margin.Large)) { val paddedModifier = Modifier.padding(horizontal = Margin.Medium) - SubscriptionInfoCard(paddedModifier = paddedModifier, dashboardScreenData = dashboardScreenData) + SubscriptionInfoCard(paddedModifier, dashboardScreenData) if (dashboardScreenData().myKSuiteTier == MyKSuiteTier.Free) { // TODO: Add this line when we'll have In-app payments @@ -112,7 +112,10 @@ private fun TopAppBar(onClose: () -> Unit) { } @Composable -private fun SubscriptionInfoCard(paddedModifier: Modifier, dashboardScreenData: () -> MyKSuiteDashboardScreenData) { +private fun SubscriptionInfoCard( + paddedModifier: Modifier, + dashboardScreenData: () -> MyKSuiteDashboardScreenData, +) { val context = LocalContext.current val localColors = LocalMyKSuiteColors.current @@ -121,7 +124,7 @@ private fun SubscriptionInfoCard(paddedModifier: Modifier, dashboardScreenData: shape = RoundedCornerShape(Dimens.largeCornerRadius), colors = CardDefaults.cardColors(containerColor = localColors.background), elevation = CardDefaults.elevatedCardElevation(defaultElevation = Dimens.cardElevation), - border = if (isSystemInDarkTheme()) BorderStroke(1.dp, localColors.cardBorderColor) else null, + border = cardBorder(), ) { Row( modifier = paddedModifier.padding(top = Margin.Medium), @@ -242,13 +245,14 @@ private fun MyKSuitePlusPromotionCard(modifier: Modifier = Modifier, onButtonCli } @Composable -private fun AdvantagesCard(modifier: Modifier = Modifier) { +private fun AdvantagesCard(modifier: Modifier) { val localColors = LocalMyKSuiteColors.current Card( modifier = modifier, shape = RoundedCornerShape(Dimens.largeCornerRadius), elevation = CardDefaults.elevatedCardElevation(defaultElevation = Dimens.cardElevation), colors = CardDefaults.elevatedCardColors(containerColor = localColors.background), + border = cardBorder(), ) { Column(modifier = Modifier.padding(Margin.Medium), verticalArrangement = Arrangement.spacedBy(Margin.Large)) { Text( @@ -266,6 +270,9 @@ private fun AdvantagesCard(modifier: Modifier = Modifier) { } } +@Composable +private fun cardBorder() = if (isSystemInDarkTheme()) BorderStroke(1.dp, LocalMyKSuiteColors.current.cardBorderColor) else null + @Parcelize data class MyKSuiteDashboardScreenData( val myKSuiteTier: MyKSuiteTier, From 502345226ed7a3c89fff46e6f46806b51e9a0129 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Wed, 19 Feb 2025 13:28:17 +0100 Subject: [PATCH 20/21] refactor(DynamicDashboard): Use MyKSuiteTextItem for quotas also --- .../ui/screens/MyKSuiteDashboardScreen.kt | 4 +-- ...itePlusTextItem.kt => MyKSuiteTextItem.kt} | 26 +++++++++++------ .../components/ProductsStorageQuotas.kt | 29 +++++-------------- 3 files changed, 26 insertions(+), 33 deletions(-) rename MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/{MyKSuitePlusTextItem.kt => MyKSuiteTextItem.kt} (72%) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index 6c2f7c8e..26588389 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -164,8 +164,8 @@ private fun SubscriptionInfoCard( ) } else { dashboardScreenData().trialExpiryDate?.let { expiryDate -> - MyKSuitePlusTextItem( - modifier = paddedModifier, + MyKSuiteTextItem( + modifier = paddedModifier.heightIn(min = Dimens.textItemMinHeight), title = stringResource(R.string.myKSuiteDashboardTrialPeriod), value = stringResource(R.string.myKSuiteDashboardUntil, expiryDate.format(FORMAT_DATE_SIMPLE)), ) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuiteTextItem.kt similarity index 72% rename from MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt rename to MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuiteTextItem.kt index bf208ef4..19604724 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuitePlusTextItem.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuiteTextItem.kt @@ -17,34 +17,42 @@ */ package com.infomaniak.core.myksuite.ui.screens.components -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview import com.infomaniak.core.FORMAT_DATE_SIMPLE import com.infomaniak.core.format import com.infomaniak.core.myksuite.R -import com.infomaniak.core.myksuite.ui.theme.Dimens import com.infomaniak.core.myksuite.ui.theme.LocalMyKSuiteColors import com.infomaniak.core.myksuite.ui.theme.Margin import com.infomaniak.core.myksuite.ui.theme.MyKSuiteTheme +import com.infomaniak.core.myksuite.ui.theme.Typography import java.util.Date @Composable -internal fun MyKSuitePlusTextItem(modifier: Modifier = Modifier, title: String, value: String) { +internal fun MyKSuiteTextItem( + modifier: Modifier = Modifier, + title: String, + value: String, + valueStyle: TextStyle = Typography.bodyRegular, +) { + val localColors = LocalMyKSuiteColors.current Row( - modifier = modifier - .heightIn(min = Dimens.textItemMinHeight) - .fillMaxWidth(), + modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { - Text(text = title) - Text(text = value, color = LocalMyKSuiteColors.current.secondaryTextColor) + Text(text = title, color = localColors.primaryTextColor) + Text(text = value, color = localColors.secondaryTextColor, style = valueStyle) } } @@ -53,7 +61,7 @@ internal fun MyKSuitePlusTextItem(modifier: Modifier = Modifier, title: String, private fun Preview() { MyKSuiteTheme { Surface { - MyKSuitePlusTextItem( + MyKSuiteTextItem( modifier = Modifier.padding(horizontal = Margin.Medium), title = stringResource(R.string.myKSuiteDashboardTrialPeriod), value = stringResource(R.string.myKSuiteDashboardUntil, Date().format(FORMAT_DATE_SIMPLE)), diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt index ca4791d3..468f9827 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt @@ -22,9 +22,7 @@ import android.os.Parcelable import androidx.compose.foundation.layout.* import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.res.stringResource @@ -32,7 +30,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.infomaniak.core.myksuite.R import com.infomaniak.core.myksuite.ui.components.MyKSuiteTier -import com.infomaniak.core.myksuite.ui.components.WeightOneSpacer import com.infomaniak.core.myksuite.ui.theme.LocalMyKSuiteColors import com.infomaniak.core.myksuite.ui.theme.Margin import com.infomaniak.core.myksuite.ui.theme.MyKSuiteTheme @@ -51,28 +48,16 @@ internal fun ProductsStorageQuotas( } @Composable -private fun ProductStorageQuota( - modifier: Modifier = Modifier, - myKSuiteTier: MyKSuiteTier, - product: KSuiteProductsWithQuotas, -) { +private fun ProductStorageQuota(myKSuiteTier: MyKSuiteTier, product: KSuiteProductsWithQuotas) { val localColors = LocalMyKSuiteColors.current val isUnlimitedMail = myKSuiteTier == MyKSuiteTier.Plus && product is KSuiteProductsWithQuotas.Mail - Column(modifier) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = product.displayName, - style = Typography.bodyRegular, - color = localColors.primaryTextColor, - ) - WeightOneSpacer(minWidth = Margin.Medium) - Text( - text = computeQuotasString(isUnlimitedMail, product), - style = Typography.bodySmallRegular, - color = localColors.secondaryTextColor, - ) - } + Column { + MyKSuiteTextItem( + title = product.displayName, + value = computeQuotasString(isUnlimitedMail, product), + valueStyle = Typography.bodySmallRegular, + ) if (!isUnlimitedMail) { Spacer(Modifier.height(Margin.Mini)) From cac048f0a1c1fc3ce443bfc64ee7e2c365329a4d Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Wed, 19 Feb 2025 13:28:33 +0100 Subject: [PATCH 21/21] chore(DynamicDashboard): Clean code --- .../java/com/infomaniak/core/myksuite/ui/data/MyKSuiteData.kt | 2 +- .../java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt | 1 - .../core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt | 2 +- .../core/myksuite/ui/views/MyKSuiteDashboardFragment.kt | 4 ++-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteData.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteData.kt index 64faf244..0b7dd26f 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteData.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteData.kt @@ -64,5 +64,5 @@ data class MyKSuiteData( get() = kSuitePack.type == KSuitePack.KSuitePackType.MY_KSUITE_PLUS || kSuitePack.type == KSuitePack.KSuitePackType.MY_KSUITE_PLUS_DRIVE_SOLO - inline val trialExpiryDate get() = trialExpiryAt?.let { Date(it * 1000) } + inline val trialExpiryDate get() = trialExpiryAt?.let { Date(it * 1_000) } } diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt index f2881b92..e311bf53 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt @@ -21,7 +21,6 @@ object ApiRoutes { const val MANAGER_URL = "https://manager.infomaniak.com/v3/ng/home" - private const val PREPROD_BASE_URL = "https://api.staging-myksuite.dev.infomaniak.ch" private const val BASE_URL = "https://api.infomaniak.com" fun myKSuiteData() = "$BASE_URL/1/my_ksuite/current?with=drive,mail,pack,can_trial,has_auto_renew" diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt index 26588389..686d6d14 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt @@ -170,7 +170,7 @@ private fun SubscriptionInfoCard( value = stringResource(R.string.myKSuiteDashboardUntil, expiryDate.format(FORMAT_DATE_SIMPLE)), ) } - Spacer(Modifier.height(Margin.Medium)) + Spacer(Modifier.height(Margin.Large)) InformationBlock( modifier = paddedModifier, text = stringResource(R.string.myKSuiteManageSubscriptionDescription), diff --git a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt index f790a30f..ffcb9e59 100644 --- a/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt +++ b/MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt @@ -32,7 +32,7 @@ import com.infomaniak.core.myksuite.ui.screens.MyKSuiteDashboardScreenData open class MyKSuiteDashboardFragment : Fragment() { private val navigationArgs: MyKSuiteDashboardFragmentArgs by navArgs() - private lateinit var composeView: ComposeView + private var composeView: ComposeView? = null private val onClose: () -> Unit by lazy { { this@MyKSuiteDashboardFragment.findNavController().popBackStack() } } @@ -47,6 +47,6 @@ open class MyKSuiteDashboardFragment : Fragment() { } protected fun resetContent(dashboardData: MyKSuiteDashboardScreenData) { - composeView.setContent { MyKSuiteDashboardScreen(dashboardScreenData = { dashboardData }, onClose = onClose) } + composeView?.setContent { MyKSuiteDashboardScreen(dashboardScreenData = { dashboardData }, onClose = onClose) } } }