Skip to content

Commit

Permalink
Feat: [:core:ui] - Migrated to KMP (#2749)
Browse files Browse the repository at this point in the history
  • Loading branch information
revanthkumarJ authored Jan 26, 2025
1 parent e43095b commit c67f9d4
Show file tree
Hide file tree
Showing 44 changed files with 632 additions and 416 deletions.
3 changes: 3 additions & 0 deletions androidApp/dependencies/demoDebugRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions androidApp/dependencies/demoReleaseRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions androidApp/dependencies/prodDebugRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions androidApp/dependencies/prodReleaseRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 28 additions & 11 deletions core/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

File renamed without changes.
Original file line number Diff line number Diff line change
@@ -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<Int, Int> {
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
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
<string name="retry">Retry</string>
<string name="no_data">No Data</string>
<string name="something_went_wrong">Something went wrong</string>
<string name="core_common_working">Core Common Working</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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),
)
}
Expand All @@ -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),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -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,
)
Expand All @@ -78,7 +69,7 @@ private fun EmptyDataViewPreview(
) {
MifosMobileTheme {
EmptyDataView(
error = R.string.no_internet,
error = Res.string.no_internet,
modifier = modifier,
)
}
Expand Down
Loading

0 comments on commit c67f9d4

Please sign in to comment.