diff --git a/androidApp/dependencies/demoDebugRuntimeClasspath.txt b/androidApp/dependencies/demoDebugRuntimeClasspath.txt index 9cba9db29..186c7cb05 100644 --- a/androidApp/dependencies/demoDebugRuntimeClasspath.txt +++ b/androidApp/dependencies/demoDebugRuntimeClasspath.txt @@ -295,6 +295,9 @@ org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.8.3-rc01 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.8.3 +org.jetbrains.androidx.navigation:navigation-common:2.8.0-alpha10 +org.jetbrains.androidx.navigation:navigation-compose:2.8.0-alpha10 +org.jetbrains.androidx.navigation:navigation-runtime:2.8.0-alpha10 org.jetbrains.androidx.savedstate:savedstate:1.2.2 org.jetbrains.compose.animation:animation-core:1.7.0-rc01 org.jetbrains.compose.animation:animation:1.7.0-rc01 diff --git a/androidApp/dependencies/demoReleaseRuntimeClasspath.txt b/androidApp/dependencies/demoReleaseRuntimeClasspath.txt index e793251a4..ede622e16 100644 --- a/androidApp/dependencies/demoReleaseRuntimeClasspath.txt +++ b/androidApp/dependencies/demoReleaseRuntimeClasspath.txt @@ -290,6 +290,9 @@ org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.8.3-rc01 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.8.3 +org.jetbrains.androidx.navigation:navigation-common:2.8.0-alpha10 +org.jetbrains.androidx.navigation:navigation-compose:2.8.0-alpha10 +org.jetbrains.androidx.navigation:navigation-runtime:2.8.0-alpha10 org.jetbrains.androidx.savedstate:savedstate:1.2.2 org.jetbrains.compose.animation:animation-core:1.7.0-rc01 org.jetbrains.compose.animation:animation:1.7.0-rc01 diff --git a/androidApp/dependencies/prodDebugRuntimeClasspath.txt b/androidApp/dependencies/prodDebugRuntimeClasspath.txt index 9cba9db29..186c7cb05 100644 --- a/androidApp/dependencies/prodDebugRuntimeClasspath.txt +++ b/androidApp/dependencies/prodDebugRuntimeClasspath.txt @@ -295,6 +295,9 @@ org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.8.3-rc01 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.8.3 +org.jetbrains.androidx.navigation:navigation-common:2.8.0-alpha10 +org.jetbrains.androidx.navigation:navigation-compose:2.8.0-alpha10 +org.jetbrains.androidx.navigation:navigation-runtime:2.8.0-alpha10 org.jetbrains.androidx.savedstate:savedstate:1.2.2 org.jetbrains.compose.animation:animation-core:1.7.0-rc01 org.jetbrains.compose.animation:animation:1.7.0-rc01 diff --git a/androidApp/dependencies/prodReleaseRuntimeClasspath.txt b/androidApp/dependencies/prodReleaseRuntimeClasspath.txt index e793251a4..ede622e16 100644 --- a/androidApp/dependencies/prodReleaseRuntimeClasspath.txt +++ b/androidApp/dependencies/prodReleaseRuntimeClasspath.txt @@ -290,6 +290,9 @@ org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.8.3-rc01 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.8.3 +org.jetbrains.androidx.navigation:navigation-common:2.8.0-alpha10 +org.jetbrains.androidx.navigation:navigation-compose:2.8.0-alpha10 +org.jetbrains.androidx.navigation:navigation-runtime:2.8.0-alpha10 org.jetbrains.androidx.savedstate:savedstate:1.2.2 org.jetbrains.compose.animation:animation-core:1.7.0-rc01 org.jetbrains.compose.animation:animation:1.7.0-rc01 diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 3b2ac907b..8e9e84b75 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -8,23 +8,40 @@ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md */ plugins { - alias(libs.plugins.mifos.android.library) - alias(libs.plugins.mifos.android.library.compose) + alias(libs.plugins.mifos.kmp.library) + alias(libs.plugins.jetbrainsCompose) + alias(libs.plugins.compose.compiler) } android { defaultConfig { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } - namespace = "org.mifos.mobile.core" + namespace = "org.mifos.mobile.core.ui" } -dependencies { - api(projects.core.designsystem) -// api(projects.core.model) - api(projects.core.common) - api(libs.androidx.metrics) - - testImplementation(libs.androidx.compose.ui.test) - androidTestImplementation(libs.bundles.androidx.compose.ui.test) +kotlin{ + sourceSets{ + androidMain.dependencies { + api(libs.androidx.metrics) + implementation(libs.androidx.compose.runtime) + implementation(libs.accompanist.pager) + } + commonMain.dependencies { + api(projects.core.designsystem) + api(libs.kotlinx.datetime) + implementation(libs.jb.composeViewmodel) + implementation(libs.jb.lifecycleViewmodel) + implementation(libs.jb.lifecycleViewmodelSavedState) + implementation(libs.coil.kt) + implementation(libs.coil.kt.compose) + implementation(compose.material3) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + implementation(libs.jb.composeNavigation) + implementation(libs.filekit.compose) + implementation(libs.filekit.core) + } + } } + diff --git a/core/ui/src/main/AndroidManifest.xml b/core/ui/src/androidMain/AndroidManifest.xml similarity index 100% rename from core/ui/src/main/AndroidManifest.xml rename to core/ui/src/androidMain/AndroidManifest.xml diff --git a/core/ui/src/androidMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.android.kt b/core/ui/src/androidMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.android.kt new file mode 100644 index 000000000..7122f00d9 --- /dev/null +++ b/core/ui/src/androidMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.android.kt @@ -0,0 +1,142 @@ +/* + * Copyright 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md + */ +package org.mifos.mobile.core.ui.utils + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.Matrix +import android.graphics.Paint +import android.util.Log +import java.io.ByteArrayOutputStream + +actual object ImageUtil { + actual val DEFAULT_MAX_WIDTH: Float = 816f + actual val DEFAULT_MAX_HEIGHT: Float = 612f + + actual fun compressImage( + decodedBytes: ByteArray, + maxWidth: Float, + maxHeight: Float, + ): ByteArray { + val options = BitmapFactory.Options().apply { + inJustDecodeBounds = true + } + + BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options) + + val (actualWidth, actualHeight) = calculateActualDimensions(options, maxWidth, maxHeight) + + options.apply { + inJustDecodeBounds = false + inSampleSize = calculateInSampleSize(this, actualWidth, actualHeight) + inTempStorage = ByteArray(16 * 1024) + } + + val bmp = try { + BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options) + } catch (e: OutOfMemoryError) { + Log.e(this::class.java.simpleName, "OutOfMemoryError while decoding bitmap", e) + return ByteArray(0) + } + + val scaledBitmap = try { + createScaledBitmap(bmp, actualWidth, actualHeight, options) + } catch (e: OutOfMemoryError) { + Log.e(this::class.java.simpleName, "OutOfMemoryError while scaling bitmap", e) + bmp + } + + val byteArrayOutputStream = ByteArrayOutputStream() + scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 85, byteArrayOutputStream) + return byteArrayOutputStream.toByteArray() + } + + private fun calculateActualDimensions( + options: BitmapFactory.Options, + maxWidth: Float, + maxHeight: Float, + ): Pair { + var actualWidth = options.outWidth + var actualHeight = options.outHeight + val imgRatio = actualWidth.toFloat() / actualHeight + val maxRatio = maxWidth / maxHeight + + if (actualHeight > maxHeight || actualWidth > maxWidth) { + when { + imgRatio < maxRatio -> { + actualHeight = maxHeight.toInt() + actualWidth = (maxHeight * imgRatio).toInt() + } + + imgRatio > maxRatio -> { + actualWidth = maxWidth.toInt() + actualHeight = (maxWidth / imgRatio).toInt() + } + + else -> { + actualHeight = maxHeight.toInt() + actualWidth = maxWidth.toInt() + } + } + } + + return Pair(actualWidth, actualHeight) + } + + private fun calculateInSampleSize( + options: BitmapFactory.Options, + reqWidth: Int, + reqHeight: Int, + ): Int { + val (height, width) = options.run { outHeight to outWidth } + var inSampleSize = 1 + + if (height > reqHeight || width > reqWidth) { + val halfHeight = height / 2 + val halfWidth = width / 2 + + while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { + inSampleSize *= 2 + } + } + + return inSampleSize + } + + private fun createScaledBitmap( + bmp: Bitmap, + actualWidth: Int, + actualHeight: Int, + options: BitmapFactory.Options, + ): Bitmap { + val scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888) + val ratioX = actualWidth / options.outWidth.toFloat() + val ratioY = actualHeight / options.outHeight.toFloat() + val middleX = actualWidth / 2f + val middleY = actualHeight / 2f + + val scaleMatrix = Matrix().apply { + setScale(ratioX, ratioY, middleX, middleY) + } + + Canvas(scaledBitmap).apply { + setMatrix(scaleMatrix) + drawBitmap( + bmp, + middleX - bmp.width / 2, + middleY - bmp.height / 2, + Paint(Paint.FILTER_BITMAP_FLAG), + ) + } + + return scaledBitmap + } +} diff --git a/core/ui/src/commonMain/composeResources/drawable/core_ui_money_in.png b/core/ui/src/commonMain/composeResources/drawable/core_ui_money_in.png new file mode 100644 index 000000000..9744fcf09 Binary files /dev/null and b/core/ui/src/commonMain/composeResources/drawable/core_ui_money_in.png differ diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/commonMain/composeResources/values/strings.xml similarity index 89% rename from core/ui/src/main/res/values/strings.xml rename to core/ui/src/commonMain/composeResources/values/strings.xml index 2f7ca73f0..fd2ecc432 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/commonMain/composeResources/values/strings.xml @@ -13,4 +13,5 @@ Retry No Data Something went wrong + Core Common Working \ No newline at end of file diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/AboutUsItemCard.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/AboutUsItemCard.kt similarity index 80% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/AboutUsItemCard.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/AboutUsItemCard.kt index 1ffca5488..da0914d66 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/AboutUsItemCard.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/AboutUsItemCard.kt @@ -9,8 +9,6 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -19,26 +17,28 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @Composable fun AboutUsItemCard( title: String, - @StringRes subtitle: Int?, - @DrawableRes iconUrl: Int?, + subtitle: StringResource?, + iconUrl: DrawableResource?, modifier: Modifier = Modifier, ) { Row( modifier = modifier.padding(16.dp), ) { - iconUrl?.let { painterResource(id = it) }?.let { + iconUrl?.let { painterResource(it) }?.let { Image( painter = it, - contentDescription = null, + contentDescription = "About Us Icon URL", modifier = Modifier.padding(end = 8.dp), ) } @@ -50,7 +50,7 @@ fun AboutUsItemCard( ) if (subtitle != null) { Text( - text = stringResource(id = subtitle), + text = stringResource(subtitle), style = MaterialTheme.typography.bodyLarge, modifier = Modifier.padding(end = 8.dp), ) diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/EmptyDataView.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/EmptyDataView.kt similarity index 73% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/EmptyDataView.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/EmptyDataView.kt index 3b94a30a4..afb2192bc 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/EmptyDataView.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/EmptyDataView.kt @@ -9,8 +9,6 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -23,24 +21,21 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import org.mifos.mobile.core.R -import org.mifos.mobile.core.designsystem.icons.MifosIcons +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.no_internet +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource +import org.mifos.mobile.core.designsystem.icon.MifosIcons import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @Composable fun EmptyDataView( - @StringRes - error: Int, + error: StringResource, modifier: Modifier = Modifier.fillMaxSize(), - @DrawableRes - icon: Int? = null, + icon: ImageVector = MifosIcons.Error, errorString: String? = null, ) { Column( @@ -52,19 +47,15 @@ fun EmptyDataView( modifier = Modifier .size(100.dp) .padding(bottom = 12.dp), - imageVector = if (icon != null) { - ImageVector.vectorResource(id = icon) - } else { - MifosIcons.Error - }, + imageVector = icon, contentDescription = null, tint = MaterialTheme.colorScheme.onSecondary, ) Text( modifier = Modifier.padding(horizontal = 20.dp), - text = errorString ?: stringResource(id = error), - style = TextStyle(fontSize = 20.sp), + text = errorString ?: stringResource(error), + style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSecondary, textAlign = TextAlign.Center, ) @@ -78,7 +69,7 @@ private fun EmptyDataViewPreview( ) { MifosMobileTheme { EmptyDataView( - error = R.string.no_internet, + error = Res.string.no_internet, modifier = modifier, ) } diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/FaqItemHolder.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/FaqItemHolder.kt similarity index 89% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/FaqItemHolder.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/FaqItemHolder.kt index 849ee2143..4f967cf67 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/FaqItemHolder.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/FaqItemHolder.kt @@ -15,7 +15,6 @@ import androidx.compose.animation.core.spring import androidx.compose.animation.expandVertically import androidx.compose.animation.fadeIn import androidx.compose.foundation.clickable -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -28,9 +27,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.scale -import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import org.mifos.mobile.core.designsystem.icons.MifosIcons +import org.mifos.mobile.core.designsystem.icon.MifosIcons import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @@ -59,7 +57,6 @@ fun FaqItemHolder( Text( text = question.orEmpty(), style = MaterialTheme.typography.bodyMedium, - color = if (isSystemInDarkTheme()) Color.White else Color.Black, modifier = Modifier .fillMaxWidth() .weight(1f), @@ -68,7 +65,6 @@ fun FaqItemHolder( Icon( imageVector = MifosIcons.ArrowDropDown, contentDescription = "drop down", - tint = if (isSystemInDarkTheme()) Color.White else Color.Gray, modifier = Modifier .scale(1f, if (isSelected) -1f else 1f), ) @@ -85,7 +81,6 @@ fun FaqItemHolder( Text( text = answer.orEmpty(), style = MaterialTheme.typography.bodyMedium, - color = if (isSystemInDarkTheme()) Color.White else Color.Black, modifier = Modifier .fillMaxWidth() .padding(bottom = 8.dp), diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MFStepProcess.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MFStepProcess.kt similarity index 100% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MFStepProcess.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MFStepProcess.kt diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosAlertDialog.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosAlertDialog.kt similarity index 91% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosAlertDialog.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosAlertDialog.kt index c87a49027..8b5115e94 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosAlertDialog.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosAlertDialog.kt @@ -15,7 +15,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import org.mifos.mobile.core.designsystem.components.MifosTextButton +import org.mifos.mobile.core.designsystem.component.MifosTextButton import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @@ -42,13 +42,13 @@ fun MifosAlertDialog( onDismissRequest = onDismissRequest, confirmButton = { MifosTextButton( - text = confirmationText, + text = { Text(text = confirmationText) }, onClick = onConfirmation, ) }, dismissButton = { MifosTextButton( - text = dismissText, + text = { Text(text = dismissText) }, onClick = onDismissRequest, ) }, diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosCheckBox.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosCheckBox.kt similarity index 100% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosCheckBox.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosCheckBox.kt diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosDropDownTextField.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosDropDownTextField.kt similarity index 88% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosDropDownTextField.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosDropDownTextField.kt index 4c03cffd6..095d62c0c 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosDropDownTextField.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosDropDownTextField.kt @@ -9,7 +9,6 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.StringRes import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.layout.Box @@ -31,9 +30,12 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import org.mifos.mobile.core.designsystem.icons.MifosIcons +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.retry +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource +import org.mifos.mobile.core.designsystem.icon.MifosIcons import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @@ -41,8 +43,7 @@ import org.mifos.mobile.core.ui.utils.DevicePreviews fun MifosDropDownTextField( onClick: (Int, String) -> Unit, modifier: Modifier = Modifier, - @StringRes - labelResId: Int = 0, + labelResId: StringResource, isEnabled: Boolean = true, supportingText: String? = null, error: Boolean = false, @@ -63,7 +64,7 @@ fun MifosDropDownTextField( OutlinedTextField( value = selectedOption ?: "", onValueChange = { }, - label = { Text(stringResource(id = labelResId)) }, + label = { Text(stringResource(labelResId)) }, interactionSource = interactionSource, modifier = Modifier .fillMaxWidth(), @@ -79,7 +80,11 @@ fun MifosDropDownTextField( } else { MifosIcons.ArrowDropDown }, - contentDescription = null, + contentDescription = if (expanded) { + "Arrow Up Icon" + } else { + "Arrow Down Icon" + }, ) }, ) @@ -108,8 +113,7 @@ fun MifosDropDownTextField( fun MifosDropDownDoubleTextField( onClick: (Int, Pair) -> Unit, modifier: Modifier = Modifier, - @StringRes - labelResId: Int = 0, + labelResId: StringResource, isEnabled: Boolean = true, supportingText: String? = null, error: Boolean = false, @@ -130,7 +134,7 @@ fun MifosDropDownDoubleTextField( OutlinedTextField( value = selectedOption ?: "", onValueChange = { }, - label = { Text(stringResource(id = labelResId)) }, + label = { Text(stringResource(labelResId)) }, interactionSource = interactionSource, modifier = Modifier .fillMaxWidth(), @@ -146,7 +150,11 @@ fun MifosDropDownDoubleTextField( } else { MifosIcons.ArrowDropDown }, - contentDescription = null, + contentDescription = if (expanded) { + "Arrow Up Icon" + } else { + "Arrow Down Icon" + }, ) }, ) @@ -186,7 +194,7 @@ private fun MifosDropDownTextFieldPreview( MifosDropDownTextField( onClick = { _, _ -> }, modifier = modifier, - labelResId = 0, + labelResId = Res.string.retry, isEnabled = true, supportingText = null, error = false, @@ -205,7 +213,7 @@ private fun MifosDropDownDoubleTextFieldPreview( MifosDropDownDoubleTextField( onClick = { _, _ -> }, modifier = modifier, - labelResId = 0, + labelResId = Res.string.retry, isEnabled = true, supportingText = null, error = false, diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosErrorComponent.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosErrorComponent.kt similarity index 86% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosErrorComponent.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosErrorComponent.kt index ce3555a40..4975c0f2d 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosErrorComponent.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosErrorComponent.kt @@ -25,13 +25,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import org.mifos.mobile.core.R -import org.mifos.mobile.core.designsystem.icons.MifosIcons +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.no_data +import mifos_mobile.core.ui.generated.resources.no_internet +import mifos_mobile.core.ui.generated.resources.retry +import mifos_mobile.core.ui.generated.resources.something_went_wrong +import org.jetbrains.compose.resources.stringResource +import org.mifos.mobile.core.designsystem.icon.MifosIcons import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @@ -74,12 +78,12 @@ fun NoInternetComponent( .size(100.dp) .padding(bottom = 12.dp), imageVector = MifosIcons.WifiOff, - contentDescription = null, + contentDescription = "Wifi Icon", tint = MaterialTheme.colorScheme.onSecondary, ) Text( - text = stringResource(id = R.string.no_internet), + text = stringResource(Res.string.no_internet), style = TextStyle(fontSize = 20.sp), color = MaterialTheme.colorScheme.onSecondary, ) @@ -88,7 +92,7 @@ fun NoInternetComponent( if (isRetryEnabled) { FilledTonalButton(onClick = { onRetry.invoke() }) { - Text(text = stringResource(id = R.string.retry)) + Text(text = stringResource(Res.string.retry)) } } } @@ -112,16 +116,16 @@ fun EmptyDataComponent( .size(100.dp) .padding(bottom = 12.dp), imageVector = MifosIcons.Info, - contentDescription = null, + contentDescription = "Info Icon", tint = MaterialTheme.colorScheme.onSecondary, ) Text( modifier = Modifier.padding(horizontal = 20.dp), text = message ?: if (isEmptyData) { - stringResource(id = R.string.no_data) + stringResource(Res.string.no_data) } else { - stringResource(id = R.string.something_went_wrong) + stringResource(Res.string.something_went_wrong) }, style = TextStyle(fontSize = 20.sp), color = MaterialTheme.colorScheme.onSecondary, @@ -133,7 +137,7 @@ fun EmptyDataComponent( modifier = Modifier.padding(top = 8.dp), onClick = { onRetry.invoke() }, ) { - Text(text = stringResource(id = R.string.retry)) + Text(text = stringResource(Res.string.retry)) } } } @@ -156,13 +160,13 @@ fun EmptyDataComponentWithModifiedMessageAndIcon( .size(100.dp) .padding(bottom = 12.dp), imageVector = if (isEmptyData) icon else MifosIcons.Info, - contentDescription = null, + contentDescription = "Info Icon", tint = MaterialTheme.colorScheme.onSecondary, ) Text( modifier = Modifier.padding(horizontal = 20.dp), - text = if (isEmptyData) message else stringResource(id = R.string.something_went_wrong), + text = if (isEmptyData) message else stringResource(Res.string.something_went_wrong), style = TextStyle(fontSize = 20.sp), color = MaterialTheme.colorScheme.onSecondary, textAlign = TextAlign.Center, diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosHiddenTextRow.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosHiddenTextRow.kt similarity index 84% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosHiddenTextRow.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosHiddenTextRow.kt index 657802e15..983413c0a 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosHiddenTextRow.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosHiddenTextRow.kt @@ -26,9 +26,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.core_ui_money_in +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.painterResource import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @@ -38,8 +41,8 @@ fun MifosHiddenTextRow( hiddenText: String, hiddenColor: Color, hidingText: String, - visibilityIconId: Int, - visibilityOffIconId: Int, + visibilityIconId: DrawableResource, + visibilityOffIconId: DrawableResource, onClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ -74,9 +77,9 @@ fun MifosHiddenTextRow( ) { Icon( painter = if (isHidden) { - painterResource(id = visibilityIconId) + painterResource(visibilityIconId) } else { - painterResource(id = visibilityOffIconId) + painterResource(visibilityOffIconId) }, contentDescription = "Show or hide total amount", tint = MaterialTheme.colorScheme.primary, @@ -96,8 +99,8 @@ private fun MifosHiddenTextRowPreview( hiddenText = "Hidden Text", hiddenColor = MaterialTheme.colorScheme.primary, hidingText = "Hiding Text", - visibilityIconId = 0, - visibilityOffIconId = 0, + visibilityIconId = Res.drawable.core_ui_money_in, + visibilityOffIconId = Res.drawable.core_ui_money_in, onClick = {}, modifier = modifier, ) diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosItemCard.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosItemCard.kt similarity index 100% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosItemCard.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosItemCard.kt diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosLinkText.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosLinkText.kt similarity index 100% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosLinkText.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosLinkText.kt diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosMobileIcon.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosMobileIcon.kt similarity index 75% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosMobileIcon.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosMobileIcon.kt index 7b6439a54..d06afa431 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosMobileIcon.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosMobileIcon.kt @@ -9,7 +9,6 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.DrawableRes import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -17,22 +16,23 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import org.mifos.mobile.core.R +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.core_ui_money_in +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.painterResource import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @Composable fun MifosMobileIcon( - @DrawableRes - id: Int, + mobileIcon: DrawableResource, modifier: Modifier = Modifier, ) { Column(modifier) { Image( - painter = painterResource(id = id), - contentDescription = null, + painter = painterResource(mobileIcon), + contentDescription = "Mobile Icon", modifier = Modifier .fillMaxWidth() .align(Alignment.CenterHorizontally) @@ -48,7 +48,7 @@ private fun MifosMobileIconPreview( ) { MifosMobileTheme { MifosMobileIcon( - id = R.drawable.core_common_circular_background, + mobileIcon = Res.drawable.core_ui_money_in, modifier = modifier, ) } diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt similarity index 92% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt index 88041d598..a00356768 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosProgressIndicator.kt @@ -18,9 +18,9 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview +import org.mifos.mobile.core.ui.utils.DevicePreviews -@Preview(showSystemUi = true) +@DevicePreviews @Composable fun MifosProgressIndicator( modifier: Modifier = Modifier.fillMaxSize(), @@ -34,7 +34,7 @@ fun MifosProgressIndicator( } } -@Preview(showSystemUi = true) +@DevicePreviews @Composable fun MifosProgressIndicatorOverlay( modifier: Modifier = Modifier.fillMaxSize(), diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosRadioButtonAlertDialog.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosRadioButtonAlertDialog.kt similarity index 87% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosRadioButtonAlertDialog.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosRadioButtonAlertDialog.kt index bd161c328..0a0faf8cf 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosRadioButtonAlertDialog.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosRadioButtonAlertDialog.kt @@ -9,7 +9,6 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.StringRes import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -26,17 +25,18 @@ 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 org.mifos.mobile.core.R +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.core_common_working +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme +import org.mifos.mobile.core.ui.utils.DevicePreviews @OptIn(ExperimentalMaterial3Api::class) @Composable fun MifosRadioButtonDialog( - @StringRes - titleResId: Int, + titleResId: StringResource, selectedItem: String, items: Array, selectItem: (item: String, index: Int) -> Unit, @@ -49,7 +49,7 @@ fun MifosRadioButtonDialog( ) { Card { Column(modifier = Modifier.padding(20.dp)) { - Text(text = stringResource(id = titleResId)) + Text(text = stringResource(titleResId)) LazyColumn( modifier = Modifier .fillMaxWidth() @@ -84,12 +84,12 @@ fun MifosRadioButtonDialog( } } -@Preview +@DevicePreviews @Composable fun PreviewRadioButtonDialog() { MifosMobileTheme { MifosRadioButtonDialog( - titleResId = R.string.core_common_working, + titleResId = Res.string.core_common_working, items = arrayOf("1", "2", "3"), selectedItem = "1", onDismissRequest = { }, diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosRoundIcon.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosRoundIcon.kt similarity index 77% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosRoundIcon.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosRoundIcon.kt index 2dd12fb2a..e2f722f18 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosRoundIcon.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosRoundIcon.kt @@ -9,7 +9,6 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.DrawableRes import androidx.compose.foundation.Image import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.CircleShape @@ -18,18 +17,18 @@ import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import org.mifos.mobile.core.R +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.core_ui_money_in +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.painterResource import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @Composable fun MifosRoundIcon( - @DrawableRes - iconId: Int, + iconId: DrawableResource, modifier: Modifier = Modifier, - contentDescription: String? = null, ) { Surface( color = MaterialTheme.colorScheme.surfaceVariant, @@ -38,8 +37,8 @@ fun MifosRoundIcon( ) { Image( modifier = Modifier.padding(all = 6.dp), - painter = painterResource(id = iconId), - contentDescription = contentDescription, + painter = painterResource(iconId), + contentDescription = "Icon", ) } } @@ -51,7 +50,7 @@ private fun MifosRoundIconPreview( ) { MifosMobileTheme { MifosRoundIcon( - iconId = R.drawable.core_common_circular_background, + iconId = Res.drawable.core_ui_money_in, modifier = modifier, ) } diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosTextButtonWithTopDrawable.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosTextButtonWithTopDrawable.kt similarity index 75% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosTextButtonWithTopDrawable.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosTextButtonWithTopDrawable.kt index 510162bdf..30b442dc9 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosTextButtonWithTopDrawable.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosTextButtonWithTopDrawable.kt @@ -9,32 +9,30 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.StringRes import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme 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.vector.ImageVector -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import org.mifos.mobile.core.R -import org.mifos.mobile.core.designsystem.components.MifosTextButton -import org.mifos.mobile.core.designsystem.icons.MifosIcons +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.core_common_working +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource +import org.mifos.mobile.core.designsystem.component.MifosTextButton +import org.mifos.mobile.core.designsystem.icon.MifosIcons import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @Composable fun MifosTextButtonWithTopDrawable( - @StringRes - textResourceId: Int, + textResourceId: StringResource, icon: ImageVector, onClick: () -> Unit, contentDescription: String?, @@ -43,9 +41,6 @@ fun MifosTextButtonWithTopDrawable( MifosTextButton( onClick = onClick, modifier = modifier, - colors = ButtonDefaults.textButtonColors( - contentColor = MaterialTheme.colorScheme.primary, - ), content = { Column( horizontalAlignment = Alignment.CenterHorizontally, @@ -57,7 +52,7 @@ fun MifosTextButtonWithTopDrawable( ) Spacer(modifier = Modifier.height(4.dp)) Text( - text = stringResource(id = textResourceId), + text = stringResource(textResourceId), textAlign = TextAlign.Center, ) } @@ -72,10 +67,10 @@ private fun MifosTextButtonWithTopDrawablePreview( ) { MifosMobileTheme { MifosTextButtonWithTopDrawable( - textResourceId = R.string.core_common_working, + textResourceId = Res.string.core_common_working, icon = MifosIcons.Add, onClick = {}, - contentDescription = null, + contentDescription = "Add Icon", modifier = modifier, ) } diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosTextUserImage.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosTextUserImage.kt similarity index 100% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosTextUserImage.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosTextUserImage.kt diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosTexts.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosTexts.kt similarity index 93% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosTexts.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosTexts.kt index 1bd0e0f92..c3aead792 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosTexts.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosTexts.kt @@ -9,7 +9,6 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.DrawableRes import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -25,11 +24,13 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import org.mifos.mobile.core.R +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.core_ui_money_in +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.painterResource import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @@ -89,8 +90,7 @@ fun MifosTextTitleDescDoubleLine( fun MifosTextTitleDescDrawableSingleLine( title: String, description: String, - @DrawableRes - imageResId: Int, + imageResId: DrawableResource, modifier: Modifier = Modifier, imageSize: Dp = 14.dp, onDrawableClick: () -> Unit = {}, @@ -115,8 +115,8 @@ fun MifosTextTitleDescDrawableSingleLine( ) Spacer(modifier = Modifier.width(5.dp)) Image( - painter = painterResource(id = imageResId), - contentDescription = null, + painter = painterResource(imageResId), + contentDescription = "Image", modifier = Modifier .size(imageSize) .clickable { onDrawableClick() }, @@ -191,7 +191,7 @@ private fun MifosTextTitleDescDrawableSingleLinePreview( MifosTextTitleDescDrawableSingleLine( title = "MifosTextTitleDescDrawableSingleLine Title", description = "MifosTextTitleDescDrawableSingleLine Description", - imageResId = R.drawable.core_common_circular_background, + imageResId = Res.drawable.core_ui_money_in, modifier = modifier, ) } diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosTitleSearchCard.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosTitleSearchCard.kt similarity index 86% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosTitleSearchCard.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosTitleSearchCard.kt index b390b7fd6..59f84ab13 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosTitleSearchCard.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosTitleSearchCard.kt @@ -9,7 +9,6 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.StringRes import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -24,21 +23,22 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue 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.text.input.TextFieldValue import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import org.mifos.mobile.core.R -import org.mifos.mobile.core.designsystem.components.MifosSearchTextField -import org.mifos.mobile.core.designsystem.icons.MifosIcons +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.core_common_working +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource +import org.mifos.mobile.core.designsystem.component.MifosSearchTextField +import org.mifos.mobile.core.designsystem.icon.MifosIcons import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @Composable fun MifosTitleSearchCard( - @StringRes - titleResourceId: Int, + titleResourceId: StringResource, searchQuery: (String) -> Unit, onSearchDismiss: () -> Unit, modifier: Modifier = Modifier, @@ -56,7 +56,7 @@ fun MifosTitleSearchCard( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = stringResource(id = titleResourceId), + text = stringResource(titleResourceId), color = MaterialTheme.colorScheme.onSurface, style = TextStyle(fontSize = 24.sp), modifier = Modifier.weight(1f), @@ -101,7 +101,7 @@ private fun MifosTitleSearchCardPreview( ) { MifosMobileTheme { MifosTitleSearchCard( - titleResourceId = R.string.core_common_working, + titleResourceId = Res.string.core_common_working, searchQuery = {}, onSearchDismiss = {}, modifier = modifier, diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosUserImage.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosUserImage.kt similarity index 91% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosUserImage.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosUserImage.kt index f1b6b9c5f..3e1812756 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosUserImage.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MifosUserImage.kt @@ -9,7 +9,6 @@ */ package org.mifos.mobile.core.ui.component -import android.graphics.Bitmap import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.shape.CircleShape @@ -17,14 +16,14 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.layout.ContentScale import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @Composable fun MifosUserImage( - bitmap: Bitmap?, + bitmap: ImageBitmap?, modifier: Modifier = Modifier, username: String? = null, ) { @@ -38,7 +37,7 @@ fun MifosUserImage( modifier = modifier .clip(CircleShape) .background(MaterialTheme.colorScheme.primary), - bitmap = bitmap.asImageBitmap(), + bitmap = bitmap, contentDescription = "Profile Image", contentScale = ContentScale.Crop, ) diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MonitorListItemWithIcon.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MonitorListItemWithIcon.kt similarity index 75% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/MonitorListItemWithIcon.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MonitorListItemWithIcon.kt index faddb190d..cde46abf0 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MonitorListItemWithIcon.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/MonitorListItemWithIcon.kt @@ -9,8 +9,6 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -25,17 +23,22 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import org.mifos.mobile.core.R +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.core_common_working +import mifos_mobile.core.ui.generated.resources.core_ui_money_in +import mifos_mobile.core.ui.generated.resources.something_went_wrong +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @Composable fun MonitorListItemWithIcon( - @StringRes titleId: Int, - @StringRes subTitleId: Int, - @DrawableRes iconId: Int, + titleId: StringResource, + subTitleId: StringResource, + iconId: DrawableResource, onClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ -52,13 +55,13 @@ fun MonitorListItemWithIcon( Spacer(modifier = Modifier.width(8.dp)) Column { Text( - text = stringResource(id = titleId), + text = stringResource(titleId), style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurface, modifier = Modifier.fillMaxWidth(), ) Text( - text = stringResource(id = subTitleId), + text = stringResource(subTitleId), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurface, modifier = Modifier @@ -76,9 +79,9 @@ private fun MonitorListItemWithIconPreview( ) { MifosMobileTheme { MonitorListItemWithIcon( - titleId = R.string.core_common_working, - subTitleId = R.string.something_went_wrong, - iconId = R.drawable.core_common_circular_background, + titleId = Res.string.core_common_working, + subTitleId = Res.string.something_went_wrong, + iconId = Res.drawable.core_ui_money_in, onClick = {}, modifier = modifier, ) diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/NoInternet.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/NoInternet.kt similarity index 78% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/NoInternet.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/NoInternet.kt index 29bedc79d..eba231e29 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/NoInternet.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/NoInternet.kt @@ -9,8 +9,6 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -27,22 +25,23 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import org.mifos.mobile.core.R -import org.mifos.mobile.core.designsystem.icons.MifosIcons +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.no_internet +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource +import org.mifos.mobile.core.designsystem.icon.MifosIcons import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @Composable fun NoInternet( - @StringRes error: Int, + error: StringResource, modifier: Modifier = Modifier, isRetryEnabled: Boolean = true, - @DrawableRes icon: Int? = null, + icon: ImageVector = MifosIcons.WifiOff, retry: () -> Unit = {}, ) { Column( @@ -56,17 +55,13 @@ fun NoInternet( modifier = Modifier .size(100.dp) .padding(bottom = 12.dp), - imageVector = if (icon != null) { - ImageVector.vectorResource(id = icon) - } else { - MifosIcons.WifiOff - }, - contentDescription = null, + imageVector = icon, + contentDescription = "No Internet Icon", tint = MaterialTheme.colorScheme.onSecondary, ) Text( - text = stringResource(id = error), + text = stringResource(error), style = TextStyle(fontSize = 20.sp), color = MaterialTheme.colorScheme.onSecondary, ) @@ -87,10 +82,9 @@ private fun NoInternetPreview( ) { MifosMobileTheme { NoInternet( - error = R.string.no_internet, + error = Res.string.no_internet, modifier = modifier, isRetryEnabled = true, - icon = R.drawable.core_common_circular_background, retry = {}, ) } diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/UserProfileField.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/UserProfileField.kt similarity index 70% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/UserProfileField.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/UserProfileField.kt index 20b41deec..0894237e7 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/UserProfileField.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/UserProfileField.kt @@ -9,10 +9,7 @@ */ package org.mifos.mobile.core.ui.component -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import androidx.compose.foundation.clickable -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -22,21 +19,24 @@ import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import org.mifos.mobile.core.R +import mifos_mobile.core.ui.generated.resources.Res +import mifos_mobile.core.ui.generated.resources.core_common_working +import mifos_mobile.core.ui.generated.resources.core_ui_money_in +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.ui.utils.DevicePreviews @Composable fun UserProfileField( - @StringRes text: Int, - @DrawableRes icon: Int, + text: StringResource, + icon: DrawableResource, onClick: (() -> Unit), modifier: Modifier = Modifier, ) { @@ -48,22 +48,20 @@ fun UserProfileField( horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - text = stringResource(id = text), - color = Color(0xFF8E9099), + text = stringResource(text), style = TextStyle(fontSize = 12.sp, fontWeight = FontWeight.SemiBold), ) Icon( - painter = painterResource(id = icon), - contentDescription = null, - tint = if (isSystemInDarkTheme()) Color.White else Color.Black, + painter = painterResource(icon), + contentDescription = "User Profile Icon", ) } - HorizontalDivider(color = if (isSystemInDarkTheme()) Color(0xFF8E9099) else Color.Black) + HorizontalDivider() } @Composable fun UserProfileField( - @StringRes label: Int, + label: StringResource, value: String, modifier: Modifier = Modifier, ) { @@ -74,17 +72,15 @@ fun UserProfileField( horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - text = stringResource(id = label), - color = Color(0xFF8E9099), + text = stringResource(label), style = TextStyle(fontSize = 12.sp, fontWeight = FontWeight.SemiBold), ) Text( text = value, - color = if (isSystemInDarkTheme()) Color.White else Color.Black, style = TextStyle(fontSize = 14.sp), ) } - HorizontalDivider(color = Color(0xFF8E9099)) + HorizontalDivider() } @DevicePreviews @@ -94,8 +90,8 @@ private fun UserProfileFieldPreview( ) { MifosMobileTheme { UserProfileField( - text = R.string.core_common_working, - icon = R.drawable.core_common_circular_background, + text = Res.string.core_common_working, + icon = Res.drawable.core_ui_money_in, onClick = {}, modifier = modifier, ) @@ -109,7 +105,7 @@ private fun UserProfileFieldValuePreview( ) { MifosMobileTheme { UserProfileField( - label = R.string.core_common_working, + label = Res.string.core_common_working, value = "UserProfileFieldValue", modifier = modifier, ) diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/UserProfileTopBar.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/UserProfileTopBar.kt similarity index 71% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/component/UserProfileTopBar.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/UserProfileTopBar.kt index 1f9346992..fa9edc3e6 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/UserProfileTopBar.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/UserProfileTopBar.kt @@ -10,7 +10,6 @@ package org.mifos.mobile.core.ui.component import androidx.compose.foundation.clickable -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -20,23 +19,22 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults 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.res.stringResource -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import org.mifos.mobile.core.designsystem.icons.MifosIcons +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource +import org.mifos.mobile.core.designsystem.icon.MifosIcons @OptIn(ExperimentalMaterial3Api::class) @Composable fun UserProfileTopBar( - text: Int, + text: StringResource, home: () -> Unit, modifier: Modifier = Modifier, ) { @@ -51,14 +49,12 @@ fun UserProfileTopBar( horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - text = stringResource(id = text), - color = if (isSystemInDarkTheme()) Color.White else Color.Black, - style = TextStyle(fontSize = 24.sp), + text = stringResource(text), + style = MaterialTheme.typography.bodySmall, ) Icon( imageVector = MifosIcons.Edit, - contentDescription = null, - tint = if (isSystemInDarkTheme()) Color.White else Color.Black, + contentDescription = "User Profile Icon", ) } }, @@ -72,22 +68,15 @@ fun UserProfileTopBar( ) { Icon( imageVector = MifosIcons.ArrowBack, - contentDescription = null, + contentDescription = "Arrow Back Icon", modifier = Modifier.clickable(onClick = { home.invoke() }), - tint = if (isSystemInDarkTheme()) Color.White else Color.Black, ) } }, colors = TopAppBarDefaults.topAppBarColors( - containerColor = if (isSystemInDarkTheme()) { - Color( - 0xFF1B1B1F, - ) - } else { - Color(0xFFFEFBFF) - }, + containerColor = MaterialTheme.colorScheme.primary, ), ) } diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/DevicePreviews.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/DevicePreviews.kt similarity index 59% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/utils/DevicePreviews.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/DevicePreviews.kt index d5f9461ca..e2b6ce675 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/DevicePreviews.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/DevicePreviews.kt @@ -9,14 +9,11 @@ */ package org.mifos.mobile.core.ui.utils -import androidx.compose.ui.tooling.preview.Preview +import org.jetbrains.compose.ui.tooling.preview.Preview /** * Multipreview annotation that represents various device sizes. Add this annotation to a composable * to render various devices. */ -@Preview(name = "phone", device = "spec:width=360dp,height=640dp,dpi=480") -@Preview(name = "landscape", device = "spec:width=640dp,height=360dp,dpi=480") -@Preview(name = "foldable", device = "spec:width=673dp,height=841dp,dpi=480") -@Preview(name = "tablet", device = "spec:width=1280dp,height=800dp,dpi=480") +@Preview annotation class DevicePreviews diff --git a/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.kt new file mode 100644 index 000000000..0761b0d39 --- /dev/null +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2024 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md + */ +package org.mifos.mobile.core.ui.utils + +expect object ImageUtil { + val DEFAULT_MAX_WIDTH: Float + val DEFAULT_MAX_HEIGHT: Float + + fun compressImage( + decodedBytes: ByteArray, + maxWidth: Float = DEFAULT_MAX_WIDTH, + maxHeight: Float = DEFAULT_MAX_HEIGHT, + ): ByteArray +} diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt similarity index 65% rename from core/ui/src/main/java/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt rename to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt index 9526fa3e9..5489dcaf9 100644 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt +++ b/core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/utils/PresentOrFutureSelectableDates.kt @@ -11,16 +11,23 @@ package org.mifos.mobile.core.ui.utils import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SelectableDates -import java.time.LocalDate +import kotlinx.datetime.Clock +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime @OptIn(ExperimentalMaterial3Api::class) object PresentOrFutureSelectableDates : SelectableDates { + @ExperimentalMaterial3Api override fun isSelectableDate(utcTimeMillis: Long): Boolean { - return utcTimeMillis >= System.currentTimeMillis() + val currentTimeMillis = Clock.System.now().toEpochMilliseconds() + return utcTimeMillis >= currentTimeMillis } override fun isSelectableYear(year: Int): Boolean { - return year >= LocalDate.now().year + val currentYear = Clock.System.now() + .toLocalDateTime(TimeZone.currentSystemDefault()) + .year + return year >= currentYear } } diff --git a/core/ui/src/desktopMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.desktop.kt b/core/ui/src/desktopMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.desktop.kt new file mode 100644 index 000000000..a4209982e --- /dev/null +++ b/core/ui/src/desktopMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.desktop.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md + */ +package org.mifos.mobile.core.ui.utils + +import java.awt.Graphics2D +import java.awt.image.BufferedImage +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import javax.imageio.ImageIO + +actual object ImageUtil { + actual val DEFAULT_MAX_WIDTH: Float = 816f + actual val DEFAULT_MAX_HEIGHT: Float = 612f + + actual fun compressImage( + decodedBytes: ByteArray, + maxWidth: Float, + maxHeight: Float, + ): ByteArray { + val inputStream = ByteArrayInputStream(decodedBytes) + val originalImage: BufferedImage = try { + ImageIO.read(inputStream) + } catch (e: Exception) { + e.printStackTrace() + return ByteArray(0) + } + + val (actualWidth, actualHeight) = calculateActualDimensions( + originalImage.width.toFloat(), + originalImage.height.toFloat(), + maxWidth, + maxHeight, + ) + + val scaledImage = createScaledImage(originalImage, actualWidth, actualHeight) + + val byteArrayOutputStream = ByteArrayOutputStream() + try { + ImageIO.write(scaledImage, "JPEG", byteArrayOutputStream) + } catch (e: Exception) { + e.printStackTrace() + } + + return byteArrayOutputStream.toByteArray() + } + + private fun calculateActualDimensions( + actualWidth: Float, + actualHeight: Float, + maxWidth: Float, + maxHeight: Float, + ): Pair { + val imgRatio = actualWidth / actualHeight + val maxRatio = maxWidth / maxHeight + + var finalWidth = actualWidth + var finalHeight = actualHeight + + if (actualHeight > maxHeight || actualWidth > maxWidth) { + when { + imgRatio < maxRatio -> { + finalHeight = maxHeight + finalWidth = maxHeight * imgRatio + } + imgRatio > maxRatio -> { + finalWidth = maxWidth + finalHeight = maxWidth / imgRatio + } + else -> { + finalWidth = maxWidth + finalHeight = maxHeight + } + } + } + + return Pair(finalWidth.toInt(), finalHeight.toInt()) + } + + private fun createScaledImage(image: BufferedImage, targetWidth: Int, targetHeight: Int): BufferedImage { + val scaledImage = BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB) + val g2d: Graphics2D = scaledImage.createGraphics() + g2d.drawImage(image, 0, 0, targetWidth, targetHeight, null) + g2d.dispose() + + return scaledImage + } +} diff --git a/core/ui/src/jsMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.js.kt b/core/ui/src/jsMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.js.kt new file mode 100644 index 000000000..17732cc71 --- /dev/null +++ b/core/ui/src/jsMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.js.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md + */ +package org.mifos.mobile.core.ui.utils + +// TODO: Not Implemented +actual object ImageUtil { + actual val DEFAULT_MAX_WIDTH: Float = 816f + actual val DEFAULT_MAX_HEIGHT: Float = 612f + + actual fun compressImage( + decodedBytes: ByteArray, + maxWidth: Float, + maxHeight: Float, + ): ByteArray { + return decodedBytes + } +} diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ColorUtils.kt b/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ColorUtils.kt deleted file mode 100644 index b7d9e9b99..000000000 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ColorUtils.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Mifos Initiative - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md - */ -package org.mifos.mobile.core.ui.utils - -import android.app.Activity -import android.content.Context -import android.util.TypedValue -import androidx.annotation.AttrRes -import androidx.annotation.ColorInt -import androidx.core.graphics.ColorUtils -import androidx.core.view.WindowCompat - -fun Activity.setStatusBarColor(@ColorInt color: Int) { - if (!window.decorView.isInEditMode) { - window.statusBarColor = color - WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = - ColorUtils.calculateLuminance(color) > 0.5 - } -} - -@ColorInt -fun Context.getThemeAttributeColor(@AttrRes colorAttribute: Int): Int { - val typedValue = TypedValue() - theme.resolveAttribute(colorAttribute, typedValue, true) - return typedValue.data -} diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ImageUtil.kt b/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ImageUtil.kt deleted file mode 100644 index 9ba20c089..000000000 --- a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ImageUtil.kt +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2024 Mifos Initiative - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md - */ -package org.mifos.mobile.core.ui.utils - -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.graphics.Canvas -import android.graphics.Matrix -import android.graphics.Paint -import android.util.Log - -object ImageUtil { - private const val DEFAULT_MAX_WIDTH = 816f - private const val DEFAULT_MAX_HEIGHT = 612f - - fun compressImage( - decodedBytes: ByteArray, - maxWidth: Float = DEFAULT_MAX_WIDTH, - maxHeight: Float = DEFAULT_MAX_HEIGHT, - ): Bitmap { - val options = BitmapFactory.Options().apply { - inJustDecodeBounds = true - } - - BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options) - - val (actualWidth, actualHeight) = calculateActualDimensions(options, maxWidth, maxHeight) - - options.apply { - inJustDecodeBounds = false - inSampleSize = calculateInSampleSize(this, actualWidth, actualHeight) - inTempStorage = ByteArray(16 * 1024) - } - - val bmp = try { - BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options) - } catch (e: OutOfMemoryError) { - Log.e(this::class.java.simpleName, "OutOfMemoryError while decoding bitmap", e) - return Bitmap.createBitmap( - 1, - 1, - Bitmap.Config.ARGB_8888, - ) // Return a 1x1 bitmap as fallback - } - - return try { - createScaledBitmap(bmp, actualWidth, actualHeight, options) - } catch (e: OutOfMemoryError) { - Log.e(this::class.java.simpleName, "OutOfMemoryError while scaling bitmap", e) - bmp // Return the original bitmap if scaling fails - } - } -} - -private fun calculateActualDimensions( - options: BitmapFactory.Options, - maxWidth: Float, - maxHeight: Float, -): Pair { - var actualWidth = options.outWidth - var actualHeight = options.outHeight - val imgRatio = actualWidth.toFloat() / actualHeight - val maxRatio = maxWidth / maxHeight - - if (actualHeight > maxHeight || actualWidth > maxWidth) { - when { - imgRatio < maxRatio -> { - actualHeight = maxHeight.toInt() - actualWidth = (maxHeight * imgRatio).toInt() - } - - imgRatio > maxRatio -> { - actualWidth = maxWidth.toInt() - actualHeight = (maxWidth / imgRatio).toInt() - } - - else -> { - actualHeight = maxHeight.toInt() - actualWidth = maxWidth.toInt() - } - } - } - - return Pair(actualWidth, actualHeight) -} - -private fun calculateInSampleSize( - options: BitmapFactory.Options, - reqWidth: Int, - reqHeight: Int, -): Int { - val (height, width) = options.run { outHeight to outWidth } - var inSampleSize = 1 - - if (height > reqHeight || width > reqWidth) { - val halfHeight = height / 2 - val halfWidth = width / 2 - - while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { - inSampleSize *= 2 - } - } - - return inSampleSize -} - -private fun createScaledBitmap( - bmp: Bitmap, - actualWidth: Int, - actualHeight: Int, - options: BitmapFactory.Options, -): Bitmap { - val scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888) - val ratioX = actualWidth / options.outWidth.toFloat() - val ratioY = actualHeight / options.outHeight.toFloat() - val middleX = actualWidth / 2f - val middleY = actualHeight / 2f - - val scaleMatrix = Matrix().apply { - setScale(ratioX, ratioY, middleX, middleY) - } - - Canvas(scaledBitmap).apply { - setMatrix(scaleMatrix) - drawBitmap( - bmp, - middleX - bmp.width / 2, - middleY - bmp.height / 2, - Paint(Paint.FILTER_BITMAP_FLAG), - ) - } - - return scaledBitmap -} diff --git a/core/ui/src/main/res/values/colors.xml b/core/ui/src/main/res/values/colors.xml deleted file mode 100644 index d7ccaf034..000000000 --- a/core/ui/src/main/res/values/colors.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - #FF325ca8 - \ No newline at end of file diff --git a/core/ui/src/nativeMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.native.kt b/core/ui/src/nativeMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.native.kt new file mode 100644 index 000000000..164de6957 --- /dev/null +++ b/core/ui/src/nativeMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.native.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md + */ +package org.mifos.mobile.core.ui.utils + +import org.jetbrains.skia.Bitmap +import org.jetbrains.skia.Canvas +import org.jetbrains.skia.Image +import org.jetbrains.skia.ImageInfo +import org.jetbrains.skia.Paint +import org.jetbrains.skia.Rect + +actual object ImageUtil { + actual val DEFAULT_MAX_WIDTH: Float = 816f + actual val DEFAULT_MAX_HEIGHT: Float = 612f + + actual fun compressImage( + decodedBytes: ByteArray, + maxWidth: Float, + maxHeight: Float, + ): ByteArray { + val image = Image.makeFromEncoded(decodedBytes) + val bitmap = Bitmap.makeFromImage(image) + + val (actualWidth, actualHeight) = calculateActualDimensions(bitmap.width, bitmap.height, maxWidth, maxHeight) + val scaledBitmap = createScaledBitmap(bitmap, actualWidth, actualHeight) + + val scaledImage = Image.makeFromBitmap(scaledBitmap) + + return scaledImage.encodeToData()?.bytes + ?: throw IllegalStateException("Failed to encode bitmap") + } + + private fun calculateActualDimensions( + width: Int, + height: Int, + maxWidth: Float, + maxHeight: Float, + ): Pair { + val imgRatio = width.toFloat() / height + val maxRatio = maxWidth / maxHeight + + return if (height > maxHeight || width > maxWidth) { + when { + imgRatio < maxRatio -> { + val newHeight = maxHeight.toInt() + val newWidth = (maxHeight * imgRatio).toInt() + Pair(newWidth, newHeight) + } + imgRatio > maxRatio -> { + val newWidth = maxWidth.toInt() + val newHeight = (maxWidth / imgRatio).toInt() + Pair(newWidth, newHeight) + } + else -> Pair(maxWidth.toInt(), maxHeight.toInt()) + } + } else { + Pair(width, height) + } + } + + private fun createScaledBitmap( + bitmap: Bitmap, + targetWidth: Int, + targetHeight: Int, + ): Bitmap { + val imageInfo = ImageInfo.makeN32Premul(targetWidth, targetHeight) + val scaledBitmap = Bitmap() + scaledBitmap.allocPixels(imageInfo) + + val canvas = Canvas(scaledBitmap) + val sourceRect = Rect.makeWH(bitmap.width.toFloat(), bitmap.height.toFloat()) + val targetRect = Rect.makeWH(targetWidth.toFloat(), targetHeight.toFloat()) + + canvas.drawImageRect( + Image.makeFromBitmap(bitmap), + sourceRect, + targetRect, + Paint(), + ) + + return scaledBitmap + } +} diff --git a/core/ui/src/wasmJsMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.wasmJs.kt b/core/ui/src/wasmJsMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.wasmJs.kt new file mode 100644 index 000000000..17732cc71 --- /dev/null +++ b/core/ui/src/wasmJsMain/kotlin/org/mifos/mobile/core/ui/utils/ImageUtil.wasmJs.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md + */ +package org.mifos.mobile.core.ui.utils + +// TODO: Not Implemented +actual object ImageUtil { + actual val DEFAULT_MAX_WIDTH: Float = 816f + actual val DEFAULT_MAX_HEIGHT: Float = 612f + + actual fun compressImage( + decodedBytes: ByteArray, + maxWidth: Float, + maxHeight: Float, + ): ByteArray { + return decodedBytes + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 60db80b2c..dd504ad87 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -267,9 +267,7 @@ kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-cor kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" } kotlinx-serialization-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "kotlinxSerializationJson" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } - ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } - ktor-client-android = { group = "io.ktor", name = "ktor-client-android", version.ref = "ktorVersion" } ktor-client-auth = { group = "io.ktor", name = "ktor-client-auth", version.ref = "ktorVersion" } ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktorVersion" }