Skip to content

Commit c67f9d4

Browse files
Feat: [:core:ui] - Migrated to KMP (#2749)
1 parent e43095b commit c67f9d4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+632
-416
lines changed

androidApp/dependencies/demoDebugRuntimeClasspath.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,9 @@ org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.8.3-rc01
295295
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3
296296
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3
297297
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.8.3
298+
org.jetbrains.androidx.navigation:navigation-common:2.8.0-alpha10
299+
org.jetbrains.androidx.navigation:navigation-compose:2.8.0-alpha10
300+
org.jetbrains.androidx.navigation:navigation-runtime:2.8.0-alpha10
298301
org.jetbrains.androidx.savedstate:savedstate:1.2.2
299302
org.jetbrains.compose.animation:animation-core:1.7.0-rc01
300303
org.jetbrains.compose.animation:animation:1.7.0-rc01

androidApp/dependencies/demoReleaseRuntimeClasspath.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.8.3-rc01
290290
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3
291291
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3
292292
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.8.3
293+
org.jetbrains.androidx.navigation:navigation-common:2.8.0-alpha10
294+
org.jetbrains.androidx.navigation:navigation-compose:2.8.0-alpha10
295+
org.jetbrains.androidx.navigation:navigation-runtime:2.8.0-alpha10
293296
org.jetbrains.androidx.savedstate:savedstate:1.2.2
294297
org.jetbrains.compose.animation:animation-core:1.7.0-rc01
295298
org.jetbrains.compose.animation:animation:1.7.0-rc01

androidApp/dependencies/prodDebugRuntimeClasspath.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,9 @@ org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.8.3-rc01
295295
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3
296296
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3
297297
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.8.3
298+
org.jetbrains.androidx.navigation:navigation-common:2.8.0-alpha10
299+
org.jetbrains.androidx.navigation:navigation-compose:2.8.0-alpha10
300+
org.jetbrains.androidx.navigation:navigation-runtime:2.8.0-alpha10
298301
org.jetbrains.androidx.savedstate:savedstate:1.2.2
299302
org.jetbrains.compose.animation:animation-core:1.7.0-rc01
300303
org.jetbrains.compose.animation:animation:1.7.0-rc01

androidApp/dependencies/prodReleaseRuntimeClasspath.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.8.3-rc01
290290
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3
291291
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3
292292
org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.8.3
293+
org.jetbrains.androidx.navigation:navigation-common:2.8.0-alpha10
294+
org.jetbrains.androidx.navigation:navigation-compose:2.8.0-alpha10
295+
org.jetbrains.androidx.navigation:navigation-runtime:2.8.0-alpha10
293296
org.jetbrains.androidx.savedstate:savedstate:1.2.2
294297
org.jetbrains.compose.animation:animation-core:1.7.0-rc01
295298
org.jetbrains.compose.animation:animation:1.7.0-rc01

core/ui/build.gradle.kts

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,40 @@
88
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
99
*/
1010
plugins {
11-
alias(libs.plugins.mifos.android.library)
12-
alias(libs.plugins.mifos.android.library.compose)
11+
alias(libs.plugins.mifos.kmp.library)
12+
alias(libs.plugins.jetbrainsCompose)
13+
alias(libs.plugins.compose.compiler)
1314
}
1415

1516
android {
1617
defaultConfig {
1718
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
1819
}
19-
namespace = "org.mifos.mobile.core"
20+
namespace = "org.mifos.mobile.core.ui"
2021
}
2122

22-
dependencies {
23-
api(projects.core.designsystem)
24-
// api(projects.core.model)
25-
api(projects.core.common)
26-
api(libs.androidx.metrics)
27-
28-
testImplementation(libs.androidx.compose.ui.test)
29-
androidTestImplementation(libs.bundles.androidx.compose.ui.test)
23+
kotlin{
24+
sourceSets{
25+
androidMain.dependencies {
26+
api(libs.androidx.metrics)
27+
implementation(libs.androidx.compose.runtime)
28+
implementation(libs.accompanist.pager)
29+
}
30+
commonMain.dependencies {
31+
api(projects.core.designsystem)
32+
api(libs.kotlinx.datetime)
33+
implementation(libs.jb.composeViewmodel)
34+
implementation(libs.jb.lifecycleViewmodel)
35+
implementation(libs.jb.lifecycleViewmodelSavedState)
36+
implementation(libs.coil.kt)
37+
implementation(libs.coil.kt.compose)
38+
implementation(compose.material3)
39+
implementation(compose.components.resources)
40+
implementation(compose.components.uiToolingPreview)
41+
implementation(libs.jb.composeNavigation)
42+
implementation(libs.filekit.compose)
43+
implementation(libs.filekit.core)
44+
}
45+
}
3046
}
47+
File renamed without changes.
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
9+
*/
10+
package org.mifos.mobile.core.ui.utils
11+
12+
import android.graphics.Bitmap
13+
import android.graphics.BitmapFactory
14+
import android.graphics.Canvas
15+
import android.graphics.Matrix
16+
import android.graphics.Paint
17+
import android.util.Log
18+
import java.io.ByteArrayOutputStream
19+
20+
actual object ImageUtil {
21+
actual val DEFAULT_MAX_WIDTH: Float = 816f
22+
actual val DEFAULT_MAX_HEIGHT: Float = 612f
23+
24+
actual fun compressImage(
25+
decodedBytes: ByteArray,
26+
maxWidth: Float,
27+
maxHeight: Float,
28+
): ByteArray {
29+
val options = BitmapFactory.Options().apply {
30+
inJustDecodeBounds = true
31+
}
32+
33+
BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options)
34+
35+
val (actualWidth, actualHeight) = calculateActualDimensions(options, maxWidth, maxHeight)
36+
37+
options.apply {
38+
inJustDecodeBounds = false
39+
inSampleSize = calculateInSampleSize(this, actualWidth, actualHeight)
40+
inTempStorage = ByteArray(16 * 1024)
41+
}
42+
43+
val bmp = try {
44+
BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options)
45+
} catch (e: OutOfMemoryError) {
46+
Log.e(this::class.java.simpleName, "OutOfMemoryError while decoding bitmap", e)
47+
return ByteArray(0)
48+
}
49+
50+
val scaledBitmap = try {
51+
createScaledBitmap(bmp, actualWidth, actualHeight, options)
52+
} catch (e: OutOfMemoryError) {
53+
Log.e(this::class.java.simpleName, "OutOfMemoryError while scaling bitmap", e)
54+
bmp
55+
}
56+
57+
val byteArrayOutputStream = ByteArrayOutputStream()
58+
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 85, byteArrayOutputStream)
59+
return byteArrayOutputStream.toByteArray()
60+
}
61+
62+
private fun calculateActualDimensions(
63+
options: BitmapFactory.Options,
64+
maxWidth: Float,
65+
maxHeight: Float,
66+
): Pair<Int, Int> {
67+
var actualWidth = options.outWidth
68+
var actualHeight = options.outHeight
69+
val imgRatio = actualWidth.toFloat() / actualHeight
70+
val maxRatio = maxWidth / maxHeight
71+
72+
if (actualHeight > maxHeight || actualWidth > maxWidth) {
73+
when {
74+
imgRatio < maxRatio -> {
75+
actualHeight = maxHeight.toInt()
76+
actualWidth = (maxHeight * imgRatio).toInt()
77+
}
78+
79+
imgRatio > maxRatio -> {
80+
actualWidth = maxWidth.toInt()
81+
actualHeight = (maxWidth / imgRatio).toInt()
82+
}
83+
84+
else -> {
85+
actualHeight = maxHeight.toInt()
86+
actualWidth = maxWidth.toInt()
87+
}
88+
}
89+
}
90+
91+
return Pair(actualWidth, actualHeight)
92+
}
93+
94+
private fun calculateInSampleSize(
95+
options: BitmapFactory.Options,
96+
reqWidth: Int,
97+
reqHeight: Int,
98+
): Int {
99+
val (height, width) = options.run { outHeight to outWidth }
100+
var inSampleSize = 1
101+
102+
if (height > reqHeight || width > reqWidth) {
103+
val halfHeight = height / 2
104+
val halfWidth = width / 2
105+
106+
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
107+
inSampleSize *= 2
108+
}
109+
}
110+
111+
return inSampleSize
112+
}
113+
114+
private fun createScaledBitmap(
115+
bmp: Bitmap,
116+
actualWidth: Int,
117+
actualHeight: Int,
118+
options: BitmapFactory.Options,
119+
): Bitmap {
120+
val scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888)
121+
val ratioX = actualWidth / options.outWidth.toFloat()
122+
val ratioY = actualHeight / options.outHeight.toFloat()
123+
val middleX = actualWidth / 2f
124+
val middleY = actualHeight / 2f
125+
126+
val scaleMatrix = Matrix().apply {
127+
setScale(ratioX, ratioY, middleX, middleY)
128+
}
129+
130+
Canvas(scaledBitmap).apply {
131+
setMatrix(scaleMatrix)
132+
drawBitmap(
133+
bmp,
134+
middleX - bmp.width / 2,
135+
middleY - bmp.height / 2,
136+
Paint(Paint.FILTER_BITMAP_FLAG),
137+
)
138+
}
139+
140+
return scaledBitmap
141+
}
142+
}
415 Bytes
Loading

