From d88e08fb20a1f9bdb13b56db64836be96193236e Mon Sep 17 00:00:00 2001 From: Biplab Dutta Date: Tue, 14 Jan 2025 23:02:55 +0530 Subject: [PATCH] feat: Migrated `:core:common` module to KMP (#2743) --- .../demoDebugRuntimeClasspath.txt | 53 ++- .../demoReleaseRuntimeClasspath.txt | 53 ++- .../prodDebugRuntimeClasspath.txt | 53 ++- .../prodReleaseRuntimeClasspath.txt | 53 ++- core/common/build.gradle.kts | 47 ++- .../{main => androidMain}/AndroidManifest.xml | 0 .../core/common/CurrencyFormatter.android.kt | 26 ++ .../mobile/core/common/FileUtils.android.kt | 12 + .../common/di/DispatchersModule.android.kt | 22 ++ .../org/mifos/mobile/core/common/Constants.kt | 0 .../mobile/core/common/CurrencyFormatter.kt | 18 + .../org/mifos/mobile/core/common/DataState.kt | 39 ++ .../mobile/core/common/DataStateExtensions.kt | 105 +++++ .../mifos/mobile/core/common/DateHelper.kt | 358 ++++++++++++++++++ .../org/mifos/mobile/core/common/FileUtils.kt | 39 ++ .../mobile/core/common}/LanguageHelper.kt | 9 +- .../mobile/core/common}/MifosDispatchers.kt | 14 +- .../mifos/mobile/core/common}/SymbolsUtils.kt | 2 +- .../org/mifos/mobile/core/common/Utils.kt | 29 ++ .../core/common/di/DispatchersModule.kt | 30 ++ .../core/common/CurrencyFormatter.jvm.kt | 26 ++ .../mifos/mobile/core/common/FileUtils.jvm.kt | 12 + .../common/di/DispatchersModule.desktop.kt | 22 ++ .../core/common/CurrencyFormatter.js.kt | 36 ++ .../mifos/mobile/core/common/FileUtils.js.kt | 12 + .../core/common/di/DispatchersModule.js.kt | 22 ++ .../mifos/mobile/core/common/ApiEndPoints.kt | 25 -- .../org/mifos/mobile/core/common/Network.kt | 131 ------- .../network/di/CoroutineScopesModule.kt | 40 -- .../common/network/di/DispatchersModule.kt | 31 -- .../mobile/core/common/utils/CurrencyUtil.kt | 67 ---- .../mobile/core/common/utils/DateHelper.kt | 168 -------- .../utils/ParcelableAndSerializableUtils.kt | 41 -- .../mifos/mobile/core/common/utils/Utils.kt | 98 ----- .../core_common_circular_background.xml | 16 - core/common/src/main/res/values/dimens.xml | 16 - core/common/src/main/res/values/strings.xml | 35 -- .../core/common/CurrencyFormatter.native.kt | 28 ++ .../mobile/core/common/FileUtils.native.kt | 45 +++ .../common/di/DispatchersModule.native.kt | 22 ++ .../core/common/CurrencyFormatter.js.kt | 35 ++ .../mobile/core/common/FileUtils.wasmJs.kt | 12 + .../core/common/di/DispatchersModule.js.kt | 22 ++ 43 files changed, 1219 insertions(+), 705 deletions(-) rename core/common/src/{main => androidMain}/AndroidManifest.xml (100%) create mode 100644 core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.android.kt create mode 100644 core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/FileUtils.android.kt create mode 100644 core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.android.kt rename core/common/src/{main/java => commonMain/kotlin}/org/mifos/mobile/core/common/Constants.kt (100%) create mode 100644 core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.kt create mode 100644 core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DataState.kt create mode 100644 core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DataStateExtensions.kt create mode 100644 core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DateHelper.kt create mode 100644 core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/FileUtils.kt rename core/common/src/{main/java/org/mifos/mobile/core/common/utils => commonMain/kotlin/org/mifos/mobile/core/common}/LanguageHelper.kt (96%) rename core/common/src/{main/java/org/mifos/mobile/core/common/network => commonMain/kotlin/org/mifos/mobile/core/common}/MifosDispatchers.kt (57%) rename core/common/src/{main/java/org/mifos/mobile/core/common/utils => commonMain/kotlin/org/mifos/mobile/core/common}/SymbolsUtils.kt (89%) create mode 100644 core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/Utils.kt create mode 100644 core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.kt create mode 100644 core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.jvm.kt create mode 100644 core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/FileUtils.jvm.kt create mode 100644 core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.desktop.kt create mode 100644 core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.js.kt create mode 100644 core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/FileUtils.js.kt create mode 100644 core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.js.kt delete mode 100644 core/common/src/main/java/org/mifos/mobile/core/common/ApiEndPoints.kt delete mode 100644 core/common/src/main/java/org/mifos/mobile/core/common/Network.kt delete mode 100644 core/common/src/main/java/org/mifos/mobile/core/common/network/di/CoroutineScopesModule.kt delete mode 100644 core/common/src/main/java/org/mifos/mobile/core/common/network/di/DispatchersModule.kt delete mode 100644 core/common/src/main/java/org/mifos/mobile/core/common/utils/CurrencyUtil.kt delete mode 100755 core/common/src/main/java/org/mifos/mobile/core/common/utils/DateHelper.kt delete mode 100644 core/common/src/main/java/org/mifos/mobile/core/common/utils/ParcelableAndSerializableUtils.kt delete mode 100644 core/common/src/main/java/org/mifos/mobile/core/common/utils/Utils.kt delete mode 100644 core/common/src/main/res/drawable/core_common_circular_background.xml delete mode 100644 core/common/src/main/res/values/dimens.xml delete mode 100644 core/common/src/main/res/values/strings.xml create mode 100644 core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.native.kt create mode 100644 core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/FileUtils.native.kt create mode 100644 core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.native.kt create mode 100644 core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.js.kt create mode 100644 core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/FileUtils.wasmJs.kt create mode 100644 core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.js.kt diff --git a/androidApp/dependencies/demoDebugRuntimeClasspath.txt b/androidApp/dependencies/demoDebugRuntimeClasspath.txt index b6e6f4a16..c5a0dba25 100644 --- a/androidApp/dependencies/demoDebugRuntimeClasspath.txt +++ b/androidApp/dependencies/demoDebugRuntimeClasspath.txt @@ -2,8 +2,8 @@ androidx.activity:activity-compose:1.9.3 androidx.activity:activity-ktx:1.9.3 androidx.activity:activity:1.9.3 androidx.annotation:annotation-experimental:1.4.1 -androidx.annotation:annotation-jvm:1.8.1 -androidx.annotation:annotation:1.8.1 +androidx.annotation:annotation-jvm:1.9.1 +androidx.annotation:annotation:1.9.1 androidx.appcompat:appcompat-resources:1.7.0 androidx.appcompat:appcompat:1.7.0 androidx.arch.core:core-common:2.2.0 @@ -72,7 +72,7 @@ androidx.documentfile:documentfile:1.0.0 androidx.drawerlayout:drawerlayout:1.0.0 androidx.emoji2:emoji2-views-helper:1.3.0 androidx.emoji2:emoji2:1.3.0 -androidx.exifinterface:exifinterface:1.3.2 +androidx.exifinterface:exifinterface:1.3.7 androidx.fragment:fragment-ktx:1.8.5 androidx.fragment:fragment:1.8.5 androidx.graphics:graphics-path:1.0.1 @@ -129,12 +129,17 @@ androidx.versionedparcelable:versionedparcelable:1.1.1 androidx.viewpager:viewpager:1.0.0 app.cash.turbine:turbine-jvm:1.1.0 app.cash.turbine:turbine:1.1.0 +co.touchlab:kermit-android-debug:2.0.4 +co.touchlab:kermit-core-android-debug:2.0.4 +co.touchlab:kermit-core:2.0.4 +co.touchlab:kermit:2.0.4 co.touchlab:stately-concurrency-jvm:2.1.0 co.touchlab:stately-concurrency:2.1.0 co.touchlab:stately-concurrent-collections-jvm:2.1.0 co.touchlab:stately-concurrent-collections:2.1.0 co.touchlab:stately-strict-jvm:2.1.0 co.touchlab:stately-strict:2.1.0 +com.caverock:androidsvg-aar:1.4 com.google.accompanist:accompanist-pager:0.34.0 com.google.accompanist:accompanist-permissions:0.34.0 com.google.android.gms:play-services-ads-identifier:18.0.0 @@ -177,12 +182,22 @@ com.google.maps.android:maps-ktx:5.0.0 com.google.zxing:core:3.5.3 com.squareup.okhttp3:logging-interceptor:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 -com.squareup.okio:okio-jvm:3.6.0 -com.squareup.okio:okio:3.6.0 +com.squareup.okio:okio-jvm:3.9.1 +com.squareup.okio:okio:3.9.1 com.squareup.retrofit2:adapter-rxjava2:2.11.0 com.squareup.retrofit2:converter-gson:2.11.0 com.squareup.retrofit2:retrofit:2.11.0 dev.chrisbanes.snapper:snapper:0.2.2 +io.coil-kt.coil3:coil-android:3.0.4 +io.coil-kt.coil3:coil-core-android:3.0.4 +io.coil-kt.coil3:coil-core:3.0.4 +io.coil-kt.coil3:coil-network-core-android:3.0.4 +io.coil-kt.coil3:coil-network-core:3.0.4 +io.coil-kt.coil3:coil-network-ktor3-android:3.0.4 +io.coil-kt.coil3:coil-network-ktor3:3.0.4 +io.coil-kt.coil3:coil-svg-android:3.0.4 +io.coil-kt.coil3:coil-svg:3.0.4 +io.coil-kt.coil3:coil:3.0.4 io.github.mr0xf00:easycrop:0.1.1 io.insert-koin:koin-android:4.0.1-RC1 io.insert-koin:koin-androidx-compose:4.0.1-RC1 @@ -197,6 +212,24 @@ io.insert-koin:koin-core-jvm:4.0.1-RC1 io.insert-koin:koin-core-viewmodel-jvm:4.0.1-RC1 io.insert-koin:koin-core-viewmodel:4.0.1-RC1 io.insert-koin:koin-core:4.0.1-RC1 +io.ktor:ktor-client-core-jvm:3.0.1 +io.ktor:ktor-client-core:3.0.1 +io.ktor:ktor-events-jvm:3.0.1 +io.ktor:ktor-events:3.0.1 +io.ktor:ktor-http-jvm:3.0.1 +io.ktor:ktor-http:3.0.1 +io.ktor:ktor-io-jvm:3.0.1 +io.ktor:ktor-io:3.0.1 +io.ktor:ktor-serialization-jvm:3.0.1 +io.ktor:ktor-serialization:3.0.1 +io.ktor:ktor-sse-jvm:3.0.1 +io.ktor:ktor-sse:3.0.1 +io.ktor:ktor-utils-jvm:3.0.1 +io.ktor:ktor-utils:3.0.1 +io.ktor:ktor-websocket-serialization-jvm:3.0.1 +io.ktor:ktor-websocket-serialization:3.0.1 +io.ktor:ktor-websockets-jvm:3.0.1 +io.ktor:ktor-websockets:3.0.1 io.michaelrocks:libphonenumber-android:8.13.35 io.reactivex.rxjava2:rxjava:2.2.21 jakarta.inject:jakarta.inject-api:2.0.1 @@ -236,7 +269,7 @@ org.jetbrains.kotlin:kotlin-android-extensions-runtime:2.1.0 org.jetbrains.kotlin:kotlin-parcelize-runtime:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-common:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.1.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 org.jetbrains.kotlin:kotlin-stdlib:2.1.0 org.jetbrains.kotlinx:atomicfu-jvm:0.23.2 org.jetbrains.kotlinx:atomicfu:0.23.2 @@ -247,8 +280,15 @@ org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.10.1 +org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.1 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1 +org.jetbrains.kotlinx:kotlinx-datetime:0.6.1 +org.jetbrains.kotlinx:kotlinx-io-bytestring-jvm:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-bytestring:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-core-jvm:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-core:0.5.4 org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3 @@ -259,3 +299,4 @@ org.jspecify:jspecify:1.0.0 org.mockito:mockito-core:5.6.0 org.objenesis:objenesis:3.3 org.reactivestreams:reactive-streams:1.0.4 +org.slf4j:slf4j-api:2.0.16 diff --git a/androidApp/dependencies/demoReleaseRuntimeClasspath.txt b/androidApp/dependencies/demoReleaseRuntimeClasspath.txt index a2d888fbb..aa400c17a 100644 --- a/androidApp/dependencies/demoReleaseRuntimeClasspath.txt +++ b/androidApp/dependencies/demoReleaseRuntimeClasspath.txt @@ -2,8 +2,8 @@ androidx.activity:activity-compose:1.9.3 androidx.activity:activity-ktx:1.9.3 androidx.activity:activity:1.9.3 androidx.annotation:annotation-experimental:1.4.1 -androidx.annotation:annotation-jvm:1.8.1 -androidx.annotation:annotation:1.8.1 +androidx.annotation:annotation-jvm:1.9.1 +androidx.annotation:annotation:1.9.1 androidx.appcompat:appcompat-resources:1.7.0 androidx.appcompat:appcompat:1.7.0 androidx.arch.core:core-common:2.2.0 @@ -67,7 +67,7 @@ androidx.documentfile:documentfile:1.0.0 androidx.drawerlayout:drawerlayout:1.0.0 androidx.emoji2:emoji2-views-helper:1.3.0 androidx.emoji2:emoji2:1.3.0 -androidx.exifinterface:exifinterface:1.3.2 +androidx.exifinterface:exifinterface:1.3.7 androidx.fragment:fragment-ktx:1.8.5 androidx.fragment:fragment:1.8.5 androidx.graphics:graphics-path:1.0.1 @@ -124,12 +124,17 @@ androidx.versionedparcelable:versionedparcelable:1.1.1 androidx.viewpager:viewpager:1.0.0 app.cash.turbine:turbine-jvm:1.1.0 app.cash.turbine:turbine:1.1.0 +co.touchlab:kermit-android:2.0.4 +co.touchlab:kermit-core-android:2.0.4 +co.touchlab:kermit-core:2.0.4 +co.touchlab:kermit:2.0.4 co.touchlab:stately-concurrency-jvm:2.1.0 co.touchlab:stately-concurrency:2.1.0 co.touchlab:stately-concurrent-collections-jvm:2.1.0 co.touchlab:stately-concurrent-collections:2.1.0 co.touchlab:stately-strict-jvm:2.1.0 co.touchlab:stately-strict:2.1.0 +com.caverock:androidsvg-aar:1.4 com.google.accompanist:accompanist-pager:0.34.0 com.google.accompanist:accompanist-permissions:0.34.0 com.google.android.gms:play-services-ads-identifier:18.0.0 @@ -172,12 +177,22 @@ com.google.maps.android:maps-ktx:5.0.0 com.google.zxing:core:3.5.3 com.squareup.okhttp3:logging-interceptor:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 -com.squareup.okio:okio-jvm:3.6.0 -com.squareup.okio:okio:3.6.0 +com.squareup.okio:okio-jvm:3.9.1 +com.squareup.okio:okio:3.9.1 com.squareup.retrofit2:adapter-rxjava2:2.11.0 com.squareup.retrofit2:converter-gson:2.11.0 com.squareup.retrofit2:retrofit:2.11.0 dev.chrisbanes.snapper:snapper:0.2.2 +io.coil-kt.coil3:coil-android:3.0.4 +io.coil-kt.coil3:coil-core-android:3.0.4 +io.coil-kt.coil3:coil-core:3.0.4 +io.coil-kt.coil3:coil-network-core-android:3.0.4 +io.coil-kt.coil3:coil-network-core:3.0.4 +io.coil-kt.coil3:coil-network-ktor3-android:3.0.4 +io.coil-kt.coil3:coil-network-ktor3:3.0.4 +io.coil-kt.coil3:coil-svg-android:3.0.4 +io.coil-kt.coil3:coil-svg:3.0.4 +io.coil-kt.coil3:coil:3.0.4 io.github.mr0xf00:easycrop:0.1.1 io.insert-koin:koin-android:4.0.1-RC1 io.insert-koin:koin-androidx-compose:4.0.1-RC1 @@ -192,6 +207,24 @@ io.insert-koin:koin-core-jvm:4.0.1-RC1 io.insert-koin:koin-core-viewmodel-jvm:4.0.1-RC1 io.insert-koin:koin-core-viewmodel:4.0.1-RC1 io.insert-koin:koin-core:4.0.1-RC1 +io.ktor:ktor-client-core-jvm:3.0.1 +io.ktor:ktor-client-core:3.0.1 +io.ktor:ktor-events-jvm:3.0.1 +io.ktor:ktor-events:3.0.1 +io.ktor:ktor-http-jvm:3.0.1 +io.ktor:ktor-http:3.0.1 +io.ktor:ktor-io-jvm:3.0.1 +io.ktor:ktor-io:3.0.1 +io.ktor:ktor-serialization-jvm:3.0.1 +io.ktor:ktor-serialization:3.0.1 +io.ktor:ktor-sse-jvm:3.0.1 +io.ktor:ktor-sse:3.0.1 +io.ktor:ktor-utils-jvm:3.0.1 +io.ktor:ktor-utils:3.0.1 +io.ktor:ktor-websocket-serialization-jvm:3.0.1 +io.ktor:ktor-websocket-serialization:3.0.1 +io.ktor:ktor-websockets-jvm:3.0.1 +io.ktor:ktor-websockets:3.0.1 io.michaelrocks:libphonenumber-android:8.13.35 io.reactivex.rxjava2:rxjava:2.2.21 jakarta.inject:jakarta.inject-api:2.0.1 @@ -231,7 +264,7 @@ org.jetbrains.kotlin:kotlin-android-extensions-runtime:2.1.0 org.jetbrains.kotlin:kotlin-parcelize-runtime:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-common:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.1.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 org.jetbrains.kotlin:kotlin-stdlib:2.1.0 org.jetbrains.kotlinx:atomicfu-jvm:0.23.2 org.jetbrains.kotlinx:atomicfu:0.23.2 @@ -242,8 +275,15 @@ org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.10.1 +org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.1 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1 +org.jetbrains.kotlinx:kotlinx-datetime:0.6.1 +org.jetbrains.kotlinx:kotlinx-io-bytestring-jvm:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-bytestring:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-core-jvm:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-core:0.5.4 org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3 @@ -254,3 +294,4 @@ org.jspecify:jspecify:1.0.0 org.mockito:mockito-core:5.6.0 org.objenesis:objenesis:3.3 org.reactivestreams:reactive-streams:1.0.4 +org.slf4j:slf4j-api:2.0.16 diff --git a/androidApp/dependencies/prodDebugRuntimeClasspath.txt b/androidApp/dependencies/prodDebugRuntimeClasspath.txt index b6e6f4a16..c5a0dba25 100644 --- a/androidApp/dependencies/prodDebugRuntimeClasspath.txt +++ b/androidApp/dependencies/prodDebugRuntimeClasspath.txt @@ -2,8 +2,8 @@ androidx.activity:activity-compose:1.9.3 androidx.activity:activity-ktx:1.9.3 androidx.activity:activity:1.9.3 androidx.annotation:annotation-experimental:1.4.1 -androidx.annotation:annotation-jvm:1.8.1 -androidx.annotation:annotation:1.8.1 +androidx.annotation:annotation-jvm:1.9.1 +androidx.annotation:annotation:1.9.1 androidx.appcompat:appcompat-resources:1.7.0 androidx.appcompat:appcompat:1.7.0 androidx.arch.core:core-common:2.2.0 @@ -72,7 +72,7 @@ androidx.documentfile:documentfile:1.0.0 androidx.drawerlayout:drawerlayout:1.0.0 androidx.emoji2:emoji2-views-helper:1.3.0 androidx.emoji2:emoji2:1.3.0 -androidx.exifinterface:exifinterface:1.3.2 +androidx.exifinterface:exifinterface:1.3.7 androidx.fragment:fragment-ktx:1.8.5 androidx.fragment:fragment:1.8.5 androidx.graphics:graphics-path:1.0.1 @@ -129,12 +129,17 @@ androidx.versionedparcelable:versionedparcelable:1.1.1 androidx.viewpager:viewpager:1.0.0 app.cash.turbine:turbine-jvm:1.1.0 app.cash.turbine:turbine:1.1.0 +co.touchlab:kermit-android-debug:2.0.4 +co.touchlab:kermit-core-android-debug:2.0.4 +co.touchlab:kermit-core:2.0.4 +co.touchlab:kermit:2.0.4 co.touchlab:stately-concurrency-jvm:2.1.0 co.touchlab:stately-concurrency:2.1.0 co.touchlab:stately-concurrent-collections-jvm:2.1.0 co.touchlab:stately-concurrent-collections:2.1.0 co.touchlab:stately-strict-jvm:2.1.0 co.touchlab:stately-strict:2.1.0 +com.caverock:androidsvg-aar:1.4 com.google.accompanist:accompanist-pager:0.34.0 com.google.accompanist:accompanist-permissions:0.34.0 com.google.android.gms:play-services-ads-identifier:18.0.0 @@ -177,12 +182,22 @@ com.google.maps.android:maps-ktx:5.0.0 com.google.zxing:core:3.5.3 com.squareup.okhttp3:logging-interceptor:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 -com.squareup.okio:okio-jvm:3.6.0 -com.squareup.okio:okio:3.6.0 +com.squareup.okio:okio-jvm:3.9.1 +com.squareup.okio:okio:3.9.1 com.squareup.retrofit2:adapter-rxjava2:2.11.0 com.squareup.retrofit2:converter-gson:2.11.0 com.squareup.retrofit2:retrofit:2.11.0 dev.chrisbanes.snapper:snapper:0.2.2 +io.coil-kt.coil3:coil-android:3.0.4 +io.coil-kt.coil3:coil-core-android:3.0.4 +io.coil-kt.coil3:coil-core:3.0.4 +io.coil-kt.coil3:coil-network-core-android:3.0.4 +io.coil-kt.coil3:coil-network-core:3.0.4 +io.coil-kt.coil3:coil-network-ktor3-android:3.0.4 +io.coil-kt.coil3:coil-network-ktor3:3.0.4 +io.coil-kt.coil3:coil-svg-android:3.0.4 +io.coil-kt.coil3:coil-svg:3.0.4 +io.coil-kt.coil3:coil:3.0.4 io.github.mr0xf00:easycrop:0.1.1 io.insert-koin:koin-android:4.0.1-RC1 io.insert-koin:koin-androidx-compose:4.0.1-RC1 @@ -197,6 +212,24 @@ io.insert-koin:koin-core-jvm:4.0.1-RC1 io.insert-koin:koin-core-viewmodel-jvm:4.0.1-RC1 io.insert-koin:koin-core-viewmodel:4.0.1-RC1 io.insert-koin:koin-core:4.0.1-RC1 +io.ktor:ktor-client-core-jvm:3.0.1 +io.ktor:ktor-client-core:3.0.1 +io.ktor:ktor-events-jvm:3.0.1 +io.ktor:ktor-events:3.0.1 +io.ktor:ktor-http-jvm:3.0.1 +io.ktor:ktor-http:3.0.1 +io.ktor:ktor-io-jvm:3.0.1 +io.ktor:ktor-io:3.0.1 +io.ktor:ktor-serialization-jvm:3.0.1 +io.ktor:ktor-serialization:3.0.1 +io.ktor:ktor-sse-jvm:3.0.1 +io.ktor:ktor-sse:3.0.1 +io.ktor:ktor-utils-jvm:3.0.1 +io.ktor:ktor-utils:3.0.1 +io.ktor:ktor-websocket-serialization-jvm:3.0.1 +io.ktor:ktor-websocket-serialization:3.0.1 +io.ktor:ktor-websockets-jvm:3.0.1 +io.ktor:ktor-websockets:3.0.1 io.michaelrocks:libphonenumber-android:8.13.35 io.reactivex.rxjava2:rxjava:2.2.21 jakarta.inject:jakarta.inject-api:2.0.1 @@ -236,7 +269,7 @@ org.jetbrains.kotlin:kotlin-android-extensions-runtime:2.1.0 org.jetbrains.kotlin:kotlin-parcelize-runtime:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-common:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.1.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 org.jetbrains.kotlin:kotlin-stdlib:2.1.0 org.jetbrains.kotlinx:atomicfu-jvm:0.23.2 org.jetbrains.kotlinx:atomicfu:0.23.2 @@ -247,8 +280,15 @@ org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.10.1 +org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.1 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1 +org.jetbrains.kotlinx:kotlinx-datetime:0.6.1 +org.jetbrains.kotlinx:kotlinx-io-bytestring-jvm:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-bytestring:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-core-jvm:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-core:0.5.4 org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3 @@ -259,3 +299,4 @@ org.jspecify:jspecify:1.0.0 org.mockito:mockito-core:5.6.0 org.objenesis:objenesis:3.3 org.reactivestreams:reactive-streams:1.0.4 +org.slf4j:slf4j-api:2.0.16 diff --git a/androidApp/dependencies/prodReleaseRuntimeClasspath.txt b/androidApp/dependencies/prodReleaseRuntimeClasspath.txt index a2d888fbb..aa400c17a 100644 --- a/androidApp/dependencies/prodReleaseRuntimeClasspath.txt +++ b/androidApp/dependencies/prodReleaseRuntimeClasspath.txt @@ -2,8 +2,8 @@ androidx.activity:activity-compose:1.9.3 androidx.activity:activity-ktx:1.9.3 androidx.activity:activity:1.9.3 androidx.annotation:annotation-experimental:1.4.1 -androidx.annotation:annotation-jvm:1.8.1 -androidx.annotation:annotation:1.8.1 +androidx.annotation:annotation-jvm:1.9.1 +androidx.annotation:annotation:1.9.1 androidx.appcompat:appcompat-resources:1.7.0 androidx.appcompat:appcompat:1.7.0 androidx.arch.core:core-common:2.2.0 @@ -67,7 +67,7 @@ androidx.documentfile:documentfile:1.0.0 androidx.drawerlayout:drawerlayout:1.0.0 androidx.emoji2:emoji2-views-helper:1.3.0 androidx.emoji2:emoji2:1.3.0 -androidx.exifinterface:exifinterface:1.3.2 +androidx.exifinterface:exifinterface:1.3.7 androidx.fragment:fragment-ktx:1.8.5 androidx.fragment:fragment:1.8.5 androidx.graphics:graphics-path:1.0.1 @@ -124,12 +124,17 @@ androidx.versionedparcelable:versionedparcelable:1.1.1 androidx.viewpager:viewpager:1.0.0 app.cash.turbine:turbine-jvm:1.1.0 app.cash.turbine:turbine:1.1.0 +co.touchlab:kermit-android:2.0.4 +co.touchlab:kermit-core-android:2.0.4 +co.touchlab:kermit-core:2.0.4 +co.touchlab:kermit:2.0.4 co.touchlab:stately-concurrency-jvm:2.1.0 co.touchlab:stately-concurrency:2.1.0 co.touchlab:stately-concurrent-collections-jvm:2.1.0 co.touchlab:stately-concurrent-collections:2.1.0 co.touchlab:stately-strict-jvm:2.1.0 co.touchlab:stately-strict:2.1.0 +com.caverock:androidsvg-aar:1.4 com.google.accompanist:accompanist-pager:0.34.0 com.google.accompanist:accompanist-permissions:0.34.0 com.google.android.gms:play-services-ads-identifier:18.0.0 @@ -172,12 +177,22 @@ com.google.maps.android:maps-ktx:5.0.0 com.google.zxing:core:3.5.3 com.squareup.okhttp3:logging-interceptor:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 -com.squareup.okio:okio-jvm:3.6.0 -com.squareup.okio:okio:3.6.0 +com.squareup.okio:okio-jvm:3.9.1 +com.squareup.okio:okio:3.9.1 com.squareup.retrofit2:adapter-rxjava2:2.11.0 com.squareup.retrofit2:converter-gson:2.11.0 com.squareup.retrofit2:retrofit:2.11.0 dev.chrisbanes.snapper:snapper:0.2.2 +io.coil-kt.coil3:coil-android:3.0.4 +io.coil-kt.coil3:coil-core-android:3.0.4 +io.coil-kt.coil3:coil-core:3.0.4 +io.coil-kt.coil3:coil-network-core-android:3.0.4 +io.coil-kt.coil3:coil-network-core:3.0.4 +io.coil-kt.coil3:coil-network-ktor3-android:3.0.4 +io.coil-kt.coil3:coil-network-ktor3:3.0.4 +io.coil-kt.coil3:coil-svg-android:3.0.4 +io.coil-kt.coil3:coil-svg:3.0.4 +io.coil-kt.coil3:coil:3.0.4 io.github.mr0xf00:easycrop:0.1.1 io.insert-koin:koin-android:4.0.1-RC1 io.insert-koin:koin-androidx-compose:4.0.1-RC1 @@ -192,6 +207,24 @@ io.insert-koin:koin-core-jvm:4.0.1-RC1 io.insert-koin:koin-core-viewmodel-jvm:4.0.1-RC1 io.insert-koin:koin-core-viewmodel:4.0.1-RC1 io.insert-koin:koin-core:4.0.1-RC1 +io.ktor:ktor-client-core-jvm:3.0.1 +io.ktor:ktor-client-core:3.0.1 +io.ktor:ktor-events-jvm:3.0.1 +io.ktor:ktor-events:3.0.1 +io.ktor:ktor-http-jvm:3.0.1 +io.ktor:ktor-http:3.0.1 +io.ktor:ktor-io-jvm:3.0.1 +io.ktor:ktor-io:3.0.1 +io.ktor:ktor-serialization-jvm:3.0.1 +io.ktor:ktor-serialization:3.0.1 +io.ktor:ktor-sse-jvm:3.0.1 +io.ktor:ktor-sse:3.0.1 +io.ktor:ktor-utils-jvm:3.0.1 +io.ktor:ktor-utils:3.0.1 +io.ktor:ktor-websocket-serialization-jvm:3.0.1 +io.ktor:ktor-websocket-serialization:3.0.1 +io.ktor:ktor-websockets-jvm:3.0.1 +io.ktor:ktor-websockets:3.0.1 io.michaelrocks:libphonenumber-android:8.13.35 io.reactivex.rxjava2:rxjava:2.2.21 jakarta.inject:jakarta.inject-api:2.0.1 @@ -231,7 +264,7 @@ org.jetbrains.kotlin:kotlin-android-extensions-runtime:2.1.0 org.jetbrains.kotlin:kotlin-parcelize-runtime:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-common:2.1.0 org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.1.0 -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 org.jetbrains.kotlin:kotlin-stdlib:2.1.0 org.jetbrains.kotlinx:atomicfu-jvm:0.23.2 org.jetbrains.kotlinx:atomicfu:0.23.2 @@ -242,8 +275,15 @@ org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.10.1 +org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm:1.10.1 org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.1 +org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.1 +org.jetbrains.kotlinx:kotlinx-datetime:0.6.1 +org.jetbrains.kotlinx:kotlinx-io-bytestring-jvm:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-bytestring:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-core-jvm:0.5.4 +org.jetbrains.kotlinx:kotlinx-io-core:0.5.4 org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3 org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3 @@ -254,3 +294,4 @@ org.jspecify:jspecify:1.0.0 org.mockito:mockito-core:5.6.0 org.objenesis:objenesis:3.3 org.reactivestreams:reactive-streams:1.0.4 +org.slf4j:slf4j-api:2.0.16 diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index fb4ec9a35..551f296d8 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -8,14 +8,55 @@ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md */ plugins { - alias(libs.plugins.mifos.android.library) - alias(libs.plugins.mifos.android.hilt) + alias(libs.plugins.mifos.kmp.library) } android { namespace = "org.mifos.mobile.core.common" } -dependencies { +kotlin { + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64(), + ).forEach { + it.binaries.framework { + isStatic = false + export(libs.kermit.simple) + } + } + + sourceSets { + commonMain.dependencies { + implementation(libs.kotlinx.coroutines.core) + api(libs.coil.kt) + api(libs.coil.core) + api(libs.coil.svg) + api(libs.coil.network.ktor) + api(libs.kermit.logging) + api(libs.squareup.okio) + api(libs.jb.kotlin.stdlib) + api(libs.kotlinx.datetime) + } + + androidMain.dependencies { + implementation(libs.kotlinx.coroutines.android) + } + commonTest.dependencies { + implementation(libs.kotlinx.coroutines.test) + } + iosMain.dependencies { + api(libs.kermit.simple) + } + desktopMain.dependencies { + implementation(libs.kotlinx.coroutines.swing) + implementation(libs.kotlin.reflect) + } + jsMain.dependencies { + api(libs.jb.kotlin.stdlib.js) + api(libs.jb.kotlin.dom) + } + } } \ No newline at end of file diff --git a/core/common/src/main/AndroidManifest.xml b/core/common/src/androidMain/AndroidManifest.xml similarity index 100% rename from core/common/src/main/AndroidManifest.xml rename to core/common/src/androidMain/AndroidManifest.xml diff --git a/core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.android.kt b/core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.android.kt new file mode 100644 index 000000000..3def7cd38 --- /dev/null +++ b/core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.android.kt @@ -0,0 +1,26 @@ +/* + * 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.common + +import java.text.NumberFormat +import java.util.Currency + +actual object CurrencyFormatter { + actual fun format( + balance: Double?, + currencyCode: String?, + maximumFractionDigits: Int?, + ): String { + val balanceFormatter = NumberFormat.getCurrencyInstance() + balanceFormatter.maximumFractionDigits = maximumFractionDigits ?: 0 + balanceFormatter.currency = Currency.getInstance(currencyCode) + return balanceFormatter.format(balance) + } +} diff --git a/core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/FileUtils.android.kt b/core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/FileUtils.android.kt new file mode 100644 index 000000000..eb86c1186 --- /dev/null +++ b/core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/FileUtils.android.kt @@ -0,0 +1,12 @@ +/* + * 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.common + +actual fun createPlatformFileUtils(): FileUtils = CommonFileUtils() diff --git a/core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.android.kt b/core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.android.kt new file mode 100644 index 000000000..090c434ba --- /dev/null +++ b/core/common/src/androidMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.android.kt @@ -0,0 +1,22 @@ +/* + * 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.common.di + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import org.koin.core.module.Module +import org.koin.core.qualifier.named +import org.koin.dsl.module +import org.mifos.mobile.core.common.MifosDispatchers + +actual val ioDispatcherModule: Module + get() = module { + single(named(MifosDispatchers.IO.name)) { Dispatchers.IO } + } diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/Constants.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/Constants.kt similarity index 100% rename from core/common/src/main/java/org/mifos/mobile/core/common/Constants.kt rename to core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/Constants.kt diff --git a/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.kt new file mode 100644 index 000000000..631b2b8a9 --- /dev/null +++ b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.kt @@ -0,0 +1,18 @@ +/* + * 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.common + +expect object CurrencyFormatter { + fun format(balance: Double?, currencyCode: String?, maximumFractionDigits: Int?): String +} + +fun List.toArrayList(): ArrayList { + return ArrayList(this) +} diff --git a/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DataState.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DataState.kt new file mode 100644 index 000000000..7f1e10f9d --- /dev/null +++ b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DataState.kt @@ -0,0 +1,39 @@ +/* + * 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.mifospay.core.common + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart + +sealed class DataState { + abstract val data: T? + + data object Loading : DataState() { + override val data: Nothing? get() = null + } + + data class Success( + override val data: T, + ) : DataState() + + data class Error( + val exception: Throwable, + override val data: T? = null, + ) : DataState() { + val message = exception.message.toString() + } +} + +fun Flow.asDataStateFlow(): Flow> = + map> { DataState.Success(it) } + .onStart { emit(DataState.Loading) } + .catch { emit(DataState.Error(it, null)) } diff --git a/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DataStateExtensions.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DataStateExtensions.kt new file mode 100644 index 000000000..f2f727a4f --- /dev/null +++ b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DataStateExtensions.kt @@ -0,0 +1,105 @@ +/* + * 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.mifospay.core.common + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.transformWhile + +inline fun DataState.map( + transform: (T) -> R, +): DataState = when (this) { + is DataState.Success -> DataState.Success(transform(data)) + is DataState.Loading -> DataState.Loading + is DataState.Error -> DataState.Error(exception, data?.let(transform)) +} + +inline fun DataState.mapNullable( + transform: (T?) -> R, +): DataState = when (this) { + is DataState.Success -> DataState.Success(data = transform(data)) + is DataState.Loading -> DataState.Loading + is DataState.Error -> DataState.Error(exception = exception, data = transform(data)) +} + +fun Flow>.takeUntilResultSuccess(): Flow> = transformWhile { + emit(it) + it !is DataState.Success +} + +fun combineResults( + dataState1: DataState, + dataState2: DataState, + transform: (t1: T1, t2: T2) -> R, +): DataState { + val nullableTransform: (T1?, T2?) -> R? = { t1, t2 -> + if (t1 != null && t2 != null) transform(t1, t2) else null + } + + return when { + // Error states have highest priority, fail fast. + dataState1 is DataState.Error -> { + DataState.Error( + exception = dataState1.exception, + data = nullableTransform(dataState1.data, dataState2.data), + ) + } + + dataState2 is DataState.Error -> { + DataState.Error( + exception = dataState2.exception, + data = nullableTransform(dataState1.data, dataState2.data), + ) + } + + // Something is still loading, we will wait for all the data. + dataState1 is DataState.Loading || dataState2 is DataState.Loading -> DataState.Loading + + // Pending state for everything while any one piece of data is updating. + // Both states are _root_ide_package_.org.mifospay.core.common.Result.Success and have data + else -> { + @Suppress("UNCHECKED_CAST") + DataState.Success(transform(dataState1.data as T1, dataState2.data as T2)) + } + } +} + +fun combineResults( + dataState1: DataState, + dataState2: DataState, + dataState3: DataState, + transform: (t1: T1, t2: T2, t3: T3) -> R, +): DataState = + dataState1 + .combineResultsWith(dataState2) { t1, t2 -> t1 to t2 } + .combineResultsWith(dataState3) { t1t2Pair, t3 -> + transform(t1t2Pair.first, t1t2Pair.second, t3) + } + +fun combineResults( + dataState1: DataState, + dataState2: DataState, + dataState3: DataState, + dataState4: DataState, + transform: (t1: T1, t2: T2, t3: T3, t4: T4) -> R, +): DataState = + dataState1 + .combineResultsWith(dataState2) { t1, t2 -> t1 to t2 } + .combineResultsWith(dataState3) { t1t2Pair, t3 -> + Triple(t1t2Pair.first, t1t2Pair.second, t3) + } + .combineResultsWith(dataState4) { t1t2t3Triple, t3 -> + transform(t1t2t3Triple.first, t1t2t3Triple.second, t1t2t3Triple.third, t3) + } + +fun DataState.combineResultsWith( + dataState2: DataState, + transform: (t1: T1, t2: T2) -> R, +): DataState = + combineResults(this, dataState2, transform) diff --git a/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DateHelper.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DateHelper.kt new file mode 100644 index 000000000..87f6073be --- /dev/null +++ b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/DateHelper.kt @@ -0,0 +1,358 @@ +/* + * 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.common + +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.Month +import kotlinx.datetime.TimeZone +import kotlinx.datetime.format +import kotlinx.datetime.format.FormatStringsInDatetimeFormats +import kotlinx.datetime.format.byUnicodePattern +import kotlinx.datetime.toLocalDateTime + +@OptIn(FormatStringsInDatetimeFormats::class) +object DateHelper { + /* + * This is the full month format for the date picker. + * "dd MM yyyy" is the format of the date picker. + */ + const val FULL_MONTH = "dd MM yyyy" + + /* + * This is the short month format for the date picker. + * "dd-MM-yyyy" is the format of the date picker. + */ + const val SHORT_MONTH = "dd-MM-yyyy" + + const val MONTH_FORMAT = "dd MMMM" + + private val fullMonthFormat = LocalDateTime.Format { + byUnicodePattern(FULL_MONTH) + } + + private val shortMonthFormat = LocalDateTime.Format { + byUnicodePattern(SHORT_MONTH) + } + + /** + * the result string uses the list given in a reverse order ([x, y, z] results in "z y x") + * + * @param integersOfDate [year-month-day] (ex [2016, 4, 14]) + * @return date in the format day month year (ex 14 Apr 2016) + */ + fun getDateAsString(integersOfDate: List): String { + val stringBuilder = StringBuilder() + stringBuilder.append(integersOfDate[2]) + .append(' ') + .append(getMonthName(integersOfDate[1])) + .append(' ') + .append(integersOfDate[0]) + return stringBuilder.toString() + } + + fun getDateAsString(integersOfDate: List, pattern: String): String { + return getFormatConverter( + currentFormat = FULL_MONTH, + requiredFormat = pattern, + dateString = getDateAsString(integersOfDate.map { it.toInt() }), + ) + } + + /** + * This Method converting the dd-MM-yyyy format type date string into dd MMMM yyyy + * + * @param format Final Format of date string + * @param dateString date string + * @return dd MMMM yyyy format date string. + */ + fun getSpecificFormat(format: String, dateString: String): String { + val pickerFormat = shortMonthFormat + val finalFormat = LocalDateTime.Format { byUnicodePattern(format) } + + return finalFormat.format(pickerFormat.parse(dateString)) + } + + private fun getFormatConverter( + currentFormat: String, + requiredFormat: String, + dateString: String, + ): String { + val pickerFormat = LocalDateTime.Format { byUnicodePattern(currentFormat) } + val finalFormat = LocalDateTime.Format { byUnicodePattern(requiredFormat) } + + return pickerFormat.parse(dateString).format(finalFormat) + } + + /** + * Gets the date string in the format "dd-MM-yyyy" from an array of integers representing the year, month, and day. + * + * @param dateComponents An array of three integers representing the year, month, and day, e.g. [2024, 11, 10] + * @return The date string in the format "dd-MM-yyyy", e.g. "10-11-2024" + */ + fun formatTransferDate(dateComponents: List, pattern: String = SHORT_MONTH): String { + require(dateComponents.size == 3) { "dateComponents must have exactly 3 elements" } + val (year, month, day) = dateComponents + + val localDate = LocalDate(year, Month(month), day) + return localDate.format(pattern) + } + + // Extension function to format LocalDate + fun LocalDate.format(pattern: String): String { + val year = this.year.toString().padStart(4, '0') + val month = this.monthNumber.toString().padStart(2, '0') + val day = this.dayOfMonth.toString().padStart(2, '0') + + return pattern + .replace("yyyy", year) + .replace("MM", month) + .replace("dd", day) + } + + /** + * @param month an integer from 1 to 12 + * @return string representation of the month like Jan or Feb..etc + */ + private fun getMonthName(month: Int): String { + return when (month) { + 1 -> "Jan" + 2 -> "Feb" + 3 -> "Mar" + 4 -> "Apr" + 5 -> "May" + 6 -> "Jun" + 7 -> "Jul" + 8 -> "Aug" + 9 -> "Sep" + 10 -> "Oct" + 11 -> "Nov" + 12 -> "Dec" + else -> throw IllegalArgumentException("Month should be between 1 and 12") + } + } + + /** + * Input timestamp string in milliseconds + * Example timestamp "1698278400000" + * Output examples: "dd-MM-yyyy" - "14-04-2016" + */ + fun getDateAsStringFromLong(timeInMillis: Long): String { + val instant = Instant.fromEpochMilliseconds(timeInMillis) + .toLocalDateTime(TimeZone.currentSystemDefault()) + + return instant.format(shortMonthFormat) + } + + /** + * Input timestamp string in milliseconds + * Example timestamp "1698278400000" + * Output examples: "14 April" + */ + fun getMonthAsStringFromLong(timeInMillis: Long): String { + val instant = Instant.fromEpochMilliseconds(timeInMillis) + .toLocalDateTime(TimeZone.currentSystemDefault()) + + val monthName = instant.month.name.lowercase().capitalize() + + return "${instant.dayOfMonth} $monthName" + } + + /** + * Gets the date string in the format "day month year" from an array of integers representing the day and month. + * + * @param integersOfDate An array of two integers representing the day and month, e.g. [11, 10] + * @return The date string in the format "day month year", e.g. "11 October" + */ + fun getDateMonthString(integersOfDate: List): String { + require(integersOfDate.size == 2) { "integersOfDate must have exactly 2 elements" } + val (day, month) = integersOfDate + + val monthName = when (month) { + 1 -> "January" + 2 -> "February" + 3 -> "March" + 4 -> "April" + 5 -> "May" + 6 -> "June" + 7 -> "July" + 8 -> "August" + 9 -> "September" + 10 -> "October" + 11 -> "November" + 12 -> "December" + else -> throw IllegalArgumentException("Invalid month value: $month") + } + + return "$day $monthName" + } + + /** + * Handles the specific format "yyyy-MM-dd HH:mm:ss.SSSSSS" + * For example "2024-09-19 05:41:18.558995" + * Possible outputs depending on current date: + * "Today at 05:41" + * "Tomorrow at 05:41" + */ + fun String.toFormattedDateTime(): String { + // Parse the datetime string + val dateTime = try { + // Split into date and time parts + val (datePart, timePart) = this.split(" ") + // Remove microseconds from time part + val simplifiedTime = timePart.split(".")[0] + // Combine date and simplified time + val isoString = "${datePart}T$simplifiedTime" + // Parse to LocalDateTime + LocalDateTime.parse(isoString) + } catch (e: Exception) { + return this // Return original string if parsing fails + } + + val timeZone = TimeZone.currentSystemDefault() + val now = Clock.System.now() + val nowDateTime = now.toLocalDateTime(timeZone) + + return when { + // Same year + nowDateTime.year == dateTime.year -> { + when { + // Same month + nowDateTime.monthNumber == dateTime.monthNumber -> { + when { + // Tomorrow + dateTime.dayOfMonth - nowDateTime.dayOfMonth == 1 -> { + "Tomorrow at ${dateTime.format()}" + } + // Today + dateTime.dayOfMonth == nowDateTime.dayOfMonth -> { + "Today at ${dateTime.format()}" + } + // Yesterday + nowDateTime.dayOfMonth - dateTime.dayOfMonth == 1 -> { + "Yesterday at ${dateTime.format()}" + } + // Same month but different day + else -> { + "${ + dateTime.month.name.lowercase().capitalize() + } ${dateTime.dayOfMonth}, ${dateTime.format()}" + } + } + } + // Different month, same year + else -> { + "${ + dateTime.month.name.lowercase().capitalize() + } ${dateTime.dayOfMonth}, ${dateTime.format()}" + } + } + } + // Different year + else -> { + "${ + dateTime.month.name.lowercase().capitalize() + } ${dateTime.dayOfMonth} ${dateTime.year}, ${dateTime.format()}" + } + } + } + + /** + * Input timestamp string in milliseconds + * Example timestamp "1698278400000" + * Output examples: + * "Today at 12:00" + * "Tomorrow at 15:30" + */ + fun String.toPrettyDate(): String { + val timestamp = this.toLong() + val instant = Instant.fromEpochMilliseconds(timestamp) + val timeZone = TimeZone.currentSystemDefault() + val nowDateTime = Clock.System.now().toLocalDateTime(timeZone) + val neededDateTime = instant.toLocalDateTime(timeZone) + + return when { + // Same year + nowDateTime.year == neededDateTime.year -> { + when { + // Same month + nowDateTime.monthNumber == neededDateTime.monthNumber -> { + when { + // Tomorrow + neededDateTime.dayOfMonth - nowDateTime.dayOfMonth == 1 -> { + val time = neededDateTime.format() + "Tomorrow at $time" + } + // Today + neededDateTime.dayOfMonth == nowDateTime.dayOfMonth -> { + val time = neededDateTime.format() + "Today at $time" + } + // Yesterday + nowDateTime.dayOfMonth - neededDateTime.dayOfMonth == 1 -> { + val time = neededDateTime.format() + "Yesterday at $time" + } + // Same month but different day + else -> { + "${ + neededDateTime.month.name.lowercase().capitalize() + } ${neededDateTime.dayOfMonth}, ${neededDateTime.format()}" + } + } + } + // Different month, same year + else -> { + "${ + neededDateTime.month.name.lowercase().capitalize() + } ${neededDateTime.dayOfMonth}, ${neededDateTime.format()}" + } + } + } + // Different year + else -> { + "${ + neededDateTime.month.name.lowercase().capitalize() + } ${neededDateTime.dayOfMonth} ${neededDateTime.year}, ${neededDateTime.format()}" + } + } + } + + // Helper function to format time + private fun LocalDateTime.format(): String { + return "${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}" + } + + // Extension to capitalize first letter + private fun String.capitalize() = replaceFirstChar { + if (it.isLowerCase()) it.titlecase() else it.toString() + } + + val currentDate = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) + + /** + * This is the full date format for the date picker. + * "dd MM yyyy" is the format of the date picker. + */ + val formattedFullDate = currentDate.format(fullMonthFormat) + + /** + * This is the short date format for the date picker. + * "dd-MM-yyyy" is the format of the date picker. + */ + val formattedShortDate = currentDate.format(shortMonthFormat) + + fun getMonth(month: Int): String { + require(month in 1..12) { "Month should be between 1 and 12" } + return Month.entries[month - 1].name.lowercase().replaceFirstChar { it.uppercase() } + } +} diff --git a/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/FileUtils.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/FileUtils.kt new file mode 100644 index 000000000..17097e525 --- /dev/null +++ b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/FileUtils.kt @@ -0,0 +1,39 @@ +/* + * 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.common + +import co.touchlab.kermit.Logger +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +interface FileUtils { + suspend fun writeInputStreamDataToFile(inputStream: ByteArray, filePath: String): Boolean + + companion object { + val logger = Logger.withTag("FileUtils") + } +} + +expect fun createPlatformFileUtils(): FileUtils + +class CommonFileUtils : FileUtils { + override suspend fun writeInputStreamDataToFile( + inputStream: ByteArray, + filePath: String, + ): Boolean = + withContext(Dispatchers.Default) { + try { + true + } catch (e: Exception) { + FileUtils.logger.e { "Error writing file: ${e.message}" } + false + } + } +} diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/utils/LanguageHelper.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/LanguageHelper.kt similarity index 96% rename from core/common/src/main/java/org/mifos/mobile/core/common/utils/LanguageHelper.kt rename to core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/LanguageHelper.kt index 20ae98cf5..6a59abfc1 100644 --- a/core/common/src/main/java/org/mifos/mobile/core/common/utils/LanguageHelper.kt +++ b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/LanguageHelper.kt @@ -7,14 +7,14 @@ * * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md */ -package org.mifos.mobile.core.common.utils - +package org.mifos.mobile.core.common +/* import android.content.Context import android.preference.PreferenceManager -import org.mifos.mobile.core.common.R import java.util.Locale - +*/ object LanguageHelper { + /* fun onAttach(context: Context): Context? { val preferences = PreferenceManager.getDefaultSharedPreferences(context) return if (preferences.getBoolean( @@ -61,4 +61,5 @@ object LanguageHelper { configuration.setLayoutDirection(locale) return context.createConfigurationContext(configuration) } + */ } diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/network/MifosDispatchers.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/MifosDispatchers.kt similarity index 57% rename from core/common/src/main/java/org/mifos/mobile/core/common/network/MifosDispatchers.kt rename to core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/MifosDispatchers.kt index 6780bdda4..da4ba915a 100644 --- a/core/common/src/main/java/org/mifos/mobile/core/common/network/MifosDispatchers.kt +++ b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/MifosDispatchers.kt @@ -7,16 +7,20 @@ * * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md */ -package org.mifos.mobile.core.common.network +package org.mifos.mobile.core.common -import javax.inject.Qualifier -import kotlin.annotation.AnnotationRetention.RUNTIME +import org.koin.core.annotation.Qualifier @Qualifier -@Retention(RUNTIME) -annotation class Dispatcher(val dispatcher: MifosDispatchers) +@Retention(AnnotationRetention.RUNTIME) +annotation class Dispatcher(val mifosDispatcher: MifosDispatchers) enum class MifosDispatchers { Default, IO, + Unconfined, } + +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class ApplicationScope diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/utils/SymbolsUtils.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/SymbolsUtils.kt similarity index 89% rename from core/common/src/main/java/org/mifos/mobile/core/common/utils/SymbolsUtils.kt rename to core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/SymbolsUtils.kt index 869180758..6df94794a 100644 --- a/core/common/src/main/java/org/mifos/mobile/core/common/utils/SymbolsUtils.kt +++ b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/SymbolsUtils.kt @@ -7,7 +7,7 @@ * * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md */ -package org.mifos.mobile.core.common.utils +package org.mifos.mobile.core.common object SymbolsUtils { const val PERCENT = "%" diff --git a/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/Utils.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/Utils.kt new file mode 100644 index 000000000..4b4837c06 --- /dev/null +++ b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/Utils.kt @@ -0,0 +1,29 @@ +/* + * 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.common + +object Utils { + fun generateFormString(data: Array>): String { + val formString = StringBuilder() + for (aData in data) { + formString.append(aData[0]).append(" : ").append(aData[1]).append('\n') + } + return formString.toString() + } + + fun formatTransactionType(type: String?): String = + type?.lowercase() + ?.split("_") + ?.joinToString(" ") { word -> + word.replaceFirstChar { it.uppercase() } + } + ?.trim() + ?: "" +} diff --git a/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.kt b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.kt new file mode 100644 index 000000000..669dd5206 --- /dev/null +++ b/core/common/src/commonMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.kt @@ -0,0 +1,30 @@ +/* + * 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.common.di + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import org.koin.core.module.Module +import org.koin.core.qualifier.named +import org.koin.dsl.module +import org.mifos.mobile.core.common.MifosDispatchers + +val DispatchersModule = module { + includes(ioDispatcherModule) + single(named(MifosDispatchers.Default.name)) { Dispatchers.Default } + single(named(MifosDispatchers.Unconfined.name)) { Dispatchers.Unconfined } + single(named("ApplicationScope")) { + CoroutineScope(SupervisorJob() + Dispatchers.Default) + } +} + +expect val ioDispatcherModule: Module diff --git a/core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.jvm.kt b/core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.jvm.kt new file mode 100644 index 000000000..452847f81 --- /dev/null +++ b/core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.jvm.kt @@ -0,0 +1,26 @@ +/* + * 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.common + +import java.text.NumberFormat +import java.util.Currency + +actual object CurrencyFormatter { + actual fun format( + balance: Double?, + currencyCode: String?, + maximumFractionDigits: Int?, + ): String { + val numberFormat = NumberFormat.getCurrencyInstance() + numberFormat.maximumFractionDigits = maximumFractionDigits ?: 0 + numberFormat.currency = Currency.getInstance(currencyCode) + return numberFormat.format(balance) + } +} diff --git a/core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/FileUtils.jvm.kt b/core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/FileUtils.jvm.kt new file mode 100644 index 000000000..eb86c1186 --- /dev/null +++ b/core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/FileUtils.jvm.kt @@ -0,0 +1,12 @@ +/* + * 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.common + +actual fun createPlatformFileUtils(): FileUtils = CommonFileUtils() diff --git a/core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.desktop.kt b/core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.desktop.kt new file mode 100644 index 000000000..461fe75c4 --- /dev/null +++ b/core/common/src/desktopMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.desktop.kt @@ -0,0 +1,22 @@ +/* + * 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.common.di + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import org.koin.core.module.Module +import org.koin.core.qualifier.named +import org.koin.dsl.module +import org.mifos.mobile.core.common.MifosDispatchers + +actual val ioDispatcherModule: Module + get() = module { + single(named(MifosDispatchers.IO.name)) { Dispatchers.Default } + } diff --git a/core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.js.kt b/core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.js.kt new file mode 100644 index 000000000..f2eda484e --- /dev/null +++ b/core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.js.kt @@ -0,0 +1,36 @@ +/* + * 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.common + +actual object CurrencyFormatter { + actual fun format( + balance: Double?, + currencyCode: String?, + maximumFractionDigits: Int?, + ): String { + if (balance == null || currencyCode == null) { + return "" + } + + val options = js("{}").unsafeCast() + options.style = "currency" + options.currency = currencyCode + if (maximumFractionDigits != null) { + options.maximumFractionDigits = maximumFractionDigits + } + + return try { + js("new Intl.NumberFormat('en-US', options).format(balance)").toString() + } catch (e: Exception) { + console.error("Error formatting currency: ${e.message}") + balance.toString() + } + } +} diff --git a/core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/FileUtils.js.kt b/core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/FileUtils.js.kt new file mode 100644 index 000000000..eb86c1186 --- /dev/null +++ b/core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/FileUtils.js.kt @@ -0,0 +1,12 @@ +/* + * 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.common + +actual fun createPlatformFileUtils(): FileUtils = CommonFileUtils() diff --git a/core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.js.kt b/core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.js.kt new file mode 100644 index 000000000..461fe75c4 --- /dev/null +++ b/core/common/src/jsMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.js.kt @@ -0,0 +1,22 @@ +/* + * 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.common.di + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import org.koin.core.module.Module +import org.koin.core.qualifier.named +import org.koin.dsl.module +import org.mifos.mobile.core.common.MifosDispatchers + +actual val ioDispatcherModule: Module + get() = module { + single(named(MifosDispatchers.IO.name)) { Dispatchers.Default } + } diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/ApiEndPoints.kt b/core/common/src/main/java/org/mifos/mobile/core/common/ApiEndPoints.kt deleted file mode 100644 index ab89e95a1..000000000 --- a/core/common/src/main/java/org/mifos/mobile/core/common/ApiEndPoints.kt +++ /dev/null @@ -1,25 +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.common - -object ApiEndPoints { - // This class contains all the Constants for API End Points - const val AUTHENTICATION = "authentication" - const val CLIENTS = "clients" - const val SAVINGS_ACCOUNTS = "savingsaccounts" - const val LOAN_ACCOUNTS = "loanAccounts" - const val RECURRING_ACCOUNTS = "recurringdepositaccounts" - const val LOANS = "loans" - const val ACCOUNT_TRANSFER = "accounttransfers" - const val BENEFICIARIES = "beneficiaries" - const val REGISTRATION = "registration" - const val DEVICE = "device" - const val USER = "user" -} diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/Network.kt b/core/common/src/main/java/org/mifos/mobile/core/common/Network.kt deleted file mode 100644 index 5f5f941b0..000000000 --- a/core/common/src/main/java/org/mifos/mobile/core/common/Network.kt +++ /dev/null @@ -1,131 +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.common - -import android.annotation.SuppressLint -import android.content.Context -import android.net.ConnectivityManager -import android.net.NetworkInfo -import android.telephony.TelephonyManager - -object Network { - /** - * Get the network info - * - * @param context - * @return - */ - @SuppressLint("MissingPermission") - private fun getNetworkInfo(context: Context): NetworkInfo? { - val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - return cm.activeNetworkInfo - } - - /** - * Check if there is any connectivity - * - * @param context - * @return - */ - @JvmStatic - fun isConnected(context: Context?): Boolean { - val info = getNetworkInfo(context!!) - if (info != null) { - return info.isConnected - } - return false - } - - /** - * Check if there is any connectivity to a Wifi network - * - * @param context - * @param type - * @return - */ - fun isConnectedWifi(context: Context): Boolean { - val info = getNetworkInfo(context) - if (info != null) { - return info.isConnected && info.type == ConnectivityManager.TYPE_WIFI - } - return false - } - - /** - * Check if there is any connectivity to a mobile network - * - * @param context - * @param type - * @return - */ - fun isConnectedMobile(context: Context): Boolean { - val info = getNetworkInfo(context) - if (info != null) { - return info.isConnected && info.type == ConnectivityManager.TYPE_MOBILE - } - return false - } - - /** - * Check if there is fast connectivity - * - * @param context - * @return - */ - fun isConnectedFast(context: Context): Boolean { - val info = getNetworkInfo(context) - if (info != null) { - return info.isConnected && isConnectionFast(info.type, info.subtype) - } - return false - } - - /** - * Check if the connection is fast - * - * @param type - * @param subType - * @return - */ - @Suppress("CyclomaticComplexMethod") - private fun isConnectionFast(type: Int, subType: Int): Boolean { - return when (type) { - ConnectivityManager.TYPE_WIFI -> { - true - } - - ConnectivityManager.TYPE_MOBILE -> { - when (subType) { - TelephonyManager.NETWORK_TYPE_1xRTT -> false // ~ 50-100 kbps - TelephonyManager.NETWORK_TYPE_CDMA -> false // ~ 14-64 kbps - TelephonyManager.NETWORK_TYPE_EDGE -> false // ~ 50-100 kbps - TelephonyManager.NETWORK_TYPE_EVDO_0 -> true // ~ 400-1000 kbps - TelephonyManager.NETWORK_TYPE_EVDO_A -> true // ~ 600-1400 kbps - TelephonyManager.NETWORK_TYPE_GPRS -> false // ~ 100 kbps - TelephonyManager.NETWORK_TYPE_HSDPA -> true // ~ 2-14 Mbps - TelephonyManager.NETWORK_TYPE_HSPA -> true // ~ 700-1700 kbps - TelephonyManager.NETWORK_TYPE_HSUPA -> true // ~ 1-23 Mbps - TelephonyManager.NETWORK_TYPE_UMTS -> true // ~ 400-7000 kbps - TelephonyManager.NETWORK_TYPE_EHRPD -> true // ~ 1-2 Mbps - TelephonyManager.NETWORK_TYPE_EVDO_B -> true // ~ 5 Mbps - TelephonyManager.NETWORK_TYPE_HSPAP -> true // ~ 10-20 Mbps - TelephonyManager.NETWORK_TYPE_IDEN -> false // ~25 kbps - TelephonyManager.NETWORK_TYPE_LTE -> true // ~ 10+ Mbps - TelephonyManager.NETWORK_TYPE_UNKNOWN -> false - else -> false - } - } - - else -> { - false - } - } - } -} diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/network/di/CoroutineScopesModule.kt b/core/common/src/main/java/org/mifos/mobile/core/common/network/di/CoroutineScopesModule.kt deleted file mode 100644 index f45c66e5c..000000000 --- a/core/common/src/main/java/org/mifos/mobile/core/common/network/di/CoroutineScopesModule.kt +++ /dev/null @@ -1,40 +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.common.network.di - -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob -import org.mifos.mobile.core.common.network.Dispatcher -import org.mifos.mobile.core.common.network.MifosDispatchers -import javax.inject.Qualifier -import javax.inject.Singleton - -@Retention(AnnotationRetention.RUNTIME) -@Qualifier -annotation class ApplicationScope - -@Module -@InstallIn(SingletonComponent::class) -object CoroutineScopesModule { - - @Provides - @Singleton - @ApplicationScope - fun providesCoroutineScope( - @Dispatcher(MifosDispatchers.Default) dispatcher: CoroutineDispatcher, - ): CoroutineScope = CoroutineScope(SupervisorJob() + dispatcher) -} - -object ApplicationModule diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/network/di/DispatchersModule.kt b/core/common/src/main/java/org/mifos/mobile/core/common/network/di/DispatchersModule.kt deleted file mode 100644 index cc60c8732..000000000 --- a/core/common/src/main/java/org/mifos/mobile/core/common/network/di/DispatchersModule.kt +++ /dev/null @@ -1,31 +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.common.network.di - -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers -import org.mifos.mobile.core.common.network.Dispatcher -import org.mifos.mobile.core.common.network.MifosDispatchers - -@Module -@InstallIn(SingletonComponent::class) -object DispatchersModule { - @Provides - @Dispatcher(MifosDispatchers.IO) - fun providesIODispatcher(): CoroutineDispatcher = Dispatchers.IO - - @Provides - @Dispatcher(MifosDispatchers.Default) - fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default -} diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/utils/CurrencyUtil.kt b/core/common/src/main/java/org/mifos/mobile/core/common/utils/CurrencyUtil.kt deleted file mode 100644 index 41f431fbc..000000000 --- a/core/common/src/main/java/org/mifos/mobile/core/common/utils/CurrencyUtil.kt +++ /dev/null @@ -1,67 +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.common.utils - -import android.content.Context -import android.telephony.TelephonyManager -import java.text.DecimalFormat -import java.util.Locale - -object CurrencyUtil { - - private val defaultLocale = Locale.US - fun formatCurrency(context: Context, amt: String?): String { - return getDecimalFormatter(context).format(amt ?: "0.0") - } - - fun formatCurrency(context: Context, amt: Long): String { - return getDecimalFormatter(context).format(amt) - } - - fun formatCurrency(context: Context?, amt: Double?): String { - return getDecimalFormatter(context).format(amt ?: 0.0) - } - - private fun getDecimalFormatter(context: Context?): DecimalFormat { - val currencyFormatter: DecimalFormat - val locale: Locale? - val tm = context?.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager - locale = try { - Locale("en", tm.networkCountryIso.uppercase(Locale.ROOT)) - } catch (e: Exception) { - defaultLocale - } - currencyFormatter = try { - DecimalFormat.getCurrencyInstance(locale) as DecimalFormat - } catch (e: Exception) { - DecimalFormat.getCurrencyInstance(defaultLocale) as DecimalFormat - } - val decimalFormatSymbols = currencyFormatter.decimalFormatSymbols - decimalFormatSymbols.currencySymbol = "" - currencyFormatter.decimalFormatSymbols = decimalFormatSymbols - return currencyFormatter - } - - fun formatCurrency(amt: Double?): String { - return getDecimalFormatter().format(amt ?: 0.0) - } - - private fun getDecimalFormatter(): DecimalFormat { - val currencyFormatter: DecimalFormat = try { - DecimalFormat.getCurrencyInstance(defaultLocale) as DecimalFormat - } catch (e: Exception) { - DecimalFormat.getCurrencyInstance(Locale.getDefault()) as DecimalFormat - } - val decimalFormatSymbols = currencyFormatter.decimalFormatSymbols - decimalFormatSymbols.currencySymbol = "" - currencyFormatter.decimalFormatSymbols = decimalFormatSymbols - return currencyFormatter - } -} diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/utils/DateHelper.kt b/core/common/src/main/java/org/mifos/mobile/core/common/utils/DateHelper.kt deleted file mode 100755 index 2895dac1c..000000000 --- a/core/common/src/main/java/org/mifos/mobile/core/common/utils/DateHelper.kt +++ /dev/null @@ -1,168 +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.common.utils - -import android.util.Log -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.Calendar -import java.util.Date -import java.util.Locale - -object DateHelper { - - private val LOG_TAG: String? = DateHelper::class.java.simpleName - const val FORMAT_MMMM = "dd MMMM yyyy" - const val FORMAT_MM = "dd-MM-yyyy" - fun getCurrentDate(dateFormat: String?, separator: String): List { - val date: MutableList = ArrayList() - val s = SimpleDateFormat(dateFormat, Locale.getDefault()).format(Date()) - for (str in s.split(separator.toRegex()).toTypedArray()) { - date.add(str.toInt()) - } - return date - } - - /** - * the result string uses the list given in a reverse order ([x, y, z] results in "z y x") - * - * @param integersOfDate [year-month-day] (ex [2016, 4, 14]) - * @return date in the format day month year (ex 14 Apr 2016) - */ - fun getDateAsString(integersOfDate: List?): String { - val stringBuilder = StringBuilder() - if (integersOfDate != null && integersOfDate.size >= 3) { - stringBuilder.append(integersOfDate[2]) - .append(' ') - .append(getMonthName(integersOfDate[1])) - .append(' ') - .append(integersOfDate[0]) - } - return stringBuilder.toString() - } - - fun getDateAsString(integersOfDate: List?, pattern: String?): String { - return getFormatConverter( - "dd MMM yyyy", - pattern, - getDateAsString(integersOfDate), - ) - } - - /** - * This Method converting the dd-MM-yyyy format type date string into dd MMMM yyyy - * - * @param format Final Format of date string - * @param dateString date string - * @return dd MMMM yyyy format date string. - */ - fun getSpecificFormat(format: String?, dateString: String?): String { - val pickerFormat = SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH) - val finalFormat = SimpleDateFormat(format, Locale.ENGLISH) - var date: Date? = null - try { - date = pickerFormat.parse(dateString) - } catch (e: ParseException) { - Log.d(LOG_TAG, e.localizedMessage) - } - return finalFormat.format(date) - } - - fun getSpecificFormat(format: String?, dateLong: Long?): String? { - try { - return SimpleDateFormat(format, Locale.getDefault()).format(dateLong) - } catch (e: ParseException) { - Log.d(LOG_TAG, e.localizedMessage) - return null - } - } - - fun getFormatConverter( - currentFormat: String?, - requiredFormat: String?, - dateString: String?, - ): String { - val pickerFormat = SimpleDateFormat(currentFormat, Locale.ENGLISH) - val finalFormat = SimpleDateFormat(requiredFormat, Locale.ENGLISH) - var date: Date? = null - try { - date = pickerFormat.parse(dateString) - } catch (e: ParseException) { - Log.d(LOG_TAG, e.localizedMessage) - } - return finalFormat.format(date) - } - - /** - * @param month an integer from 1 to 12 - * @return string representation of the month like Jan or Feb..etc - */ - private fun getMonthName(month: Int?): String { - var monthName = "" - when (month) { - 1 -> monthName = "Jan" - 2 -> monthName = "Feb" - 3 -> monthName = "Mar" - 4 -> monthName = "Apr" - 5 -> monthName = "May" - 6 -> monthName = "Jun" - 7 -> monthName = "Jul" - 8 -> monthName = "Aug" - 9 -> monthName = "Sep" - 10 -> monthName = "Oct" - 11 -> monthName = "Nov" - 12 -> monthName = "Dec" - } - return monthName - } - - fun getDateAsLongFromString(dateStr: String?, pattern: String?): Long? { - val sdf = SimpleDateFormat(pattern, Locale.getDefault()) - var date: Date? = null - try { - date = sdf.parse(dateStr) - } catch (e: ParseException) { - Log.d("TAG", e.message ?: "") - } - return date?.time - } - - @JvmStatic - fun getDateAsLongFromList(integersOfDate: List?): Long? { - val dateStr = getDateAsString(integersOfDate) - return getDateAsLongFromString(dateStr, "dd MMM yyyy") - } - - fun subtractWeeks(number: Int): Long { - val calendar = Calendar.getInstance() - calendar.add(Calendar.WEEK_OF_YEAR, -number) - return calendar.timeInMillis - } - - fun subtractMonths(number: Int): Long { - val calendar = Calendar.getInstance() - calendar.add(Calendar.MONTH, -number) - return calendar.timeInMillis - } - - fun getDateAsStringFromLong(timeInMillis: Long?): String { - val sdf = SimpleDateFormat("dd MMM yyyy", Locale.getDefault()) - return sdf.format(timeInMillis?.let { Date(it) }) - } - - @JvmStatic - fun getDateAndTimeAsStringFromLong(timeInMillis: Long?): String { - val sdf = SimpleDateFormat("HH:mm a dd MMM yyyy", Locale.getDefault()) - return sdf.format(timeInMillis?.let { Date(it) }) - } -} - -fun getTodayFormatted(): String = - SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(Calendar.getInstance().time) diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/utils/ParcelableAndSerializableUtils.kt b/core/common/src/main/java/org/mifos/mobile/core/common/utils/ParcelableAndSerializableUtils.kt deleted file mode 100644 index 9def51789..000000000 --- a/core/common/src/main/java/org/mifos/mobile/core/common/utils/ParcelableAndSerializableUtils.kt +++ /dev/null @@ -1,41 +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.common.utils - -import android.os.Build -import android.os.Bundle -import java.io.Serializable - -object ParcelableAndSerializableUtils { - - fun Bundle.getCheckedArrayListFromParcelable(classType: Class, key: String): List? { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - this.getParcelableArrayList(key, classType) - } else { - this.getParcelableArrayList(key) - } - } - - fun Bundle.getCheckedParcelable(classType: Class, key: String): T? { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - this.getParcelable(key, classType) - } else { - this.getParcelable(key) - } - } - - fun Bundle.getCheckedSerializable(classType: Class, key: String): Any? { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - this.getSerializable(key, classType) - } else { - this.getSerializable(key) - } - } -} diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/utils/Utils.kt b/core/common/src/main/java/org/mifos/mobile/core/common/utils/Utils.kt deleted file mode 100644 index 20586025f..000000000 --- a/core/common/src/main/java/org/mifos/mobile/core/common/utils/Utils.kt +++ /dev/null @@ -1,98 +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.common.utils - -import android.content.Context -import android.graphics.Bitmap -import android.graphics.PorterDuff -import android.graphics.drawable.ColorDrawable -import android.graphics.drawable.Drawable -import android.graphics.drawable.LayerDrawable -import android.net.Uri -import android.util.Log -import android.view.Menu -import androidx.core.content.ContextCompat -import androidx.core.content.FileProvider -import org.mifos.mobile.core.common.R -import java.io.File -import java.io.FileOutputStream -import java.io.IOException -import java.text.DateFormatSymbols -import java.util.Locale - -object Utils { - fun getMonth(month: Int): String { - return DateFormatSymbols().months[month - 1] - } - - fun setToolbarIconColor(context: Context?, menu: Menu, color: Int) { - for (i in 0 until menu.size()) { - val drawable = menu.getItem(i).icon - if (drawable != null) { - drawable.mutate() - drawable.setColorFilter( - ContextCompat.getColor(context!!, color), - PorterDuff.Mode.SRC_IN, - ) - } - } - } - - fun setCircularBackground(colorId: Int, context: Context?): LayerDrawable { - val color: Drawable = ColorDrawable(ContextCompat.getColor(context!!, colorId)) - val image = ContextCompat.getDrawable(context, R.drawable.core_common_circular_background) - return LayerDrawable(arrayOf(image, color)) - } - - fun getImageUri(context: Context?, bitmap: Bitmap): Uri { - try { - val cachePath = File(context?.cacheDir, "images") - cachePath.mkdirs() - val stream = FileOutputStream("$cachePath/image.png") - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream) - stream.close() - } catch (e: IOException) { - Log.d(Utils::class.java.name, e.toString()) - } - val imagePath = File(context?.cacheDir, "images") - val newFile = File(imagePath, "image.png") - return FileProvider.getUriForFile( - context!!, - "${context.packageName}.fileprovider", - newFile, - ) - } - - fun generateFormString(data: Array>): String { - val formString = StringBuilder() - formString.setLength(0) - for (aData in data) { - formString.append(aData[0]).append(" : ").append(aData[1]).append('\n') - } - return formString.toString() - } - - @JvmStatic - fun formatTransactionType(type: String?): String { - val builder = StringBuilder() - try { - for (str in type?.lowercase(Locale.ROOT)?.split("_".toRegex())?.toTypedArray()!!) { - builder.append( - str[0].toString().uppercase(Locale.ROOT) + str.substring( - 1, - str.length, - ) + " ", - ) - } - } catch (_: Exception) { - } - return builder.toString() - } -} diff --git a/core/common/src/main/res/drawable/core_common_circular_background.xml b/core/common/src/main/res/drawable/core_common_circular_background.xml deleted file mode 100644 index 5802b153b..000000000 --- a/core/common/src/main/res/drawable/core_common_circular_background.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - \ No newline at end of file diff --git a/core/common/src/main/res/values/dimens.xml b/core/common/src/main/res/values/dimens.xml deleted file mode 100644 index 37a37ed7c..000000000 --- a/core/common/src/main/res/values/dimens.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - 25dp - 25dp - - \ No newline at end of file diff --git a/core/common/src/main/res/values/strings.xml b/core/common/src/main/res/values/strings.xml deleted file mode 100644 index bb3f13a0c..000000000 --- a/core/common/src/main/res/values/strings.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - Working - default_system_language - language_type - - System_Language - en - hi - ar - ur - bn - es - fr - in - km - kn - te - my - pl - pt - ru - sw - fa - - \ No newline at end of file diff --git a/core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.native.kt b/core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.native.kt new file mode 100644 index 000000000..bba9abd9a --- /dev/null +++ b/core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.native.kt @@ -0,0 +1,28 @@ +/* + * 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.common + +import platform.Foundation.NSNumber +import platform.Foundation.NSNumberFormatter +import platform.Foundation.NSNumberFormatterCurrencyStyle + +actual object CurrencyFormatter { + actual fun format( + balance: Double?, + currencyCode: String?, + maximumFractionDigits: Int?, + ): String { + val numberFormatter = NSNumberFormatter() + numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle + numberFormatter.currencyCode = currencyCode ?: "$" + numberFormatter.maximumFractionDigits = (maximumFractionDigits ?: 0).toULong() + return numberFormatter.stringFromNumber(NSNumber(balance ?: 0.0)) ?: "" + } +} diff --git a/core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/FileUtils.native.kt b/core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/FileUtils.native.kt new file mode 100644 index 000000000..ca9c1dfd8 --- /dev/null +++ b/core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/FileUtils.native.kt @@ -0,0 +1,45 @@ +/* + * 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.common + +import kotlinx.cinterop.BetaInteropApi +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.allocArrayOf +import kotlinx.cinterop.memScoped +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import platform.Foundation.NSData +import platform.Foundation.create +import platform.Foundation.writeToFile + +// iOS implementation +@BetaInteropApi +@OptIn(ExperimentalForeignApi::class) +actual fun createPlatformFileUtils(): FileUtils = object : FileUtils { + override suspend fun writeInputStreamDataToFile( + inputStream: ByteArray, + filePath: String, + ): Boolean = + withContext(Dispatchers.Default) { + try { + val nsData = inputStream.toNSData() + nsData.writeToFile(filePath, true) + true + } catch (e: Exception) { + FileUtils.logger.e { "Error writing file: ${e.message}" } + false + } + } + + @BetaInteropApi + fun ByteArray.toNSData(): NSData = memScoped { + NSData.create(bytes = allocArrayOf(this@toNSData), length = this@toNSData.size.toULong()) + } +} diff --git a/core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.native.kt b/core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.native.kt new file mode 100644 index 000000000..461fe75c4 --- /dev/null +++ b/core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.native.kt @@ -0,0 +1,22 @@ +/* + * 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.common.di + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import org.koin.core.module.Module +import org.koin.core.qualifier.named +import org.koin.dsl.module +import org.mifos.mobile.core.common.MifosDispatchers + +actual val ioDispatcherModule: Module + get() = module { + single(named(MifosDispatchers.IO.name)) { Dispatchers.Default } + } diff --git a/core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.js.kt b/core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.js.kt new file mode 100644 index 000000000..4e808c01f --- /dev/null +++ b/core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.js.kt @@ -0,0 +1,35 @@ +/* + * 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.common + +actual object CurrencyFormatter { + actual fun format( + balance: Double?, + currencyCode: String?, + maximumFractionDigits: Int?, + ): String { + return "$currencyCode ${ + balance?.let { + val formattedBalance = balance.toString() + val fractionDigits = formattedBalance.substringAfterLast(".") + val fractionDigitsLength = fractionDigits.length + val fractionDigitsToDisplay = if (fractionDigitsLength > maximumFractionDigits!!) { + fractionDigits.substring(0, maximumFractionDigits) + } else { + fractionDigits + } + val integerDigits = formattedBalance.substringBeforeLast(".") + val integerDigitsWithCommas = + integerDigits.reversed().chunked(3).joinToString(",").reversed() + "$integerDigitsWithCommas.$fractionDigitsToDisplay" + } ?: "0.00" + }" + } +} diff --git a/core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/FileUtils.wasmJs.kt b/core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/FileUtils.wasmJs.kt new file mode 100644 index 000000000..eb86c1186 --- /dev/null +++ b/core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/FileUtils.wasmJs.kt @@ -0,0 +1,12 @@ +/* + * 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.common + +actual fun createPlatformFileUtils(): FileUtils = CommonFileUtils() diff --git a/core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.js.kt b/core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.js.kt new file mode 100644 index 000000000..461fe75c4 --- /dev/null +++ b/core/common/src/wasmJsMain/kotlin/org/mifos/mobile/core/common/di/DispatchersModule.js.kt @@ -0,0 +1,22 @@ +/* + * 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.common.di + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import org.koin.core.module.Module +import org.koin.core.qualifier.named +import org.koin.dsl.module +import org.mifos.mobile.core.common.MifosDispatchers + +actual val ioDispatcherModule: Module + get() = module { + single(named(MifosDispatchers.IO.name)) { Dispatchers.Default } + }