core/ui/src/main/res/values/strings.xml renamed to core/ui/src/commonMain/composeResources/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
<string name="retry">Retry</string>
1414
<string name="no_data">No Data</string>
1515
<string name="something_went_wrong">Something went wrong</string>
16+
<string name="core_common_working">Core Common Working</string>
1617
</resources>

core/ui/src/main/java/org/mifos/mobile/core/ui/component/AboutUsItemCard.kt renamed to core/ui/src/commonMain/kotlin/org/mifos/mobile/core/ui/component/AboutUsItemCard.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
*/
1010
package org.mifos.mobile.core.ui.component
1111

12-
import androidx.annotation.DrawableRes
13-
import androidx.annotation.StringRes
1412
import androidx.compose.foundation.Image
1513
import androidx.compose.foundation.layout.Column
1614
import androidx.compose.foundation.layout.Row
@@ -19,26 +17,28 @@ import androidx.compose.material3.MaterialTheme
1917
import androidx.compose.material3.Text
2018
import androidx.compose.runtime.Composable
2119
import androidx.compose.ui.Modifier
22-
import androidx.compose.ui.res.painterResource
23-
import androidx.compose.ui.res.stringResource
2420
import androidx.compose.ui.unit.dp
21+
import org.jetbrains.compose.resources.DrawableResource
22+
import org.jetbrains.compose.resources.StringResource
23+
import org.jetbrains.compose.resources.painterResource
24+
import org.jetbrains.compose.resources.stringResource
2525
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
2626
import org.mifos.mobile.core.ui.utils.DevicePreviews
2727

2828
@Composable
2929
fun AboutUsItemCard(
3030
title: String,
31-
@StringRes subtitle: Int?,
32-
@DrawableRes iconUrl: Int?,
31+
subtitle: StringResource?,
32+
iconUrl: DrawableResource?,
3333
modifier: Modifier = Modifier,
3434
) {
3535
Row(
3636
modifier = modifier.padding(16.dp),
3737
) {
38-
iconUrl?.let { painterResource(id = it) }?.let {
38+
iconUrl?.let { painterResource(it) }?.let {
3939
Image(
4040
painter = it,
41-
contentDescription = null,
41+
contentDescription = "About Us Icon URL",
4242
modifier = Modifier.padding(end = 8.dp),
4343
)
4444
}
@@ -50,7 +50,7 @@ fun AboutUsItemCard(
5050
)
5151
if (subtitle != null) {
5252
Text(
53-
text = stringResource(id = subtitle),
53+
text = stringResource(subtitle),
5454
style = MaterialTheme.typography.bodyLarge,
5555
modifier = Modifier.padding(end = 8.dp),
5656
)

0 commit comments

Comments
 (0)