diff --git a/.github/workflows/feature_branch_ci.yml b/.github/workflows/feature_branch_ci.yml
index 7d1456878..49117f243 100644
--- a/.github/workflows/feature_branch_ci.yml
+++ b/.github/workflows/feature_branch_ci.yml
@@ -7,28 +7,180 @@ on:
- '!development'
- '!master'
+concurrency:
+ group: build-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+ pull-requests: write
+
jobs:
- build:
- name: Build APK
+ setup:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: 17
+ - uses: gradle/actions/setup-gradle@v4
+
+ - name: Cache Gradle and build outputs
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ build
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: ${{ runner.os }}-gradle-
+
+ checks:
+ needs: setup
runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ check: [ build_logic, spotless, detekt ]
steps:
- - name: Checking out repository
- uses: actions/checkout@v4
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: 17
+ - name: Run ${{ matrix.check }}
+ id: run_check
+ run: |
+ if [ "${{ matrix.check }}" = "build_logic" ]; then
+ ./gradlew check -p build-logic
+ elif [ "${{ matrix.check }}" = "spotless" ]; then
+ ./gradlew spotlessCheck --no-configuration-cache --no-daemon
+ elif [ "${{ matrix.check }}" = "detekt" ]; then
+ ./gradlew detekt
+ fi
- # Set up JDK
- - name: Set Up JDK 1.8
- uses: actions/setup-java@v1
+ - name: Upload Detekt Reports
+ if: ${{ matrix.check == 'detekt' && steps.run_check.outcome == 'success' }}
+ uses: actions/upload-artifact@v4
with:
- java-version: 11
+ name: detekt-reports
+ path: |
+ **/build/reports/detekt/detekt.md
+
+
- # Install NDK
- - name: Install NDK
- run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;20.0.5594570" --sdk_root=${ANDROID_SDK_ROOT}
- # Update Gradle Permission
- - name: Change gradlew Permission
- run: chmod +x gradlew
- # Build App
- - name: Build with Gradle
- run: ./gradlew assemble
\ No newline at end of file
+ dependency_guard:
+ needs: setup
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: 17
+
+ - name: Check Dependency Guard
+ id: dependencyguard_verify
+ continue-on-error: true
+ run: ./gradlew dependencyGuard
+
+ - name: Prevent updating Dependency Guard baselines if this is a fork
+ id: checkfork_dependencyguard
+ if: steps.dependencyguard_verify.outcome == 'failure' && github.event.pull_request.head.repo.full_name != github.repository
+ run: |
+ echo "::error::Dependency Guard failed, please update baselines with: ./gradlew dependencyGuardBaseline" && exit 1
+
+ # Runs if previous job failed
+ - name: Generate new Dependency Guard baselines if verification failed and it's a PR
+ id: dependencyguard_baseline
+ if: steps.dependencyguard_verify.outcome == 'failure' && github.event_name == 'pull_request'
+ run: |
+ ./gradlew dependencyGuardBaseline
+
+ - name: Push new Dependency Guard baselines if available
+ uses: stefanzweifel/git-auto-commit-action@v5
+ if: steps.dependencyguard_baseline.outcome == 'success'
+ with:
+ file_pattern: '**/dependencies/*.txt'
+ disable_globbing: true
+ commit_message: "🤖 Updates baselines for Dependency Guard"
+
+ tests_and_lint:
+ needs: setup
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: 17
+
+ - name: Run tests
+ run: |
+ ./gradlew testDebug :lint:test :androidApp:lintRelease :lint:lint
+
+ - name: Upload reports
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-and-lint-reports
+ path: |
+ **/build/reports/lint-results-*.html
+ **/build/test-results/test*UnitTest/**.xml
+
+
+ # Add `createProdDebugUnitTestCoverageReport` if we ever add JVM tests for prod
+ - name: Generate coverage reports for Debug variants (only API 30)
+ run: ./gradlew createDebugCombinedCoverageReport
+
+ - name: Upload test reports
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-reports-${{ matrix.api-level }}
+ path: '**/build/reports/androidTests'
+
+ - name: Display local test coverage (only API 30)
+ id: jacoco
+ uses: madrapps/jacoco-report@v1.6.1
+ with:
+ title: Combined test coverage report
+ min-coverage-overall: 40
+ min-coverage-changed-files: 60
+ paths: |
+ ${{ github.workspace }}/**/build/reports/jacoco/**/*Report.xml
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Upload local coverage reports (XML + HTML) (only API 30)
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-reports
+ if-no-files-found: error
+ compression-level: 1
+ overwrite: false
+ path: '**/build/reports/jacoco/'
+
+ build:
+ needs: [ checks, dependency_guard, tests_and_lint ]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: 17
+
+ - name: Build APKs
+ run: ./gradlew :androidApp:assembleDebug
+
+ - name: Check badging
+ run: ./gradlew :androidApp:checkReleaseBadging
+
+ - name: Upload APKs
+ uses: actions/upload-artifact@v4
+ with:
+ name: APKs
+ path: '**/build/outputs/apk/**/*.apk'
\ No newline at end of file
diff --git a/.github/workflows/master_dev_ci.yml b/.github/workflows/master_dev_ci.yml
index 8cca27497..99774b93e 100644
--- a/.github/workflows/master_dev_ci.yml
+++ b/.github/workflows/master_dev_ci.yml
@@ -7,22 +7,24 @@ on:
- 'development'
- 'master'
+concurrency:
+ group: build-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+ pull-requests: write
+
jobs:
- build:
- name: Build APK
+ setup:
runs-on: ubuntu-latest
steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Set up JDK 17
- uses: actions/setup-java@v4
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17
-
- - name: Setup Gradle
- uses: gradle/actions/setup-gradle@v4
+ - uses: gradle/actions/setup-gradle@v4
- name: Cache Gradle and build outputs
uses: actions/cache@v4
@@ -34,7 +36,151 @@ jobs:
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: ${{ runner.os }}-gradle-
- - name: Build With Gradle
- run: ./gradlew assembleDebug
+ checks:
+ needs: setup
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ check: [ build_logic, spotless, detekt ]
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: 17
+ - name: Run ${{ matrix.check }}
+ id: run_check
+ run: |
+ if [ "${{ matrix.check }}" = "build_logic" ]; then
+ ./gradlew check -p build-logic
+ elif [ "${{ matrix.check }}" = "spotless" ]; then
+ ./gradlew spotlessCheck --no-configuration-cache --no-daemon
+ elif [ "${{ matrix.check }}" = "detekt" ]; then
+ ./gradlew detekt
+ fi
+
+ - name: Upload Detekt Reports
+ if: ${{ matrix.check == 'detekt' && steps.run_check.outcome == 'success' }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: detekt-reports
+ path: |
+ **/build/reports/detekt/detekt.md
+
+
+
+
+ dependency_guard:
+ needs: setup
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: 17
+
+ - name: Check Dependency Guard
+ id: dependencyguard_verify
+ continue-on-error: true
+ run: ./gradlew dependencyGuard
+
+ - name: Prevent updating Dependency Guard baselines if this is a fork
+ id: checkfork_dependencyguard
+ if: steps.dependencyguard_verify.outcome == 'failure' && github.event.pull_request.head.repo.full_name != github.repository
+ run: |
+ echo "::error::Dependency Guard failed, please update baselines with: ./gradlew dependencyGuardBaseline" && exit 1
+
+ # Runs if previous job failed
+ - name: Generate new Dependency Guard baselines if verification failed and it's a PR
+ id: dependencyguard_baseline
+ if: steps.dependencyguard_verify.outcome == 'failure' && github.event_name == 'pull_request'
+ run: |
+ ./gradlew dependencyGuardBaseline
+
+ - name: Push new Dependency Guard baselines if available
+ uses: stefanzweifel/git-auto-commit-action@v5
+ if: steps.dependencyguard_baseline.outcome == 'success'
+ with:
+ file_pattern: '**/dependencies/*.txt'
+ disable_globbing: true
+ commit_message: "🤖 Updates baselines for Dependency Guard"
+
+ tests_and_lint:
+ needs: setup
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: 17
+
+ - name: Run tests
+ run: |
+ ./gradlew testDebug :lint:test :androidApp:lintRelease :lint:lint
+
+ - name: Upload reports
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-and-lint-reports
+ path: |
+ **/build/reports/lint-results-*.html
+ **/build/test-results/test*UnitTest/**.xml
+
+
+ # Add `createDebugUnitTestCoverageReport` if we ever add JVM tests for prod
+ - name: Generate coverage reports for Debug variants (only API 30)
+ run: ./gradlew createDebugCombinedCoverageReport
+
+ - name: Upload test reports
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-reports-${{ matrix.api-level }}
+ path: '**/build/reports/androidTests'
+ - name: Display local test coverage (only API 30)
+ id: jacoco
+ uses: madrapps/jacoco-report@v1.6.1
+ with:
+ title: Combined test coverage report
+ min-coverage-overall: 40
+ min-coverage-changed-files: 60
+ paths: |
+ ${{ github.workspace }}/**/build/reports/jacoco/**/*Report.xml
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Upload local coverage reports (XML + HTML) (only API 30)
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-reports
+ if-no-files-found: error
+ compression-level: 1
+ overwrite: false
+ path: '**/build/reports/jacoco/'
+
+ build:
+ needs: [ checks, dependency_guard, tests_and_lint ]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: 17
+
+ - name: Build APKs
+ run: ./gradlew :androidApp:assembleDebug
+
+ - name: Check badging
+ run: ./gradlew :androidApp:checkReleaseBadging
+
+ - name: Upload APKs
+ uses: actions/upload-artifact@v4
+ with:
+ name: APKs
+ path: '**/build/outputs/apk/**/*.apk'
diff --git a/.gitignore b/.gitignore
index 3c7ee5b81..496785d43 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,12 +26,8 @@ build/
.classpath
.project
-# Windows thumbnail db
-.DS_Store
-
# IDEA/Android Studio project files, because
# the project can be imported from settings.gradle.kts
-*.iml
.idea/*
!.idea/copyright
# Keep the code styles.
@@ -40,8 +36,6 @@ build/
!/.idea/codeStyles/Project.xml
!/.idea/codeStyles/codeStyleConfig.xml
-# Gradle cache
-.gradle
# Kotlin
.kotlin
diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts
index 50bf3108e..cac320cb6 100644
--- a/androidApp/build.gradle.kts
+++ b/androidApp/build.gradle.kts
@@ -1,27 +1,34 @@
+/*
+ * 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
+ */
plugins {
alias(libs.plugins.mifos.android.application)
alias(libs.plugins.mifos.android.application.compose)
alias(libs.plugins.mifos.android.hilt)
alias(libs.plugins.mifos.android.application.firebase)
id("com.google.android.gms.oss-licenses-plugin")
- id("kotlin-parcelize")
alias(libs.plugins.roborazzi)
}
-apply(from = "../config/quality/quality.gradle")
-
android {
namespace = "org.mifos.mobile"
+
defaultConfig {
- applicationId = "org.mifos.mobile"
versionCode = 1
versionName = "1.0"
+ applicationId = "org.mifos.mobile"
vectorDrawables.useSupportLibrary = true
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ multiDexEnabled = true
ndk {
abiFilters.addAll(arrayOf("armeabi-v7a", "x86", "x86_64", "arm64-v8a"))
}
- multiDexEnabled = true
}
signingConfigs {
@@ -42,40 +49,19 @@ android {
}
}
- sourceSets {
- val commonTestDir = "src/commonTest/java"
- getByName("main"){
- java.srcDir(commonTestDir)
- }
- getByName("androidTest"){
- java.srcDir(commonTestDir)
- }
- getByName("test"){
- java.srcDir(commonTestDir)
- }
- }
-
buildFeatures {
- dataBinding = true
- viewBinding = true
compose = true
buildConfig = true
}
-
- lint {
- abortOnError = false
- disable.add("InvalidPackage")
- }
}
dependencyGuard {
- configuration("debugCompileClasspath")
- configuration("debugRuntimeClasspath")
- configuration("releaseCompileClasspath")
- configuration("releaseRuntimeClasspath")
+ configuration("releaseRuntimeClasspath"){
+ modules = true
+ tree = true
+ }
}
-
dependencies {
implementation (projects.shared)
@@ -108,120 +94,40 @@ dependencies {
implementation(projects.feature.auth)
implementation(projects.feature.userProfile)
- implementation(libs.androidx.legacy.support.v4)
- implementation(libs.androidx.lifecycle.ktx)
- implementation(libs.androidx.lifecycle.extensions)
- implementation(libs.androidx.appcompat)
- implementation(libs.material)
- implementation(libs.androidx.preference)
-
-
- // DBFlow
- implementation(libs.dbflow)
- kapt(libs.dbflow.processor)
- implementation(libs.dbflow.core)
-
- implementation(libs.androidx.recyclerview)
- implementation(libs.androidx.vectordrawable)
- implementation(libs.google.oss.licenses)
-
-// implementation(libs.simplecropview)
+ implementation(projects.libs.mifosPasscode)
+ // Jetpack Compose
+ implementation(libs.androidx.appcompat)
implementation(libs.androidx.activity.ktx)
- implementation(libs.androidx.fragment.ktx)
-
- //Country Code picker
-// implementation(libs.ccp)
-// implementation(libs.countrycodechooser)
-
- //Square dependencies
- implementation(libs.squareup.retrofit2) {
- // exclude Retrofit’s OkHttp peer-dependency module and define your own module import
- exclude(module = "okhttp")
- }
- implementation(libs.squareup.retrofit.adapter.rxjava)
- implementation(libs.squareup.retrofit.converter.gson)
- implementation(libs.squareup.okhttp)
- implementation(libs.squareup.logging.interceptor)
-
- //rxjava Dependencies
- implementation(libs.reactivex.rxjava2.android)
- implementation(libs.reactivex.rxjava2)
-
- //Butter Knife
- implementation(libs.jakewharton.butterknife)
- implementation(libs.jakewharton.compiler)
-
- //Annotation library
- implementation(libs.androidx.annotation)
-
- //qr code
-// implementation(libs.zxing.core)
-// implementation(libs.zxing)
-
- //sweet error dependency
- implementation(libs.sweet.error)
-
- //mifos passcode
- implementation(libs.mifos.passcode)
-
- //multidex
+ implementation(libs.androidx.activity.compose)
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.compose.material3)
+ implementation(libs.androidx.compose.material)
+ implementation(libs.androidx.compose.foundation)
+ implementation(libs.androidx.compose.foundation.layout)
+ implementation(libs.androidx.compose.material.iconsExtended)
+ implementation(libs.androidx.compose.runtime)
+ implementation(libs.androidx.compose.ui.tooling.preview)
+ implementation(libs.androidx.compose.ui.util)
+ implementation(libs.androidx.lifecycle.runtimeCompose)
+ implementation(libs.androidx.hilt.navigation.compose)
+
+ implementation(libs.androidx.core.splashscreen)
+
+ implementation(libs.androidx.tracing.ktx)
+ implementation(libs.androidx.profileinstaller)
+ implementation(libs.google.oss.licenses)
implementation(libs.androidx.multidex)
- //TableView
-// implementation(libs.tableview)
-
- //Biometric Authentication
- implementation(libs.androidx.biometric)
-
- // Coroutines
- implementation(libs.kotlinx.coroutines.android)
- testImplementation(libs.kotlinx.coroutines.test)
-
- // Unit tests dependencies
- testImplementation(libs.junit)
- testImplementation(libs.mockito.core)
- implementation(libs.mockito.core)
- //turbine
- testImplementation(libs.turbine)
- implementation(libs.mockito.android)
- androidTestImplementation((libs.junit))
- androidTestImplementation(libs.mockito.core)
- androidTestImplementation(libs.mockito.android)
- androidTestImplementation(libs.androidx.annotation)
- implementation(libs.androidx.core.testing)
- androidTestImplementation(libs.androidx.espresso.contrib)
- androidTestImplementation(libs.espresso.core)
- androidTestImplementation(libs.androidx.runner)
- androidTestImplementation(libs.androidx.rules)
-
- implementation(libs.uihouse)
-
- // Jetpack Compose
- api(libs.androidx.activity.compose)
- api(platform(libs.androidx.compose.bom))
- api(libs.androidx.compose.material3)
- api(libs.androidx.compose.material)
- api(libs.androidx.compose.foundation)
- api(libs.androidx.compose.foundation.layout)
- api(libs.androidx.compose.material.iconsExtended)
- api(libs.androidx.compose.runtime)
- api(libs.androidx.compose.ui.tooling.preview)
- api(libs.androidx.compose.ui.util)
- api(libs.androidx.lifecycle.runtimeCompose)
- debugApi(libs.androidx.compose.ui.tooling)
- api(libs.androidx.hilt.navigation.compose)
-
+ testImplementation(projects.core.testing)
+ testImplementation(libs.hilt.android.testing)
+ testImplementation(libs.work.testing)
- //image cropper
- implementation(libs.android.image.cropper)
+ androidTestImplementation(kotlin("test"))
+ androidTestImplementation(projects.core.testing)
+ androidTestImplementation(libs.androidx.test.espresso.core)
+ androidTestImplementation(libs.androidx.navigation.testing)
+ androidTestImplementation(libs.hilt.android.testing)
- // Google Bar code scanner
- implementation(libs.google.app.code.scanner)
-
- //cameraX
- implementation(libs.androidx.camera.camera2)
- implementation(libs.androidx.camera.lifecycle)
- implementation(libs.androidx.camera.view)
- implementation(libs.androidx.camera.core)
+ debugApi(libs.androidx.compose.ui.tooling)
}
\ No newline at end of file
diff --git a/androidApp/dependencies/releaseRuntimeClasspath.tree.txt b/androidApp/dependencies/releaseRuntimeClasspath.tree.txt
new file mode 100644
index 000000000..4ff9ad2bb
--- /dev/null
+++ b/androidApp/dependencies/releaseRuntimeClasspath.tree.txt
@@ -0,0 +1,1630 @@
++--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20
+| +--- org.jetbrains:annotations:13.0 -> 23.0.0
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0 -> 2.0.20 (c)
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0 -> 1.9.10 (c)
+| \--- org.jetbrains.kotlin:kotlin-stdlib-common:2.0.20 (c)
++--- androidx.compose:compose-bom:2024.08.00
+| +--- androidx.compose.foundation:foundation:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.foundation:foundation-layout:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.material:material:1.6.8 (c)
+| +--- androidx.compose.material:material-icons-extended:1.6.8 (c)
+| +--- androidx.compose.material3:material3:1.2.1 (c)
+| +--- androidx.compose.runtime:runtime:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-tooling-preview:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-util:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.runtime:runtime-saveable:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.material3:material3-android:1.2.1 (c)
+| +--- androidx.compose.material:material-android:1.6.8 (c)
+| +--- androidx.compose.material:material-icons-extended-android:1.6.8 (c)
+| +--- androidx.compose.animation:animation:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.animation:animation-core:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.material:material-icons-core:1.6.8 (c)
+| +--- androidx.compose.material:material-ripple:1.6.8 (c)
+| +--- androidx.compose.ui:ui-graphics:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-text:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.foundation:foundation-layout-android:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.runtime:runtime-android:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.runtime:runtime-saveable-android:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-android:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.animation:animation-android:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.material:material-icons-core-android:1.6.8 (c)
+| +--- androidx.compose.material:material-ripple-android:1.6.8 (c)
+| +--- androidx.compose.ui:ui-unit:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-geometry:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-util-android:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.foundation:foundation-android:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-unit-android:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-graphics-android:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-text-android:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-tooling-preview-android:1.6.8 -> 1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-geometry-android:1.6.8 -> 1.7.0-rc01 (c)
+| \--- androidx.compose.animation:animation-core-android:1.6.8 -> 1.7.0-rc01 (c)
++--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01
+| \--- androidx.compose.ui:ui-tooling-preview-android:1.7.0-rc01
+| +--- androidx.annotation:annotation:1.2.0 -> 1.8.1
+| | \--- androidx.annotation:annotation-jvm:1.8.1
+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.7.10 -> 2.0.20 (*)
+| +--- androidx.compose.runtime:runtime:1.7.0-rc01
+| | \--- androidx.compose.runtime:runtime-android:1.7.0-rc01
+| | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1
+| | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.7.10 -> 2.0.20 (*)
+| | +--- androidx.collection:collection:1.4.0 -> 1.4.2
+| | | \--- androidx.collection:collection-jvm:1.4.2
+| | | +--- androidx.annotation:annotation:1.8.1 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | +--- androidx.collection:collection-ktx:1.4.2 (c)
+| | | \--- androidx.collection:collection-ktx:1.3.0 -> 1.4.2 (c)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0
+| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0
+| | | | \--- org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0
+| | | | +--- org.jetbrains:annotations:23.0.0
+| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0 (c)
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0 (c)
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0 (c)
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.0 (c)
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm:1.8.0 (c)
+| | | | | \--- org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0 (c)
+| | | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 2.0.20 (*)
+| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0 (*)
+| | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 2.0.20 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*)
+| | \--- androidx.compose.runtime:runtime-saveable:1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui:1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-geometry:1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-text:1.7.0-rc01 (c)
+| +--- androidx.compose.ui:ui-unit:1.7.0-rc01 (c)
+| \--- androidx.compose.ui:ui-util:1.7.0-rc01 (c)
++--- com.google.dagger:hilt-android:2.52
+| +--- com.google.dagger:dagger:2.52
+| | +--- jakarta.inject:jakarta.inject-api:2.0.1
+| | \--- javax.inject:javax.inject:1
+| +--- com.google.dagger:dagger-lint-aar:2.52
+| +--- com.google.dagger:hilt-core:2.52
+| | +--- com.google.dagger:dagger:2.52 (*)
+| | +--- com.google.code.findbugs:jsr305:3.0.2
+| | \--- javax.inject:javax.inject:1
+| +--- com.google.code.findbugs:jsr305:3.0.2
+| +--- androidx.activity:activity:1.5.1 -> 1.9.1
+| | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | +--- androidx.collection:collection:1.0.0 -> 1.4.2 (*)
+| | +--- androidx.core:core:1.13.0 -> 1.13.1
+| | | +--- androidx.annotation:annotation:1.6.0 -> 1.8.1 (*)
+| | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
+| | | +--- androidx.collection:collection:1.0.0 -> 1.4.2 (*)
+| | | +--- androidx.concurrent:concurrent-futures:1.0.0 -> 1.1.0
+| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | \--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava
+| | | +--- androidx.interpolator:interpolator:1.0.0
+| | | | \--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
+| | | +--- androidx.lifecycle:lifecycle-runtime:2.6.2 -> 2.8.4
+| | | | \--- androidx.lifecycle:lifecycle-runtime-android:2.8.4
+| | | | +--- androidx.annotation:annotation:1.8.0 -> 1.8.1 (*)
+| | | | +--- androidx.arch.core:core-common:2.2.0
+| | | | | \--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | +--- androidx.arch.core:core-runtime:2.2.0
+| | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | | \--- androidx.arch.core:core-common:2.2.0 (*)
+| | | | +--- androidx.lifecycle:lifecycle-common:2.8.4
+| | | | | \--- androidx.lifecycle:lifecycle-common-jvm:2.8.4
+| | | | | +--- androidx.annotation:annotation:1.8.0 -> 1.8.1 (*)
+| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*)
+| | | | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | | | | \--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | | | +--- androidx.profileinstaller:profileinstaller:1.3.1
+| | | | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | | | | +--- androidx.concurrent:concurrent-futures:1.1.0 (*)
+| | | | | +--- androidx.startup:startup-runtime:1.1.1
+| | | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | | | \--- androidx.tracing:tracing:1.0.0 -> 1.2.0
+| | | | | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | | | | | \--- androidx.tracing:tracing-ktx:1.2.0 (c)
+| | | | | \--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*)
+| | | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | | | \--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | | +--- androidx.versionedparcelable:versionedparcelable:1.1.1
+| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | \--- androidx.collection:collection:1.0.0 -> 1.4.2 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | \--- androidx.core:core-ktx:1.13.1 (c)
+| | +--- androidx.lifecycle:lifecycle-runtime:2.6.1 -> 2.8.4 (*)
+| | +--- androidx.lifecycle:lifecycle-viewmodel:2.6.1 -> 2.8.4
+| | | \--- androidx.lifecycle:lifecycle-viewmodel-android:2.8.4
+| | | +--- androidx.annotation:annotation:1.8.0 -> 1.8.1 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*)
+| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*)
+| | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | | \--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1 -> 2.8.4
+| | | +--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
+| | | +--- androidx.core:core-ktx:1.2.0 -> 1.13.1
+| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | +--- androidx.core:core:1.13.1 (*)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | \--- androidx.core:core:1.13.1 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4
+| | | | +--- androidx.arch.core:core-common:2.2.0 (*)
+| | | | +--- androidx.arch.core:core-runtime:2.2.0 (*)
+| | | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (*)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | | | \--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (*)
+| | | +--- androidx.savedstate:savedstate:1.2.1
+| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | +--- androidx.arch.core:core-common:2.1.0 -> 2.2.0 (*)
+| | | | +--- androidx.lifecycle:lifecycle-common:2.6.1 -> 2.8.4 (*)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.10 -> 2.0.20 (*)
+| | | | \--- androidx.savedstate:savedstate-ktx:1.2.1 (c)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*)
+| | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | \--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | +--- androidx.profileinstaller:profileinstaller:1.3.1 (*)
+| | +--- androidx.savedstate:savedstate:1.2.1 (*)
+| | +--- androidx.tracing:tracing:1.0.0 -> 1.2.0 (*)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | +--- androidx.activity:activity-compose:1.9.1 (c)
+| | \--- androidx.activity:activity-ktx:1.9.1 (c)
+| +--- androidx.annotation:annotation:1.3.0 -> 1.8.1 (*)
+| +--- androidx.annotation:annotation-experimental:1.3.1 -> 1.4.1 (*)
+| +--- androidx.fragment:fragment:1.5.1 -> 1.7.1
+| | +--- androidx.activity:activity:1.8.1 -> 1.9.1 (*)
+| | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
+| | +--- androidx.collection:collection:1.1.0 -> 1.4.2 (*)
+| | +--- androidx.core:core-ktx:1.2.0 -> 1.13.1 (*)
+| | +--- androidx.lifecycle:lifecycle-livedata-core:2.6.1 -> 2.8.4 (*)
+| | +--- androidx.lifecycle:lifecycle-runtime:2.6.1 -> 2.8.4 (*)
+| | +--- androidx.lifecycle:lifecycle-viewmodel:2.6.1 -> 2.8.4 (*)
+| | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1 -> 2.8.4 (*)
+| | +--- androidx.loader:loader:1.0.0
+| | | +--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
+| | | +--- androidx.core:core:1.0.0 -> 1.13.1 (*)
+| | | +--- androidx.lifecycle:lifecycle-livedata:2.0.0 -> 2.8.4
+| | | | +--- androidx.arch.core:core-common:2.2.0 (*)
+| | | | +--- androidx.arch.core:core-runtime:2.2.0 (*)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (*)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4
+| | | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (*)
+| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | | | | \--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*)
+| | | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | | | \--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | | \--- androidx.lifecycle:lifecycle-viewmodel:2.0.0 -> 2.8.4 (*)
+| | +--- androidx.profileinstaller:profileinstaller:1.3.1 (*)
+| | +--- androidx.savedstate:savedstate:1.2.1 (*)
+| | +--- androidx.viewpager:viewpager:1.0.0
+| | | +--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
+| | | +--- androidx.core:core:1.0.0 -> 1.13.1 (*)
+| | | \--- androidx.customview:customview:1.0.0
+| | | +--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
+| | | \--- androidx.core:core:1.0.0 -> 1.13.1 (*)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | \--- androidx.fragment:fragment-ktx:1.7.1 (c)
+| +--- androidx.lifecycle:lifecycle-common:2.5.1 -> 2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel:2.5.1 -> 2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1 -> 2.8.4 (*)
+| +--- androidx.savedstate:savedstate:1.2.0 -> 1.2.1 (*)
+| +--- javax.inject:javax.inject:1
+| \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.24 -> 2.0.20 (*)
++--- com.google.firebase:firebase-bom:33.2.0
+| +--- com.google.firebase:firebase-perf-ktx:21.0.1 (c)
+| +--- com.google.firebase:firebase-crashlytics-ktx:19.0.3 (c)
+| +--- com.google.firebase:firebase-analytics-ktx:22.1.0 (c)
+| +--- com.google.firebase:firebase-messaging-ktx:24.0.1 (c)
+| +--- com.google.firebase:firebase-perf:21.0.1 (c)
+| +--- com.google.firebase:firebase-common:21.0.0 (c)
+| +--- com.google.firebase:firebase-common-ktx:21.0.0 (c)
+| +--- com.google.firebase:firebase-crashlytics:19.0.3 (c)
+| +--- com.google.firebase:firebase-analytics:22.1.0 (c)
+| +--- com.google.firebase:firebase-messaging:24.0.1 (c)
+| +--- com.google.firebase:firebase-config:22.0.0 (c)
+| +--- com.google.firebase:firebase-installations:18.0.0 (c)
+| \--- com.google.firebase:firebase-encoders:17.0.0 (c)
++--- com.google.firebase:firebase-analytics-ktx -> 22.1.0
+| +--- com.google.firebase:firebase-analytics:22.1.0
+| | +--- com.google.android.gms:play-services-measurement:22.1.0
+| | | +--- androidx.collection:collection:1.0.0 -> 1.4.2 (*)
+| | | +--- androidx.legacy:legacy-support-core-utils:1.0.0
+| | | | +--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
+| | | | +--- androidx.core:core:1.0.0 -> 1.13.1 (*)
+| | | | +--- androidx.documentfile:documentfile:1.0.0
+| | | | | \--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
+| | | | +--- androidx.loader:loader:1.0.0 (*)
+| | | | +--- androidx.localbroadcastmanager:localbroadcastmanager:1.0.0
+| | | | | \--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
+| | | | \--- androidx.print:print:1.0.0
+| | | | \--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
+| | | +--- com.google.android.gms:play-services-ads-identifier:18.0.0
+| | | | \--- com.google.android.gms:play-services-basement:18.0.0 -> 18.4.0
+| | | | +--- androidx.collection:collection:1.0.0 -> 1.4.2 (*)
+| | | | +--- androidx.core:core:1.2.0 -> 1.13.1 (*)
+| | | | \--- androidx.fragment:fragment:1.1.0 -> 1.7.1 (*)
+| | | +--- com.google.android.gms:play-services-basement:18.4.0 (*)
+| | | +--- com.google.android.gms:play-services-measurement-base:22.1.0
+| | | | \--- com.google.android.gms:play-services-basement:18.4.0 (*)
+| | | +--- com.google.android.gms:play-services-measurement-impl:22.1.0
+| | | | +--- androidx.collection:collection:1.0.0 -> 1.4.2 (*)
+| | | | +--- androidx.core:core:1.9.0 -> 1.13.1 (*)
+| | | | +--- androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05
+| | | | | +--- androidx.annotation:annotation:1.6.0 -> 1.8.1 (*)
+| | | | | +--- androidx.core:core-ktx:1.8.0 -> 1.13.1 (*)
+| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.21 -> 2.0.20 (*)
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1 -> 1.8.0 (*)
+| | | | | \--- androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05 (c)
+| | | | +--- androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05
+| | | | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | | | | +--- androidx.concurrent:concurrent-futures:1.1.0 (*)
+| | | | | +--- androidx.core:core-ktx:1.8.0 -> 1.13.1 (*)
+| | | | | +--- androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 (*)
+| | | | | +--- com.google.guava:guava:31.1-android
+| | | | | | +--- com.google.guava:failureaccess:1.0.1
+| | | | | | +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
+| | | | | | +--- com.google.code.findbugs:jsr305:3.0.2
+| | | | | | +--- org.checkerframework:checker-qual:3.12.0
+| | | | | | +--- com.google.errorprone:error_prone_annotations:2.11.0 -> 2.26.0
+| | | | | | \--- com.google.j2objc:j2objc-annotations:1.3
+| | | | | +--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava
+| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.21 -> 2.0.20 (*)
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1 -> 1.8.0 (*)
+| | | | | \--- androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05 (c)
+| | | | +--- com.google.android.gms:play-services-ads-identifier:18.0.0 (*)
+| | | | +--- com.google.android.gms:play-services-basement:18.4.0 (*)
+| | | | +--- com.google.android.gms:play-services-measurement-base:22.1.0 (*)
+| | | | +--- com.google.android.gms:play-services-stats:17.0.2
+| | | | | +--- androidx.legacy:legacy-support-core-utils:1.0.0 (*)
+| | | | | \--- com.google.android.gms:play-services-basement:18.0.0 -> 18.4.0 (*)
+| | | | \--- com.google.guava:guava:31.1-android (*)
+| | | \--- com.google.android.gms:play-services-stats:17.0.2 (*)
+| | +--- com.google.android.gms:play-services-measurement-api:22.1.0
+| | | +--- com.google.android.gms:play-services-ads-identifier:18.0.0 (*)
+| | | +--- com.google.android.gms:play-services-basement:18.4.0 (*)
+| | | +--- com.google.android.gms:play-services-measurement-base:22.1.0 (*)
+| | | +--- com.google.android.gms:play-services-measurement-sdk-api:22.1.0
+| | | | +--- com.google.android.gms:play-services-basement:18.4.0 (*)
+| | | | \--- com.google.android.gms:play-services-measurement-base:22.1.0 (*)
+| | | +--- com.google.android.gms:play-services-tasks:18.2.0
+| | | | \--- com.google.android.gms:play-services-basement:18.4.0 (*)
+| | | +--- com.google.firebase:firebase-common:21.0.0
+| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.4 -> 1.8.0
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0 (*)
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0 (*)
+| | | | | +--- com.google.android.gms:play-services-tasks:16.0.1 -> 18.2.0 (*)
+| | | | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 2.0.20 (*)
+| | | | +--- com.google.firebase:firebase-components:18.0.0
+| | | | | +--- com.google.firebase:firebase-annotations:16.2.0
+| | | | | | \--- javax.inject:javax.inject:1
+| | | | | +--- androidx.annotation:annotation:1.5.0 -> 1.8.1 (*)
+| | | | | \--- com.google.errorprone:error_prone_annotations:2.26.0
+| | | | +--- com.google.firebase:firebase-annotations:16.2.0 (*)
+| | | | +--- androidx.annotation:annotation:1.5.0 -> 1.8.1 (*)
+| | | | +--- androidx.concurrent:concurrent-futures:1.1.0 (*)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | +--- com.google.android.gms:play-services-basement:18.3.0 -> 18.4.0 (*)
+| | | | \--- com.google.android.gms:play-services-tasks:18.1.0 -> 18.2.0 (*)
+| | | +--- com.google.firebase:firebase-common-ktx:21.0.0
+| | | | +--- com.google.firebase:firebase-common:21.0.0 (*)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 -> 1.9.10
+| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.9.10 -> 2.0.20 (*)
+| | | | | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 -> 2.0.20
+| | | | | \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| | | | +--- com.google.firebase:firebase-components:18.0.0 (*)
+| | | | \--- com.google.firebase:firebase-annotations:16.2.0 (*)
+| | | +--- com.google.firebase:firebase-components:18.0.0 (*)
+| | | +--- com.google.firebase:firebase-installations:17.0.1 -> 18.0.0
+| | | | +--- com.google.android.gms:play-services-tasks:18.0.1 -> 18.2.0 (*)
+| | | | +--- com.google.firebase:firebase-annotations:16.2.0 (*)
+| | | | +--- com.google.firebase:firebase-common:21.0.0 (*)
+| | | | +--- com.google.firebase:firebase-common-ktx:21.0.0 (*)
+| | | | +--- com.google.firebase:firebase-components:18.0.0 (*)
+| | | | +--- com.google.firebase:firebase-installations-interop:17.1.1 -> 17.2.0
+| | | | | +--- com.google.android.gms:play-services-tasks:18.0.1 -> 18.2.0 (*)
+| | | | | \--- com.google.firebase:firebase-annotations:16.2.0 (*)
+| | | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | +--- com.google.firebase:firebase-installations-interop:17.0.0 -> 17.2.0 (*)
+| | | +--- com.google.firebase:firebase-measurement-connector:19.0.0 -> 20.0.1
+| | | | +--- com.google.android.gms:play-services-basement:18.0.0 -> 18.4.0 (*)
+| | | | \--- com.google.firebase:firebase-annotations:16.0.0 -> 16.2.0 (*)
+| | | +--- com.google.guava:guava:31.1-android (*)
+| | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.7.10 -> 2.0.20 (*)
+| | \--- com.google.android.gms:play-services-measurement-sdk:22.1.0
+| | +--- androidx.collection:collection:1.0.0 -> 1.4.2 (*)
+| | +--- com.google.android.gms:play-services-basement:18.4.0 (*)
+| | +--- com.google.android.gms:play-services-measurement-base:22.1.0 (*)
+| | \--- com.google.android.gms:play-services-measurement-impl:22.1.0 (*)
+| +--- com.google.firebase:firebase-common:21.0.0 (*)
+| +--- com.google.firebase:firebase-common-ktx:21.0.0 (*)
+| +--- com.google.firebase:firebase-components:18.0.0 (*)
+| \--- org.jetbrains.kotlin:kotlin-stdlib:1.7.10 -> 2.0.20 (*)
++--- com.google.firebase:firebase-perf-ktx -> 21.0.1
+| +--- com.google.firebase:firebase-perf:21.0.1
+| | +--- com.google.firebase:firebase-annotations:16.2.0 (*)
+| | +--- com.google.firebase:firebase-installations-interop:17.1.0 -> 17.2.0 (*)
+| | +--- com.google.firebase:protolite-well-known-types:18.0.0
+| | | \--- com.google.protobuf:protobuf-javalite:3.14.0 -> 3.21.11
+| | +--- com.google.firebase:firebase-common:21.0.0 (*)
+| | +--- com.google.firebase:firebase-common-ktx:21.0.0 (*)
+| | +--- com.google.firebase:firebase-components:18.0.0 (*)
+| | +--- com.google.firebase:firebase-config:21.5.0 -> 22.0.0
+| | | +--- com.google.firebase:firebase-config-interop:16.0.1
+| | | | +--- com.google.firebase:firebase-encoders-json:18.0.1
+| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10 -> 1.9.10 (*)
+| | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | | \--- com.google.firebase:firebase-encoders:17.0.0
+| | | | | \--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | \--- com.google.firebase:firebase-encoders:17.0.0 (*)
+| | | +--- com.google.firebase:firebase-annotations:16.2.0 (*)
+| | | +--- com.google.firebase:firebase-installations-interop:17.1.0 -> 17.2.0 (*)
+| | | +--- com.google.firebase:firebase-abt:21.1.1
+| | | | +--- com.google.firebase:firebase-measurement-connector:18.0.0 -> 20.0.1 (*)
+| | | | \--- com.google.android.gms:play-services-basement:18.1.0 -> 18.4.0 (*)
+| | | +--- com.google.firebase:firebase-measurement-connector:18.0.0 -> 20.0.1 (*)
+| | | +--- com.google.firebase:firebase-common:21.0.0 (*)
+| | | +--- com.google.firebase:firebase-common-ktx:21.0.0 (*)
+| | | +--- com.google.firebase:firebase-components:18.0.0 (*)
+| | | +--- com.google.firebase:firebase-installations:17.2.0 -> 18.0.0 (*)
+| | | +--- com.google.android.gms:play-services-tasks:18.0.1 -> 18.2.0 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | \--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | +--- com.google.firebase:firebase-installations:17.2.0 -> 18.0.0 (*)
+| | +--- com.google.firebase:firebase-sessions:2.0.0 -> 2.0.3
+| | | +--- com.google.firebase:firebase-common:21.0.0 (*)
+| | | +--- com.google.firebase:firebase-common-ktx:21.0.0 (*)
+| | | +--- com.google.firebase:firebase-components:18.0.0 (*)
+| | | +--- com.google.firebase:firebase-installations-interop:17.2.0 (*)
+| | | +--- com.google.firebase:firebase-annotations:16.2.0 (*)
+| | | +--- com.google.firebase:firebase-encoders:17.0.0 (*)
+| | | +--- com.google.firebase:firebase-encoders-json:18.0.1 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 -> 1.9.10 (*)
+| | | +--- com.google.firebase:firebase-installations:18.0.0 (*)
+| | | +--- com.google.firebase:firebase-datatransport:19.0.0
+| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | +--- com.google.android.datatransport:transport-api:3.1.0 -> 3.2.0
+| | | | | \--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | +--- com.google.android.datatransport:transport-backend-cct:3.2.0 -> 3.3.0
+| | | | | +--- com.google.android.datatransport:transport-api:3.2.0 (*)
+| | | | | +--- com.google.android.datatransport:transport-runtime:3.3.0
+| | | | | | +--- com.google.android.datatransport:transport-api:3.2.0 (*)
+| | | | | | +--- androidx.annotation:annotation:1.3.0 -> 1.8.1 (*)
+| | | | | | +--- javax.inject:javax.inject:1
+| | | | | | +--- com.google.firebase:firebase-encoders:17.0.0 (*)
+| | | | | | \--- com.google.firebase:firebase-encoders-proto:16.0.0
+| | | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | | | \--- com.google.firebase:firebase-encoders:17.0.0 (*)
+| | | | | +--- com.google.firebase:firebase-encoders:17.0.0 (*)
+| | | | | +--- com.google.firebase:firebase-encoders-json:18.0.0 -> 18.0.1 (*)
+| | | | | \--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | +--- com.google.android.datatransport:transport-runtime:3.2.0 -> 3.3.0 (*)
+| | | | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 -> 1.9.10 (*)
+| | | +--- androidx.datastore:datastore-preferences:1.0.0
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.10 -> 2.0.20 (*)
+| | | | +--- androidx.datastore:datastore:1.0.0
+| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.10 -> 2.0.20 (*)
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0 -> 1.8.0 (*)
+| | | | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | | | | \--- androidx.datastore:datastore-core:1.0.0
+| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.10 -> 2.0.20 (*)
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0 -> 1.8.0 (*)
+| | | | | \--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | \--- androidx.datastore:datastore-preferences-core:1.0.0
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.10 -> 2.0.20 (*)
+| | | | \--- androidx.datastore:datastore-core:1.0.0 (*)
+| | | +--- com.google.android.datatransport:transport-api:3.2.0 (*)
+| | | \--- androidx.annotation:annotation:1.5.0 -> 1.8.1 (*)
+| | +--- com.google.firebase:firebase-datatransport:18.1.8 -> 19.0.0 (*)
+| | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | +--- androidx.lifecycle:lifecycle-process:2.3.1 -> 2.8.4
+| | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (*)
+| | | +--- androidx.startup:startup-runtime:1.1.1 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | | \--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | +--- com.google.android.gms:play-services-tasks:18.0.1 -> 18.2.0 (*)
+| | +--- com.google.protobuf:protobuf-javalite:3.21.11
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | +--- androidx.appcompat:appcompat:1.2.0 -> 1.7.0
+| | | +--- androidx.activity:activity:1.7.0 -> 1.9.1 (*)
+| | | +--- androidx.annotation:annotation:1.3.0 -> 1.8.1 (*)
+| | | +--- androidx.appcompat:appcompat-resources:1.7.0
+| | | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | | | +--- androidx.collection:collection:1.0.0 -> 1.4.2 (*)
+| | | | +--- androidx.core:core:1.6.0 -> 1.13.1 (*)
+| | | | +--- androidx.vectordrawable:vectordrawable:1.1.0
+| | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | | +--- androidx.core:core:1.1.0 -> 1.13.1 (*)
+| | | | | \--- androidx.collection:collection:1.1.0 -> 1.4.2 (*)
+| | | | +--- androidx.vectordrawable:vectordrawable-animated:1.1.0
+| | | | | +--- androidx.vectordrawable:vectordrawable:1.1.0 (*)
+| | | | | +--- androidx.interpolator:interpolator:1.0.0 (*)
+| | | | | \--- androidx.collection:collection:1.1.0 -> 1.4.2 (*)
+| | | | \--- androidx.appcompat:appcompat:1.7.0 (c)
+| | | +--- androidx.collection:collection:1.0.0 -> 1.4.2 (*)
+| | | +--- androidx.core:core:1.13.0 -> 1.13.1 (*)
+| | | +--- androidx.core:core-ktx:1.13.0 -> 1.13.1 (*)
+| | | +--- androidx.cursoradapter:cursoradapter:1.0.0
+| | | | \--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
+| | | +--- androidx.drawerlayout:drawerlayout:1.0.0
+| | | | +--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
+| | | | +--- androidx.core:core:1.0.0 -> 1.13.1 (*)
+| | | | \--- androidx.customview:customview:1.0.0 (*)
+| | | +--- androidx.emoji2:emoji2:1.3.0
+| | | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | | | +--- androidx.collection:collection:1.1.0 -> 1.4.2 (*)
+| | | | +--- androidx.core:core:1.3.0 -> 1.13.1 (*)
+| | | | +--- androidx.lifecycle:lifecycle-process:2.4.1 -> 2.8.4 (*)
+| | | | +--- androidx.startup:startup-runtime:1.0.0 -> 1.1.1 (*)
+| | | | \--- androidx.emoji2:emoji2-views-helper:1.3.0 (c)
+| | | +--- androidx.emoji2:emoji2-views-helper:1.2.0 -> 1.3.0
+| | | | +--- androidx.collection:collection:1.1.0 -> 1.4.2 (*)
+| | | | +--- androidx.core:core:1.3.0 -> 1.13.1 (*)
+| | | | +--- androidx.emoji2:emoji2:1.3.0 (*)
+| | | | \--- androidx.emoji2:emoji2:1.3.0 (c)
+| | | +--- androidx.fragment:fragment:1.5.4 -> 1.7.1 (*)
+| | | +--- androidx.lifecycle:lifecycle-runtime:2.6.1 -> 2.8.4 (*)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.6.1 -> 2.8.4 (*)
+| | | +--- androidx.profileinstaller:profileinstaller:1.3.1 (*)
+| | | +--- androidx.resourceinspection:resourceinspection-annotation:1.0.1
+| | | | \--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | +--- androidx.savedstate:savedstate:1.2.1 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | \--- androidx.appcompat:appcompat-resources:1.7.0 (c)
+| | +--- com.google.android.datatransport:transport-api:3.0.0 -> 3.2.0 (*)
+| | +--- com.google.dagger:dagger:2.27 -> 2.52 (*)
+| | \--- com.squareup.okhttp3:okhttp:3.12.1 -> 4.12.0
+| | +--- com.squareup.okio:okio:3.6.0
+| | | \--- com.squareup.okio:okio-jvm:3.6.0
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 (*)
+| | | \--- org.jetbrains.kotlin:kotlin-stdlib-common:1.9.10 -> 2.0.20
+| | | \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21 -> 1.9.10 (*)
+| +--- com.google.firebase:firebase-common:21.0.0 (*)
+| +--- com.google.firebase:firebase-common-ktx:21.0.0 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 -> 1.9.10 (*)
+| \--- com.google.firebase:firebase-components:18.0.0 (*)
++--- com.google.firebase:firebase-crashlytics-ktx -> 19.0.3
+| +--- com.google.firebase:firebase-crashlytics:19.0.3
+| | +--- com.google.firebase:firebase-sessions:2.0.3 (*)
+| | +--- com.google.android.gms:play-services-tasks:18.1.0 -> 18.2.0 (*)
+| | +--- com.google.firebase:firebase-annotations:16.2.0 (*)
+| | +--- com.google.firebase:firebase-common:21.0.0 (*)
+| | +--- com.google.firebase:firebase-common-ktx:21.0.0 (*)
+| | +--- com.google.firebase:firebase-components:18.0.0 (*)
+| | +--- com.google.firebase:firebase-config-interop:16.0.1 (*)
+| | +--- com.google.firebase:firebase-encoders:17.0.0 (*)
+| | +--- com.google.firebase:firebase-encoders-json:18.0.1 (*)
+| | +--- com.google.firebase:firebase-installations:18.0.0 (*)
+| | +--- com.google.firebase:firebase-installations-interop:17.2.0 (*)
+| | +--- com.google.firebase:firebase-measurement-connector:20.0.1 (*)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 -> 1.9.10 (*)
+| | +--- com.google.android.datatransport:transport-api:3.2.0 (*)
+| | +--- com.google.android.datatransport:transport-backend-cct:3.3.0 (*)
+| | +--- com.google.android.datatransport:transport-runtime:3.3.0 (*)
+| | \--- androidx.annotation:annotation:1.5.0 -> 1.8.1 (*)
+| +--- com.google.firebase:firebase-common:21.0.0 (*)
+| +--- com.google.firebase:firebase-common-ktx:21.0.0 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 -> 1.9.10 (*)
+| \--- com.google.firebase:firebase-components:18.0.0 (*)
++--- com.google.firebase:firebase-messaging-ktx -> 24.0.1
+| +--- com.google.firebase:firebase-messaging:24.0.1
+| | +--- com.google.firebase:firebase-common:21.0.0 (*)
+| | +--- com.google.firebase:firebase-common-ktx:21.0.0 (*)
+| | +--- com.google.firebase:firebase-components:18.0.0 (*)
+| | +--- com.google.firebase:firebase-datatransport:18.2.0 -> 19.0.0 (*)
+| | +--- com.google.firebase:firebase-encoders:17.0.0 (*)
+| | +--- com.google.firebase:firebase-encoders-json:18.0.0 -> 18.0.1 (*)
+| | +--- com.google.firebase:firebase-encoders-proto:16.0.0 (*)
+| | +--- com.google.firebase:firebase-iid-interop:17.1.0
+| | | +--- com.google.android.gms:play-services-basement:17.0.0 -> 18.4.0 (*)
+| | | \--- com.google.android.gms:play-services-tasks:17.0.0 -> 18.2.0 (*)
+| | +--- com.google.firebase:firebase-installations:17.2.0 -> 18.0.0 (*)
+| | +--- com.google.firebase:firebase-installations-interop:17.1.0 -> 17.2.0 (*)
+| | +--- com.google.firebase:firebase-measurement-connector:19.0.0 -> 20.0.1 (*)
+| | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | +--- com.google.android.datatransport:transport-api:3.1.0 -> 3.2.0 (*)
+| | +--- com.google.android.datatransport:transport-backend-cct:3.1.8 -> 3.3.0 (*)
+| | +--- com.google.android.datatransport:transport-runtime:3.1.8 -> 3.3.0 (*)
+| | +--- com.google.android.gms:play-services-base:18.0.1 -> 18.5.0
+| | | +--- androidx.collection:collection:1.0.0 -> 1.4.2 (*)
+| | | +--- androidx.core:core:1.2.0 -> 1.13.1 (*)
+| | | +--- androidx.fragment:fragment:1.0.0 -> 1.7.1 (*)
+| | | +--- com.google.android.gms:play-services-basement:18.4.0 (*)
+| | | \--- com.google.android.gms:play-services-tasks:18.2.0 (*)
+| | +--- com.google.android.gms:play-services-basement:18.1.0 -> 18.4.0 (*)
+| | +--- com.google.android.gms:play-services-cloud-messaging:17.2.0
+| | | +--- com.google.android.gms:play-services-basement:18.3.0 -> 18.4.0 (*)
+| | | \--- com.google.android.gms:play-services-tasks:18.1.0 -> 18.2.0 (*)
+| | +--- com.google.android.gms:play-services-stats:17.0.2 (*)
+| | +--- com.google.android.gms:play-services-tasks:18.0.1 -> 18.2.0 (*)
+| | +--- com.google.errorprone:error_prone_annotations:2.9.0 -> 2.26.0
+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| +--- com.google.firebase:firebase-common:21.0.0 (*)
+| +--- com.google.firebase:firebase-common-ktx:21.0.0 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 -> 1.9.10 (*)
+| \--- com.google.firebase:firebase-components:18.0.0 (*)
++--- project :shared
+| +--- org.jetbrains.compose.ui:ui-tooling-preview:1.6.11
+| | \--- androidx.compose.ui:ui-tooling-preview:1.6.7 -> 1.7.0-rc01 (*)
+| +--- androidx.activity:activity-compose:1.9.1
+| | +--- androidx.activity:activity-ktx:1.9.1
+| | | +--- androidx.activity:activity:1.9.1 (*)
+| | | +--- androidx.core:core-ktx:1.13.0 -> 1.13.1 (*)
+| | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.6.1 -> 2.8.4
+| | | | \--- androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.4
+| | | | +--- androidx.annotation:annotation:1.8.0 -> 1.8.1 (*)
+| | | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (*)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*)
+| | | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | | | \--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1 -> 2.8.4
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (*)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*)
+| | | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | | \--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | | +--- androidx.savedstate:savedstate-ktx:1.2.1
+| | | | +--- androidx.savedstate:savedstate:1.2.1 (*)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.10 -> 2.0.20 (*)
+| | | | \--- androidx.savedstate:savedstate:1.2.1 (c)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | +--- androidx.activity:activity:1.9.1 (c)
+| | | \--- androidx.activity:activity-compose:1.9.1 (c)
+| | +--- androidx.compose.runtime:runtime:1.0.1 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.runtime:runtime-saveable:1.0.1 -> 1.7.0-rc01
+| | | \--- androidx.compose.runtime:runtime-saveable-android:1.7.0-rc01
+| | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | \--- androidx.compose.runtime:runtime:1.7.0-rc01 (c)
+| | +--- androidx.compose.ui:ui:1.0.1 -> 1.7.0-rc01
+| | | \--- androidx.compose.ui:ui-android:1.7.0-rc01
+| | | +--- androidx.activity:activity-ktx:1.7.0 -> 1.9.1 (*)
+| | | +--- androidx.annotation:annotation:1.6.0 -> 1.8.1 (*)
+| | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
+| | | +--- androidx.autofill:autofill:1.0.0
+| | | | \--- androidx.core:core:1.1.0 -> 1.13.1 (*)
+| | | +--- androidx.collection:collection:1.0.0 -> 1.4.2 (*)
+| | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*)
+| | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*)
+| | | +--- androidx.compose.runtime:runtime-saveable:1.7.0-rc01 (*)
+| | | +--- androidx.compose.ui:ui-geometry:1.7.0-rc01
+| | | | \--- androidx.compose.ui:ui-geometry-android:1.7.0-rc01
+| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui-util:1.7.0-rc01
+| | | | | \--- androidx.compose.ui:ui-util-android:1.7.0-rc01
+| | | | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
+| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | | +--- androidx.compose.ui:ui:1.7.0-rc01 (c)
+| | | | | +--- androidx.compose.ui:ui-geometry:1.7.0-rc01 (c)
+| | | | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (c)
+| | | | | +--- androidx.compose.ui:ui-text:1.7.0-rc01 (c)
+| | | | | +--- androidx.compose.ui:ui-tooling-preview:1.7.0-rc01 (c)
+| | | | | \--- androidx.compose.ui:ui-unit:1.7.0-rc01 (c)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | +--- androidx.compose.ui:ui:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-text:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-tooling-preview:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-unit:1.7.0-rc01 (c)
+| | | | \--- androidx.compose.ui:ui-util:1.7.0-rc01 (c)
+| | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01
+| | | | \--- androidx.compose.ui:ui-graphics-android:1.7.0-rc01
+| | | | +--- androidx.annotation:annotation:1.7.0 -> 1.8.1 (*)
+| | | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
+| | | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*)
+| | | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui-unit:1.7.0-rc01
+| | | | | \--- androidx.compose.ui:ui-unit-android:1.7.0-rc01
+| | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
+| | | | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*)
+| | | | | +--- androidx.collection:collection-ktx:1.2.0 -> 1.4.2
+| | | | | | +--- androidx.collection:collection:1.4.2 (*)
+| | | | | | \--- androidx.collection:collection:1.4.2 (c)
+| | | | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*)
+| | | | | +--- androidx.compose.ui:ui-geometry:1.7.0-rc01 (*)
+| | | | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*)
+| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | | +--- androidx.compose.ui:ui:1.7.0-rc01 (c)
+| | | | | +--- androidx.compose.ui:ui-geometry:1.7.0-rc01 (c)
+| | | | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (c)
+| | | | | +--- androidx.compose.ui:ui-text:1.7.0-rc01 (c)
+| | | | | +--- androidx.compose.ui:ui-tooling-preview:1.7.0-rc01 (c)
+| | | | | \--- androidx.compose.ui:ui-util:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*)
+| | | | +--- androidx.core:core:1.12.0 -> 1.13.1 (*)
+| | | | +--- androidx.graphics:graphics-path:1.0.1
+| | | | | +--- androidx.core:core:1.12.0 -> 1.13.1 (*)
+| | | | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | +--- androidx.compose.ui:ui:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-geometry:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-text:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-tooling-preview:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-unit:1.7.0-rc01 (c)
+| | | | \--- androidx.compose.ui:ui-util:1.7.0-rc01 (c)
+| | | +--- androidx.compose.ui:ui-text:1.7.0-rc01
+| | | | \--- androidx.compose.ui:ui-text-android:1.7.0-rc01
+| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
+| | | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*)
+| | | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*)
+| | | | +--- androidx.compose.runtime:runtime-saveable:1.6.0 -> 1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui-unit:1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*)
+| | | | +--- androidx.core:core:1.7.0 -> 1.13.1 (*)
+| | | | +--- androidx.emoji2:emoji2:1.2.0 -> 1.3.0 (*)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*)
+| | | | +--- androidx.compose.ui:ui:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-geometry:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-tooling-preview:1.7.0-rc01 (c)
+| | | | +--- androidx.compose.ui:ui-unit:1.7.0-rc01 (c)
+| | | | \--- androidx.compose.ui:ui-util:1.7.0-rc01 (c)
+| | | +--- androidx.compose.ui:ui-unit:1.7.0-rc01 (*)
+| | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*)
+| | | +--- androidx.core:core:1.12.0 -> 1.13.1 (*)
+| | | +--- androidx.customview:customview-poolingcontainer:1.0.0
+| | | | +--- androidx.core:core-ktx:1.5.0 -> 1.13.1 (*)
+| | | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 2.0.20 (*)
+| | | +--- androidx.emoji2:emoji2:1.2.0 -> 1.3.0 (*)
+| | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.3 -> 2.8.4
+| | | | \--- androidx.lifecycle:lifecycle-runtime-compose-android:2.8.4
+| | | | +--- androidx.annotation:annotation:1.8.0 -> 1.8.1 (*)
+| | | | +--- androidx.compose.runtime:runtime:1.6.5 -> 1.7.0-rc01 (*)
+| | | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (*)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (*)
+| | | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | | \--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.6.1 -> 2.8.4 (*)
+| | | +--- androidx.profileinstaller:profileinstaller:1.3.1 (*)
+| | | +--- androidx.savedstate:savedstate-ktx:1.2.1 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*)
+| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*)
+| | | +--- androidx.compose.ui:ui-geometry:1.7.0-rc01 (c)
+| | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (c)
+| | | +--- androidx.compose.ui:ui-text:1.7.0-rc01 (c)
+| | | +--- androidx.compose.ui:ui-tooling-preview:1.7.0-rc01 (c)
+| | | +--- androidx.compose.ui:ui-unit:1.7.0-rc01 (c)
+| | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (c)
+| | | \--- androidx.compose.foundation:foundation:1.7.0-rc01 (c)
+| | +--- androidx.lifecycle:lifecycle-viewmodel:2.6.1 -> 2.8.4 (*)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | +--- androidx.activity:activity:1.9.1 (c)
+| | \--- androidx.activity:activity-ktx:1.9.1 (c)
+| +--- io.insert-koin:koin-android:3.6.0-Beta4
+| | +--- io.insert-koin:koin-core:3.6.0-Beta4
+| | | \--- io.insert-koin:koin-core-jvm:3.6.0-Beta4
+| | | +--- co.touchlab:stately-concurrency:2.0.6
+| | | | \--- co.touchlab:stately-concurrency-jvm:2.0.6
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 (*)
+| | | | \--- co.touchlab:stately-strict:2.0.6
+| | | | \--- co.touchlab:stately-strict-jvm:2.0.6
+| | | | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 (*)
+| | | +--- co.touchlab:stately-concurrent-collections:2.0.6
+| | | | \--- co.touchlab:stately-concurrent-collections-jvm:2.0.6
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 (*)
+| | | | \--- co.touchlab:stately-concurrency:2.0.6 (*)
+| | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.24 -> 2.0.20 (*)
+| | +--- androidx.appcompat:appcompat:1.6.1 -> 1.7.0 (*)
+| | +--- androidx.activity:activity-ktx:1.9.0 -> 1.9.1 (*)
+| | +--- androidx.fragment:fragment-ktx:1.7.1
+| | | +--- androidx.activity:activity-ktx:1.8.1 -> 1.9.1 (*)
+| | | +--- androidx.collection:collection-ktx:1.1.0 -> 1.4.2 (*)
+| | | +--- androidx.core:core-ktx:1.2.0 -> 1.13.1 (*)
+| | | +--- androidx.fragment:fragment:1.7.1 (*)
+| | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.6.1 -> 2.8.4 (*)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1 -> 2.8.4 (*)
+| | | +--- androidx.savedstate:savedstate-ktx:1.2.1 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | \--- androidx.fragment:fragment:1.7.1 (c)
+| | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0 -> 2.8.4 (*)
+| | +--- androidx.lifecycle:lifecycle-common-java8:2.8.0 -> 2.8.4
+| | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (*)
+| | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | | \--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.24 -> 2.0.20 (*)
+| +--- io.insert-koin:koin-androidx-compose:3.6.0-Beta4
+| | +--- io.insert-koin:koin-android:3.6.0-Beta4 (*)
+| | +--- io.insert-koin:koin-compose:1.2.0-Beta4
+| | | \--- io.insert-koin:koin-compose-jvm:1.2.0-Beta4
+| | | +--- io.insert-koin:koin-core:3.6.0-Beta4 (*)
+| | | +--- org.jetbrains.compose.runtime:runtime:1.6.10-rc03
+| | | | \--- androidx.compose.runtime:runtime:1.6.7 -> 1.7.0-rc01 (*)
+| | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.24 -> 2.0.20 (*)
+| | +--- androidx.compose.runtime:runtime:1.6.7 -> 1.7.0-rc01 (*)
+| | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0 -> 2.8.4
+| | | \--- androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.4
+| | | +--- androidx.annotation:annotation:1.8.0 -> 1.8.1 (*)
+| | | +--- androidx.compose.runtime:runtime:1.6.0 -> 1.7.0-rc01 (*)
+| | | +--- androidx.compose.ui:ui:1.6.0 -> 1.7.0-rc01 (*)
+| | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (*)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (*)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c)
+| | | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c)
+| | | \--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.24 -> 2.0.20 (*)
+| +--- io.insert-koin:koin-core:3.6.0-Beta4 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- org.jetbrains.compose.material:material:1.6.11
+| | \--- androidx.compose.material:material:1.6.7 -> 1.6.8
+| | \--- androidx.compose.material:material-android:1.6.8
+| | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | +--- androidx.compose.animation:animation:1.6.8 -> 1.7.0-rc01
+| | | \--- androidx.compose.animation:animation-android:1.7.0-rc01
+| | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
+| | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*)
+| | | +--- androidx.compose.animation:animation-core:1.7.0-rc01
+| | | | \--- androidx.compose.animation:animation-core-android:1.7.0-rc01
+| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*)
+| | | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui:1.6.0 -> 1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui-unit:1.6.0 -> 1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*)
+| | | | \--- androidx.compose.animation:animation:1.7.0-rc01 (c)
+| | | +--- androidx.compose.foundation:foundation-layout:1.6.0 -> 1.7.0-rc01
+| | | | \--- androidx.compose.foundation:foundation-layout-android:1.7.0-rc01
+| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
+| | | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*)
+| | | | +--- androidx.compose.animation:animation-core:1.2.1 -> 1.7.0-rc01 (*)
+| | | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui:1.6.0 -> 1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui-unit:1.7.0-rc01 (*)
+| | | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*)
+| | | | +--- androidx.core:core:1.7.0 -> 1.13.1 (*)
+| | | | \--- androidx.compose.foundation:foundation:1.7.0-rc01 (c)
+| | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*)
+| | | +--- androidx.compose.ui:ui:1.7.0-rc01 (*)
+| | | +--- androidx.compose.ui:ui-geometry:1.6.0 -> 1.7.0-rc01 (*)
+| | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (*)
+| | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*)
+| | | \--- androidx.compose.animation:animation-core:1.7.0-rc01 (c)
+| | +--- androidx.compose.animation:animation-core:1.6.8 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.foundation:foundation:1.6.8 -> 1.7.0-rc01
+| | | \--- androidx.compose.foundation:foundation-android:1.7.0-rc01
+| | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
+| | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*)
+| | | +--- androidx.compose.animation:animation:1.7.0-rc01 (*)
+| | | +--- androidx.compose.foundation:foundation-layout:1.7.0-rc01 (*)
+| | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*)
+| | | +--- androidx.compose.ui:ui:1.7.0-rc01 (*)
+| | | +--- androidx.compose.ui:ui-text:1.6.0 -> 1.7.0-rc01 (*)
+| | | +--- androidx.compose.ui:ui-util:1.6.0 -> 1.7.0-rc01 (*)
+| | | +--- androidx.core:core:1.13.1 (*)
+| | | +--- androidx.emoji2:emoji2:1.3.0 (*)
+| | | \--- androidx.compose.foundation:foundation-layout:1.7.0-rc01 (c)
+| | +--- androidx.compose.foundation:foundation-layout:1.6.8 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.material:material-icons-core:1.6.8
+| | | \--- androidx.compose.material:material-icons-core-android:1.6.8
+| | | +--- androidx.compose.ui:ui:1.6.8 -> 1.7.0-rc01 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | +--- androidx.compose.material:material:1.6.8 (c)
+| | | +--- androidx.compose.material:material-icons-extended:1.6.8 (c)
+| | | \--- androidx.compose.material:material-ripple:1.6.8 (c)
+| | +--- androidx.compose.material:material-ripple:1.6.8
+| | | \--- androidx.compose.material:material-ripple-android:1.6.8
+| | | +--- androidx.compose.animation:animation:1.6.8 -> 1.7.0-rc01 (*)
+| | | +--- androidx.compose.foundation:foundation:1.6.8 -> 1.7.0-rc01 (*)
+| | | +--- androidx.compose.runtime:runtime:1.6.8 -> 1.7.0-rc01 (*)
+| | | +--- androidx.compose.ui:ui-util:1.6.8 -> 1.7.0-rc01 (*)
+| | | +--- androidx.compose.material:material:1.6.8 (c)
+| | | +--- androidx.compose.material:material-icons-core:1.6.8 (c)
+| | | \--- androidx.compose.material:material-icons-extended:1.6.8 (c)
+| | +--- androidx.compose.runtime:runtime:1.6.8 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.ui:ui:1.6.8 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.ui:ui-text:1.6.8 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.ui:ui-util:1.6.8 -> 1.7.0-rc01 (*)
+| | +--- androidx.lifecycle:lifecycle-runtime:2.6.1 -> 2.8.4 (*)
+| | +--- androidx.lifecycle:lifecycle-viewmodel:2.6.1 -> 2.8.4 (*)
+| | +--- androidx.savedstate:savedstate:1.2.1 (*)
+| | +--- androidx.compose.material:material-icons-core:1.6.8 (c)
+| | +--- androidx.compose.material:material-icons-extended:1.6.8 (c)
+| | \--- androidx.compose.material:material-ripple:1.6.8 (c)
+| +--- org.jetbrains.compose.material3:material3:1.6.11
+| | \--- androidx.compose.material3:material3:1.2.1
+| | \--- androidx.compose.material3:material3-android:1.2.1
+| | +--- androidx.activity:activity-compose:1.5.0 -> 1.9.1 (*)
+| | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*)
+| | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*)
+| | +--- androidx.compose.animation:animation-core:1.6.0 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.foundation:foundation:1.6.0 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.foundation:foundation-layout:1.6.0 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.material:material-icons-core:1.6.0 -> 1.6.8 (*)
+| | +--- androidx.compose.material:material-ripple:1.6.0 -> 1.6.8 (*)
+| | +--- androidx.compose.runtime:runtime:1.6.0 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.ui:ui-graphics:1.6.0 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.ui:ui-text:1.6.0 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.ui:ui-util:1.6.0 -> 1.7.0-rc01 (*)
+| | +--- androidx.lifecycle:lifecycle-common-java8:2.6.1 -> 2.8.4 (*)
+| | +--- androidx.lifecycle:lifecycle-runtime:2.6.1 -> 2.8.4 (*)
+| | +--- androidx.lifecycle:lifecycle-viewmodel:2.6.1 -> 2.8.4 (*)
+| | \--- androidx.savedstate:savedstate-ktx:1.2.1 (*)
+| +--- io.insert-koin:koin-compose:1.2.0-Beta4 (*)
+| \--- io.insert-koin:koin-compose-viewmodel:1.2.0-Beta4
+| \--- io.insert-koin:koin-compose-viewmodel-jvm:1.2.0-Beta4
+| +--- io.insert-koin:koin-compose:1.2.0-Beta4 (*)
+| +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0-rc03
+| | \--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0 -> 2.8.4 (*)
+| +--- org.jetbrains.androidx.navigation:navigation-compose:2.7.0-alpha06
+| | \--- androidx.navigation:navigation-compose:2.7.7 -> 2.8.0-rc01
+| | +--- androidx.activity:activity-compose:1.8.0 -> 1.9.1 (*)
+| | +--- androidx.compose.animation:animation:1.7.0-rc01 (*)
+| | +--- androidx.compose.foundation:foundation-layout:1.7.0-rc01 (*)
+| | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*)
+| | +--- androidx.compose.runtime:runtime-saveable:1.7.0-rc01 (*)
+| | +--- androidx.compose.ui:ui:1.7.0-rc01 (*)
+| | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2 -> 2.8.4 (*)
+| | +--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01
+| | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01
+| | | | +--- androidx.navigation:navigation-common:2.8.0-rc01
+| | | | | +--- androidx.annotation:annotation:1.8.1 (*)
+| | | | | +--- androidx.collection:collection-ktx:1.4.2 (*)
+| | | | | +--- androidx.core:core-ktx:1.1.0 -> 1.13.1 (*)
+| | | | | +--- androidx.lifecycle:lifecycle-common:2.6.2 -> 2.8.4 (*)
+| | | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 -> 2.8.4 (*)
+| | | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2 -> 2.8.4 (*)
+| | | | | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2 -> 2.8.4 (*)
+| | | | | +--- androidx.profileinstaller:profileinstaller:1.3.1 (*)
+| | | | | +--- androidx.savedstate:savedstate-ktx:1.2.1 (*)
+| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3
+| | | | | | \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.3
+| | | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.9.22 -> 2.0.20 (*)
+| | | | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3
+| | | | | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.3 (c)
+| | | | | | | \--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3 (c)
+| | | | | | \--- org.jetbrains.kotlin:kotlin-stdlib-common:1.9.22 -> 2.0.20 (*)
+| | | | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c)
+| | | | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c)
+| | | | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c)
+| | | | | \--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c)
+| | | | +--- androidx.navigation:navigation-common:2.8.0-rc01 (c)
+| | | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c)
+| | | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c)
+| | | | \--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c)
+| | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01
+| | | | +--- androidx.activity:activity-ktx:1.7.1 -> 1.9.1 (*)
+| | | | +--- androidx.annotation:annotation-experimental:1.4.1 (*)
+| | | | +--- androidx.collection:collection:1.4.2 (*)
+| | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 -> 2.8.4 (*)
+| | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2 -> 2.8.4 (*)
+| | | | +--- androidx.navigation:navigation-common:2.8.0-rc01 (*)
+| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3 (*)
+| | | | +--- androidx.navigation:navigation-common:2.8.0-rc01 (c)
+| | | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c)
+| | | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c)
+| | | | \--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c)
+| | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c)
+| | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c)
+| | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c)
+| | | \--- androidx.navigation:navigation-common:2.8.0-rc01 (c)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3 (*)
+| | +--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c)
+| | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c)
+| | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c)
+| | \--- androidx.navigation:navigation-common:2.8.0-rc01 (c)
+| \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.24 -> 2.0.20 (*)
++--- project :core:logs
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- androidx.compose.runtime:runtime -> 1.7.0-rc01 (*)
+| +--- com.google.firebase:firebase-bom:33.2.0 (*)
+| \--- com.google.firebase:firebase-analytics-ktx -> 22.1.0 (*)
++--- project :core:common
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| \--- com.google.dagger:hilt-android:2.52 (*)
++--- project :core:model
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.20 (*)
+| +--- com.squareup.retrofit2:converter-gson:2.11.0
+| | +--- com.squareup.retrofit2:retrofit:2.11.0
+| | | \--- com.squareup.okhttp3:okhttp:3.14.9 -> 4.12.0 (*)
+| | \--- com.google.code.gson:gson:2.10.1
+| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.20
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| \--- org.jetbrains.kotlin:kotlin-android-extensions-runtime:2.0.20
+| \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
++--- project :core:data
+| +--- project :core:common (*)
+| +--- project :core:model (*)
+| +--- project :core:network
+| | +--- project :core:common (*)
+| | +--- project :core:model (*)
+| | +--- project :core:datastore
+| | | +--- project :core:common (*)
+| | | +--- project :core:model (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| | | +--- com.google.dagger:hilt-android:2.52 (*)
+| | | +--- com.squareup.retrofit2:converter-gson:2.11.0 (*)
+| | | +--- com.github.Raizlabs.DBFlow:dbflow:4.2.4
+| | | | +--- com.github.Raizlabs.DBFlow:dbflow-core:4.2.4
+| | | | \--- com.android.support:support-annotations:26.0.1 -> androidx.annotation:annotation:1.8.1 (*)
+| | | +--- com.github.Raizlabs.DBFlow:dbflow-core:4.2.4
+| | | +--- io.reactivex.rxjava2:rxandroid:2.1.1
+| | | | \--- io.reactivex.rxjava2:rxjava:2.2.6 -> 2.2.21
+| | | | \--- org.reactivestreams:reactive-streams:1.0.3 -> 1.0.4
+| | | \--- io.reactivex.rxjava2:rxjava:2.2.21 (*)
+| | +--- com.google.dagger:hilt-android:2.52 (*)
+| | +--- com.squareup.retrofit2:retrofit:2.11.0 (*)
+| | +--- com.squareup.retrofit2:adapter-rxjava2:2.11.0
+| | | +--- com.squareup.retrofit2:retrofit:2.11.0 (*)
+| | | +--- io.reactivex.rxjava2:rxjava:2.2.21 (*)
+| | | \--- org.reactivestreams:reactive-streams:1.0.4
+| | +--- com.squareup.retrofit2:converter-gson:2.11.0 (*)
+| | +--- com.squareup.okhttp3:logging-interceptor:4.12.0
+| | | +--- com.squareup.okhttp3:okhttp:4.12.0 (*)
+| | | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21 -> 1.9.10 (*)
+| | +--- com.squareup.okhttp3:okhttp:4.12.0 (*)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.20 (*)
+| | \--- org.mockito:mockito-core:5.4.0
+| | +--- net.bytebuddy:byte-buddy:1.14.5
+| | +--- net.bytebuddy:byte-buddy-agent:1.14.5
+| | \--- org.objenesis:objenesis:3.3
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- com.squareup.retrofit2:retrofit:2.11.0 (*)
+| +--- com.squareup.okhttp3:okhttp:4.12.0 (*)
+| +--- org.mockito:mockito-core:5.4.0 (*)
+| +--- app.cash.turbine:turbine:1.1.0
+| | \--- app.cash.turbine:turbine-jvm:1.1.0
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0 (*)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.9.22 -> 2.0.20 (*)
+| | \--- org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0
+| | \--- org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm:1.8.0
+| | +--- org.jetbrains:annotations:23.0.0
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0 (*)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 2.0.20 (*)
+| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.20 (*)
++--- project :core:datastore (*)
++--- project :core:ui
+| +--- project :core:designsystem
+| | +--- androidx.compose.ui:ui -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.foundation:foundation -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.foundation:foundation-layout -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.material:material-icons-extended -> 1.6.8
+| | | \--- androidx.compose.material:material-icons-extended-android:1.6.8
+| | | +--- androidx.compose.material:material-icons-core:1.6.8 (*)
+| | | +--- androidx.compose.runtime:runtime:1.6.8 -> 1.7.0-rc01 (*)
+| | | +--- androidx.compose.material:material:1.6.8 (c)
+| | | +--- androidx.compose.material:material-icons-core:1.6.8 (c)
+| | | \--- androidx.compose.material:material-ripple:1.6.8 (c)
+| | +--- androidx.compose.material3:material3:1.2.1 (*)
+| | +--- androidx.compose.runtime:runtime -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.ui:ui-util -> 1.7.0-rc01 (*)
+| | +--- androidx.activity:activity-compose:1.9.1 (*)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| | +--- androidx.compose:compose-bom:2024.08.00 (*)
+| | +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| | \--- com.google.accompanist:accompanist-pager:0.34.0
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4 -> 1.8.0 (*)
+| | +--- androidx.compose.foundation:foundation:1.6.0 -> 1.7.0-rc01 (*)
+| | +--- dev.chrisbanes.snapper:snapper:0.2.2
+| | | +--- androidx.compose.foundation:foundation:1.1.1 -> 1.7.0-rc01 (*)
+| | | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10 -> 1.9.10 (*)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.22 -> 2.0.20 (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- androidx.metrics:metrics-performance:1.0.0-beta01
+| | +--- androidx.collection:collection:1.1.0 -> 1.4.2 (*)
+| | +--- androidx.core:core:1.5.0 -> 1.13.1 (*)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| \--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
++--- project :core:designsystem (*)
++--- project :feature:loan
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7
+| | \--- org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.7
+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 2.0.20 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0
+| | +--- androidx.compose.runtime:runtime:1.0.1 -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.ui:ui:1.0.1 -> 1.7.0-rc01 (*)
+| | +--- androidx.hilt:hilt-navigation:1.2.0
+| | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | +--- androidx.navigation:navigation-runtime:2.5.1 -> 2.8.0-rc01 (*)
+| | | +--- com.google.dagger:hilt-android:2.49 -> 2.52 (*)
+| | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1 -> 2.8.4 (*)
+| | +--- androidx.navigation:navigation-compose:2.5.1 -> 2.8.0-rc01 (*)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| \--- project :core:qrcode
+| +--- com.google.zxing:core:3.5.3
+| +--- com.squareup.retrofit2:converter-gson:2.11.0 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| +--- project :core:model (*)
+| +--- androidx.compose.ui:ui -> 1.7.0-rc01 (*)
+| +--- androidx.camera:camera-camera2:1.3.4
+| | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | +--- androidx.camera:camera-core:1.3.4
+| | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | | +--- androidx.annotation:annotation-experimental:1.1.0 -> 1.4.1 (*)
+| | | +--- androidx.concurrent:concurrent-futures:1.0.0 -> 1.1.0 (*)
+| | | +--- androidx.core:core:1.1.0 -> 1.13.1 (*)
+| | | +--- androidx.exifinterface:exifinterface:1.3.2
+| | | | \--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*)
+| | | +--- androidx.lifecycle:lifecycle-common:2.1.0 -> 2.8.4 (*)
+| | | +--- androidx.lifecycle:lifecycle-livedata:2.1.0 -> 2.8.4 (*)
+| | | +--- com.google.auto.value:auto-value-annotations:1.6.3
+| | | +--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*)
+| | | +--- androidx.camera:camera-camera2:1.3.4 (c)
+| | | +--- androidx.camera:camera-lifecycle:1.3.4 (c)
+| | | +--- androidx.camera:camera-view:1.3.4 (c)
+| | | \--- androidx.camera:camera-video:1.3.4 (c)
+| | +--- androidx.concurrent:concurrent-futures:1.0.0 -> 1.1.0 (*)
+| | +--- androidx.core:core:1.1.0 -> 1.13.1 (*)
+| | +--- com.google.auto.value:auto-value-annotations:1.6.3
+| | +--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava
+| | +--- androidx.camera:camera-core:1.3.4 (c)
+| | +--- androidx.camera:camera-lifecycle:1.3.4 (c)
+| | +--- androidx.camera:camera-view:1.3.4 (c)
+| | \--- androidx.camera:camera-video:1.3.4 (c)
+| +--- androidx.camera:camera-lifecycle:1.3.4
+| | +--- androidx.camera:camera-core:1.3.4 (*)
+| | +--- androidx.concurrent:concurrent-futures:1.0.0 -> 1.1.0 (*)
+| | +--- androidx.core:core:1.1.0 -> 1.13.1 (*)
+| | +--- androidx.lifecycle:lifecycle-common:2.1.0 -> 2.8.4 (*)
+| | +--- com.google.auto.value:auto-value-annotations:1.6.3
+| | +--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava
+| | +--- androidx.camera:camera-camera2:1.3.4 (c)
+| | +--- androidx.camera:camera-core:1.3.4 (c)
+| | +--- androidx.camera:camera-view:1.3.4 (c)
+| | \--- androidx.camera:camera-video:1.3.4 (c)
+| +--- androidx.camera:camera-view:1.3.4
+| | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | +--- androidx.annotation:annotation-experimental:1.3.1 -> 1.4.1 (*)
+| | +--- androidx.appcompat:appcompat:1.1.0 -> 1.7.0 (*)
+| | +--- androidx.camera:camera-core:1.3.4 (*)
+| | +--- androidx.camera:camera-lifecycle:1.3.4 (*)
+| | +--- androidx.camera:camera-video:1.3.4
+| | | +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| | | +--- androidx.camera:camera-core:1.3.4 (*)
+| | | +--- androidx.concurrent:concurrent-futures:1.0.0 -> 1.1.0 (*)
+| | | +--- androidx.core:core:1.1.0 -> 1.13.1 (*)
+| | | +--- com.google.auto.value:auto-value-annotations:1.6.3
+| | | +--- androidx.camera:camera-camera2:1.3.4 (c)
+| | | +--- androidx.camera:camera-core:1.3.4 (c)
+| | | +--- androidx.camera:camera-lifecycle:1.3.4 (c)
+| | | \--- androidx.camera:camera-view:1.3.4 (c)
+| | +--- androidx.concurrent:concurrent-futures:1.0.0 -> 1.1.0 (*)
+| | +--- androidx.core:core:1.3.2 -> 1.13.1 (*)
+| | +--- androidx.lifecycle:lifecycle-common:2.0.0 -> 2.8.4 (*)
+| | +--- com.google.auto.value:auto-value-annotations:1.6.3
+| | +--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava
+| | +--- androidx.camera:camera-camera2:1.3.4 (c)
+| | +--- androidx.camera:camera-core:1.3.4 (c)
+| | +--- androidx.camera:camera-lifecycle:1.3.4 (c)
+| | \--- androidx.camera:camera-video:1.3.4 (c)
+| \--- androidx.camera:camera-core:1.3.4 (*)
++--- project :feature:beneficiary
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| \--- com.squareup.okhttp3:okhttp:4.12.0 (*)
++--- project :feature:guarantor
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| \--- com.squareup.okhttp3:okhttp:4.12.0 (*)
++--- project :feature:savings
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| +--- project :core:qrcode (*)
+| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.20 (*)
++--- project :feature:qr
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| +--- project :core:qrcode (*)
+| +--- io.github.mr0xf00:easycrop:0.1.1
+| | +--- androidx.compose:compose-bom:2023.01.00 -> 2024.08.00 (*)
+| | +--- androidx.core:core-ktx:1.9.0 -> 1.13.1 (*)
+| | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.5.1 -> 2.8.4 (*)
+| | +--- androidx.activity:activity-compose:1.6.1 -> 1.9.1 (*)
+| | +--- androidx.compose.material:material -> 1.6.8 (*)
+| | +--- androidx.compose.ui:ui -> 1.7.0-rc01 (*)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0 -> 1.9.10 (*)
+| +--- com.google.accompanist:accompanist-permissions:0.34.0
+| | +--- androidx.activity:activity-compose:1.7.2 -> 1.9.1 (*)
+| | +--- androidx.compose.foundation:foundation:1.6.0 -> 1.7.0-rc01 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4 -> 1.8.0 (*)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.22 -> 2.0.20 (*)
+| \--- androidx.camera:camera-core:1.3.4 (*)
++--- project :feature:transfer-process
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| \--- com.squareup.retrofit2:converter-gson:2.11.0 (*)
++--- project :feature:account
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| +--- project :libs:pullrefresh
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| | +--- androidx.compose:compose-bom:2024.08.00 (*)
+| | +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.animation:animation -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.material3:material3:1.2.1 (*)
+| | +--- androidx.compose.runtime:runtime -> 1.7.0-rc01 (*)
+| | \--- androidx.compose.ui:ui-util -> 1.7.0-rc01 (*)
+| \--- com.google.accompanist:accompanist-pager:0.34.0 (*)
++--- project :feature:client-charge
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| \--- com.github.Raizlabs.DBFlow:dbflow:4.2.4 (*)
++--- project :feature:recent-transaction
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| \--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
++--- project :feature:third-party-transfer
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.20 (*)
++--- project :feature:help
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| \--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
++--- project :feature:notification
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| +--- project :core:datastore (*)
+| \--- com.github.Raizlabs.DBFlow:dbflow:4.2.4 (*)
++--- project :feature:location
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| \--- com.google.maps.android:maps-compose:4.4.1
+| +--- androidx.compose:compose-bom:2024.04.00 -> 2024.08.00 (*)
+| +--- androidx.core:core-ktx:1.12.0 -> 1.13.1 (*)
+| +--- androidx.compose.foundation:foundation -> 1.7.0-rc01 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.23 -> 2.0.20 (*)
+| \--- com.google.maps.android:maps-ktx:5.0.0
+| +--- com.google.android.gms:play-services-maps:18.2.0
+| | +--- androidx.fragment:fragment:1.0.0 -> 1.7.1 (*)
+| | +--- com.google.android.gms:play-services-base:18.0.1 -> 18.5.0 (*)
+| | +--- com.google.android.gms:play-services-basement:18.0.0 -> 18.4.0 (*)
+| | \--- com.google.android.gms:play-services-tasks:18.0.1 -> 18.2.0 (*)
+| \--- androidx.databinding:viewbinding:7.4.2
+| \--- androidx.annotation:annotation:1.0.0 -> 1.8.1 (*)
++--- project :feature:about
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| \--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
++--- project :feature:settings
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| \--- androidx.appcompat:appcompat:1.7.0 (*)
++--- project :feature:update-password
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| \--- com.squareup.okhttp3:okhttp:4.12.0 (*)
++--- project :feature:home
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| \--- com.squareup.okhttp3:okhttp:4.12.0 (*)
++--- project :feature:auth
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| +--- project :libs:country-code-picker
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| | +--- androidx.compose:compose-bom:2024.08.00 (*)
+| | +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.foundation:foundation -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.foundation:foundation-layout -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.material:material-icons-extended -> 1.6.8 (*)
+| | +--- androidx.compose.material3:material3:1.2.1 (*)
+| | +--- androidx.compose.runtime:runtime -> 1.7.0-rc01 (*)
+| | +--- androidx.compose.ui:ui-util -> 1.7.0-rc01 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| | \--- io.michaelrocks:libphonenumber-android:8.13.35
+| \--- com.squareup.okhttp3:okhttp:4.12.0 (*)
++--- project :feature:user-profile
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- project :core:designsystem (*)
+| +--- project :core:ui (*)
+| +--- project :core:data (*)
+| +--- project :core:model (*)
+| +--- project :core:common (*)
+| +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7 (*)
+| +--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| +--- com.squareup.okhttp3:okhttp:4.12.0 (*)
+| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.20 (*)
++--- project :libs:mifos-passcode
+| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*)
+| +--- androidx.compose:compose-bom:2024.08.00 (*)
+| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*)
+| +--- com.google.dagger:hilt-android:2.52 (*)
+| +--- androidx.core:core-ktx:1.13.1 (*)
+| +--- androidx.compose.foundation:foundation -> 1.7.0-rc01 (*)
+| +--- androidx.compose.foundation:foundation-layout -> 1.7.0-rc01 (*)
+| +--- androidx.compose.material:material-icons-extended -> 1.6.8 (*)
+| +--- androidx.compose.material3:material3:1.2.1 (*)
+| +--- androidx.compose.runtime:runtime -> 1.7.0-rc01 (*)
+| +--- androidx.compose.ui:ui-util -> 1.7.0-rc01 (*)
+| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
+| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*)
+| +--- androidx.navigation:navigation-compose:2.8.0-rc01 (*)
+| \--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
++--- androidx.appcompat:appcompat:1.7.0 (*)
++--- androidx.activity:activity-ktx:1.9.1 (*)
++--- androidx.activity:activity-compose:1.9.1 (*)
++--- androidx.compose.material3:material3:1.2.1 (*)
++--- androidx.compose.material:material:1.6.8 (*)
++--- androidx.compose.foundation:foundation -> 1.7.0-rc01 (*)
++--- androidx.compose.foundation:foundation-layout -> 1.7.0-rc01 (*)
++--- androidx.compose.material:material-icons-extended -> 1.6.8 (*)
++--- androidx.compose.runtime:runtime -> 1.7.0-rc01 (*)
++--- androidx.compose.ui:ui-util -> 1.7.0-rc01 (*)
++--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*)
++--- androidx.hilt:hilt-navigation-compose:1.2.0 (*)
++--- androidx.core:core-splashscreen:1.0.1
+| +--- androidx.annotation:annotation:1.2.0 -> 1.8.1 (*)
+| \--- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 2.0.20 (*)
++--- androidx.tracing:tracing-ktx:1.2.0
+| +--- androidx.tracing:tracing:1.2.0 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.21 -> 2.0.20 (*)
+| \--- androidx.tracing:tracing:1.2.0 (c)
++--- androidx.profileinstaller:profileinstaller:1.3.1 (*)
++--- com.google.android.gms:play-services-oss-licenses:17.1.0
+| +--- androidx.appcompat:appcompat:1.0.0 -> 1.7.0 (*)
+| +--- androidx.loader:loader:1.0.0 (*)
+| +--- com.google.android.gms:play-services-base:18.5.0 (*)
+| +--- com.google.android.gms:play-services-basement:18.4.0 (*)
+| \--- com.google.android.gms:play-services-tasks:18.2.0 (*)
+\--- androidx.multidex:multidex:2.0.1
diff --git a/androidApp/dependencies/releaseRuntimeClasspath.txt b/androidApp/dependencies/releaseRuntimeClasspath.txt
new file mode 100644
index 000000000..c1d6603bb
--- /dev/null
+++ b/androidApp/dependencies/releaseRuntimeClasspath.txt
@@ -0,0 +1,278 @@
+:core:common
+:core:data
+:core:datastore
+:core:designsystem
+:core:logs
+:core:model
+:core:network
+:core:qrcode
+:core:ui
+:feature:about
+:feature:account
+:feature:auth
+:feature:beneficiary
+:feature:client-charge
+:feature:guarantor
+:feature:help
+:feature:home
+:feature:loan
+:feature:location
+:feature:notification
+:feature:qr
+:feature:recent-transaction
+:feature:savings
+:feature:settings
+:feature:third-party-transfer
+:feature:transfer-process
+:feature:update-password
+:feature:user-profile
+:libs:country-code-picker
+:libs:mifos-passcode
+:libs:pullrefresh
+:shared
+androidx.activity:activity-compose:1.9.1
+androidx.activity:activity-ktx:1.9.1
+androidx.activity:activity:1.9.1
+androidx.annotation:annotation-experimental:1.4.1
+androidx.annotation:annotation-jvm:1.8.1
+androidx.annotation:annotation:1.8.1
+androidx.appcompat:appcompat-resources:1.7.0
+androidx.appcompat:appcompat:1.7.0
+androidx.arch.core:core-common:2.2.0
+androidx.arch.core:core-runtime:2.2.0
+androidx.autofill:autofill:1.0.0
+androidx.camera:camera-camera2:1.3.4
+androidx.camera:camera-core:1.3.4
+androidx.camera:camera-lifecycle:1.3.4
+androidx.camera:camera-video:1.3.4
+androidx.camera:camera-view:1.3.4
+androidx.collection:collection-jvm:1.4.2
+androidx.collection:collection-ktx:1.4.2
+androidx.collection:collection:1.4.2
+androidx.compose.animation:animation-android:1.7.0-rc01
+androidx.compose.animation:animation-core-android:1.7.0-rc01
+androidx.compose.animation:animation-core:1.7.0-rc01
+androidx.compose.animation:animation:1.7.0-rc01
+androidx.compose.foundation:foundation-android:1.7.0-rc01
+androidx.compose.foundation:foundation-layout-android:1.7.0-rc01
+androidx.compose.foundation:foundation-layout:1.7.0-rc01
+androidx.compose.foundation:foundation:1.7.0-rc01
+androidx.compose.material3:material3-android:1.2.1
+androidx.compose.material3:material3:1.2.1
+androidx.compose.material:material-android:1.6.8
+androidx.compose.material:material-icons-core-android:1.6.8
+androidx.compose.material:material-icons-core:1.6.8
+androidx.compose.material:material-icons-extended-android:1.6.8
+androidx.compose.material:material-icons-extended:1.6.8
+androidx.compose.material:material-ripple-android:1.6.8
+androidx.compose.material:material-ripple:1.6.8
+androidx.compose.material:material:1.6.8
+androidx.compose.runtime:runtime-android:1.7.0-rc01
+androidx.compose.runtime:runtime-saveable-android:1.7.0-rc01
+androidx.compose.runtime:runtime-saveable:1.7.0-rc01
+androidx.compose.runtime:runtime:1.7.0-rc01
+androidx.compose.ui:ui-android:1.7.0-rc01
+androidx.compose.ui:ui-geometry-android:1.7.0-rc01
+androidx.compose.ui:ui-geometry:1.7.0-rc01
+androidx.compose.ui:ui-graphics-android:1.7.0-rc01
+androidx.compose.ui:ui-graphics:1.7.0-rc01
+androidx.compose.ui:ui-text-android:1.7.0-rc01
+androidx.compose.ui:ui-text:1.7.0-rc01
+androidx.compose.ui:ui-tooling-preview-android:1.7.0-rc01
+androidx.compose.ui:ui-tooling-preview:1.7.0-rc01
+androidx.compose.ui:ui-unit-android:1.7.0-rc01
+androidx.compose.ui:ui-unit:1.7.0-rc01
+androidx.compose.ui:ui-util-android:1.7.0-rc01
+androidx.compose.ui:ui-util:1.7.0-rc01
+androidx.compose.ui:ui:1.7.0-rc01
+androidx.compose:compose-bom:2024.08.00
+androidx.concurrent:concurrent-futures:1.1.0
+androidx.core:core-ktx:1.13.1
+androidx.core:core-splashscreen:1.0.1
+androidx.core:core:1.13.1
+androidx.cursoradapter:cursoradapter:1.0.0
+androidx.customview:customview-poolingcontainer:1.0.0
+androidx.customview:customview:1.0.0
+androidx.databinding:viewbinding:7.4.2
+androidx.datastore:datastore-core:1.0.0
+androidx.datastore:datastore-preferences-core:1.0.0
+androidx.datastore:datastore-preferences:1.0.0
+androidx.datastore:datastore:1.0.0
+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.fragment:fragment-ktx:1.7.1
+androidx.fragment:fragment:1.7.1
+androidx.graphics:graphics-path:1.0.1
+androidx.hilt:hilt-navigation-compose:1.2.0
+androidx.hilt:hilt-navigation:1.2.0
+androidx.interpolator:interpolator:1.0.0
+androidx.legacy:legacy-support-core-utils:1.0.0
+androidx.lifecycle:lifecycle-common-java8:2.8.4
+androidx.lifecycle:lifecycle-common-jvm:2.8.4
+androidx.lifecycle:lifecycle-common:2.8.4
+androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4
+androidx.lifecycle:lifecycle-livedata-core:2.8.4
+androidx.lifecycle:lifecycle-livedata:2.8.4
+androidx.lifecycle:lifecycle-process:2.8.4
+androidx.lifecycle:lifecycle-runtime-android:2.8.4
+androidx.lifecycle:lifecycle-runtime-compose-android:2.8.4
+androidx.lifecycle:lifecycle-runtime-compose:2.8.4
+androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.4
+androidx.lifecycle:lifecycle-runtime-ktx:2.8.4
+androidx.lifecycle:lifecycle-runtime:2.8.4
+androidx.lifecycle:lifecycle-viewmodel-android:2.8.4
+androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.4
+androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4
+androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4
+androidx.lifecycle:lifecycle-viewmodel:2.8.4
+androidx.loader:loader:1.0.0
+androidx.localbroadcastmanager:localbroadcastmanager:1.0.0
+androidx.metrics:metrics-performance:1.0.0-beta01
+androidx.multidex:multidex:2.0.1
+androidx.navigation:navigation-common-ktx:2.8.0-rc01
+androidx.navigation:navigation-common:2.8.0-rc01
+androidx.navigation:navigation-compose:2.8.0-rc01
+androidx.navigation:navigation-runtime-ktx:2.8.0-rc01
+androidx.navigation:navigation-runtime:2.8.0-rc01
+androidx.print:print:1.0.0
+androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05
+androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05
+androidx.profileinstaller:profileinstaller:1.3.1
+androidx.resourceinspection:resourceinspection-annotation:1.0.1
+androidx.savedstate:savedstate-ktx:1.2.1
+androidx.savedstate:savedstate:1.2.1
+androidx.startup:startup-runtime:1.1.1
+androidx.tracing:tracing-ktx:1.2.0
+androidx.tracing:tracing:1.2.0
+androidx.vectordrawable:vectordrawable-animated:1.1.0
+androidx.vectordrawable:vectordrawable:1.1.0
+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:stately-concurrency-jvm:2.0.6
+co.touchlab:stately-concurrency:2.0.6
+co.touchlab:stately-concurrent-collections-jvm:2.0.6
+co.touchlab:stately-concurrent-collections:2.0.6
+co.touchlab:stately-strict-jvm:2.0.6
+co.touchlab:stately-strict:2.0.6
+com.github.Raizlabs.DBFlow:dbflow-core:4.2.4
+com.github.Raizlabs.DBFlow:dbflow:4.2.4
+com.google.accompanist:accompanist-pager:0.34.0
+com.google.accompanist:accompanist-permissions:0.34.0
+com.google.android.datatransport:transport-api:3.2.0
+com.google.android.datatransport:transport-backend-cct:3.3.0
+com.google.android.datatransport:transport-runtime:3.3.0
+com.google.android.gms:play-services-ads-identifier:18.0.0
+com.google.android.gms:play-services-base:18.5.0
+com.google.android.gms:play-services-basement:18.4.0
+com.google.android.gms:play-services-cloud-messaging:17.2.0
+com.google.android.gms:play-services-maps:18.2.0
+com.google.android.gms:play-services-measurement-api:22.1.0
+com.google.android.gms:play-services-measurement-base:22.1.0
+com.google.android.gms:play-services-measurement-impl:22.1.0
+com.google.android.gms:play-services-measurement-sdk-api:22.1.0
+com.google.android.gms:play-services-measurement-sdk:22.1.0
+com.google.android.gms:play-services-measurement:22.1.0
+com.google.android.gms:play-services-oss-licenses:17.1.0
+com.google.android.gms:play-services-stats:17.0.2
+com.google.android.gms:play-services-tasks:18.2.0
+com.google.auto.value:auto-value-annotations:1.6.3
+com.google.code.findbugs:jsr305:3.0.2
+com.google.code.gson:gson:2.10.1
+com.google.dagger:dagger-lint-aar:2.52
+com.google.dagger:dagger:2.52
+com.google.dagger:hilt-android:2.52
+com.google.dagger:hilt-core:2.52
+com.google.errorprone:error_prone_annotations:2.26.0
+com.google.firebase:firebase-abt:21.1.1
+com.google.firebase:firebase-analytics-ktx:22.1.0
+com.google.firebase:firebase-analytics:22.1.0
+com.google.firebase:firebase-annotations:16.2.0
+com.google.firebase:firebase-bom:33.2.0
+com.google.firebase:firebase-common-ktx:21.0.0
+com.google.firebase:firebase-common:21.0.0
+com.google.firebase:firebase-components:18.0.0
+com.google.firebase:firebase-config-interop:16.0.1
+com.google.firebase:firebase-config:22.0.0
+com.google.firebase:firebase-crashlytics-ktx:19.0.3
+com.google.firebase:firebase-crashlytics:19.0.3
+com.google.firebase:firebase-datatransport:19.0.0
+com.google.firebase:firebase-encoders-json:18.0.1
+com.google.firebase:firebase-encoders-proto:16.0.0
+com.google.firebase:firebase-encoders:17.0.0
+com.google.firebase:firebase-iid-interop:17.1.0
+com.google.firebase:firebase-installations-interop:17.2.0
+com.google.firebase:firebase-installations:18.0.0
+com.google.firebase:firebase-measurement-connector:20.0.1
+com.google.firebase:firebase-messaging-ktx:24.0.1
+com.google.firebase:firebase-messaging:24.0.1
+com.google.firebase:firebase-perf-ktx:21.0.1
+com.google.firebase:firebase-perf:21.0.1
+com.google.firebase:firebase-sessions:2.0.3
+com.google.firebase:protolite-well-known-types:18.0.0
+com.google.guava:failureaccess:1.0.1
+com.google.guava:guava:31.1-android
+com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
+com.google.j2objc:j2objc-annotations:1.3
+com.google.maps.android:maps-compose:4.4.1
+com.google.maps.android:maps-ktx:5.0.0
+com.google.protobuf:protobuf-javalite:3.21.11
+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.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.github.mr0xf00:easycrop:0.1.1
+io.insert-koin:koin-android:3.6.0-Beta4
+io.insert-koin:koin-androidx-compose:3.6.0-Beta4
+io.insert-koin:koin-compose-jvm:1.2.0-Beta4
+io.insert-koin:koin-compose-viewmodel-jvm:1.2.0-Beta4
+io.insert-koin:koin-compose-viewmodel:1.2.0-Beta4
+io.insert-koin:koin-compose:1.2.0-Beta4
+io.insert-koin:koin-core-jvm:3.6.0-Beta4
+io.insert-koin:koin-core:3.6.0-Beta4
+io.michaelrocks:libphonenumber-android:8.13.35
+io.reactivex.rxjava2:rxandroid:2.1.1
+io.reactivex.rxjava2:rxjava:2.2.21
+jakarta.inject:jakarta.inject-api:2.0.1
+javax.inject:javax.inject:1
+net.bytebuddy:byte-buddy-agent:1.14.5
+net.bytebuddy:byte-buddy:1.14.5
+org.checkerframework:checker-qual:3.12.0
+org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0-rc03
+org.jetbrains.androidx.navigation:navigation-compose:2.7.0-alpha06
+org.jetbrains.compose.material3:material3:1.6.11
+org.jetbrains.compose.material:material:1.6.11
+org.jetbrains.compose.runtime:runtime:1.6.10-rc03
+org.jetbrains.compose.ui:ui-tooling-preview:1.6.11
+org.jetbrains.kotlin:kotlin-android-extensions-runtime:2.0.20
+org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.20
+org.jetbrains.kotlin:kotlin-stdlib-common:2.0.20
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.20
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10
+org.jetbrains.kotlin:kotlin-stdlib:2.0.20
+org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.7
+org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7
+org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0
+org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.0
+org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0
+org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0
+org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.0
+org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm:1.8.0
+org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0
+org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3
+org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.3
+org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3
+org.jetbrains:annotations:23.0.0
+org.mockito:mockito-core:5.4.0
+org.objenesis:objenesis:3.3
+org.reactivestreams:reactive-streams:1.0.4
diff --git a/androidApp/lint-baseline.xml b/androidApp/lint-baseline.xml
new file mode 100644
index 000000000..230ed0be7
--- /dev/null
+++ b/androidApp/lint-baseline.xml
@@ -0,0 +1,46876 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/androidApp/release-badging.txt b/androidApp/release-badging.txt
new file mode 100644
index 000000000..1bda46167
--- /dev/null
+++ b/androidApp/release-badging.txt
@@ -0,0 +1,138 @@
+package: name='org.mifos.mobile' versionCode='1' versionName='1.0' platformBuildVersionName='14' platformBuildVersionCode='34' compileSdkVersion='34' compileSdkVersionCodename='14'
+sdkVersion:'26'
+targetSdkVersion:'34'
+uses-permission: name='android.permission.INTERNET'
+uses-permission: name='android.permission.READ_MEDIA_IMAGES'
+uses-permission: name='android.permission.CAMERA'
+uses-permission: name='android.permission.READ_PHONE_STATE'
+uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE'
+uses-permission: name='android.permission.READ_EXTERNAL_STORAGE'
+uses-permission: name='android.permission.ACCESS_NETWORK_STATE'
+uses-permission: name='android.permission.VIBRATE'
+uses-permission: name='android.permission.POST_NOTIFICATIONS'
+uses-permission: name='android.permission.WAKE_LOCK'
+uses-permission: name='com.google.android.c2dm.permission.RECEIVE'
+uses-permission: name='com.google.android.gms.permission.AD_ID'
+uses-permission: name='android.permission.ACCESS_ADSERVICES_ATTRIBUTION'
+uses-permission: name='android.permission.ACCESS_ADSERVICES_AD_ID'
+uses-permission: name='com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE'
+uses-permission: name='org.mifos.mobile.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION'
+application-label:'Mifos Mobile'
+application-label-af:'Mifos Mobile'
+application-label-am:'Mifos Mobile'
+application-label-ar:'ميفوس موبايل'
+application-label-as:'Mifos Mobile'
+application-label-az:'Mifos Mobile'
+application-label-be:'Mifos Mobile'
+application-label-bg:'Mifos Mobile'
+application-label-bn:'Mifos Mobile'
+application-label-bs:'Mifos Mobile'
+application-label-ca:'Mifos Mobile'
+application-label-cs:'Mifos Mobile'
+application-label-da:'Mifos Mobile'
+application-label-de:'Mifos Mobile'
+application-label-el:'Mifos Mobile'
+application-label-en-AU:'Mifos Mobile'
+application-label-en-CA:'Mifos Mobile'
+application-label-en-GB:'Mifos Mobile'
+application-label-en-IN:'Mifos Mobile'
+application-label-en-XC:'Mifos Mobile'
+application-label-es:'Mifos Mobile'
+application-label-es-US:'Mifos Mobile'
+application-label-et:'Mifos Mobile'
+application-label-eu:'Mifos Mobile'
+application-label-fa:'Mifos Mobile'
+application-label-fa-AF:'Mifos Mobile'
+application-label-fi:'Mifos Mobile'
+application-label-fr:'Mifos Mobile'
+application-label-fr-CA:'Mifos Mobile'
+application-label-gl:'Mifos Mobile'
+application-label-gu:'Mifos Mobile'
+application-label-hi:'Mifos Mobile'
+application-label-hr:'Mifos Mobile'
+application-label-hu:'Mifos Mobile'
+application-label-hy:'Mifos Mobile'
+application-label-in:'Mifos Mobile'
+application-label-is:'Mifos Mobile'
+application-label-it:'Mifos Mobile'
+application-label-it-IT:'Mifos Mobile'
+application-label-iw:'Mifos Mobile'
+application-label-ja:'Mifos Mobile'
+application-label-ka:'Mifos Mobile'
+application-label-kk:'Mifos Mobile'
+application-label-km:'Mifos Mobile'
+application-label-kn:'Mifos Mobile'
+application-label-ko:'Mifos Mobile'
+application-label-ky:'Mifos Mobile'
+application-label-lo:'Mifos Mobile'
+application-label-lt:'Mifos Mobile'
+application-label-lv:'Mifos Mobile'
+application-label-mk:'Mifos Mobile'
+application-label-ml:'Mifos Mobile'
+application-label-mn:'Mifos Mobile'
+application-label-mr:'Mifos Mobile'
+application-label-ms:'Mifos Mobile'
+application-label-my:'Mifos မိုဘိုင်း'
+application-label-nb:'Mifos Mobile'
+application-label-ne:'Mifos Mobile'
+application-label-nl:'Mifos Mobile'
+application-label-or:'Mifos Mobile'
+application-label-pa:'Mifos Mobile'
+application-label-pl:'Mifos Mobile'
+application-label-pt:'Mifos Mobile'
+application-label-pt-BR:'Mifos Mobile'
+application-label-pt-PT:'Mifos Mobile'
+application-label-ro:'Mifos Mobile'
+application-label-ru:'Mifos Mobile'
+application-label-ru-RU:'Mifos Mobile'
+application-label-si:'Mifos Mobile'
+application-label-sk:'Mifos Mobile'
+application-label-sl:'Mifos Mobile'
+application-label-so:'Mifos Mobile'
+application-label-sq:'Mifos Mobile'
+application-label-sr:'Mifos Mobile'
+application-label-sr-Latn:'Mifos Mobile'
+application-label-sv:'Mifos Mobile'
+application-label-sw:'Mifos Mobile'
+application-label-ta:'Mifos Mobile'
+application-label-te:'Mifos Mobile'
+application-label-th:'Mifos Mobile'
+application-label-tl:'Mifos Mobile'
+application-label-tr:'Mifos Mobile'
+application-label-tr-TR:'Mifos Mobile'
+application-label-uk:'Mifos Mobile'
+application-label-ur:'Mifos موبائل'
+application-label-uz:'Mifos Mobile'
+application-label-vi:'Mifos Mobile'
+application-label-zh:'Mifos Mobile'
+application-label-zh-CN:'Mifos Mobile'
+application-label-zh-HK:'Mifos Mobile'
+application-label-zh-TW:'Mifos Mobile'
+application-label-zu:'Mifos Mobile'
+application-icon-120:'res/mipmap-anydpi-v26/ic_launcher.xml'
+application-icon-160:'res/mipmap-anydpi-v26/ic_launcher.xml'
+application-icon-240:'res/mipmap-anydpi-v26/ic_launcher.xml'
+application-icon-320:'res/mipmap-anydpi-v26/ic_launcher.xml'
+application-icon-480:'res/mipmap-anydpi-v26/ic_launcher.xml'
+application-icon-640:'res/mipmap-anydpi-v26/ic_launcher.xml'
+application-icon-65534:'res/mipmap-anydpi-v26/ic_launcher.xml'
+application: label='Mifos Mobile' icon='res/mipmap-anydpi-v26/ic_launcher.xml'
+launchable-activity: name='org.mifos.mobile.HomeActivity' label='' icon=''
+uses-library-not-required:'org.apache.http.legacy'
+property: name='android.adservices.AD_SERVICES_CONFIG' resource='res/xml/ga_ad_services_config.xml'
+uses-library-not-required:'android.ext.adservices'
+feature-group: label=''
+ uses-gl-es: '0x20000'
+ uses-feature-not-required: name='android.hardware.camera'
+ uses-feature: name='android.hardware.camera.any'
+ uses-feature: name='android.hardware.faketouch'
+ uses-implied-feature: name='android.hardware.faketouch' reason='default feature for all apps'
+main
+other-activities
+other-receivers
+other-services
+supports-screens: 'small' 'normal' 'large' 'xlarge'
+supports-any-density: 'true'
+locales: '--_--' 'af' 'am' 'ar' 'as' 'az' 'be' 'bg' 'bn' 'bs' 'ca' 'cs' 'da' 'de' 'el' 'en-AU' 'en-CA' 'en-GB' 'en-IN' 'en-XC' 'es' 'es-US' 'et' 'eu' 'fa' 'fa-AF' 'fi' 'fr' 'fr-CA' 'gl' 'gu' 'hi' 'hr' 'hu' 'hy' 'in' 'is' 'it' 'it-IT' 'iw' 'ja' 'ka' 'kk' 'km' 'kn' 'ko' 'ky' 'lo' 'lt' 'lv' 'mk' 'ml' 'mn' 'mr' 'ms' 'my' 'nb' 'ne' 'nl' 'or' 'pa' 'pl' 'pt' 'pt-BR' 'pt-PT' 'ro' 'ru' 'ru-RU' 'si' 'sk' 'sl' 'so' 'sq' 'sr' 'sr-Latn' 'sv' 'sw' 'ta' 'te' 'th' 'tl' 'tr' 'tr-TR' 'uk' 'ur' 'uz' 'vi' 'zh' 'zh-CN' 'zh-HK' 'zh-TW' 'zu'
+densities: '120' '160' '240' '320' '480' '640' '65534'
+native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64'
diff --git a/androidApp/src/androidTest/java/org/mifos/mobile/ApplicationTest.kt b/androidApp/src/androidTest/kotlin/org/mifos/mobile/ApplicationTest.kt
similarity index 63%
rename from androidApp/src/androidTest/java/org/mifos/mobile/ApplicationTest.kt
rename to androidApp/src/androidTest/kotlin/org/mifos/mobile/ApplicationTest.kt
index ed59a89a4..fed7a4073 100644
--- a/androidApp/src/androidTest/java/org/mifos/mobile/ApplicationTest.kt
+++ b/androidApp/src/androidTest/kotlin/org/mifos/mobile/ApplicationTest.kt
@@ -1,3 +1,12 @@
+/*
+ * 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
import androidx.test.InstrumentationRegistry
diff --git a/androidApp/src/debug/res/values/api_keys.xml b/androidApp/src/debug/res/values/api_keys.xml
deleted file mode 100644
index ac8673992..000000000
--- a/androidApp/src/debug/res/values/api_keys.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- AIzaSyBbeT2BaMWLj-lReCgYoNmXs_TIyRLr9qQ
-
-
diff --git a/androidApp/src/main/AndroidManifest.xml b/androidApp/src/main/AndroidManifest.xml
index 5789de63f..59800427c 100644
--- a/androidApp/src/main/AndroidManifest.xml
+++ b/androidApp/src/main/AndroidManifest.xml
@@ -1,7 +1,15 @@
+
+ xmlns:tools="http://schemas.android.com/tools">
+
+
@@ -18,53 +28,32 @@
-
-
-
-
-
-
-
-
+ android:theme="@style/Theme.MifosSplash">
+ android:theme="@style/Theme.MifosSplash"
+ android:windowSoftInputMode="adjustPan">
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/ic_launcher-playstore.png b/androidApp/src/main/ic_launcher-playstore.png
new file mode 100644
index 000000000..a09ac89ad
Binary files /dev/null and b/androidApp/src/main/ic_launcher-playstore.png differ
diff --git a/androidApp/src/main/java/org/mifos/mobile/MifosSelfServiceApp.kt b/androidApp/src/main/java/org/mifos/mobile/MifosSelfServiceApp.kt
deleted file mode 100644
index 53cf8ec12..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/MifosSelfServiceApp.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.mifos.mobile
-
-import android.content.Context
-import android.content.res.Configuration
-import androidx.appcompat.app.AppCompatDelegate
-import androidx.multidex.MultiDex
-import androidx.multidex.MultiDexApplication
-import com.google.firebase.crashlytics.FirebaseCrashlytics
-import com.raizlabs.android.dbflow.config.FlowConfig
-import com.raizlabs.android.dbflow.config.FlowManager
-import dagger.hilt.android.HiltAndroidApp
-import org.mifos.mobile.core.common.utils.LanguageHelper.onAttach
-import org.mifos.mobile.core.datastore.PreferencesHelper
-import org.mifos.mobile.feature.settings.applySavedTheme
-
-/**
- * @author ishan
- * @since 08/07/16
- */
-@HiltAndroidApp
-class MifosSelfServiceApp : MultiDexApplication() {
-
- companion object {
- private var instance: MifosSelfServiceApp? = null
- operator fun get(context: Context): MifosSelfServiceApp {
- return context.applicationContext as MifosSelfServiceApp
- }
-
- val context: Context?
- get() = instance
-
- init {
- AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
- }
- }
-
- override fun onCreate() {
- super.onCreate()
- MultiDex.install(this)
- FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true)
- instance = this
- FlowManager.init(FlowConfig.Builder(this).build())
- PreferencesHelper(this).applySavedTheme()
- }
-
- override fun onConfigurationChanged(newConfig: Configuration) {
- super.onConfigurationChanged(newConfig)
- context?.let { onAttach(it) }
- }
-
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/di/ApplicationModule.kt b/androidApp/src/main/java/org/mifos/mobile/di/ApplicationModule.kt
deleted file mode 100644
index c94097198..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/di/ApplicationModule.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.mifos.mobile.di
-
-import android.content.Context
-import dagger.Module
-import dagger.Provides
-import dagger.hilt.InstallIn
-import dagger.hilt.android.qualifiers.ApplicationContext
-import dagger.hilt.components.SingletonComponent
-import org.mifos.mobile.core.datastore.DatabaseHelper
-import org.mifos.mobile.core.datastore.PreferencesHelper
-import org.mifos.mobile.core.network.BaseApiManager
-import org.mifos.mobile.core.network.DataManager
-import org.mifos.mobile.core.network.services.AuthenticationService
-import org.mifos.mobile.core.network.services.BeneficiaryService
-import org.mifos.mobile.core.network.services.ClientChargeService
-import org.mifos.mobile.core.network.services.ClientService
-import org.mifos.mobile.core.network.services.GuarantorService
-import org.mifos.mobile.core.network.services.LoanAccountsListService
-import org.mifos.mobile.core.network.services.NotificationService
-import org.mifos.mobile.core.network.services.RecentTransactionsService
-import org.mifos.mobile.core.network.services.RegistrationService
-import org.mifos.mobile.core.network.services.SavingAccountsListService
-import org.mifos.mobile.core.network.services.ThirdPartyTransferService
-import org.mifos.mobile.core.network.services.UserDetailsService
-import javax.inject.Singleton
-
-/**
- * @author ishan
- * @since 08/07/16
- */
-@Module
-@InstallIn(SingletonComponent::class)
-object ApplicationModule {
-
- @Provides
- @Singleton
- fun providePrefManager(@ApplicationContext context: Context?): PreferencesHelper {
- return PreferencesHelper(context)
- }
-
- @Provides
- @Singleton
- fun provideBaseApiManager(
- authenticationService: AuthenticationService,
- clientsService: ClientService,
- savingAccountsListService: SavingAccountsListService,
- loanAccountsListService: LoanAccountsListService,
- recentTransactionsService: RecentTransactionsService,
- clientChargeService: ClientChargeService,
- beneficiaryService: BeneficiaryService,
- thirdPartyTransferService: ThirdPartyTransferService,
- registrationService: RegistrationService,
- notificationService: NotificationService,
- userDetailsService: UserDetailsService,
- guarantorService: GuarantorService
- ): BaseApiManager {
- return BaseApiManager(
- authenticationService,
- clientsService,
- savingAccountsListService,
- loanAccountsListService,
- recentTransactionsService,
- clientChargeService,
- beneficiaryService,
- thirdPartyTransferService,
- registrationService,
- notificationService,
- userDetailsService,
- guarantorService
- )
- }
-
- @Provides
- @Singleton
- fun providesDataManager(
- preferencesHelper: PreferencesHelper,
- baseApiManager: BaseApiManager,
- databaseHelper: DatabaseHelper
- ): DataManager {
- return DataManager(preferencesHelper, baseApiManager, databaseHelper)
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/ui/activities/BiometricAuthentication.kt b/androidApp/src/main/java/org/mifos/mobile/ui/activities/BiometricAuthentication.kt
deleted file mode 100644
index 33bb6ae89..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/ui/activities/BiometricAuthentication.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-package org.mifos.mobile.ui.activities
-
-import android.content.Intent
-import android.os.Build
-import android.provider.Settings
-import android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED
-import androidx.activity.result.ActivityResultLauncher
-import androidx.biometric.BiometricManager
-import androidx.biometric.BiometricPrompt
-import androidx.core.content.ContextCompat
-import androidx.fragment.app.FragmentActivity
-import org.mifos.mobile.R
-import org.mifos.mobile.core.model.enums.BiometricCapability
-
-open class BiometricAuthentication(
- val context: FragmentActivity,
-) {
- private val executor = ContextCompat.getMainExecutor(context)
- private val callback = object : BiometricPrompt.AuthenticationCallback() {
-
- override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
- super.onAuthenticationSucceeded(result)
- val intent = Intent(context, HomeActivity::class.java)
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
- context.startActivity(intent)
- context.finish()
- }
- }
-
- private val biometricPrompt = BiometricPrompt(context, executor, callback)
-
- fun launchBiometricEnrollment(resultLauncher: ActivityResultLauncher) {
- val intent: Intent = when {
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
- Intent(Settings.ACTION_BIOMETRIC_ENROLL).putExtra(
- EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
- BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.BIOMETRIC_WEAK,
- )
- }
-
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> {
- Intent(Settings.ACTION_FINGERPRINT_ENROLL)
- }
-
- else -> {
- Intent(Settings.ACTION_SECURITY_SETTINGS)
- }
- }
- resultLauncher.launch(intent)
- }
-
- fun getBiometricCapabilities(): BiometricCapability {
- val biometricManager = BiometricManager.from(context)
- return when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK or BiometricManager.Authenticators.DEVICE_CREDENTIAL)) {
- BiometricManager.BIOMETRIC_SUCCESS -> {
- BiometricCapability.HAS_BIOMETRIC_AUTH
- }
-
- BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
- BiometricCapability.NOT_ENROLLED
- }
-
- else -> {
- BiometricCapability.NOT_SUPPORTED
- }
- }
- }
-
- fun authenticateWithBiometrics() {
- val promptInfo = BiometricPrompt.PromptInfo.Builder().apply {
- setTitle(context.getString(R.string.sign_in_fingerprint))
- setDescription(context.getString(R.string.scan_your_fingerprint))
- setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_WEAK or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
- }.build()
-
- biometricPrompt.authenticate(promptInfo)
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/ui/activities/HomeActivity.kt b/androidApp/src/main/java/org/mifos/mobile/ui/activities/HomeActivity.kt
deleted file mode 100644
index fa54a0662..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/ui/activities/HomeActivity.kt
+++ /dev/null
@@ -1,135 +0,0 @@
-package org.mifos.mobile.ui.activities
-
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.os.Bundle
-import android.os.Handler
-import android.util.Log
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.enableEdgeToEdge
-import androidx.activity.viewModels
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
-import androidx.navigation.NavHostController
-import androidx.navigation.compose.rememberNavController
-import com.google.android.gms.common.ConnectionResult
-import com.google.android.gms.common.GoogleApiAvailability
-import dagger.hilt.android.AndroidEntryPoint
-import org.mifos.mobile.R
-import org.mifos.mobile.core.common.Constants
-import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
-import org.mifos.mobile.feature.home.navigation.HomeNavigation
-import org.mifos.mobile.feature.user.profile.viewmodel.UserDetailViewModel
-import org.mifos.mobile.navigation.RootNavGraph
-import org.mifos.mobile.utils.Toaster
-import org.mifos.mobile.utils.fcm.RegistrationIntentService
-
-/**
- * @author Vishwajeet
- * @since 14/07/2016
- */
-@AndroidEntryPoint
-class HomeActivity : ComponentActivity() {
-
- private val viewModel: UserDetailViewModel by viewModels()
- private var isReceiverRegistered = false
- private var doubleBackToExitPressedOnce = false
- private lateinit var navHostController: NavHostController
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- if (checkPlayServices()) {
- // Start IntentService to register this application with GCM.
- val intent = Intent(this, RegistrationIntentService::class.java)
- startService(intent)
- }
-
- enableEdgeToEdge()
- setContent {
- MifosMobileTheme {
- navHostController = rememberNavController()
- RootNavGraph(
- startDestination = HomeNavigation.HomeBase.route,
- navController = navHostController,
- )
- }
- }
- }
-
- override fun onPause() {
- LocalBroadcastManager.getInstance(this).unregisterReceiver(registerReceiver)
- isReceiverRegistered = false
- super.onPause()
- }
-
- override fun onResume() {
- super.onResume()
- if (!isReceiverRegistered) {
- LocalBroadcastManager.getInstance(this).registerReceiver(
- registerReceiver,
- IntentFilter(Constants.REGISTER_ON_SERVER),
- )
- isReceiverRegistered = true
- }
- }
-
- /**
- * Handling back press
- */
- @Deprecated("Deprecated in Java")
- override fun onBackPressed() {
- val currentRoute = navHostController.currentBackStackEntry?.destination?.route
-
- if (currentRoute == HomeNavigation.HomeScreen.route) {
-// if (doubleBackToExitPressedOnce && stackCount() == 0) {
-// finish()
-// return
-// }
- doubleBackToExitPressedOnce = true
- Toaster.show(findViewById(android.R.id.content), getString(R.string.exit_message))
- Handler().postDelayed({ doubleBackToExitPressedOnce = false }, 2000)
- }
-
- super.onBackPressed()
-
-// if (stackCount() != 0) {
-// super.onBackPressed()
-// }
- }
-
-
- /**
- * Check the device to make sure it has the Google Play Services APK. If
- * it doesn't, display a dialog that allows users to download the APK from
- * the Google Play Store or enable it in the device's system settings.
- */
- private fun checkPlayServices(): Boolean {
- val apiAvailability = GoogleApiAvailability.getInstance()
- val resultCode = apiAvailability.isGooglePlayServicesAvailable(this)
- if (resultCode != ConnectionResult.SUCCESS) {
- if (apiAvailability.isUserResolvableError(resultCode)) {
- apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)
- ?.show()
- } else {
- Log.i(HomeActivity::class.java.name, "This device is not supported.")
- finish()
- }
- return false
- }
- return true
- }
-
- private val registerReceiver: BroadcastReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- val token = intent.getStringExtra(Constants.TOKEN)
- token?.let { viewModel.registerNotification(it) }
- }
- }
-
- companion object {
- private const val PLAY_SERVICES_RESOLUTION_REQUEST = 9000
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/main/java/org/mifos/mobile/ui/activities/LoginActivity.kt b/androidApp/src/main/java/org/mifos/mobile/ui/activities/LoginActivity.kt
deleted file mode 100644
index 679d61763..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/ui/activities/LoginActivity.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.mifos.mobile.ui.activities
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.enableEdgeToEdge
-import androidx.navigation.compose.rememberNavController
-import dagger.hilt.android.AndroidEntryPoint
-import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
-import org.mifos.mobile.feature.auth.navigation.AuthenticationNavigation
-import org.mifos.mobile.navigation.RootNavGraph
-
-/**
- * @author Vishwajeet
- * @since 05/06/16
- */
-@AndroidEntryPoint
-class LoginActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- enableEdgeToEdge()
- setContent {
- MifosMobileTheme {
- val navController = rememberNavController()
- RootNavGraph(
- startDestination = AuthenticationNavigation.AuthenticationBase.route,
- navController = navController,
- )
- }
- }
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/ui/activities/PassCodeActivity.kt b/androidApp/src/main/java/org/mifos/mobile/ui/activities/PassCodeActivity.kt
deleted file mode 100644
index ad5ea3485..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/ui/activities/PassCodeActivity.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.mifos.mobile.ui.activities
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.enableEdgeToEdge
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.statusBarsPadding
-import androidx.compose.ui.Modifier
-import androidx.navigation.compose.rememberNavController
-import dagger.hilt.android.AndroidEntryPoint
-import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
-import org.mifos.mobile.navigation.PASSCODE_SCREEN
-import org.mifos.mobile.navigation.RootNavGraph
-
-@AndroidEntryPoint
-class PassCodeActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- enableEdgeToEdge()
- setContent {
- MifosMobileTheme {
- val navController = rememberNavController()
-
- Box(modifier = Modifier.statusBarsPadding()) {
- RootNavGraph(
- startDestination = PASSCODE_SCREEN,
- navController = navController,
- )
- }
- }
- }
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/ui/activities/SplashActivity.kt b/androidApp/src/main/java/org/mifos/mobile/ui/activities/SplashActivity.kt
deleted file mode 100644
index 69169dd48..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/ui/activities/SplashActivity.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.mifos.mobile.ui.activities
-
-import android.content.Intent
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-
-/*
-* Created by saksham on 01/June/2018
-*/
-class SplashActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // TODO:: check for user logged in or not, and if logged in move to PasscodeActivity instead.
- val intent = Intent(this, LoginActivity::class.java)
-
- startActivity(intent)
- finish()
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/ui/views/BaseActivityCallback.kt b/androidApp/src/main/java/org/mifos/mobile/ui/views/BaseActivityCallback.kt
deleted file mode 100644
index 64fababdc..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/ui/views/BaseActivityCallback.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.mifos.mobile.ui.views
-
-/**
- * @author Rajan Maurya
- */
-interface BaseActivityCallback {
-
- fun showProgressDialog(message: String?)
-
- fun hideProgressDialog()
-
- fun setToolbarTitle(title: String?)
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/ui/widgets/ChargeAppWidget.kt b/androidApp/src/main/java/org/mifos/mobile/ui/widgets/ChargeAppWidget.kt
deleted file mode 100644
index 0dda7998d..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/ui/widgets/ChargeAppWidget.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.mifos.mobile.ui.widgets
-
-import android.annotation.TargetApi
-import android.appwidget.AppWidgetManager
-import android.appwidget.AppWidgetProvider
-import android.content.Context
-import android.content.Intent
-import android.os.Build
-import android.widget.RemoteViews
-import org.mifos.mobile.R
-
-/**
- * Implementation of App Widget functionality.
- */
-class ChargeAppWidget : AppWidgetProvider() {
-
- override fun onUpdate(
- context: Context,
- appWidgetManager: AppWidgetManager,
- appWidgetIds: IntArray,
- ) {
- // There may be multiple widgets active, so update all of them
- for (appWidgetId in appWidgetIds) {
- updateAppWidget(context, appWidgetManager, appWidgetId)
- }
- super.onUpdate(context, appWidgetManager, appWidgetIds)
- }
-
- override fun onEnabled(context: Context) {
- // Enter relevant functionality for when the first widget is created
- }
-
- override fun onDisabled(context: Context) {
- // Enter relevant functionality for when the last widget is disabled
- }
-
- companion object {
- fun updateAppWidget(
- context: Context,
- appWidgetManager: AppWidgetManager,
- appWidgetId: Int,
- ) {
- val views = RemoteViews(context.packageName, R.layout.charge_app_widget)
-
- // Set up the collection
- setRemoteAdapter(context, views)
- // Instruct the widget manager to update the widget
- appWidgetManager.updateAppWidget(appWidgetId, views)
- }
-
- /**
- * Sets the remote adapter used to fill in the list items
- *
- * @param views RemoteViews to set the RemoteAdapter
- */
- @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
- private fun setRemoteAdapter(context: Context, views: RemoteViews) {
- views.setRemoteAdapter(
- R.id.lv_charges,
- Intent(context, ChargeWidgetService::class.java),
- )
- }
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/ui/widgets/ChargeWidgetDataProvider.kt b/androidApp/src/main/java/org/mifos/mobile/ui/widgets/ChargeWidgetDataProvider.kt
deleted file mode 100644
index c82ad4c9f..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/ui/widgets/ChargeWidgetDataProvider.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-package org.mifos.mobile.ui.widgets
-
-import android.content.Context
-import android.widget.RemoteViews
-import android.widget.RemoteViewsService.RemoteViewsFactory
-import android.widget.Toast
-import dagger.hilt.android.qualifiers.ApplicationContext
-import org.mifos.mobile.R
-import org.mifos.mobile.core.data.repository.ClientChargeRepository
-import org.mifos.mobile.core.datastore.model.Charge
-import java.util.concurrent.locks.ReentrantLock
-import javax.inject.Inject
-
-/**
- * ChargeWidgetDataProvider acts as the adapter for the collection view widget,
- * providing RemoteViews to the widget in the getViewAt method.
- */
-class ChargeWidgetDataProvider(@param:ApplicationContext private val context: Context) :
- RemoteViewsFactory {
-
- @Inject
- lateinit var clientChargeRepository: ClientChargeRepository
-
-
- private var charges: List?
- private val `object`: ReentrantLock = ReentrantLock()
- private val condition = `object`.newCondition()
-
- override fun onCreate() {
-
- }
-
- override fun onDataSetChanged() {
- // TODO Make ClientId Dynamic
- //clientChargeViewModel.loadClientLocalCharges()
- synchronized(`object`) {
- try {
- // Calling wait() will block this thread until another thread
- // calls notify() on the object.
- condition.await()
- } catch (e: InterruptedException) {
- // Happens if someone interrupts your thread.
- }
- }
- }
-
- override fun getCount(): Int {
- return if (charges != null) {
- charges!!.size
- } else {
- 0
- }
- }
-
- override fun getViewAt(position: Int): RemoteViews {
- val charge = charges?.get(position)
- val itemId = R.layout.item_widget_client_charge
- val view = RemoteViews(context.packageName, itemId)
- view.setTextViewText(R.id.tv_charge_name, charge?.name)
- view.setTextViewText(R.id.tv_charge_amount, charge?.amount.toString())
- view.setImageViewResource(R.id.circle_status, R.drawable.ic_attach_money_black_24dp)
- return view
- }
-
- override fun getLoadingView(): RemoteViews? {
- return null
- }
-
- override fun getViewTypeCount(): Int {
- return 1
- }
-
- override fun getItemId(position: Int): Long {
- return position.toLong()
- }
-
- override fun hasStableIds(): Boolean {
- return false
- }
-
- fun showErrorFetchingClientCharges(message: String?) {
- Toast.makeText(
- context,
- context.getString(R.string.error_client_charge_loading),
- Toast.LENGTH_SHORT,
- ).show()
- }
-
- fun showClientCharges(clientChargesList: List?) {
- charges = clientChargesList
- synchronized(`object`) {
- condition.signal()
- }
- }
-
- fun showProgress() {}
-
- fun hideProgress() {}
-
- override fun onDestroy() {
- }
-
- companion object {
- private val LOG_TAG = ChargeWidgetDataProvider::class.java.simpleName
- }
-
- init {
- charges = ArrayList()
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/ui/widgets/ChargeWidgetService.kt b/androidApp/src/main/java/org/mifos/mobile/ui/widgets/ChargeWidgetService.kt
deleted file mode 100644
index e2f40873e..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/ui/widgets/ChargeWidgetService.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.mifos.mobile.ui.widgets
-
-import android.content.Intent
-import android.widget.RemoteViewsService
-
-/**
- * ChargeWidgetService is the [RemoteViewsService] that will return our RemoteViewsFactory
- */
-class ChargeWidgetService : RemoteViewsService() {
-
- override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
- return ChargeWidgetDataProvider(this)
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/CheckSelfPermissionAndRequest.kt b/androidApp/src/main/java/org/mifos/mobile/utils/CheckSelfPermissionAndRequest.kt
deleted file mode 100644
index cad06575a..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/CheckSelfPermissionAndRequest.kt
+++ /dev/null
@@ -1,152 +0,0 @@
-package org.mifos.mobile.utils
-
-import android.annotation.TargetApi
-import android.content.Context
-import android.content.DialogInterface
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.net.Uri
-import android.os.Build
-import android.provider.Settings
-import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.app.ActivityCompat
-import androidx.core.content.ContextCompat
-import org.mifos.mobile.R
-import org.mifos.mobile.core.datastore.PreferencesHelper
-
-/**
- * Created by dilpreet on 14/7/17.
- */
-object CheckSelfPermissionAndRequest {
- /**
- * This Method Check the Permission is granted or not to the App. If the Permission granted,
- * returns true and If not permission denied then returns false.
- *
- * @param context Context
- * @param permission Manifest.permission...Permission...
- * @return Boolean True or False.
- */
- @JvmStatic
- fun checkSelfPermission(context: Context?, permission: String?): Boolean {
- return ContextCompat.checkSelfPermission(context!!, permission!!) ==
- PackageManager.PERMISSION_GRANTED
- }
-
- /**
- * This Method is requesting to device to grant the permission. When App is trying to
- * request the device to grant the permission, then their is Three cases.
- * 1. First case Device Prompt the Permission Dialog to user and user accepted or denied the
- * Permission.
- * 2. Second case will come, if user will denied the permission, after onclick dialog denied
- * button and next time App ask for permission, It will show a Material Dialog and there
- * will be a message to tell the user that you have denied the permission before, So do
- * you want to give this permission to app or not, If yes then click on Re-Try dialog button
- * and if not then click on Dialog button "I'm Sure", to not to give this permission to the
- * app.
- *
- *
- * And as user will click on "Re-Try" dialog button, he will be prompt with the with
- * permission dialog with "[-] never ask again" and have two options first one to click on
- * denied button again and put Un check the never ask check box. In this case, user will
- * prompt with permission dialog with "[-] never ask again" in the loop, whenever app ask
- * for that permission.
- *
- *
- * and If user will click on "[_/] never ask again" check box then permission dialog with
- * that permission will not prompt to the user.
- * 3. Third case will came. when user have denied to accept permission with never ask again.
- * then user will prompt with dialog and message that you have denied this permission with
- * never ask again. but this is necessary permission to this app feature. and to grant
- * this permission please click on dialog app settings button and give the permission to
- * work with this feature.
- *
- * @param activity AppCompatActivity
- * @param permission Manifest.permission...Permission...
- * @param permissionRequestCode Permission Request Code.
- * @param dialogMessageRetry Dialog Message Retry
- * @param messageNeverAskAgain Dialog Message Never Ask Again
- * @param permissionDeniedStatus Permission Denied Status
- */
- @JvmStatic
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- fun requestPermission(
- activity: AppCompatActivity,
- permission: String,
- permissionRequestCode: Int,
- dialogMessageRetry: String?,
- messageNeverAskAgain: String?,
- permissionDeniedStatus: String?,
- ) {
- // Should we show an explanation?
- if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
- // Show an explanation to the user *asynchronously* -- don't block
- // this thread waiting for the user's response! After the user
- // sees the explanation, try again to request the permission.
- MaterialDialog.Builder().init(activity)
- .setTitle(R.string.dialog_permission_denied)
- .setMessage(dialogMessageRetry)
- .setPositiveButton(
- R.string.dialog_action_re_try,
- DialogInterface.OnClickListener { _, _ ->
- ActivityCompat.requestPermissions(
- activity,
- arrayOf(permission),
- permissionRequestCode,
- )
- },
- )
- .setNegativeButton(R.string.dialog_action_i_am_sure)
- .createMaterialDialog()
- .show()
- } else {
- // Requesting Permission, first time to the device.
- val preferencesHelper = PreferencesHelper(activity.applicationContext)
- if (preferencesHelper.getBoolean(permissionDeniedStatus, true) == true) {
- preferencesHelper.putBoolean(permissionDeniedStatus, false)
- ActivityCompat.requestPermissions(
- activity,
- arrayOf(permission),
- permissionRequestCode,
- )
- } else {
- // Requesting Permission, more the one time and opening the setting to change
- // the Permission in App Settings.
- MaterialDialog.Builder().init(activity)
- .setMessage(messageNeverAskAgain)
- .setNegativeButton(R.string.dialog_action_cancel)
- .setPositiveButton(
- R.string.dialog_action_app_settings,
- DialogInterface.OnClickListener { _, _ -> // Making the Intent to grant the permission
- val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
- val uri = Uri.fromParts(
- activity.resources.getString(
- R.string.package_name,
- ),
- activity.packageName,
- null,
- )
- intent.data = uri
- val pm = activity.packageManager
- if (intent.resolveActivity(pm) != null) {
- activity.startActivityForResult(
- intent,
- org.mifos.mobile.core.common.Constants.REQUEST_PERMISSION_SETTING,
- )
- } else {
- Toast.makeText(
- activity,
- activity.getString(
- R.string.msg_setting_activity_not_found,
- ),
- Toast.LENGTH_LONG,
- ).show()
- }
- },
- )
- .createMaterialDialog()
- .show()
- }
- }
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/ComparatorBasedOnId.kt b/androidApp/src/main/java/org/mifos/mobile/utils/ComparatorBasedOnId.kt
deleted file mode 100644
index 4b84615d9..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/ComparatorBasedOnId.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.mifos.mobile.utils
-
-import org.mifos.mobile.core.model.entity.accounts.Account
-
-/**
- * Created by dilpreet on 14/6/17.
- */
-open class ComparatorBasedOnId : Comparator {
- /**
- * Compares [Account] based on their Id
- * @param o1 the first object to be compared.
- * @param o2 the second object to be compared.
- * @return a negative integer, zero, or a positive integer as the
- * first argument is less than, equal to, or greater than the
- * second.
- */
- override fun compare(o1: Account?, o2: Account?): Int {
- return if (o1 != null && o2 != null) {
- o2.id.toFloat().compareTo(o1.id.toFloat())
- } else {
- 0
- }
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/ConfigurationDialogFragmentCompat.kt b/androidApp/src/main/java/org/mifos/mobile/utils/ConfigurationDialogFragmentCompat.kt
deleted file mode 100644
index 9cdeafb0a..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/ConfigurationDialogFragmentCompat.kt
+++ /dev/null
@@ -1,130 +0,0 @@
-package org.mifos.mobile.utils
-
-import android.content.Context
-import android.content.Intent
-import android.text.Editable
-import android.text.TextWatcher
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.EditText
-import androidx.preference.DialogPreference.TargetFragment
-import androidx.preference.Preference
-import androidx.preference.PreferenceDialogFragmentCompat
-import butterknife.BindView
-import butterknife.ButterKnife
-import com.google.android.material.textfield.TextInputLayout
-import org.mifos.mobile.R
-import org.mifos.mobile.core.datastore.PreferencesHelper
-import org.mifos.mobile.ui.activities.LoginActivity
-import java.net.MalformedURLException
-import java.net.URL
-
-/**
- * Created by dilpreet on 11/03/18.
- */
-class ConfigurationDialogFragmentCompat :
- PreferenceDialogFragmentCompat(),
- TargetFragment,
- TextWatcher {
- @JvmField
- @BindView(R.id.et_tenant)
- var etTenant: EditText? = null
-
- @JvmField
- @BindView(R.id.et_base_url)
- var etBaseUrl: EditText? = null
-
- @JvmField
- @BindView(R.id.til_tenant)
- var tilTenant: TextInputLayout? = null
-
- @JvmField
- @BindView(R.id.til_base_url)
- var tilBaseUrl: TextInputLayout? = null
- private var preferencesHelper: PreferencesHelper? = null
- override fun onCreateDialogView(context: Context): View {
- return LayoutInflater.from(context).inflate(R.layout.preference_configuration, null)
- }
-
- override fun onBindDialogView(view: View) {
- super.onBindDialogView(view)
- ButterKnife.bind(this, view)
- preferencesHelper = PreferencesHelper(context)
- val preference = preference as ConfigurationPreference
- etBaseUrl?.setText(preference.baseUrl)
- etTenant?.setText(preference.tenant)
- etBaseUrl?.text?.length?.let { etBaseUrl?.setSelection(it) }
- etTenant?.addTextChangedListener(this)
- etBaseUrl?.addTextChangedListener(this)
- }
-
- override fun onDialogClosed(positiveResult: Boolean) {
- if (positiveResult && !isFieldEmpty && isUrlValid) {
- val preference = preference as ConfigurationPreference
- if (!(
- preference.baseUrl.toString()
- .equals(etBaseUrl!!.text.toString()) && preference.tenant.toString()
- .equals(etTenant!!.text.toString())
- )
- ) {
- preference.updateConfigurations(
- etBaseUrl?.text.toString(),
- etTenant?.text.toString(),
- )
- preferencesHelper?.clear()
- val loginIntent = Intent(activity, LoginActivity::class.java)
- loginIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
- startActivity(loginIntent)
- activity?.finish()
- }
- }
- }
-
- override fun findPreference(key: CharSequence): Preference {
- return preference
- }
-
- val isFieldEmpty: Boolean
- get() {
- if (etBaseUrl?.text.toString().trim { it <= ' ' }.isEmpty()) {
- return true
- }
- return etTenant?.text.toString().trim { it <= ' ' }.isEmpty()
- }
- val isUrlValid: Boolean
- get() = try {
- URL(etBaseUrl?.text.toString())
- true
- } catch (e: MalformedURLException) {
- false
- }
-
- override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
- override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
- if (s.toString().isEmpty()) {
- if (etBaseUrl?.text.toString().isEmpty()) {
- tilBaseUrl?.isErrorEnabled = true
- tilBaseUrl?.error = getString(
- R.string.error_validation_blank,
- getString(R.string.base_url),
- )
- }
- if (etTenant?.text.toString().isEmpty()) {
- tilTenant?.isErrorEnabled = true
- tilTenant?.error = getString(
- R.string.error_validation_blank,
- getString(R.string.tenant),
- )
- }
- } else {
- if (etBaseUrl?.text.toString().length != 0) {
- tilBaseUrl?.isErrorEnabled = false
- }
- if (etTenant?.text.toString().length != 0) {
- tilTenant?.isErrorEnabled = false
- }
- }
- }
-
- override fun afterTextChanged(s: Editable) {}
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/ConfigurationPreference.kt b/androidApp/src/main/java/org/mifos/mobile/utils/ConfigurationPreference.kt
deleted file mode 100644
index 8430001bd..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/ConfigurationPreference.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.mifos.mobile.utils
-
-import android.content.Context
-import android.util.AttributeSet
-import androidx.preference.DialogPreference
-import org.mifos.mobile.core.datastore.PreferencesHelper
-
-/**
- * Created by dilpreet on 11/03/18.
- */
-class ConfigurationPreference : DialogPreference {
- private var preferencesHelper: PreferencesHelper? = null
-
- constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
- context,
- attrs,
- defStyleAttr,
- ) {
- init()
- }
-
- constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
- init()
- }
-
- constructor(context: Context?) : super(context) {
- init()
- }
-
- private fun init() {
- isPersistent = false
- preferencesHelper = PreferencesHelper(context)
- }
-
- val baseUrl: String?
- get() = preferencesHelper?.baseUrl
-
- fun updateConfigurations(baseUrl: String?, tenant: String?) {
- preferencesHelper?.updateConfiguration(baseUrl, tenant)
- }
-
- val tenant: String?
- get() = preferencesHelper?.tenant
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/DatePick.kt b/androidApp/src/main/java/org/mifos/mobile/utils/DatePick.kt
deleted file mode 100644
index fe9af7639..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/DatePick.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package org.mifos.mobile.utils
-
-/**
- * Created by dilpreet on 6/3/17.
- */
-enum class DatePick {
- START, END
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/EndlessRecyclerViewScrollListener.kt b/androidApp/src/main/java/org/mifos/mobile/utils/EndlessRecyclerViewScrollListener.kt
deleted file mode 100644
index ca89e43fd..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/EndlessRecyclerViewScrollListener.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-package org.mifos.mobile.utils
-
-import androidx.recyclerview.widget.GridLayoutManager
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.StaggeredGridLayoutManager
-
-abstract class EndlessRecyclerViewScrollListener : RecyclerView.OnScrollListener {
- // The minimum amount of items to have below your current scroll position
- // before loading more.
- private var visibleThreshold = 5
-
- // The current offset index of data you have loaded
- private var currentPage = 0
-
- // The total number of items in the dataset after the last load
- private var previousTotalItemCount = 0
-
- // True if we are still waiting for the last set of data to load.
- private var loading = true
-
- // Sets the starting page index
- private val startingPageIndex = 0
- var mLayoutManager: RecyclerView.LayoutManager
-
- constructor(layoutManager: LinearLayoutManager) {
- mLayoutManager = layoutManager
- }
-
- constructor(layoutManager: GridLayoutManager) {
- mLayoutManager = layoutManager
- visibleThreshold = visibleThreshold * layoutManager.spanCount
- }
-
- constructor(layoutManager: StaggeredGridLayoutManager) {
- mLayoutManager = layoutManager
- visibleThreshold = visibleThreshold * layoutManager.spanCount
- }
-
- fun getLastVisibleItem(lastVisibleItemPositions: IntArray): Int {
- var maxSize = 0
- for (i in lastVisibleItemPositions.indices) {
- if (i == 0) {
- maxSize = lastVisibleItemPositions[i]
- } else if (lastVisibleItemPositions[i] > maxSize) {
- maxSize = lastVisibleItemPositions[i]
- }
- }
- return maxSize
- }
-
- // This happens many times a second during a scroll, so be wary of the code you place here.
- // We are given a few useful parameters to help us work out if we need to load some more data,
- // but first we check if we are waiting for the previous load to finish.
- override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
- var lastVisibleItemPosition = 0
- val totalItemCount = mLayoutManager.itemCount
- if (mLayoutManager is StaggeredGridLayoutManager) {
- val lastVisibleItemPositions =
- (mLayoutManager as StaggeredGridLayoutManager).findLastVisibleItemPositions(
- null,
- )
- // get maximum element within the list
- lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions)
- } else if (mLayoutManager is GridLayoutManager) {
- lastVisibleItemPosition =
- (mLayoutManager as GridLayoutManager).findLastVisibleItemPosition()
- } else if (mLayoutManager is LinearLayoutManager) {
- lastVisibleItemPosition =
- (mLayoutManager as LinearLayoutManager).findLastVisibleItemPosition()
- }
-
- // If the total item count is zero and the previous isn't, assume the
- // list is invalidated and should be reset back to initial state
- if (totalItemCount < previousTotalItemCount) {
- currentPage = startingPageIndex
- previousTotalItemCount = totalItemCount
- if (totalItemCount == 0) {
- loading = true
- }
- }
- // If it’s still loading, we check to see if the dataset count has
- // changed, if so we conclude it has finished loading and update the current page
- // number and total item count.
- if (loading && totalItemCount > previousTotalItemCount) {
- loading = false
- previousTotalItemCount = totalItemCount
- }
-
- // If it isn’t currently loading, we check to see if we have breached
- // the visibleThreshold and need to reload more data.
- // If we do need to reload some more data, we execute onLoadMore to fetch the data.
- // threshold should reflect how many total columns there are too
- if (!loading && lastVisibleItemPosition + visibleThreshold > totalItemCount) {
- currentPage++
- onLoadMore(currentPage, totalItemCount, view)
- loading = true
- }
- }
-
- // Call this method whenever performing new searches
- fun resetState() {
- currentPage = startingPageIndex
- previousTotalItemCount = 0
- loading = true
- }
-
- // Defines the process for actually loading more data based on page
- abstract fun onLoadMore(page: Int, totalItemsCount: Int, view: RecyclerView?)
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/GuarantorUiState.kt b/androidApp/src/main/java/org/mifos/mobile/utils/GuarantorUiState.kt
deleted file mode 100644
index b95f9aaa1..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/GuarantorUiState.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.mifos.mobile.utils
-
-import org.mifos.mobile.core.model.entity.guarantor.GuarantorApplicationPayload
-import org.mifos.mobile.core.model.entity.guarantor.GuarantorPayload
-import org.mifos.mobile.core.model.entity.guarantor.GuarantorTemplatePayload
-
-sealed class GuarantorUiState {
- object Loading : GuarantorUiState()
-
- data class ShowError(val message: String?) : GuarantorUiState()
-
- data class GuarantorDeletedSuccessfully(val message: String?) : GuarantorUiState()
-
- data class GuarantorUpdatedSuccessfully(val message: String?) : GuarantorUiState()
-
- data class ShowGuarantorListSuccessfully(val payload: List?) :
- GuarantorUiState()
-
- data class ShowGuarantorApplication(val template: GuarantorTemplatePayload?) :
- GuarantorUiState()
-
- data class ShowGuarantorUpdation(val template: GuarantorTemplatePayload?) : GuarantorUiState()
-
- data class SubmittedSuccessfully(
- val message: String?,
- val payload: GuarantorApplicationPayload?
- ) : GuarantorUiState()
-}
\ No newline at end of file
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/ImageUtil.kt b/androidApp/src/main/java/org/mifos/mobile/utils/ImageUtil.kt
deleted file mode 100644
index dd0d3eca4..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/ImageUtil.kt
+++ /dev/null
@@ -1,136 +0,0 @@
-package org.mifos.mobile.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
-
-/**
- * Created by dilpreet on 10/8/17.
- */
-class ImageUtil {
- // Default width and height
- fun compressImage(decodedBytes: ByteArray): Bitmap? {
- return compress(decodedBytes, 816.0f, 612.0f)
- }
-
- fun compressImage(decodedBytes: ByteArray, maxHeight: Float, maxWidth: Float): Bitmap? {
- return compress(decodedBytes, maxHeight, maxWidth)
- }
-
- private fun compress(decodedBytes: ByteArray, maxHeight: Float, maxWidth: Float): Bitmap? {
- var scaledBitmap: Bitmap? = null
- val options = BitmapFactory.Options()
-
-// by setting this field as true, the actual bitmap pixels are not loaded in the memory.
-// Just the bounds are loaded. If
-// you try the use the bitmap here, you will get null.
- options.inJustDecodeBounds = true
- var bmp = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options)
- var actualHeight = options.outHeight
- var actualWidth = options.outWidth
- var imgRatio = actualWidth / actualHeight.toFloat()
- val maxRatio = maxWidth / maxHeight
-
-// width and height values are set maintaining the aspect ratio of the image
- if (actualHeight > maxHeight || actualWidth > maxWidth) {
- if (imgRatio < maxRatio) {
- imgRatio = maxHeight / actualHeight
- actualWidth = (imgRatio * actualWidth).toInt()
- actualHeight = maxHeight.toInt()
- } else if (imgRatio > maxRatio) {
- imgRatio = maxWidth / actualWidth
- actualHeight = (imgRatio * actualHeight).toInt()
- actualWidth = maxWidth.toInt()
- } else {
- actualHeight = maxHeight.toInt()
- actualWidth = maxWidth.toInt()
- }
- }
-
-// setting inSampleSize value allows to load a scaled down version of the original image
- options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight)
-
-// inJustDecodeBounds set to false to load the actual bitmap
- options.inJustDecodeBounds = false
-
-// this options allow android to claim the bitmap memory if it runs low on memory
- options.inPurgeable = true
- options.inInputShareable = true
- options.inTempStorage = ByteArray(16 * 1024)
- try {
- bmp = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options)
- } catch (exception: OutOfMemoryError) {
- Log.e(ImageUtil::class.java.name, exception.toString())
- }
- try {
- scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888)
- } catch (exception: OutOfMemoryError) {
- Log.e(ImageUtil::class.java.name, exception.toString())
- }
- val ratioX = actualWidth / options.outWidth.toFloat()
- val ratioY = actualHeight / options.outHeight.toFloat()
- val middleX = actualWidth / 2.0f
- val middleY = actualHeight / 2.0f
- val scaleMatrix = Matrix()
- scaleMatrix.setScale(ratioX, ratioY, middleX, middleY)
- val canvas = Canvas(scaledBitmap!!)
- canvas.setMatrix(scaleMatrix)
- canvas.drawBitmap(
- bmp,
- middleX - bmp.width / 2,
- middleY - bmp.height / 2,
- Paint(Paint.FILTER_BITMAP_FLAG),
- )
- scaledBitmap = Bitmap.createBitmap(
- scaledBitmap,
- 0,
- 0,
- scaledBitmap.width,
- scaledBitmap.height,
- null,
- true,
- )
- return scaledBitmap
- }
-
- private fun calculateInSampleSize(
- options: BitmapFactory.Options,
- reqWidth: Int,
- reqHeight: Int,
- ): Int {
- val height = options.outHeight
- val width = options.outWidth
- var inSampleSize = 1
- if (height > reqHeight || width > reqWidth) {
- val heightRatio = Math.round(height.toFloat() / reqHeight.toFloat())
- val widthRatio = Math.round(width.toFloat() / reqWidth.toFloat())
- inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio
- }
- val totalPixels = width * height.toFloat()
- val totalReqPixelsCap = reqWidth * reqHeight * 2.toFloat()
- while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
- inSampleSize++
- }
- return inSampleSize
- }
-
- companion object {
- /**
- * Reference : https://developer.android.com/topic/performance/graphics/load-bitmap.html
- * And for scaling :
- * https://stackoverflow.com/questions/8722359/scale-rotate-bitmap-using-matrix-in-android/8722592#8722592
- */
- @JvmStatic
- var instance: ImageUtil? = null
- get() {
- if (field == null) {
- field = ImageUtil()
- }
- return field
- }
- private set
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/MFErrorParser.kt b/androidApp/src/main/java/org/mifos/mobile/utils/MFErrorParser.kt
deleted file mode 100644
index 01a2178e2..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/MFErrorParser.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.mifos.mobile.utils
-
-import com.google.gson.Gson
-import io.reactivex.plugins.RxJavaPlugins
-import org.mifos.mobile.core.model.entity.mifoserror.MifosError
-import retrofit2.HttpException
-
-object MFErrorParser {
- const val LOG_TAG = "MFErrorParser"
- private val gson = Gson()
- fun parseError(serverResponse: String?): MifosError {
- return gson.fromJson(serverResponse, MifosError::class.java)
- }
-
- @JvmStatic
- fun errorMessage(throwableError: Throwable?): String? {
- var errorMessage: String? = ""
- try {
- if (throwableError is HttpException) {
- errorMessage = throwableError.response()?.errorBody()?.string()
- errorMessage = parseError(errorMessage).errors[0].defaultUserMessage
- }
- } catch (throwable: Throwable) {
- RxJavaPlugins.getErrorHandler()
- }
- return errorMessage
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/MaterialDialog.kt b/androidApp/src/main/java/org/mifos/mobile/utils/MaterialDialog.kt
deleted file mode 100644
index 531b6a72d..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/MaterialDialog.kt
+++ /dev/null
@@ -1,205 +0,0 @@
-package org.mifos.mobile.utils
-
-import android.content.Context
-import android.content.DialogInterface
-import android.view.View
-import androidx.annotation.StringRes
-import androidx.appcompat.app.AlertDialog
-import androidx.core.content.ContextCompat
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import org.mifos.mobile.R
-
-/**
- * This Class is the Material Dialog Builder Class
- * Created by Rajan Maurya on 03/08/16.
- */
-class MaterialDialog {
-
- class Builder {
-
- private var mMaterialDialogBuilder: AlertDialog.Builder? = null
- private var context: Context? = null
-
- // This is the Default Builder Initialization with Material Style
- fun init(context: Context?): Builder {
- mMaterialDialogBuilder = MaterialAlertDialogBuilder(context!!)
- this.context = context
- return this
- }
-
- // This method set the custom Material Style
- fun init(context: Context?, theme: Int): Builder {
- mMaterialDialogBuilder = AlertDialog.Builder(context!!, theme)
- this.context = context
- return this
- }
-
- // This method set the String Title
- fun setTitle(title: String?): Builder {
- mMaterialDialogBuilder?.setTitle(title)
- return this
- }
-
- // This Method set the String Resources to Title
- fun setTitle(@StringRes title: Int): Builder {
- mMaterialDialogBuilder?.setTitle(title)
- return this
- }
-
- // This Method set the String Message
- fun setMessage(message: String?): Builder {
- mMaterialDialogBuilder?.setMessage(message)
- return this
- }
-
- // This Method set the String Resources message
- fun setMessage(@StringRes message: Int): Builder {
- mMaterialDialogBuilder?.setMessage(message)
- return this
- }
-
- // This Method set String Test to the Positive Button and set the Onclick null
- fun setPositiveButton(positiveText: String?): Builder {
- mMaterialDialogBuilder?.setPositiveButton(positiveText, null)
- return this
- }
-
- // This Method Set the String Resources Text To Positive Button
- fun setPositiveButton(@StringRes positiveText: Int): Builder {
- mMaterialDialogBuilder?.setPositiveButton(positiveText, null)
- return this
- }
-
- // This Method set the String Text to Positive Button and set the OnClick Event to it
- fun setPositiveButton(
- positiveText: String?,
- listener: DialogInterface.OnClickListener?,
- ): Builder {
- mMaterialDialogBuilder?.setPositiveButton(positiveText, listener)
- return this
- }
-
- // This method set the String Resources text To Positive button and set the Onclick Event
- fun setPositiveButton(
- @StringRes positiveText: Int,
- listener: DialogInterface.OnClickListener?,
- ): Builder {
- mMaterialDialogBuilder?.setPositiveButton(positiveText, listener)
- return this
- }
-
- // This Method the String Text to Negative Button and Set the onclick event to null
- fun setNegativeButton(negativeText: String?): Builder {
- mMaterialDialogBuilder?.setNegativeButton(negativeText, null)
- return this
- }
-
- // This Method set the String Resources Text to Negative button
- // and set the onclick event to null
- fun setNegativeButton(@StringRes negativeText: Int): Builder {
- mMaterialDialogBuilder?.setNegativeButton(negativeText, null)
- return this
- }
-
- // This Method set String Text to Negative Button and
- // Set the Onclick event
- fun setNegativeButton(
- negativeText: String?,
- listener: DialogInterface.OnClickListener?,
- ): Builder {
- mMaterialDialogBuilder?.setNegativeButton(negativeText, listener)
- return this
- }
-
- // This method set String Resources Text to Negative Button and set Onclick Event
- fun setNegativeButton(
- @StringRes negativeText: Int,
- listener: DialogInterface.OnClickListener?,
- ): Builder {
- mMaterialDialogBuilder?.setNegativeButton(negativeText, listener)
- return this
- }
-
- // This Method the String Text to Neutral Button and Set the onclick event to null
- fun setNeutralButton(neutralText: String?): Builder {
- mMaterialDialogBuilder?.setNeutralButton(neutralText, null)
- return this
- }
-
- // This Method set the String Resources Text to Neutral button
- // and set the onclick event to null
- fun setNeutralButton(@StringRes neutralText: Int): Builder {
- mMaterialDialogBuilder?.setNeutralButton(neutralText, null)
- return this
- }
-
- // This Method set String Text to Neutral Button and
- // Set the Onclick event
- fun setNeutralButton(
- neutralText: String?,
- listener: DialogInterface.OnClickListener?,
- ): Builder {
- mMaterialDialogBuilder?.setNeutralButton(neutralText, listener)
- return this
- }
-
- // This method set String Resources Text to Neutral Button and set Onclick Event
- fun setNeutralButton(
- @StringRes neutralText: Int,
- listener: DialogInterface.OnClickListener?,
- ): Builder {
- mMaterialDialogBuilder?.setNeutralButton(neutralText, listener)
- return this
- }
-
- fun setCancelable(cancelable: Boolean?): Builder {
- mMaterialDialogBuilder?.setCancelable(cancelable!!)
- return this
- }
-
- fun setItems(items: Int, listener: DialogInterface.OnClickListener?): Builder {
- mMaterialDialogBuilder?.setItems(items, listener)
- return this
- }
-
- fun setItems(items: Array?, listener: DialogInterface.OnClickListener?): Builder {
- mMaterialDialogBuilder?.setItems(items, listener)
- return this
- }
-
- fun addView(view: View?): Builder {
- mMaterialDialogBuilder?.setView(view)
- return this
- }
-
- // This Method Create the Final Material Dialog
- fun createMaterialDialog(): Builder {
- mMaterialDialogBuilder?.create()
- return this
- }
-
- // This Method Show the Dialog
- fun show(): Builder {
- val dialog = mMaterialDialogBuilder?.show()
- dialog?.getButton(DialogInterface.BUTTON_POSITIVE)?.setTextColor(
- ContextCompat.getColor(
- context!!,
- R.color.accent,
- ),
- )
- dialog?.getButton(DialogInterface.BUTTON_NEGATIVE)?.setTextColor(
- ContextCompat.getColor(
- context!!,
- R.color.gray_dark,
- ),
- )
- dialog?.getButton(DialogInterface.BUTTON_NEUTRAL)?.setTextColor(
- ContextCompat.getColor(
- context!!,
- R.color.black,
- ),
- )
- return this
- }
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/ProcessView.kt b/androidApp/src/main/java/org/mifos/mobile/utils/ProcessView.kt
deleted file mode 100644
index 8601dccb2..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/ProcessView.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-package org.mifos.mobile.utils
-
-import android.content.Context
-import android.graphics.Canvas
-import android.graphics.Paint
-import android.util.AttributeSet
-import android.view.View
-import androidx.core.content.ContextCompat
-import org.mifos.mobile.R
-import org.mifos.mobile.ui.getThemeAttributeColor
-
-/**
- * Created by dilpreet on 30/6/17.
- */
-class ProcessView : View {
- private var valueStr: String? = null
- private var textPaint: Paint? = null
- private var backgroundPaint: Paint? = null
-
- constructor(context: Context?) : super(context, null)
- constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
- val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ProcessView)
- valueStr = typedArray.getString(R.styleable.ProcessView_value)
- typedArray.recycle()
- textPaint = Paint()
- textPaint?.color = getColorCompat(android.R.color.white)
- textPaint?.isAntiAlias = true
- textPaint?.style = Paint.Style.FILL
- textPaint?.strokeWidth = 1f
- textPaint?.textSize = 40f
- backgroundPaint = Paint()
- backgroundPaint?.color = getColorCompat(R.color.gray_dark)
- backgroundPaint?.isAntiAlias = true
- backgroundPaint?.style = Paint.Style.FILL
- }
-
- override fun onDraw(canvas: Canvas) {
- super.onDraw(canvas)
- val xPos =
- canvas.width / 2 - (textPaint?.measureText(valueStr.toString())?.div(2))?.toInt()!!
- val yPos = (
- canvas.height / 2 - (
- (
- textPaint?.descent()
- ?.plus(textPaint?.ascent()!!)
- )?.div(2)
- )!!
- ).toInt()
- val usableWidth = width - (paddingLeft + paddingRight)
- val usableHeight = height - (paddingTop + paddingBottom)
- val radius = usableWidth.coerceAtMost(usableHeight) / 2
- val cx = paddingLeft + usableWidth / 2
- val cy = paddingTop + usableHeight / 2
- canvas.drawCircle(cx.toFloat(), cy.toFloat(), radius.toFloat(), backgroundPaint!!)
- canvas.drawText(valueStr!!, xPos.toFloat(), yPos.toFloat(), textPaint!!)
- }
-
- fun setCurrentActive() {
- backgroundPaint?.color = context.getThemeAttributeColor(R.attr.colorPrimary)
- invalidate()
- }
-
- fun setCurrentCompleted() {
- backgroundPaint?.color = context.getThemeAttributeColor(R.attr.colorPrimary)
- valueStr = "\u2713"
- invalidate()
- }
-
- private fun getColorCompat(colorId: Int): Int {
- return ContextCompat.getColor(context, colorId)
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/RetrofitUtils.kt b/androidApp/src/main/java/org/mifos/mobile/utils/RetrofitUtils.kt
deleted file mode 100644
index 420ac7296..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/RetrofitUtils.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.mifos.mobile.utils
-
-import okhttp3.MediaType
-import okhttp3.Protocol
-import okhttp3.ResponseBody
-import retrofit2.HttpException
-import retrofit2.Response
-
-/**
- * Created by dilpreet on 29/7/17.
- */
-object RetrofitUtils {
- fun getResponseForError(errorCode: Int): Exception {
- val message = if (errorCode == 401) "UnAuthorized" else "Not Found"
- val responseBody =
- ResponseBody.create(MediaType.parse("application/json"), "{\"message\":\"$message\"}")
- val response = okhttp3.Response.Builder().code(errorCode)
- .message(message)
- .protocol(Protocol.HTTP_1_1)
- .request(okhttp3.Request.Builder().url("http://localhost/").build())
- .build()
- return HttpException(Response.error(responseBody, response))
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/RxBus.kt b/androidApp/src/main/java/org/mifos/mobile/utils/RxBus.kt
deleted file mode 100644
index 240167b82..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/RxBus.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.mifos.mobile.utils
-
-import io.reactivex.Observable
-import io.reactivex.subjects.PublishSubject
-
-/*
- * Created by saksham on 31/July/2018
-*/
-
-object RxBus {
-
- @JvmStatic
- private val publisher = PublishSubject.create()
-
- @JvmStatic
- fun publish(event: Any?) {
- if (event != null) publisher.onNext(event)
- }
-
- @JvmStatic
- fun listen(eventType: Class): Observable {
- return publisher.ofType(eventType)
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/RxEvent.kt b/androidApp/src/main/java/org/mifos/mobile/utils/RxEvent.kt
deleted file mode 100644
index 590edcaa9..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/RxEvent.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.mifos.mobile.utils
-
-import org.mifos.mobile.core.model.entity.guarantor.GuarantorApplicationPayload
-
-/*
- * Created by saksham on 29/July/2018
-*/
-
-class RxEvent {
-
- data class AddGuarantorEvent(var payload: GuarantorApplicationPayload?, var index: Int?)
-
- data class DeleteGuarantorEvent(var index: Int?)
-
- data class UpdateGuarantorEvent(var payload: GuarantorApplicationPayload?, var index: Int?)
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/SavingsAccountUiState.kt b/androidApp/src/main/java/org/mifos/mobile/utils/SavingsAccountUiState.kt
deleted file mode 100644
index 615543926..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/SavingsAccountUiState.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.mifos.mobile.utils
-
-import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations
-import org.mifos.mobile.core.model.entity.accounts.savings.Transactions
-import org.mifos.mobile.core.model.entity.templates.account.AccountOptionsTemplate
-import org.mifos.mobile.core.model.entity.templates.savings.SavingsAccountTemplate
-
-sealed class SavingsAccountUiState {
- object Initial : SavingsAccountUiState()
- object Loading : SavingsAccountUiState()
- object Error : SavingsAccountUiState()
- data class SuccessLoadingSavingsWithAssociations(val savingAccount: SavingsWithAssociations) :
- SavingsAccountUiState()
-
- data class ShowFilteredTransactionsList(val savingAccountsTransactionList: List?) :
- SavingsAccountUiState()
-
- object SavingsAccountUpdateSuccess : SavingsAccountUiState()
- object SavingsAccountApplicationSuccess : SavingsAccountUiState()
- data class ShowUserInterfaceSavingAccountUpdate(val template: SavingsAccountTemplate) :
- SavingsAccountUiState()
-
- data class ShowUserInterfaceSavingAccountApplication(val template: SavingsAccountTemplate) :
- SavingsAccountUiState()
-
- data class ErrorMessage(val error: Throwable) : SavingsAccountUiState()
- object HideProgress : SavingsAccountUiState()
- object SavingsAccountWithdrawSuccess : SavingsAccountUiState()
-
- data class ShowSavingsAccountTemplate(val accountOptionsTemplate: AccountOptionsTemplate) : SavingsAccountUiState()
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/ScrollFabBehavior.kt b/androidApp/src/main/java/org/mifos/mobile/utils/ScrollFabBehavior.kt
deleted file mode 100644
index c6c2e97ac..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/ScrollFabBehavior.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-package org.mifos.mobile.utils
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-import androidx.coordinatorlayout.widget.CoordinatorLayout
-import androidx.core.view.ViewCompat
-import com.google.android.material.floatingactionbutton.FloatingActionButton
-import com.google.android.material.floatingactionbutton.FloatingActionButton.OnVisibilityChangedListener
-
-/**
- * Created by dilpreet on 23/8/17.
- */
-class ScrollFabBehavior(context: Context?, attrs: AttributeSet?) : FloatingActionButton.Behavior() {
- override fun onStartNestedScroll(
- coordinatorLayout: CoordinatorLayout,
- child: FloatingActionButton,
- directTargetChild: View,
- target: View,
- nestedScrollAxes: Int,
- ): Boolean {
- return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL ||
- super.onStartNestedScroll(
- coordinatorLayout,
- child,
- directTargetChild,
- target,
- nestedScrollAxes,
- )
- }
-
- override fun onNestedScroll(
- coordinatorLayout: CoordinatorLayout,
- child: FloatingActionButton,
- target: View,
- dxConsumed: Int,
- dyConsumed: Int,
- dxUnconsumed: Int,
- dyUnconsumed: Int,
- ) {
- super.onNestedScroll(
- coordinatorLayout,
- child,
- target,
- dxConsumed,
- dyConsumed,
- dxUnconsumed,
- dyUnconsumed,
- )
- if (dyConsumed > 0 && child.visibility == View.VISIBLE) {
- // Reason to set fab visiblity as INVISIBLE :
- // https://stackoverflow.com/a/41386278/4672688
- child.hide(object : OnVisibilityChangedListener() {
- override fun onHidden(fab: FloatingActionButton) {
- super.onHidden(fab)
- fab.visibility = View.INVISIBLE
- }
- })
- } else if (dyConsumed < 0 && child.visibility != View.VISIBLE) {
- child.show()
- }
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/StatusUtils.kt b/androidApp/src/main/java/org/mifos/mobile/utils/StatusUtils.kt
deleted file mode 100644
index 41f2edb55..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/StatusUtils.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.mifos.mobile.utils
-
-import android.content.Context
-import androidx.core.content.ContextCompat
-import org.mifos.mobile.R
-import org.mifos.mobile.core.model.entity.CheckboxStatus
-
-/**
- * Created by dilpreet on 3/7/17.
- */
-object StatusUtils {
-
- fun getSavingsAccountTransactionList(context: Context?): List {
- val arrayList = ArrayList()
- arrayList.add(
- CheckboxStatus(
- context?.getString(R.string.feature_account_deposit),
- ContextCompat
- .getColor(context!!, R.color.deposit_green),
- ),
- )
- arrayList.add(
- CheckboxStatus(
- context.getString(R.string.feature_account_dividend_payout),
- ContextCompat
- .getColor(context, R.color.red_light),
- ),
- )
- arrayList.add(
- CheckboxStatus(
- context.getString(R.string.feature_account_withdrawal),
- ContextCompat
- .getColor(context, R.color.red_light),
- ),
- )
- arrayList.add(
- CheckboxStatus(
- context.getString(R.string.feature_account_interest_posting),
- ContextCompat.getColor(context, R.color.green_light),
- ),
- )
- arrayList.add(
- CheckboxStatus(
- context.getString(R.string.feature_account_fee_deduction),
- ContextCompat
- .getColor(context, R.color.red_light),
- ),
- )
- arrayList.add(
- CheckboxStatus(
- context.getString(R.string.feature_account_withdrawal_transfer),
- ContextCompat.getColor(context, R.color.red_light),
- ),
- )
- arrayList.add(
- CheckboxStatus(
- context.getString(R.string.feature_account_rejected_transfer),
- ContextCompat.getColor(context, R.color.green_light),
- ),
- )
- arrayList.add(
- CheckboxStatus(
- context.getString(R.string.feature_account_overdraft_fee),
- ContextCompat
- .getColor(context, R.color.red_light),
- ),
- )
- return arrayList
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/TextDrawable.kt b/androidApp/src/main/java/org/mifos/mobile/utils/TextDrawable.kt
deleted file mode 100644
index 82f369af9..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/TextDrawable.kt
+++ /dev/null
@@ -1,281 +0,0 @@
-package org.mifos.mobile.utils
-
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.ColorFilter
-import android.graphics.Paint
-import android.graphics.PixelFormat
-import android.graphics.RectF
-import android.graphics.Typeface
-import android.graphics.drawable.ShapeDrawable
-import android.graphics.drawable.shapes.OvalShape
-import android.graphics.drawable.shapes.RectShape
-import android.graphics.drawable.shapes.RoundRectShape
-import java.util.Locale
-
-class TextDrawable private constructor(builder: Builder) : ShapeDrawable(builder.shape) {
- private val textPaint: Paint
- private val borderPaint: Paint
- private val text: String
- private val color: Int
- private val shape: RectShape
- private val height: Int
- private val width: Int
- private val fontSize: Int
- private val radius: Float
- private val borderThickness: Int
- private fun getDarkerShade(color: Int): Int {
- return Color.rgb(
- (SHADE_FACTOR * Color.red(color)).toInt(),
- (SHADE_FACTOR * Color.green(color)).toInt(),
- (SHADE_FACTOR * Color.blue(color)).toInt(),
- )
- }
-
- override fun draw(canvas: Canvas) {
- super.draw(canvas)
- val r = bounds
-
- // draw border
- if (borderThickness > 0) {
- drawBorder(canvas)
- }
- val count = canvas.save()
- canvas.translate(r.left.toFloat(), r.top.toFloat())
-
- // draw text
- val width = if (width < 0) r.width() else width
- val height = if (height < 0) r.height() else height
- val fontSize = if (fontSize < 0) Math.min(width, height) / 2 else fontSize
- textPaint.textSize = fontSize.toFloat()
- canvas.drawText(
- text,
- width / 2.toFloat(),
- height / 2 - (textPaint.descent() + textPaint.ascent()) / 2,
- textPaint,
- )
- canvas.restoreToCount(count)
- }
-
- private fun drawBorder(canvas: Canvas) {
- val rect = RectF(bounds)
- rect.inset(borderThickness / 2.toFloat(), borderThickness / 2.toFloat())
- if (shape is OvalShape) {
- canvas.drawOval(rect, borderPaint)
- } else if (shape is RoundRectShape) {
- canvas.drawRoundRect(rect, radius, radius, borderPaint)
- } else {
- canvas.drawRect(rect, borderPaint)
- }
- }
-
- override fun setAlpha(alpha: Int) {
- textPaint.alpha = alpha
- }
-
- override fun setColorFilter(cf: ColorFilter?) {
- textPaint.colorFilter = cf
- }
-
- override fun getOpacity(): Int {
- return PixelFormat.TRANSLUCENT
- }
-
- override fun getIntrinsicWidth(): Int {
- return width
- }
-
- override fun getIntrinsicHeight(): Int {
- return height
- }
-
- class Builder : IConfigBuilder, IShapeBuilder, IBuilder {
- var text = ""
- var color: Int
- var borderThickness: Int
- var iconWidth: Int
- var iconHeight: Int
- var font: Typeface
- var shape: RectShape
- var iconTextColor: Int
- var iconFontSize: Int
- var isBold: Boolean
- var iconToUpperCase: Boolean
- var radius = 0f
- override fun width(width: Int): IConfigBuilder {
- iconWidth = width
- return this
- }
-
- override fun height(height: Int): IConfigBuilder {
- iconHeight = height
- return this
- }
-
- override fun textColor(color: Int): IConfigBuilder {
- iconTextColor = color
- return this
- }
-
- override fun withBorder(thickness: Int): IConfigBuilder {
- borderThickness = thickness
- return this
- }
-
- override fun useFont(font: Typeface): IConfigBuilder {
- this.font = font
- return this
- }
-
- override fun fontSize(size: Int): IConfigBuilder {
- iconFontSize = size
- return this
- }
-
- override fun bold(): IConfigBuilder {
- isBold = true
- return this
- }
-
- override fun toUpperCase(): IConfigBuilder {
- iconToUpperCase = true
- return this
- }
-
- override fun beginConfig(): IConfigBuilder {
- return this
- }
-
- override fun endConfig(): IShapeBuilder {
- return this
- }
-
- override fun rect(): IBuilder {
- shape = RectShape()
- return this
- }
-
- override fun round(): IBuilder {
- shape = OvalShape()
- return this
- }
-
- override fun roundRect(radius: Int): IBuilder {
- this.radius = radius.toFloat()
- val radii = floatArrayOf(
- radius.toFloat(),
- radius.toFloat(),
- radius.toFloat(),
- radius.toFloat(),
- radius.toFloat(),
- radius.toFloat(),
- radius.toFloat(),
- radius.toFloat(),
- )
- shape = RoundRectShape(radii, null, null)
- return this
- }
-
- override fun buildRect(text: String, color: Int): TextDrawable {
- rect()
- return build(text, color)
- }
-
- override fun buildRoundRect(text: String, color: Int, radius: Int): TextDrawable {
- roundRect(radius)
- return build(text, color)
- }
-
- override fun buildRound(text: String?, color: Int): TextDrawable {
- round()
- return build(text!!, color)
- }
-
- override fun build(text: String, color: Int): TextDrawable {
- this.color = color
- this.text = text
- return TextDrawable(this)
- }
-
- init {
- color = Color.GRAY
- iconTextColor = Color.WHITE
- borderThickness = 0
- iconWidth = -1
- iconHeight = -1
- shape = RectShape()
- font = Typeface.create("sans-serif-light", Typeface.NORMAL)
- iconFontSize = -1
- isBold = false
- iconToUpperCase = false
- }
- }
-
- interface IConfigBuilder {
- fun width(width: Int): IConfigBuilder
- fun height(height: Int): IConfigBuilder
- fun textColor(color: Int): IConfigBuilder
- fun withBorder(thickness: Int): IConfigBuilder
- fun useFont(font: Typeface): IConfigBuilder
- fun fontSize(size: Int): IConfigBuilder
- fun bold(): IConfigBuilder
- fun toUpperCase(): IConfigBuilder
- fun endConfig(): IShapeBuilder
- }
-
- interface IBuilder {
- fun build(text: String, color: Int): TextDrawable
- }
-
- interface IShapeBuilder {
- fun beginConfig(): IConfigBuilder
- fun rect(): IBuilder
- fun round(): IBuilder
- fun roundRect(radius: Int): IBuilder
- fun buildRect(text: String, color: Int): TextDrawable
- fun buildRoundRect(text: String, color: Int, radius: Int): TextDrawable
- fun buildRound(text: String?, color: Int): TextDrawable
- }
-
- companion object {
- private const val SHADE_FACTOR = 0.9f
- fun builder(): IShapeBuilder {
- return Builder()
- }
- }
-
- init {
-
- // shape properties
- shape = builder.shape
- height = builder.iconHeight
- width = builder.iconWidth
- radius = builder.radius
-
- // text and color
- text = if (builder.iconToUpperCase) builder.text.uppercase(Locale.ROOT) else builder.text
- color = builder.color
-
- // text paint settings
- fontSize = builder.iconFontSize
- textPaint = Paint()
- textPaint.color = builder.iconTextColor
- textPaint.isAntiAlias = true
- textPaint.isFakeBoldText = builder.isBold
- textPaint.style = Paint.Style.FILL
- textPaint.typeface = builder.font
- textPaint.textAlign = Paint.Align.CENTER
- textPaint.strokeWidth = builder.borderThickness.toFloat()
-
- // border paint settings
- borderThickness = builder.borderThickness
- borderPaint = Paint()
- borderPaint.color = getDarkerShade(color)
- borderPaint.style = Paint.Style.STROKE
- borderPaint.strokeWidth = borderThickness.toFloat()
-
- // drawable paint color
- val paint = paint
- paint.color = color
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/Toaster.kt b/androidApp/src/main/java/org/mifos/mobile/utils/Toaster.kt
deleted file mode 100644
index 869884f82..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/Toaster.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.mifos.mobile.utils
-
-import android.content.Context
-import android.view.View
-import android.view.inputmethod.InputMethodManager
-import com.google.android.material.snackbar.Snackbar
-import org.mifos.mobile.MifosSelfServiceApp
-
-object Toaster {
-
- private val snackbarsQueue = ArrayList()
-
- @JvmOverloads
- fun show(view: View?, text: String?, duration: Int = Snackbar.LENGTH_LONG) {
- val imm =
- MifosSelfServiceApp.context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
- imm.hideSoftInputFromWindow(view?.windowToken, 0)
- val snackbar = Snackbar.make(view!!, text!!, duration)
- snackbar.setAction("OK") { }
- snackbar.addCallback(object : Snackbar.Callback() {
-
- override fun onDismissed(transientBottomBar: Snackbar, event: Int) {
- super.onDismissed(transientBottomBar, event)
- if (snackbarsQueue.isNotEmpty()) {
- snackbarsQueue.removeAt(0)
- if (snackbarsQueue.isNotEmpty()) {
- snackbarsQueue[0].show()
- }
- }
- }
- })
- snackbarsQueue.add(snackbar)
- if (!snackbarsQueue[0].isShown) {
- snackbarsQueue[0].show()
- }
- }
-
- fun show(view: View, res: Int, duration: Int) {
- show(view, MifosSelfServiceApp.context?.resources?.getString(res), duration)
- }
-
- fun cancelTransfer(
- view: View?,
- text: String?,
- buttonText: String?,
- listener: View.OnClickListener?,
- ) {
- val snackbar = Snackbar.make(view!!, text!!, Snackbar.LENGTH_LONG)
- snackbar.setAction(buttonText, listener)
- snackbar.show()
- }
-
- fun show(view: View?, res: Int) {
- show(view, MifosSelfServiceApp.context?.resources?.getString(res))
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/fcm/MifosFirebaseMessagingService.kt b/androidApp/src/main/java/org/mifos/mobile/utils/fcm/MifosFirebaseMessagingService.kt
deleted file mode 100644
index 39523e0cc..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/fcm/MifosFirebaseMessagingService.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/**
- * Copyright 2015 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.mifos.mobile.utils.fcm
-
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.Context
-import android.content.Intent
-import android.media.RingtoneManager
-import android.util.Log
-import androidx.core.app.NotificationCompat
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
-import com.google.firebase.messaging.FirebaseMessagingService
-import com.google.firebase.messaging.RemoteMessage
-import org.mifos.mobile.R
-import org.mifos.mobile.ui.activities.HomeActivity
-
-class MifosFirebaseMessagingService : FirebaseMessagingService() {
- // [START receive_message]
- override fun onMessageReceived(remoteMessage: RemoteMessage) {
- val from = remoteMessage.from
- val data = remoteMessage.data
- val message = data[getString(R.string.message)]
- if ((from?.startsWith("/topics/") == true)) {
- // message received from some topic.
- } else {
- // normal downstream message.
- }
- sendNotification(message)
- val registrationComplete = Intent(org.mifos.mobile.core.common.Constants.NOTIFY_HOME_FRAGMENT)
- LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete)
- }
- // [END receive_message]
- /**
- * Create and show a simple notification containing the received FCM message.
- *
- * @param message FCM message received.
- */
- private fun sendNotification(message: String?) {
- val intent = Intent(this, HomeActivity::class.java)
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
- intent.putExtra(getString(R.string.notification), true)
- val pendingIntent = PendingIntent.getActivity(
- this,
- 0 /* Request code */,
- intent,
- PendingIntent.FLAG_ONE_SHOT,
- )
- val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
- val notificationBuilder = NotificationCompat.Builder(this)
- .setSmallIcon(R.mipmap.core_common_mifos_icon)
- .setContentTitle(getString(R.string.feature_about_app_name))
- .setContentText(message)
- .setAutoCancel(true)
- .setSound(defaultSoundUri)
- .setContentIntent(pendingIntent)
- val notificationManager =
- getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- val notification = org.mifos.mobile.core.datastore.model.MifosNotification()
- notification.msg = message
- notification.timeStamp = System.currentTimeMillis()
- notification.setRead(false)
- notificationManager.notify(0, notificationBuilder.build())
- }
-
- /**
- * Called if InstanceID token is updated. This may occur if the security of
- * the previous token had been compromised. Note that this is called when the InstanceID token
- * is initially generated so this is where you would retrieve the token.
- */
- override fun onNewToken(s: String) {
- super.onNewToken(s)
- Log.d(TAG, "Refreshed token: $s")
- val intent = Intent(this, RegistrationIntentService::class.java)
- startService(intent)
- }
-
- companion object {
- private val TAG = MifosFirebaseMessagingService::class.java.simpleName
- }
-}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/fcm/RegistrationIntentService.kt b/androidApp/src/main/java/org/mifos/mobile/utils/fcm/RegistrationIntentService.kt
deleted file mode 100644
index 2b8a689b2..000000000
--- a/androidApp/src/main/java/org/mifos/mobile/utils/fcm/RegistrationIntentService.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * Copyright 2015 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.mifos.mobile.utils.fcm
-
-import android.app.IntentService
-import android.content.Intent
-import android.util.Log
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
-import com.google.android.gms.tasks.OnCompleteListener
-import com.google.firebase.messaging.FirebaseMessaging
-
-class RegistrationIntentService : IntentService(TAG) {
- override fun onHandleIntent(intent: Intent?) {
- FirebaseMessaging.getInstance().token
- .addOnCompleteListener(
- OnCompleteListener { task ->
- if (!task.isSuccessful) {
- Log.w(TAG, "getInstanceId failed", task.exception)
- return@OnCompleteListener
- }
-
- // Get new Instance ID token
- val token = task.result
- sendRegistrationToServer(token)
- },
- )
- }
-
- private fun sendRegistrationToServer(token: String?) {
- val registrationComplete = Intent(org.mifos.mobile.core.common.Constants.REGISTER_ON_SERVER)
- registrationComplete.putExtra(org.mifos.mobile.core.common.Constants.TOKEN, token)
- LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete)
- }
-
- companion object {
- private const val TAG = "RegIntentService"
- }
-}
diff --git a/androidApp/src/main/kotlin/org/mifos/mobile/HomeActivity.kt b/androidApp/src/main/kotlin/org/mifos/mobile/HomeActivity.kt
new file mode 100644
index 000000000..7ced2b270
--- /dev/null
+++ b/androidApp/src/main/kotlin/org/mifos/mobile/HomeActivity.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.mifos.mobile
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.viewModels
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.navigation.compose.rememberNavController
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import org.mifos.mobile.HomeActivityUiState.Success
+import org.mifos.mobile.core.data.utils.NetworkMonitor
+import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
+import org.mifos.mobile.navigation.MifosNavGraph.AUTH_GRAPH
+import org.mifos.mobile.navigation.MifosNavGraph.PASSCODE_GRAPH
+import org.mifos.mobile.navigation.RootNavGraph
+import org.mifos.mobile.ui.rememberMifosMobileState
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class HomeActivity : ComponentActivity() {
+
+ @Inject
+ lateinit var networkMonitor: NetworkMonitor
+
+ private val viewModel: HomeActivityViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ val splashScreen = installSplashScreen()
+ super.onCreate(savedInstanceState)
+
+ var uiState: HomeActivityUiState by mutableStateOf(HomeActivityUiState.Loading)
+
+ // Update the uiState
+ lifecycleScope.launch {
+ lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.uiState
+ .onEach { uiState = it }
+ .collect()
+ }
+ }
+
+ splashScreen.setKeepOnScreenCondition {
+ when (uiState) {
+ HomeActivityUiState.Loading -> true
+ is Success -> false
+ }
+ }
+
+ enableEdgeToEdge()
+
+ setContent {
+ val navController = rememberNavController()
+
+ val appState = rememberMifosMobileState(networkMonitor = networkMonitor)
+
+ val navDestination = when (uiState) {
+ is Success -> if ((uiState as Success).userData.isAuthenticated) {
+ PASSCODE_GRAPH
+ } else {
+ AUTH_GRAPH
+ }
+
+ else -> AUTH_GRAPH
+ }
+
+ CompositionLocalProvider {
+ MifosMobileTheme {
+ RootNavGraph(
+ appState = appState,
+ navHostController = navController,
+ startDestination = navDestination,
+ onClickLogout = {
+ viewModel.logOut()
+ navController.navigate(AUTH_GRAPH) {
+ popUpTo(navController.graph.id) {
+ inclusive = true
+ }
+ }
+ },
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/androidApp/src/main/kotlin/org/mifos/mobile/HomeActivityViewModel.kt b/androidApp/src/main/kotlin/org/mifos/mobile/HomeActivityViewModel.kt
new file mode 100644
index 000000000..fdfc7e533
--- /dev/null
+++ b/androidApp/src/main/kotlin/org/mifos/mobile/HomeActivityViewModel.kt
@@ -0,0 +1,50 @@
+/*
+ * 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
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import org.mifos.library.passcode.data.PasscodeManager
+import org.mifos.mobile.core.data.repository.UserDataRepository
+import org.mifos.mobile.core.model.UserData
+import javax.inject.Inject
+
+@HiltViewModel
+class HomeActivityViewModel @Inject constructor(
+ private val userDataRepository: UserDataRepository,
+ private val passcodeManager: PasscodeManager,
+) : ViewModel() {
+
+ val uiState: StateFlow = userDataRepository.userData.map {
+ HomeActivityUiState.Success(it)
+ }.stateIn(
+ scope = viewModelScope,
+ initialValue = HomeActivityUiState.Loading,
+ started = SharingStarted.WhileSubscribed(5_000),
+ )
+
+ fun logOut() {
+ viewModelScope.launch {
+ userDataRepository.logOut()
+ passcodeManager.clearPasscode()
+ }
+ }
+}
+
+sealed interface HomeActivityUiState {
+ data object Loading : HomeActivityUiState
+ data class Success(val userData: UserData) : HomeActivityUiState
+}
diff --git a/androidApp/src/main/kotlin/org/mifos/mobile/MifosSelfServiceApp.kt b/androidApp/src/main/kotlin/org/mifos/mobile/MifosSelfServiceApp.kt
new file mode 100644
index 000000000..cf4270510
--- /dev/null
+++ b/androidApp/src/main/kotlin/org/mifos/mobile/MifosSelfServiceApp.kt
@@ -0,0 +1,27 @@
+/*
+ * 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
+
+import androidx.multidex.MultiDex
+import androidx.multidex.MultiDexApplication
+import com.google.firebase.crashlytics.FirebaseCrashlytics
+import dagger.hilt.android.HiltAndroidApp
+import org.mifos.mobile.core.datastore.PreferencesHelper
+import org.mifos.mobile.feature.settings.applySavedTheme
+
+@HiltAndroidApp
+class MifosSelfServiceApp : MultiDexApplication() {
+ override fun onCreate() {
+ super.onCreate()
+ MultiDex.install(this)
+ FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true)
+ PreferencesHelper(this).applySavedTheme()
+ }
+}
diff --git a/androidApp/src/main/kotlin/org/mifos/mobile/navigation/MifosNavGraph.kt b/androidApp/src/main/kotlin/org/mifos/mobile/navigation/MifosNavGraph.kt
new file mode 100644
index 000000000..b20112e4b
--- /dev/null
+++ b/androidApp/src/main/kotlin/org/mifos/mobile/navigation/MifosNavGraph.kt
@@ -0,0 +1,17 @@
+/*
+ * 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.navigation
+
+internal object MifosNavGraph {
+ const val ROOT_GRAPH = "root_graph"
+ const val AUTH_GRAPH = "auth_graph"
+ const val PASSCODE_GRAPH = "passcode_graph"
+ const val MAIN_GRAPH = "main_graph"
+}
diff --git a/androidApp/src/main/java/org/mifos/mobile/navigation/MifosNavGraph.kt b/androidApp/src/main/kotlin/org/mifos/mobile/navigation/MifosNavHost.kt
similarity index 76%
rename from androidApp/src/main/java/org/mifos/mobile/navigation/MifosNavGraph.kt
rename to androidApp/src/main/kotlin/org/mifos/mobile/navigation/MifosNavHost.kt
index 07f743199..e161cd448 100644
--- a/androidApp/src/main/java/org/mifos/mobile/navigation/MifosNavGraph.kt
+++ b/androidApp/src/main/kotlin/org/mifos/mobile/navigation/MifosNavHost.kt
@@ -1,3 +1,12 @@
+/*
+ * 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.navigation
import android.app.Activity
@@ -7,16 +16,14 @@ import android.net.Uri
import android.provider.Settings
import android.widget.Toast
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
-import androidx.navigation.NavController
import androidx.navigation.NavHostController
-import androidx.navigation.NavOptions
import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
-import com.mifos.compose.component.PasscodeScreen
+import org.mifos.mobile.HomeActivity
import org.mifos.mobile.R
-import org.mifos.mobile.core.common.Constants.INTIAL_LOGIN
import org.mifos.mobile.core.common.Constants.TRANSFER_PAY_TO
import org.mifos.mobile.core.model.enums.AccountType
import org.mifos.mobile.core.model.enums.ChargeType
@@ -24,7 +31,6 @@ import org.mifos.mobile.feature.about.navigation.aboutUsNavGraph
import org.mifos.mobile.feature.about.navigation.navigateToAboutUsScreen
import org.mifos.mobile.feature.account.navigation.clientAccountsNavGraph
import org.mifos.mobile.feature.account.navigation.navigateToClientAccountsScreen
-import org.mifos.mobile.feature.auth.navigation.authenticationNavGraph
import org.mifos.mobile.feature.auth.navigation.navigateToLoginScreen
import org.mifos.mobile.feature.beneficiary.navigation.beneficiaryNavGraph
import org.mifos.mobile.feature.beneficiary.navigation.navigateToAddBeneficiaryScreen
@@ -37,8 +43,8 @@ import org.mifos.mobile.feature.guarantor.navigation.navigateToGuarantorScreen
import org.mifos.mobile.feature.help.navigation.helpNavGraph
import org.mifos.mobile.feature.help.navigation.navigateToHelpScreen
import org.mifos.mobile.feature.home.navigation.HomeDestinations
+import org.mifos.mobile.feature.home.navigation.HomeNavigation
import org.mifos.mobile.feature.home.navigation.homeNavGraph
-import org.mifos.mobile.feature.home.navigation.navigateToHomeScreen
import org.mifos.mobile.feature.loan.navigation.loanNavGraph
import org.mifos.mobile.feature.loan.navigation.navigateToLoanApplication
import org.mifos.mobile.feature.loan.navigation.navigateToLoanDetailScreen
@@ -62,51 +68,44 @@ import org.mifos.mobile.feature.transaction.navigation.navigateToRecentTransacti
import org.mifos.mobile.feature.transaction.navigation.recentTransactionNavGraph
import org.mifos.mobile.feature.transfer.process.navigation.navigateToTransferProcessScreen
import org.mifos.mobile.feature.transfer.process.navigation.transferProcessNavGraph
-import org.mifos.mobile.feature.update_password.navigation.navigateToUpdatePassword
-import org.mifos.mobile.feature.update_password.navigation.updatePasswordNavGraph
+import org.mifos.mobile.feature.update.password.navigation.navigateToUpdatePassword
+import org.mifos.mobile.feature.update.password.navigation.updatePasswordNavGraph
import org.mifos.mobile.feature.user.profile.navigation.navigateToUserProfile
import org.mifos.mobile.feature.user.profile.navigation.userProfileNavGraph
-import org.mifos.mobile.ui.activities.HomeActivity
-import org.mifos.mobile.ui.activities.PassCodeActivity
-
@Composable
-fun RootNavGraph(
- startDestination: String,
- navController: NavHostController,
+fun MifosNavHost(
+ onClickLogout: () -> Unit,
+ modifier: Modifier = Modifier,
) {
val context = LocalContext.current
+ val navController = rememberNavController()
NavHost(
navController = navController,
- startDestination = startDestination,
+ route = MifosNavGraph.MAIN_GRAPH,
+ startDestination = HomeNavigation.HomeBase.route,
+ modifier = modifier,
) {
homeNavGraph(
- onNavigate = { handleHomeNavigation(navController, it, context) },
+ onNavigate = { handleHomeNavigation(navController, it, onClickLogout, context) },
callHelpline = { callHelpline(context) },
- mailHelpline = { mailHelpline(context) }
+ mailHelpline = { mailHelpline(context) },
)
- authenticationNavGraph(
- navController = navController,
- navigateToPasscodeScreen = { startPassCodeActivity(context = context) }
- )
-
- guarantorNavGraph(
- navController = navController,
- )
+ guarantorNavGraph(navController = navController)
loanNavGraph(
navController = navController,
viewQr = navController::navigateToQrDisplayScreen,
viewGuarantor = navController::navigateToGuarantorScreen,
viewCharges = navController::navigateToClientChargeScreen,
- makePayment = navController::navigateToSavingsMakeTransfer
+ makePayment = navController::navigateToSavingsMakeTransfer,
)
userProfileNavGraph(
navigateBack = navController::popBackStack,
- navigateToChangePassword = navController::navigateToUpdatePassword
+ navigateToChangePassword = navController::navigateToUpdatePassword,
)
updatePasswordNavGraph(navigateBack = navController::popBackStack)
@@ -114,24 +113,20 @@ fun RootNavGraph(
thirdPartyTransferNavGraph(
navigateBack = navController::popBackStack,
addBeneficiary = navController::navigateToAddBeneficiaryScreen,
- reviewTransfer = navController::navigateToTransferProcessScreen
+ reviewTransfer = navController::navigateToTransferProcessScreen,
)
settingsNavGraph(
navigateBack = navController::popBackStack,
changePassword = navController::navigateToUpdatePassword,
- changePasscode = {}, // { navigateToUpdatePasscodeActivity(it, context) },
+ changePasscode = {},
navigateToLoginScreen = navController::navigateToLoginScreen,
- languageChanged = { startActivity(context, HomeActivity::class.java) }
+ languageChanged = { startActivity(context, HomeActivity::class.java) },
)
- recentTransactionNavGraph(
- navigateBack = navController::popBackStack
- )
+ recentTransactionNavGraph(navigateBack = navController::popBackStack)
- notificationNavGraph(
- navigateBack = navController::popBackStack
- )
+ notificationNavGraph(navigateBack = navController::popBackStack)
locationsNavGraph()
@@ -139,16 +134,16 @@ fun RootNavGraph(
findLocations = navController::navigateToLocationsScreen,
navigateBack = navController::popBackStack,
callHelpline = { callHelpline(context) },
- mailHelpline = { mailHelpline(context) }
+ mailHelpline = { mailHelpline(context) },
)
- clientChargeNavGraph(
- navigateBack = navController::popBackStack
- )
+ clientChargeNavGraph(navigateBack = navController::popBackStack)
aboutUsNavGraph(
navController = navController,
- navigateToOssLicense = { startActivity(context, OssLicensesMenuActivity::class.java) }
+ navigateToOssLicense = {
+ context.startActivity(Intent(context, OssLicensesMenuActivity::class.java))
+ },
)
transferProcessNavGraph(navigateBack = navController::popBackStack)
@@ -156,7 +151,7 @@ fun RootNavGraph(
beneficiaryNavGraph(
navController = navController,
openQrImportScreen = navController::navigateToQrImportScreen,
- openQrReaderScreen = navController::navigateToQrReaderScreen
+ openQrReaderScreen = navController::navigateToQrReaderScreen,
)
qrNavGraph(
@@ -169,7 +164,7 @@ fun RootNavGraph(
viewCharges = navController::navigateToClientChargeScreen,
viewQrCode = navController::navigateToQrDisplayScreen,
callHelpline = { callHelpline(context) },
- reviewTransfer = navController::navigateToTransferProcessScreen
+ reviewTransfer = navController::navigateToTransferProcessScreen,
)
clientAccountsNavGraph(
@@ -182,38 +177,28 @@ fun RootNavGraph(
AccountType.LOAN -> navController.navigateToLoanDetailScreen(loanId = id)
AccountType.SHARE -> {}
}
- }
+ },
)
-
- composable(PASSCODE_SCREEN) {
- PasscodeScreen(
- onForgotButton = navController::navigateToHomeScreen,
- onSkipButton = navController::navigateToHomeScreen,
- onPasscodeConfirm = {
- navController.navigateToHomeScreen()
- },
- onPasscodeRejected = {}
- )
- }
}
}
-const val PASSCODE_SCREEN = "passcode_screen"
-
-fun NavController.navigateToPasscodeScreen(navOptions: NavOptions? = null) {
- return this.navigate(PASSCODE_SCREEN, navOptions)
-}
-
fun handleHomeNavigation(
navController: NavHostController,
homeDestinations: HomeDestinations,
- context: Context
+ onClickLogout: () -> Unit,
+ context: Context,
) {
when (homeDestinations) {
HomeDestinations.HOME -> Unit
HomeDestinations.ACCOUNTS -> navController.navigateToClientAccountsScreen()
- HomeDestinations.LOAN_ACCOUNT -> navController.navigateToClientAccountsScreen(accountType = AccountType.LOAN)
- HomeDestinations.SAVINGS_ACCOUNT -> navController.navigateToClientAccountsScreen(accountType = AccountType.SAVINGS)
+ HomeDestinations.LOAN_ACCOUNT -> {
+ navController.navigateToClientAccountsScreen(accountType = AccountType.LOAN)
+ }
+
+ HomeDestinations.SAVINGS_ACCOUNT -> {
+ navController.navigateToClientAccountsScreen(accountType = AccountType.SAVINGS)
+ }
+
HomeDestinations.RECENT_TRANSACTIONS -> navController.navigateToRecentTransaction()
HomeDestinations.CHARGES -> navController.navigateToClientChargeScreen(ChargeType.CLIENT)
HomeDestinations.THIRD_PARTY_TRANSFER -> navController.navigateToThirdPartyTransfer()
@@ -228,10 +213,10 @@ fun handleHomeNavigation(
openAppInfo(context)
}
- HomeDestinations.LOGOUT -> navController.navigateToLoginScreen()
+ HomeDestinations.LOGOUT -> onClickLogout.invoke()
HomeDestinations.TRANSFER -> navController.navigateToSavingsMakeTransfer(
accountId = 1,
- transferType = TRANSFER_PAY_TO
+ transferType = TRANSFER_PAY_TO,
)
HomeDestinations.BENEFICIARIES -> navController.navigateToBeneficiaryListScreen()
@@ -245,20 +230,6 @@ fun startActivity(context: Context, clazz: Class) {
context.startActivity(Intent(context, clazz))
}
-private fun startPassCodeActivity(context: Context) {
- val intent = Intent(context, PassCodeActivity::class.java)
- intent.putExtra(INTIAL_LOGIN, true)
- context.startActivity(intent)
-}
-//
-//private fun navigateToUpdatePasscodeActivity(passcode: String, context: Context) {
-// val intent = Intent(context, PassCodeActivity::class.java).apply {
-// putExtra(CURR_PASSWORD, passcode)
-// putExtra(IS_TO_UPDATE_PASS_CODE, true)s
-// }
-// context.startActivity(intent)ss
-//}
-
private fun callHelpline(context: Context) {
val intent = Intent(Intent.ACTION_DIAL)
intent.data =
@@ -271,11 +242,11 @@ private fun mailHelpline(context: Context) {
data = Uri.parse("mailto:")
putExtra(
Intent.EXTRA_EMAIL,
- arrayOf(context.getString(org.mifos.mobile.feature.home.R.string.contact_email))
+ arrayOf(context.getString(org.mifos.mobile.feature.home.R.string.contact_email)),
)
putExtra(
Intent.EXTRA_SUBJECT,
- context.getString(org.mifos.mobile.feature.home.R.string.user_query)
+ context.getString(org.mifos.mobile.feature.home.R.string.user_query),
)
}
try {
diff --git a/androidApp/src/main/kotlin/org/mifos/mobile/navigation/PasscodeNavGraph.kt b/androidApp/src/main/kotlin/org/mifos/mobile/navigation/PasscodeNavGraph.kt
new file mode 100644
index 000000000..b9be8e693
--- /dev/null
+++ b/androidApp/src/main/kotlin/org/mifos/mobile/navigation/PasscodeNavGraph.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.navigation
+
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavHostController
+import androidx.navigation.navigation
+import org.mifos.library.passcode.PASSCODE_SCREEN
+import org.mifos.library.passcode.passcodeRoute
+
+internal fun NavGraphBuilder.passcodeNavGraph(navController: NavHostController) {
+ navigation(
+ route = MifosNavGraph.PASSCODE_GRAPH,
+ startDestination = PASSCODE_SCREEN,
+ ) {
+ passcodeRoute(
+ onForgotButton = {
+ navController.popBackStack()
+ navController.navigate(MifosNavGraph.MAIN_GRAPH)
+ },
+ onSkipButton = {
+ navController.popBackStack()
+ navController.navigate(MifosNavGraph.MAIN_GRAPH)
+ },
+ onPasscodeConfirm = {
+ navController.popBackStack()
+ navController.navigate(MifosNavGraph.MAIN_GRAPH)
+ },
+ onPasscodeRejected = {
+ navController.popBackStack()
+ navController.navigate(MifosNavGraph.MAIN_GRAPH)
+ },
+ )
+ }
+}
diff --git a/androidApp/src/main/kotlin/org/mifos/mobile/navigation/RootNavGraph.kt b/androidApp/src/main/kotlin/org/mifos/mobile/navigation/RootNavGraph.kt
new file mode 100644
index 000000000..cc8d007da
--- /dev/null
+++ b/androidApp/src/main/kotlin/org/mifos/mobile/navigation/RootNavGraph.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.navigation
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import org.mifos.library.passcode.navigateToPasscodeScreen
+import org.mifos.mobile.feature.auth.navigation.authenticationNavGraph
+import org.mifos.mobile.navigation.MifosNavGraph.AUTH_GRAPH
+import org.mifos.mobile.ui.MifosApp
+import org.mifos.mobile.ui.MifosMobileState
+
+@Composable
+internal fun RootNavGraph(
+ appState: MifosMobileState,
+ navHostController: NavHostController,
+ startDestination: String,
+ onClickLogout: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ NavHost(
+ navController = navHostController,
+ startDestination = startDestination,
+ route = MifosNavGraph.ROOT_GRAPH,
+ modifier = modifier,
+ ) {
+ authenticationNavGraph(
+ navController = navHostController,
+ route = AUTH_GRAPH,
+ navigateToPasscodeScreen = navHostController::navigateToPasscodeScreen,
+ )
+
+ passcodeNavGraph(navHostController)
+
+ composable(MifosNavGraph.MAIN_GRAPH) {
+ MifosApp(
+ appState = appState,
+ onClickLogout = onClickLogout,
+ )
+ }
+ }
+}
diff --git a/androidApp/src/main/kotlin/org/mifos/mobile/ui/MifosApp.kt b/androidApp/src/main/kotlin/org/mifos/mobile/ui/MifosApp.kt
new file mode 100644
index 000000000..c8a46c215
--- /dev/null
+++ b/androidApp/src/main/kotlin/org/mifos/mobile/ui/MifosApp.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.WindowInsetsSides
+import androidx.compose.foundation.layout.consumeWindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.only
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SnackbarDuration.Indefinite
+import androidx.compose.material3.SnackbarHost
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.testTagsAsResourceId
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import org.mifos.mobile.R
+import org.mifos.mobile.core.designsystem.theme.MifosBackground
+import org.mifos.mobile.navigation.MifosNavHost
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+fun MifosApp(
+ appState: MifosMobileState,
+ onClickLogout: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ MifosBackground(modifier) {
+ val snackbarHostState = remember { SnackbarHostState() }
+
+ val isOffline by appState.isOffline.collectAsStateWithLifecycle()
+
+ // If user is not connected to the internet show a snack bar to inform them.
+ val notConnectedMessage = stringResource(R.string.not_connected)
+ LaunchedEffect(isOffline) {
+ if (isOffline) {
+ snackbarHostState.showSnackbar(
+ message = notConnectedMessage,
+ duration = Indefinite,
+ )
+ }
+ }
+
+ Scaffold(
+ modifier = Modifier.semantics {
+ testTagsAsResourceId = true
+ },
+ containerColor = Color.Transparent,
+ contentColor = MaterialTheme.colorScheme.onBackground,
+ snackbarHost = { SnackbarHost(snackbarHostState) },
+ ) { padding ->
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(padding)
+ .consumeWindowInsets(padding)
+ .windowInsetsPadding(
+ WindowInsets.safeDrawing.only(
+ WindowInsetsSides.Horizontal,
+ ),
+ ),
+ ) {
+ MifosNavHost(
+ onClickLogout = onClickLogout,
+ modifier = Modifier,
+ )
+ }
+ }
+ }
+}
diff --git a/androidApp/src/main/kotlin/org/mifos/mobile/ui/MifosMobileState.kt b/androidApp/src/main/kotlin/org/mifos/mobile/ui/MifosMobileState.kt
new file mode 100644
index 000000000..b24792113
--- /dev/null
+++ b/androidApp/src/main/kotlin/org/mifos/mobile/ui/MifosMobileState.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.ui
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.navigation.NavDestination
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.rememberNavController
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import org.mifos.mobile.core.data.utils.NetworkMonitor
+
+@Composable
+fun rememberMifosMobileState(
+ networkMonitor: NetworkMonitor,
+ coroutineScope: CoroutineScope = rememberCoroutineScope(),
+ navController: NavHostController = rememberNavController(),
+): MifosMobileState {
+ return remember(
+ navController,
+ coroutineScope,
+ networkMonitor,
+ ) {
+ MifosMobileState(
+ navController = navController,
+ coroutineScope = coroutineScope,
+ networkMonitor = networkMonitor,
+ )
+ }
+}
+
+@Stable
+class MifosMobileState(
+ val navController: NavHostController,
+ coroutineScope: CoroutineScope,
+ networkMonitor: NetworkMonitor,
+) {
+ val currentDestination: NavDestination?
+ @Composable get() = navController
+ .currentBackStackEntryAsState().value?.destination
+
+ val isOffline = networkMonitor.isOnline
+ .map(Boolean::not)
+ .stateIn(
+ scope = coroutineScope,
+ started = SharingStarted.WhileSubscribed(5_000),
+ initialValue = false,
+ )
+}
diff --git a/androidApp/src/main/res/anim/check_animation.xml b/androidApp/src/main/res/anim/check_animation.xml
deleted file mode 100644
index c566312fa..000000000
--- a/androidApp/src/main/res/anim/check_animation.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable-hdpi/ic_qrcode_scan_gray_dark.xml b/androidApp/src/main/res/drawable-hdpi/ic_qrcode_scan_gray_dark.xml
deleted file mode 100644
index d70535a7b..000000000
--- a/androidApp/src/main/res/drawable-hdpi/ic_qrcode_scan_gray_dark.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable-nodpi/example_appwidget_preview.png b/androidApp/src/main/res/drawable-nodpi/example_appwidget_preview.png
deleted file mode 100644
index 894b069a4..000000000
Binary files a/androidApp/src/main/res/drawable-nodpi/example_appwidget_preview.png and /dev/null differ
diff --git a/androidApp/src/main/res/drawable-v21/animated_check.xml b/androidApp/src/main/res/drawable-v21/animated_check.xml
deleted file mode 100644
index 9bd298dd7..000000000
--- a/androidApp/src/main/res/drawable-v21/animated_check.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable-v21/mifos_splash_screen_logo.png b/androidApp/src/main/res/drawable-v21/mifos_splash_screen_logo.png
deleted file mode 100644
index c1819abf0..000000000
Binary files a/androidApp/src/main/res/drawable-v21/mifos_splash_screen_logo.png and /dev/null differ
diff --git a/androidApp/src/main/res/drawable/animated_check.xml b/androidApp/src/main/res/drawable/animated_check.xml
deleted file mode 100644
index c43401e89..000000000
--- a/androidApp/src/main/res/drawable/animated_check.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/bg_about.png b/androidApp/src/main/res/drawable/bg_about.png
deleted file mode 100644
index ae673c4bf..000000000
Binary files a/androidApp/src/main/res/drawable/bg_about.png and /dev/null differ
diff --git a/androidApp/src/main/res/drawable/ic_about_us_black_24dp.xml b/androidApp/src/main/res/drawable/ic_about_us_black_24dp.xml
deleted file mode 100644
index 6e75dbfc3..000000000
--- a/androidApp/src/main/res/drawable/ic_about_us_black_24dp.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_account_balance_black_24dp.xml b/androidApp/src/main/res/drawable/ic_account_balance_black_24dp.xml
deleted file mode 100644
index 4da1d5287..000000000
--- a/androidApp/src/main/res/drawable/ic_account_balance_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_account_balance_wallet_black_24dp.xml b/androidApp/src/main/res/drawable/ic_account_balance_wallet_black_24dp.xml
deleted file mode 100644
index fee88542c..000000000
--- a/androidApp/src/main/res/drawable/ic_account_balance_wallet_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_account_balance_wallet_black_background_24dp.xml b/androidApp/src/main/res/drawable/ic_account_balance_wallet_black_background_24dp.xml
deleted file mode 100644
index dbd73b85a..000000000
--- a/androidApp/src/main/res/drawable/ic_account_balance_wallet_black_background_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_add_white_24dp.xml b/androidApp/src/main/res/drawable/ic_add_white_24dp.xml
deleted file mode 100644
index d29480261..000000000
--- a/androidApp/src/main/res/drawable/ic_add_white_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_arrow_drop_down.xml b/androidApp/src/main/res/drawable/ic_arrow_drop_down.xml
deleted file mode 100644
index e51044cc7..000000000
--- a/androidApp/src/main/res/drawable/ic_arrow_drop_down.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_arrow_up.xml b/androidApp/src/main/res/drawable/ic_arrow_up.xml
deleted file mode 100644
index 7d4b75ca4..000000000
--- a/androidApp/src/main/res/drawable/ic_arrow_up.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_assignment_turned_in_black_24dp.xml b/androidApp/src/main/res/drawable/ic_assignment_turned_in_black_24dp.xml
deleted file mode 100644
index 7aeaab097..000000000
--- a/androidApp/src/main/res/drawable/ic_assignment_turned_in_black_24dp.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_attach_money_black_24dp.xml b/androidApp/src/main/res/drawable/ic_attach_money_black_24dp.xml
deleted file mode 100644
index be66a221e..000000000
--- a/androidApp/src/main/res/drawable/ic_attach_money_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_backspace_48px.xml b/androidApp/src/main/res/drawable/ic_backspace_48px.xml
deleted file mode 100644
index 7e1e137f1..000000000
--- a/androidApp/src/main/res/drawable/ic_backspace_48px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_baseline_arrow_back_24.xml b/androidApp/src/main/res/drawable/ic_baseline_arrow_back_24.xml
deleted file mode 100644
index cd06f3098..000000000
--- a/androidApp/src/main/res/drawable/ic_baseline_arrow_back_24.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_baseline_cloud_off_24.xml b/androidApp/src/main/res/drawable/ic_baseline_cloud_off_24.xml
deleted file mode 100644
index 5207b48a9..000000000
--- a/androidApp/src/main/res/drawable/ic_baseline_cloud_off_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_baseline_dark_mode_24.xml b/androidApp/src/main/res/drawable/ic_baseline_dark_mode_24.xml
deleted file mode 100644
index e52c5baff..000000000
--- a/androidApp/src/main/res/drawable/ic_baseline_dark_mode_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_baseline_filter_list_24.xml b/androidApp/src/main/res/drawable/ic_baseline_filter_list_24.xml
deleted file mode 100644
index 52d959093..000000000
--- a/androidApp/src/main/res/drawable/ic_baseline_filter_list_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_beneficiaries_48px.xml b/androidApp/src/main/res/drawable/ic_beneficiaries_48px.xml
deleted file mode 100644
index 1ccd32405..000000000
--- a/androidApp/src/main/res/drawable/ic_beneficiaries_48px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_beneficiary_add_48px.xml b/androidApp/src/main/res/drawable/ic_beneficiary_add_48px.xml
deleted file mode 100644
index 4ef00e2c0..000000000
--- a/androidApp/src/main/res/drawable/ic_beneficiary_add_48px.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_beneficiary_wrapper.xml b/androidApp/src/main/res/drawable/ic_beneficiary_wrapper.xml
deleted file mode 100644
index 8436109e0..000000000
--- a/androidApp/src/main/res/drawable/ic_beneficiary_wrapper.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_border_color_black_24dp.xml b/androidApp/src/main/res/drawable/ic_border_color_black_24dp.xml
deleted file mode 100644
index 6821236b8..000000000
--- a/androidApp/src/main/res/drawable/ic_border_color_black_24dp.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_cake_24dp.xml b/androidApp/src/main/res/drawable/ic_cake_24dp.xml
deleted file mode 100644
index 1b1025e7a..000000000
--- a/androidApp/src/main/res/drawable/ic_cake_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_cake_wrapper.xml b/androidApp/src/main/res/drawable/ic_cake_wrapper.xml
deleted file mode 100644
index 11bc4ec9d..000000000
--- a/androidApp/src/main/res/drawable/ic_cake_wrapper.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_charges.xml b/androidApp/src/main/res/drawable/ic_charges.xml
deleted file mode 100644
index dc27ed8af..000000000
--- a/androidApp/src/main/res/drawable/ic_charges.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_check_circle_green_24px.xml b/androidApp/src/main/res/drawable/ic_check_circle_green_24px.xml
deleted file mode 100644
index e9c6a620c..000000000
--- a/androidApp/src/main/res/drawable/ic_check_circle_green_24px.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_clients.png b/androidApp/src/main/res/drawable/ic_clients.png
deleted file mode 100644
index 5a39aa030..000000000
Binary files a/androidApp/src/main/res/drawable/ic_clients.png and /dev/null differ
diff --git a/androidApp/src/main/res/drawable/ic_compare_arrows_black_24dp.xml b/androidApp/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
deleted file mode 100644
index a16bbb784..000000000
--- a/androidApp/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_edit.xml b/androidApp/src/main/res/drawable/ic_edit.xml
deleted file mode 100644
index c66b75b93..000000000
--- a/androidApp/src/main/res/drawable/ic_edit.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_edit_black_24dp.xml b/androidApp/src/main/res/drawable/ic_edit_black_24dp.xml
deleted file mode 100644
index dbc718244..000000000
--- a/androidApp/src/main/res/drawable/ic_edit_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_edit_wrapper.xml b/androidApp/src/main/res/drawable/ic_edit_wrapper.xml
deleted file mode 100644
index 5f836a0b6..000000000
--- a/androidApp/src/main/res/drawable/ic_edit_wrapper.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_error_black_24dp.xml b/androidApp/src/main/res/drawable/ic_error_black_24dp.xml
deleted file mode 100644
index b666c1818..000000000
--- a/androidApp/src/main/res/drawable/ic_error_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_file_upload_black_24dp.xml b/androidApp/src/main/res/drawable/ic_file_upload_black_24dp.xml
deleted file mode 100644
index 7bd6fa147..000000000
--- a/androidApp/src/main/res/drawable/ic_file_upload_black_24dp.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_filter_list_black_24dp.xml b/androidApp/src/main/res/drawable/ic_filter_list_black_24dp.xml
deleted file mode 100644
index d47884fbf..000000000
--- a/androidApp/src/main/res/drawable/ic_filter_list_black_24dp.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_flash_off.xml b/androidApp/src/main/res/drawable/ic_flash_off.xml
deleted file mode 100644
index ea32cd62e..000000000
--- a/androidApp/src/main/res/drawable/ic_flash_off.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_flash_on.xml b/androidApp/src/main/res/drawable/ic_flash_on.xml
deleted file mode 100644
index c7fb081c9..000000000
--- a/androidApp/src/main/res/drawable/ic_flash_on.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_gender_24dp.xml b/androidApp/src/main/res/drawable/ic_gender_24dp.xml
deleted file mode 100644
index fb8596be4..000000000
--- a/androidApp/src/main/res/drawable/ic_gender_24dp.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_gender_wrapper.xml b/androidApp/src/main/res/drawable/ic_gender_wrapper.xml
deleted file mode 100644
index 670cd3555..000000000
--- a/androidApp/src/main/res/drawable/ic_gender_wrapper.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_help_black_24dp.xml b/androidApp/src/main/res/drawable/ic_help_black_24dp.xml
deleted file mode 100644
index b1d7a2cf5..000000000
--- a/androidApp/src/main/res/drawable/ic_help_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_info_black_24dp.xml b/androidApp/src/main/res/drawable/ic_info_black_24dp.xml
deleted file mode 100644
index 0cbb99601..000000000
--- a/androidApp/src/main/res/drawable/ic_info_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_keyboard_arrow_right_black_24dp.xml b/androidApp/src/main/res/drawable/ic_keyboard_arrow_right_black_24dp.xml
deleted file mode 100644
index 138fcc3eb..000000000
--- a/androidApp/src/main/res/drawable/ic_keyboard_arrow_right_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_label_black_24dp.xml b/androidApp/src/main/res/drawable/ic_label_black_24dp.xml
deleted file mode 100644
index 0e887d432..000000000
--- a/androidApp/src/main/res/drawable/ic_label_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_loan.xml b/androidApp/src/main/res/drawable/ic_loan.xml
deleted file mode 100644
index 83555f96a..000000000
--- a/androidApp/src/main/res/drawable/ic_loan.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_local_atm_black_24dp.xml b/androidApp/src/main/res/drawable/ic_local_atm_black_24dp.xml
deleted file mode 100644
index 07b27418c..000000000
--- a/androidApp/src/main/res/drawable/ic_local_atm_black_24dp.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_location.xml b/androidApp/src/main/res/drawable/ic_location.xml
deleted file mode 100644
index 84e8820d2..000000000
--- a/androidApp/src/main/res/drawable/ic_location.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_location_wrapper.xml b/androidApp/src/main/res/drawable/ic_location_wrapper.xml
deleted file mode 100644
index 8e4a381e1..000000000
--- a/androidApp/src/main/res/drawable/ic_location_wrapper.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_logout.xml b/androidApp/src/main/res/drawable/ic_logout.xml
deleted file mode 100644
index a5b54e5c9..000000000
--- a/androidApp/src/main/res/drawable/ic_logout.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_mail.xml b/androidApp/src/main/res/drawable/ic_mail.xml
deleted file mode 100644
index 0a291858e..000000000
--- a/androidApp/src/main/res/drawable/ic_mail.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_mail_wrapper.xml b/androidApp/src/main/res/drawable/ic_mail_wrapper.xml
deleted file mode 100644
index 0c92f4ae4..000000000
--- a/androidApp/src/main/res/drawable/ic_mail_wrapper.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_notification_wrapper.xml b/androidApp/src/main/res/drawable/ic_notification_wrapper.xml
deleted file mode 100644
index ed08903a3..000000000
--- a/androidApp/src/main/res/drawable/ic_notification_wrapper.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_person_black_24dp.xml b/androidApp/src/main/res/drawable/ic_person_black_24dp.xml
deleted file mode 100644
index 55495d5a0..000000000
--- a/androidApp/src/main/res/drawable/ic_person_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_phone_24dp.xml b/androidApp/src/main/res/drawable/ic_phone_24dp.xml
deleted file mode 100644
index b9a9fdf2b..000000000
--- a/androidApp/src/main/res/drawable/ic_phone_24dp.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_phone_wrapper.xml b/androidApp/src/main/res/drawable/ic_phone_wrapper.xml
deleted file mode 100644
index a1c9b14e8..000000000
--- a/androidApp/src/main/res/drawable/ic_phone_wrapper.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_qrcode_scan.xml b/androidApp/src/main/res/drawable/ic_qrcode_scan.xml
deleted file mode 100644
index 5b0e12ba0..000000000
--- a/androidApp/src/main/res/drawable/ic_qrcode_scan.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_question_answer_black_24dp.xml b/androidApp/src/main/res/drawable/ic_question_answer_black_24dp.xml
deleted file mode 100644
index 4932df431..000000000
--- a/androidApp/src/main/res/drawable/ic_question_answer_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_refresh_black_24dp.xml b/androidApp/src/main/res/drawable/ic_refresh_black_24dp.xml
deleted file mode 100644
index 1f9072a36..000000000
--- a/androidApp/src/main/res/drawable/ic_refresh_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_report_problem_red_24px.xml b/androidApp/src/main/res/drawable/ic_report_problem_red_24px.xml
deleted file mode 100644
index fa6d437a1..000000000
--- a/androidApp/src/main/res/drawable/ic_report_problem_red_24px.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_search.png b/androidApp/src/main/res/drawable/ic_search.png
deleted file mode 100644
index bfa4d4f08..000000000
Binary files a/androidApp/src/main/res/drawable/ic_search.png and /dev/null differ
diff --git a/androidApp/src/main/res/drawable/ic_settings.xml b/androidApp/src/main/res/drawable/ic_settings.xml
deleted file mode 100644
index bfa34fea9..000000000
--- a/androidApp/src/main/res/drawable/ic_settings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_share.xml b/androidApp/src/main/res/drawable/ic_share.xml
deleted file mode 100644
index 57536fdfc..000000000
--- a/androidApp/src/main/res/drawable/ic_share.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_share_black_24dp.xml b/androidApp/src/main/res/drawable/ic_share_black_24dp.xml
deleted file mode 100644
index 338d95ad5..000000000
--- a/androidApp/src/main/res/drawable/ic_share_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_share_wrapper.xml b/androidApp/src/main/res/drawable/ic_share_wrapper.xml
deleted file mode 100644
index fda1d8c45..000000000
--- a/androidApp/src/main/res/drawable/ic_share_wrapper.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_success.xml b/androidApp/src/main/res/drawable/ic_success.xml
deleted file mode 100644
index fb51d988b..000000000
--- a/androidApp/src/main/res/drawable/ic_success.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_supervisor_account_black_24dp.xml b/androidApp/src/main/res/drawable/ic_supervisor_account_black_24dp.xml
deleted file mode 100644
index 800c58d45..000000000
--- a/androidApp/src/main/res/drawable/ic_supervisor_account_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/drawable/ic_visibility_24px.xml b/androidApp/src/main/res/drawable/ic_visibility_24px.xml
deleted file mode 100644
index 229fda028..000000000
--- a/androidApp/src/main/res/drawable/ic_visibility_24px.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_visibility_off_24px.xml b/androidApp/src/main/res/drawable/ic_visibility_off_24px.xml
deleted file mode 100644
index 8e840f753..000000000
--- a/androidApp/src/main/res/drawable/ic_visibility_off_24px.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_wrapped_lock_black_24dp.xml b/androidApp/src/main/res/drawable/ic_wrapped_lock_black_24dp.xml
deleted file mode 100644
index f0b49c99e..000000000
--- a/androidApp/src/main/res/drawable/ic_wrapped_lock_black_24dp.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_wrapped_person_black_24dp.xml b/androidApp/src/main/res/drawable/ic_wrapped_person_black_24dp.xml
deleted file mode 100644
index 0b1822d0a..000000000
--- a/androidApp/src/main/res/drawable/ic_wrapped_person_black_24dp.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable-hdpi/mifos_logo.png b/androidApp/src/main/res/drawable/mifos_logo.png
similarity index 100%
rename from androidApp/src/main/res/drawable-hdpi/mifos_logo.png
rename to androidApp/src/main/res/drawable/mifos_logo.png
diff --git a/androidApp/src/main/res/drawable/mifos_splash_screen_logo.png b/androidApp/src/main/res/drawable/mifos_splash_screen_logo.png
new file mode 100644
index 000000000..4a515337a
Binary files /dev/null and b/androidApp/src/main/res/drawable/mifos_splash_screen_logo.png differ
diff --git a/androidApp/src/main/res/drawable/notification_circle.xml b/androidApp/src/main/res/drawable/notification_circle.xml
deleted file mode 100644
index 4de8cdbd3..000000000
--- a/androidApp/src/main/res/drawable/notification_circle.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/rectangular_border.xml b/androidApp/src/main/res/drawable/rectangular_border.xml
deleted file mode 100644
index 92845c9b6..000000000
--- a/androidApp/src/main/res/drawable/rectangular_border.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/round_corner.xml b/androidApp/src/main/res/drawable/round_corner.xml
deleted file mode 100644
index 4e4da7946..000000000
--- a/androidApp/src/main/res/drawable/round_corner.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/scrim.xml b/androidApp/src/main/res/drawable/scrim.xml
deleted file mode 100644
index 4320acfb9..000000000
--- a/androidApp/src/main/res/drawable/scrim.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/splash_background.xml b/androidApp/src/main/res/drawable/splash_background.xml
deleted file mode 100644
index 2262620e4..000000000
--- a/androidApp/src/main/res/drawable/splash_background.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
- -
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/splash_icon.xml b/androidApp/src/main/res/drawable/splash_icon.xml
new file mode 100644
index 000000000..fb9902e0e
--- /dev/null
+++ b/androidApp/src/main/res/drawable/splash_icon.xml
@@ -0,0 +1,21 @@
+
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/triangular_green_view.xml b/androidApp/src/main/res/drawable/triangular_green_view.xml
deleted file mode 100644
index d6cd9b495..000000000
--- a/androidApp/src/main/res/drawable/triangular_green_view.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/triangular_red_view.xml b/androidApp/src/main/res/drawable/triangular_red_view.xml
deleted file mode 100644
index 66994c39a..000000000
--- a/androidApp/src/main/res/drawable/triangular_red_view.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/layout-land/fragment_beneficiary_add_options.xml b/androidApp/src/main/res/layout-land/fragment_beneficiary_add_options.xml
deleted file mode 100644
index 96b2fc91b..000000000
--- a/androidApp/src/main/res/layout-land/fragment_beneficiary_add_options.xml
+++ /dev/null
@@ -1,117 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/account_spinner_layout.xml b/androidApp/src/main/res/layout/account_spinner_layout.xml
deleted file mode 100644
index 2dfc79e1c..000000000
--- a/androidApp/src/main/res/layout/account_spinner_layout.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/action_bar_notification_layout.xml b/androidApp/src/main/res/layout/action_bar_notification_layout.xml
deleted file mode 100644
index c94345343..000000000
--- a/androidApp/src/main/res/layout/action_bar_notification_layout.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/layout/activity_accounts.xml b/androidApp/src/main/res/layout/activity_accounts.xml
deleted file mode 100644
index 1627e9856..000000000
--- a/androidApp/src/main/res/layout/activity_accounts.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/activity_client_list.xml b/androidApp/src/main/res/layout/activity_client_list.xml
deleted file mode 100644
index 2908c07e6..000000000
--- a/androidApp/src/main/res/layout/activity_client_list.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/layout/activity_container.xml b/androidApp/src/main/res/layout/activity_container.xml
deleted file mode 100644
index 5f6d1704a..000000000
--- a/androidApp/src/main/res/layout/activity_container.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/activity_edit_user_detail.xml b/androidApp/src/main/res/layout/activity_edit_user_detail.xml
deleted file mode 100644
index 757dd55ab..000000000
--- a/androidApp/src/main/res/layout/activity_edit_user_detail.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/activity_home.xml b/androidApp/src/main/res/layout/activity_home.xml
deleted file mode 100644
index f568fc257..000000000
--- a/androidApp/src/main/res/layout/activity_home.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/activity_loan_application.xml b/androidApp/src/main/res/layout/activity_loan_application.xml
deleted file mode 100644
index 5539c1cdb..000000000
--- a/androidApp/src/main/res/layout/activity_loan_application.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/activity_login.xml b/androidApp/src/main/res/layout/activity_login.xml
deleted file mode 100644
index 6d2c55418..000000000
--- a/androidApp/src/main/res/layout/activity_login.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/activity_notification.xml b/androidApp/src/main/res/layout/activity_notification.xml
deleted file mode 100644
index e59a89413..000000000
--- a/androidApp/src/main/res/layout/activity_notification.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/activity_privacy_policy.xml b/androidApp/src/main/res/layout/activity_privacy_policy.xml
deleted file mode 100644
index bc5d8731f..000000000
--- a/androidApp/src/main/res/layout/activity_privacy_policy.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/activity_registration.xml b/androidApp/src/main/res/layout/activity_registration.xml
deleted file mode 100644
index de6d8abd0..000000000
--- a/androidApp/src/main/res/layout/activity_registration.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/activity_savings_account_application.xml b/androidApp/src/main/res/layout/activity_savings_account_application.xml
deleted file mode 100644
index 64839f87c..000000000
--- a/androidApp/src/main/res/layout/activity_savings_account_application.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/activity_settings.xml b/androidApp/src/main/res/layout/activity_settings.xml
deleted file mode 100644
index 5dcdfe206..000000000
--- a/androidApp/src/main/res/layout/activity_settings.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/activity_user_profile.xml b/androidApp/src/main/res/layout/activity_user_profile.xml
deleted file mode 100644
index 990ea065e..000000000
--- a/androidApp/src/main/res/layout/activity_user_profile.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
diff --git a/androidApp/src/main/res/layout/beneficiary_spinner_layout.xml b/androidApp/src/main/res/layout/beneficiary_spinner_layout.xml
deleted file mode 100644
index 241b870b8..000000000
--- a/androidApp/src/main/res/layout/beneficiary_spinner_layout.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/cell_loan_repayment_schedule.xml b/androidApp/src/main/res/layout/cell_loan_repayment_schedule.xml
deleted file mode 100644
index eb5de87b3..000000000
--- a/androidApp/src/main/res/layout/cell_loan_repayment_schedule.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/charge_app_widget.xml b/androidApp/src/main/res/layout/charge_app_widget.xml
deleted file mode 100644
index 24c12e745..000000000
--- a/androidApp/src/main/res/layout/charge_app_widget.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/core_common_toolbar.xml b/androidApp/src/main/res/layout/core_common_toolbar.xml
deleted file mode 100644
index 3b73f3805..000000000
--- a/androidApp/src/main/res/layout/core_common_toolbar.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/layout/corner_view_loan_repayment_schedule.xml b/androidApp/src/main/res/layout/corner_view_loan_repayment_schedule.xml
deleted file mode 100644
index 5fe1f707b..000000000
--- a/androidApp/src/main/res/layout/corner_view_loan_repayment_schedule.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/error_layout.xml b/androidApp/src/main/res/layout/error_layout.xml
deleted file mode 100644
index 4353d8048..000000000
--- a/androidApp/src/main/res/layout/error_layout.xml
+++ /dev/null
@@ -1,178 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_account_overview.xml b/androidApp/src/main/res/layout/fragment_account_overview.xml
deleted file mode 100644
index 3ab384dfb..000000000
--- a/androidApp/src/main/res/layout/fragment_account_overview.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_accounts.xml b/androidApp/src/main/res/layout/fragment_accounts.xml
deleted file mode 100644
index a403e2b52..000000000
--- a/androidApp/src/main/res/layout/fragment_accounts.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_add_guarantor.xml b/androidApp/src/main/res/layout/fragment_add_guarantor.xml
deleted file mode 100644
index fa43cb62d..000000000
--- a/androidApp/src/main/res/layout/fragment_add_guarantor.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/layout/fragment_add_loan_application.xml b/androidApp/src/main/res/layout/fragment_add_loan_application.xml
deleted file mode 100644
index c776a06cd..000000000
--- a/androidApp/src/main/res/layout/fragment_add_loan_application.xml
+++ /dev/null
@@ -1,188 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_beneficiary_add_options.xml b/androidApp/src/main/res/layout/fragment_beneficiary_add_options.xml
deleted file mode 100644
index e6fcf86ff..000000000
--- a/androidApp/src/main/res/layout/fragment_beneficiary_add_options.xml
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_beneficiary_application.xml b/androidApp/src/main/res/layout/fragment_beneficiary_application.xml
deleted file mode 100644
index 0ac8be177..000000000
--- a/androidApp/src/main/res/layout/fragment_beneficiary_application.xml
+++ /dev/null
@@ -1,119 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_beneficiary_detail.xml b/androidApp/src/main/res/layout/fragment_beneficiary_detail.xml
deleted file mode 100644
index 4b40a0ee6..000000000
--- a/androidApp/src/main/res/layout/fragment_beneficiary_detail.xml
+++ /dev/null
@@ -1,141 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_beneficiary_list.xml b/androidApp/src/main/res/layout/fragment_beneficiary_list.xml
deleted file mode 100644
index 5efdfc0a0..000000000
--- a/androidApp/src/main/res/layout/fragment_beneficiary_list.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_client_accounts.xml b/androidApp/src/main/res/layout/fragment_client_accounts.xml
deleted file mode 100644
index ead92f8cf..000000000
--- a/androidApp/src/main/res/layout/fragment_client_accounts.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_client_charge.xml b/androidApp/src/main/res/layout/fragment_client_charge.xml
deleted file mode 100644
index f1502e593..000000000
--- a/androidApp/src/main/res/layout/fragment_client_charge.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_guarantor_detail.xml b/androidApp/src/main/res/layout/fragment_guarantor_detail.xml
deleted file mode 100644
index e1abd42b7..000000000
--- a/androidApp/src/main/res/layout/fragment_guarantor_detail.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_guarantor_list.xml b/androidApp/src/main/res/layout/fragment_guarantor_list.xml
deleted file mode 100644
index 5e97bdb78..000000000
--- a/androidApp/src/main/res/layout/fragment_guarantor_list.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_help.xml b/androidApp/src/main/res/layout/fragment_help.xml
deleted file mode 100644
index 7d2813347..000000000
--- a/androidApp/src/main/res/layout/fragment_help.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_home_old.xml b/androidApp/src/main/res/layout/fragment_home_old.xml
deleted file mode 100644
index 5cde0d1e9..000000000
--- a/androidApp/src/main/res/layout/fragment_home_old.xml
+++ /dev/null
@@ -1,415 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_home_ui.xml b/androidApp/src/main/res/layout/fragment_home_ui.xml
deleted file mode 100644
index 50002a55b..000000000
--- a/androidApp/src/main/res/layout/fragment_home_ui.xml
+++ /dev/null
@@ -1,572 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_loan_account_details.xml b/androidApp/src/main/res/layout/fragment_loan_account_details.xml
deleted file mode 100644
index 5a31d36e5..000000000
--- a/androidApp/src/main/res/layout/fragment_loan_account_details.xml
+++ /dev/null
@@ -1,363 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_loan_account_summary.xml b/androidApp/src/main/res/layout/fragment_loan_account_summary.xml
deleted file mode 100644
index 673cc24d7..000000000
--- a/androidApp/src/main/res/layout/fragment_loan_account_summary.xml
+++ /dev/null
@@ -1,467 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_loan_account_transactions.xml b/androidApp/src/main/res/layout/fragment_loan_account_transactions.xml
deleted file mode 100644
index 6d5d13094..000000000
--- a/androidApp/src/main/res/layout/fragment_loan_account_transactions.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_loan_withdraw.xml b/androidApp/src/main/res/layout/fragment_loan_withdraw.xml
deleted file mode 100644
index 93d4a8bee..000000000
--- a/androidApp/src/main/res/layout/fragment_loan_withdraw.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_locations.xml b/androidApp/src/main/res/layout/fragment_locations.xml
deleted file mode 100644
index efca737f0..000000000
--- a/androidApp/src/main/res/layout/fragment_locations.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_notification.xml b/androidApp/src/main/res/layout/fragment_notification.xml
deleted file mode 100644
index 28bbe853a..000000000
--- a/androidApp/src/main/res/layout/fragment_notification.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_recent_transactions.xml b/androidApp/src/main/res/layout/fragment_recent_transactions.xml
deleted file mode 100644
index ff4da72ca..000000000
--- a/androidApp/src/main/res/layout/fragment_recent_transactions.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_registration_verification.xml b/androidApp/src/main/res/layout/fragment_registration_verification.xml
deleted file mode 100644
index 3d414ffe8..000000000
--- a/androidApp/src/main/res/layout/fragment_registration_verification.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_review_loan_application.xml b/androidApp/src/main/res/layout/fragment_review_loan_application.xml
deleted file mode 100644
index 5e709e982..000000000
--- a/androidApp/src/main/res/layout/fragment_review_loan_application.xml
+++ /dev/null
@@ -1,171 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_saving_account_details.xml b/androidApp/src/main/res/layout/fragment_saving_account_details.xml
deleted file mode 100644
index 9e76469db..000000000
--- a/androidApp/src/main/res/layout/fragment_saving_account_details.xml
+++ /dev/null
@@ -1,414 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_saving_account_transactions.xml b/androidApp/src/main/res/layout/fragment_saving_account_transactions.xml
deleted file mode 100644
index 32aa7c778..000000000
--- a/androidApp/src/main/res/layout/fragment_saving_account_transactions.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_savings_account_application.xml b/androidApp/src/main/res/layout/fragment_savings_account_application.xml
deleted file mode 100644
index bcf012e5e..000000000
--- a/androidApp/src/main/res/layout/fragment_savings_account_application.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_savings_account_withdraw_fragment.xml b/androidApp/src/main/res/layout/fragment_savings_account_withdraw_fragment.xml
deleted file mode 100644
index 779031de9..000000000
--- a/androidApp/src/main/res/layout/fragment_savings_account_withdraw_fragment.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_savings_make_transfer.xml b/androidApp/src/main/res/layout/fragment_savings_make_transfer.xml
deleted file mode 100644
index d8f55c633..000000000
--- a/androidApp/src/main/res/layout/fragment_savings_make_transfer.xml
+++ /dev/null
@@ -1,293 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_third_party_transfer.xml b/androidApp/src/main/res/layout/fragment_third_party_transfer.xml
deleted file mode 100644
index 411f92920..000000000
--- a/androidApp/src/main/res/layout/fragment_third_party_transfer.xml
+++ /dev/null
@@ -1,344 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_transfer_process.xml b/androidApp/src/main/res/layout/fragment_transfer_process.xml
deleted file mode 100644
index 912e5f323..000000000
--- a/androidApp/src/main/res/layout/fragment_transfer_process.xml
+++ /dev/null
@@ -1,174 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/fragment_user_profile.xml b/androidApp/src/main/res/layout/fragment_user_profile.xml
deleted file mode 100644
index c4151b02b..000000000
--- a/androidApp/src/main/res/layout/fragment_user_profile.xml
+++ /dev/null
@@ -1,365 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/item_widget_client_charge.xml b/androidApp/src/main/res/layout/item_widget_client_charge.xml
deleted file mode 100644
index beaf66328..000000000
--- a/androidApp/src/main/res/layout/item_widget_client_charge.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/layout_error.xml b/androidApp/src/main/res/layout/layout_error.xml
deleted file mode 100644
index 02ef171f8..000000000
--- a/androidApp/src/main/res/layout/layout_error.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/layout_filter_dialog.xml b/androidApp/src/main/res/layout/layout_filter_dialog.xml
deleted file mode 100644
index 5adf0f886..000000000
--- a/androidApp/src/main/res/layout/layout_filter_dialog.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/nav_drawer_header.xml b/androidApp/src/main/res/layout/nav_drawer_header.xml
deleted file mode 100644
index ff081ec1f..000000000
--- a/androidApp/src/main/res/layout/nav_drawer_header.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/preference_configuration.xml b/androidApp/src/main/res/layout/preference_configuration.xml
deleted file mode 100644
index 31b696303..000000000
--- a/androidApp/src/main/res/layout/preference_configuration.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/row_beneficiary.xml b/androidApp/src/main/res/layout/row_beneficiary.xml
deleted file mode 100644
index 704cb37e0..000000000
--- a/androidApp/src/main/res/layout/row_beneficiary.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/row_checkbox.xml b/androidApp/src/main/res/layout/row_checkbox.xml
deleted file mode 100644
index 40fd4a5a5..000000000
--- a/androidApp/src/main/res/layout/row_checkbox.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
diff --git a/androidApp/src/main/res/layout/row_client_charge.xml b/androidApp/src/main/res/layout/row_client_charge.xml
deleted file mode 100644
index 04d9dd706..000000000
--- a/androidApp/src/main/res/layout/row_client_charge.xml
+++ /dev/null
@@ -1,142 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/row_client_name.xml b/androidApp/src/main/res/layout/row_client_name.xml
deleted file mode 100644
index 9beb2ad79..000000000
--- a/androidApp/src/main/res/layout/row_client_name.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/row_guarantor.xml b/androidApp/src/main/res/layout/row_guarantor.xml
deleted file mode 100644
index 1bc33b5ed..000000000
--- a/androidApp/src/main/res/layout/row_guarantor.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/row_header_loan_repayment_schedule.xml b/androidApp/src/main/res/layout/row_header_loan_repayment_schedule.xml
deleted file mode 100644
index cad5cb519..000000000
--- a/androidApp/src/main/res/layout/row_header_loan_repayment_schedule.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/layout/row_loan_account.xml b/androidApp/src/main/res/layout/row_loan_account.xml
deleted file mode 100644
index aafd574ce..000000000
--- a/androidApp/src/main/res/layout/row_loan_account.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/row_notification.xml b/androidApp/src/main/res/layout/row_notification.xml
deleted file mode 100644
index cdba14212..000000000
--- a/androidApp/src/main/res/layout/row_notification.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/row_recent_transaction.xml b/androidApp/src/main/res/layout/row_recent_transaction.xml
deleted file mode 100644
index 399e5620e..000000000
--- a/androidApp/src/main/res/layout/row_recent_transaction.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/row_saving_account.xml b/androidApp/src/main/res/layout/row_saving_account.xml
deleted file mode 100644
index 3e9f6ce85..000000000
--- a/androidApp/src/main/res/layout/row_saving_account.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/row_saving_account_transaction.xml b/androidApp/src/main/res/layout/row_saving_account_transaction.xml
deleted file mode 100644
index ad9370fad..000000000
--- a/androidApp/src/main/res/layout/row_saving_account_transaction.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/row_share_account.xml b/androidApp/src/main/res/layout/row_share_account.xml
deleted file mode 100644
index 0fb401bb9..000000000
--- a/androidApp/src/main/res/layout/row_share_account.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/layout/toolbar.xml b/androidApp/src/main/res/layout/toolbar.xml
deleted file mode 100644
index a2e1f5f2d..000000000
--- a/androidApp/src/main/res/layout/toolbar.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
diff --git a/androidApp/src/main/res/menu/edit_profile.xml b/androidApp/src/main/res/menu/edit_profile.xml
deleted file mode 100644
index 5c4dcd0ba..000000000
--- a/androidApp/src/main/res/menu/edit_profile.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/menu/menu_account.xml b/androidApp/src/main/res/menu/menu_account.xml
deleted file mode 100644
index 598be7372..000000000
--- a/androidApp/src/main/res/menu/menu_account.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/menu/menu_account_overview.xml b/androidApp/src/main/res/menu/menu_account_overview.xml
deleted file mode 100644
index ea9bb1977..000000000
--- a/androidApp/src/main/res/menu/menu_account_overview.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
diff --git a/androidApp/src/main/res/menu/menu_beneficiary.xml b/androidApp/src/main/res/menu/menu_beneficiary.xml
deleted file mode 100644
index 3e99a377f..000000000
--- a/androidApp/src/main/res/menu/menu_beneficiary.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/menu/menu_guarantor.xml b/androidApp/src/main/res/menu/menu_guarantor.xml
deleted file mode 100644
index d75db3b62..000000000
--- a/androidApp/src/main/res/menu/menu_guarantor.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/menu/menu_help.xml b/androidApp/src/main/res/menu/menu_help.xml
deleted file mode 100644
index 0798c8eef..000000000
--- a/androidApp/src/main/res/menu/menu_help.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/menu/menu_help_bottom_bar.xml b/androidApp/src/main/res/menu/menu_help_bottom_bar.xml
deleted file mode 100644
index f7bf0ce54..000000000
--- a/androidApp/src/main/res/menu/menu_help_bottom_bar.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/menu/menu_loan_details.xml b/androidApp/src/main/res/menu/menu_loan_details.xml
deleted file mode 100644
index 2de0462de..000000000
--- a/androidApp/src/main/res/menu/menu_loan_details.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/menu/menu_main.xml b/androidApp/src/main/res/menu/menu_main.xml
deleted file mode 100644
index ad0372d5d..000000000
--- a/androidApp/src/main/res/menu/menu_main.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/menu/menu_nav_drawer.xml b/androidApp/src/main/res/menu/menu_nav_drawer.xml
deleted file mode 100644
index 656120b96..000000000
--- a/androidApp/src/main/res/menu/menu_nav_drawer.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
diff --git a/androidApp/src/main/res/menu/menu_qr_code_display.xml b/androidApp/src/main/res/menu/menu_qr_code_display.xml
deleted file mode 100644
index 0d46b178b..000000000
--- a/androidApp/src/main/res/menu/menu_qr_code_display.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/menu/menu_saving_accounts_transaction.xml b/androidApp/src/main/res/menu/menu_saving_accounts_transaction.xml
deleted file mode 100644
index 910919d99..000000000
--- a/androidApp/src/main/res/menu/menu_saving_accounts_transaction.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/menu/menu_savings_account_detail.xml b/androidApp/src/main/res/menu/menu_savings_account_detail.xml
deleted file mode 100644
index b53db3dd6..000000000
--- a/androidApp/src/main/res/menu/menu_savings_account_detail.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/menu/menu_transfer.xml b/androidApp/src/main/res/menu/menu_transfer.xml
deleted file mode 100644
index ba4c26ad1..000000000
--- a/androidApp/src/main/res/menu/menu_transfer.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..b68a884e0
--- /dev/null
+++ b/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..b68a884e0
--- /dev/null
+++ b/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index cde69bccc..000000000
Binary files a/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/androidApp/src/main/res/mipmap-hdpi/ic_launcher.webp b/androidApp/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 000000000..2ab722b85
Binary files /dev/null and b/androidApp/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/androidApp/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/androidApp/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000..dbcdb7759
Binary files /dev/null and b/androidApp/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ
diff --git a/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..67e766a4e
Binary files /dev/null and b/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/androidApp/src/main/res/mipmap-hdpi/mifos_icon.png b/androidApp/src/main/res/mipmap-hdpi/mifos_icon.png
deleted file mode 100644
index fe9d645b0..000000000
Binary files a/androidApp/src/main/res/mipmap-hdpi/mifos_icon.png and /dev/null differ
diff --git a/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index c133a0cbd..000000000
Binary files a/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/androidApp/src/main/res/mipmap-mdpi/ic_launcher.webp b/androidApp/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 000000000..902978b80
Binary files /dev/null and b/androidApp/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/androidApp/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/androidApp/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000..6cedd733b
Binary files /dev/null and b/androidApp/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ
diff --git a/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..2a6d454d9
Binary files /dev/null and b/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/androidApp/src/main/res/mipmap-mdpi/mifos_icon.png b/androidApp/src/main/res/mipmap-mdpi/mifos_icon.png
deleted file mode 100644
index 74f40c8a6..000000000
Binary files a/androidApp/src/main/res/mipmap-mdpi/mifos_icon.png and /dev/null differ
diff --git a/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index bfa42f0e7..000000000
Binary files a/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.webp b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 000000000..488bfc88a
Binary files /dev/null and b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000..9f4fb0fd0
Binary files /dev/null and b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ
diff --git a/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..91d6f36d5
Binary files /dev/null and b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/androidApp/src/main/res/mipmap-xhdpi/mifos_icon.png b/androidApp/src/main/res/mipmap-xhdpi/mifos_icon.png
deleted file mode 100644
index a5f1fe2c5..000000000
Binary files a/androidApp/src/main/res/mipmap-xhdpi/mifos_icon.png and /dev/null differ
diff --git a/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 324e72cdd..000000000
Binary files a/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..6e2e93e94
Binary files /dev/null and b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000..fa30b40fe
Binary files /dev/null and b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ
diff --git a/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..797fa51e2
Binary files /dev/null and b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/androidApp/src/main/res/mipmap-xxhdpi/mifos_icon.png b/androidApp/src/main/res/mipmap-xxhdpi/mifos_icon.png
deleted file mode 100644
index e591b0182..000000000
Binary files a/androidApp/src/main/res/mipmap-xxhdpi/mifos_icon.png and /dev/null differ
diff --git a/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index aee44e138..000000000
Binary files a/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..9591870ed
Binary files /dev/null and b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000..0f923702c
Binary files /dev/null and b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ
diff --git a/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..1c6a1ddf9
Binary files /dev/null and b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/androidApp/src/main/res/mipmap-xxxhdpi/mifos_icon.png b/androidApp/src/main/res/mipmap-xxxhdpi/mifos_icon.png
deleted file mode 100644
index 83744a0b5..000000000
Binary files a/androidApp/src/main/res/mipmap-xxxhdpi/mifos_icon.png and /dev/null differ
diff --git a/androidApp/src/main/res/values-ar/strings.xml b/androidApp/src/main/res/values-ar/strings.xml
index 93860afdc..fc9dfe802 100644
--- a/androidApp/src/main/res/values-ar/strings.xml
+++ b/androidApp/src/main/res/values-ar/strings.xml
@@ -1,4 +1,13 @@
+
ميفوس موبايل
تسجيل الدخول
diff --git a/androidApp/src/main/res/values-bn/strings.xml b/androidApp/src/main/res/values-bn/strings.xml
index 267a97235..3b469b8fc 100644
--- a/androidApp/src/main/res/values-bn/strings.xml
+++ b/androidApp/src/main/res/values-bn/strings.xml
@@ -1,3 +1,13 @@
+
+
লগ ইন করুন
হ্যালো, %1$s।
diff --git a/androidApp/src/main/res/values-es/strings.xml b/androidApp/src/main/res/values-es/strings.xml
index 708639a7e..10af9cd67 100644
--- a/androidApp/src/main/res/values-es/strings.xml
+++ b/androidApp/src/main/res/values-es/strings.xml
@@ -1,3 +1,13 @@
+
+
Iniciar sesión
Hola, %1$s.
diff --git a/androidApp/src/main/res/values-fa-rAF/strings.xml b/androidApp/src/main/res/values-fa-rAF/strings.xml
index 857819144..ea2f3b877 100644
--- a/androidApp/src/main/res/values-fa-rAF/strings.xml
+++ b/androidApp/src/main/res/values-fa-rAF/strings.xml
@@ -1,4 +1,13 @@
+
Mifos Mobile
درباره ما
diff --git a/androidApp/src/main/res/values-fr/strings.xml b/androidApp/src/main/res/values-fr/strings.xml
index 212ee6be7..eb067a45d 100644
--- a/androidApp/src/main/res/values-fr/strings.xml
+++ b/androidApp/src/main/res/values-fr/strings.xml
@@ -1,3 +1,13 @@
+
+
Se connecter
Bonjour, %1$s.
diff --git a/androidApp/src/main/res/values-hi/strings.xml b/androidApp/src/main/res/values-hi/strings.xml
index 6df2e0a64..74c44b9c9 100644
--- a/androidApp/src/main/res/values-hi/strings.xml
+++ b/androidApp/src/main/res/values-hi/strings.xml
@@ -1,4 +1,13 @@
+
Mifos Mobile
लॉग इन करें
diff --git a/androidApp/src/main/res/values-in/strings.xml b/androidApp/src/main/res/values-in/strings.xml
index af697ed4f..95b39ad04 100644
--- a/androidApp/src/main/res/values-in/strings.xml
+++ b/androidApp/src/main/res/values-in/strings.xml
@@ -1,3 +1,13 @@
+
+
Masuk
Halo, %1$s.
diff --git a/androidApp/src/main/res/values-km/strings.xml b/androidApp/src/main/res/values-km/strings.xml
index 3bd44e730..839a28e33 100644
--- a/androidApp/src/main/res/values-km/strings.xml
+++ b/androidApp/src/main/res/values-km/strings.xml
@@ -1,3 +1,13 @@
+
+
ចូល
សួស្តី, %1$s ។
diff --git a/androidApp/src/main/res/values-kn/strings.xml b/androidApp/src/main/res/values-kn/strings.xml
index 57f2705a5..47c9e6fb7 100644
--- a/androidApp/src/main/res/values-kn/strings.xml
+++ b/androidApp/src/main/res/values-kn/strings.xml
@@ -1,3 +1,13 @@
+
+
ಲಾಗ್ ಇನ್ ಮಾಡಿ
ಹಲೋ, %1$s.
diff --git a/androidApp/src/main/res/values-my/strings.xml b/androidApp/src/main/res/values-my/strings.xml
index d9f033632..ac34907d1 100644
--- a/androidApp/src/main/res/values-my/strings.xml
+++ b/androidApp/src/main/res/values-my/strings.xml
@@ -1,4 +1,13 @@
+
Mifos မိုဘိုင်း
လော့ဂ်အင်
diff --git a/androidApp/src/main/res/values-pl/strings.xml b/androidApp/src/main/res/values-pl/strings.xml
index 2a798576a..d2dd4bbbb 100644
--- a/androidApp/src/main/res/values-pl/strings.xml
+++ b/androidApp/src/main/res/values-pl/strings.xml
@@ -1,3 +1,13 @@
+
+
Zaloguj Się
Witaj, %1$s.
diff --git a/androidApp/src/main/res/values-pt/strings.xml b/androidApp/src/main/res/values-pt/strings.xml
index e5d74b0f8..80ec682fd 100644
--- a/androidApp/src/main/res/values-pt/strings.xml
+++ b/androidApp/src/main/res/values-pt/strings.xml
@@ -1,3 +1,13 @@
+
+
Mifos Mobile
Entrar
diff --git a/androidApp/src/main/res/values-ru/strings.xml b/androidApp/src/main/res/values-ru/strings.xml
index d46a6f9d5..655eba2f7 100644
--- a/androidApp/src/main/res/values-ru/strings.xml
+++ b/androidApp/src/main/res/values-ru/strings.xml
@@ -1,3 +1,13 @@
+
+
Вход
Здравствуйте, %1$s.
diff --git a/androidApp/src/main/res/values-sw/strings.xml b/androidApp/src/main/res/values-sw/strings.xml
index f71df8311..075d11734 100644
--- a/androidApp/src/main/res/values-sw/strings.xml
+++ b/androidApp/src/main/res/values-sw/strings.xml
@@ -1,3 +1,13 @@
+
+
Ingia
Sawadi, %1$s.
diff --git a/androidApp/src/main/res/values-te/strings.xml b/androidApp/src/main/res/values-te/strings.xml
index fea90700b..979e447fc 100644
--- a/androidApp/src/main/res/values-te/strings.xml
+++ b/androidApp/src/main/res/values-te/strings.xml
@@ -1,3 +1,13 @@
+
+
Mifos Mobile
లాగిన్
diff --git a/androidApp/src/main/res/values-ur/strings.xml b/androidApp/src/main/res/values-ur/strings.xml
index f6db476fb..0ddd267dd 100644
--- a/androidApp/src/main/res/values-ur/strings.xml
+++ b/androidApp/src/main/res/values-ur/strings.xml
@@ -1,3 +1,13 @@
+
+
Mifos موبائل
لاگ ان کریں
diff --git a/androidApp/src/main/res/values/attr.xml b/androidApp/src/main/res/values/attr.xml
deleted file mode 100644
index 2b2e2a3f5..000000000
--- a/androidApp/src/main/res/values/attr.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/values/colors.xml b/androidApp/src/main/res/values/colors.xml
index 9648f048d..94394e4c5 100644
--- a/androidApp/src/main/res/values/colors.xml
+++ b/androidApp/src/main/res/values/colors.xml
@@ -1,4 +1,13 @@
+
#ffffffff
#000000
diff --git a/androidApp/src/main/res/values/dimens.xml b/androidApp/src/main/res/values/dimens.xml
deleted file mode 100644
index 009d60d82..000000000
--- a/androidApp/src/main/res/values/dimens.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-
-
-
-
- 32dp
- 28dp
- 72dp
- 0dp
- 32dp
- 70dp
- 56dp
- 65dp
- 60dp
- 40dp
- 0.1dp
-
-
-
- 0dp
- 32dp
- 70dp
- 56dp
- 65dp
- 60dp
- 0.2dp
- 40dp
- 4dp
-
-
-
- 4dp
- 8dp
- 16dp
- 24dp
- 30dp
- 48dp
- 16dp
- 16dp
- 16dp
- 30dp
- 30dp
- 56dp
- 12dp
- 24dp
- 15dp
- 5dp
- 10dp
-
-
-
- 20dp
- 30dp
- 20dp
- 50dp
- 50dp
- 20dp
- 8dp
- 36dp
- 4dp
- 16dp
- 32dp
- 10dp
- 5dp
- 24dp
-
-
- 24sp
- 20sp
- 16sp
- 14sp
- 10sp
-
-
- 1dp
-
-
- 0dp
-
-
- 30dp
-
-
-
- 25dp
- 25dp
-
-
- 55dp
- 55dp
-
-
- 16dp
- 16dp
-
- 8dp
- 8dp
-
- 15dp
- 24dp
-
- 3dp
- 5dp
- 160dp
- 225dp
-
- 50dp
- 1dp
- 14dp
- 40dp
- 55dp
- 14sp
- 1dp
-
- 100dp
- 4dp
-
- 85dp
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/values/ic_launcher_background.xml b/androidApp/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000..2c7f96d2a
--- /dev/null
+++ b/androidApp/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,13 @@
+
+
+
+ #F6F8F7
+
\ No newline at end of file
diff --git a/androidApp/src/main/res/values/ids.xml b/androidApp/src/main/res/values/ids.xml
deleted file mode 100644
index f76ac19fe..000000000
--- a/androidApp/src/main/res/values/ids.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/values/splash.xml b/androidApp/src/main/res/values/splash.xml
new file mode 100644
index 000000000..9ee17867e
--- /dev/null
+++ b/androidApp/src/main/res/values/splash.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/androidApp/src/main/res/values/strings.xml b/androidApp/src/main/res/values/strings.xml
index 0fbff8a0a..3b9f8633a 100644
--- a/androidApp/src/main/res/values/strings.xml
+++ b/androidApp/src/main/res/values/strings.xml
@@ -1,3 +1,13 @@
+
+
Mifos Mobile
Login
@@ -643,4 +653,5 @@
Required
S.No
No Transaction Found
+ ⚠️ You aren’t connected to the internet
diff --git a/androidApp/src/main/res/values/styles.xml b/androidApp/src/main/res/values/styles.xml
deleted file mode 100644
index cd1726160..000000000
--- a/androidApp/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
diff --git a/androidApp/src/main/res/values/styles_frame.xml b/androidApp/src/main/res/values/styles_frame.xml
deleted file mode 100644
index 4c2c04a80..000000000
--- a/androidApp/src/main/res/values/styles_frame.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/values/validations.xml b/androidApp/src/main/res/values/validations.xml
deleted file mode 100644
index 7214521ed..000000000
--- a/androidApp/src/main/res/values/validations.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- 5
- 6
- 500
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/xml/charge_app_widget_info.xml b/androidApp/src/main/res/xml/charge_app_widget_info.xml
deleted file mode 100644
index 76e03f936..000000000
--- a/androidApp/src/main/res/xml/charge_app_widget_info.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/xml/fileproviderpath.xml b/androidApp/src/main/res/xml/fileproviderpath.xml
index 1464dd90a..ddd276e16 100644
--- a/androidApp/src/main/res/xml/fileproviderpath.xml
+++ b/androidApp/src/main/res/xml/fileproviderpath.xml
@@ -1,4 +1,13 @@
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/androidApp/src/release/res/values/api_keys.xml b/androidApp/src/release/res/values/api_keys.xml
index eed606bee..c24c8aa0f 100644
--- a/androidApp/src/release/res/values/api_keys.xml
+++ b/androidApp/src/release/res/values/api_keys.xml
@@ -1,3 +1,13 @@
+
+
YOUR_KEY_HERE
diff --git a/androidApp/src/test/java/org/mifos/mobile/util/CheckUnSucess.kt b/androidApp/src/test/java/org/mifos/mobile/util/CheckUnSucess.kt
deleted file mode 100644
index f2c4d37da..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/util/CheckUnSucess.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.mifos.mobile.util
-
-import kotlinx.coroutines.flow.Flow
-import java.io.IOException
-
-suspend fun checkForUnsuccessfulOperation(result: Flow): Boolean {
- val resultList = mutableListOf()
- var errorOccurred = false
- try {
- result.collect { resultList.add(it) }
- } catch (e: RuntimeException) {
- errorOccurred = true
- // Handle the error (optional)
- }
- return errorOccurred
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/util/CoroutineTestRule.kt b/androidApp/src/test/java/org/mifos/mobile/util/CoroutineTestRule.kt
deleted file mode 100644
index 060e55d2d..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/util/CoroutineTestRule.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.*
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-
-@ExperimentalCoroutinesApi
-class CoroutineTestRule(
- private val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
-) : TestRule, TestCoroutineScope by TestCoroutineScope(testDispatcher) {
-
- override fun apply(base: Statement?, description: Description?): Statement {
- return object : Statement() {
- override fun evaluate() {
- Dispatchers.setMain(testDispatcher)
- base?.evaluate()
- cleanupTestCoroutines()
- Dispatchers.resetMain()
- }
- }
- }
-
- fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) {
- testDispatcher.runBlockingTest(block)
- }
-}
diff --git a/androidApp/src/test/java/org/mifos/mobile/util/RxSchedulersOverrideRule.kt b/androidApp/src/test/java/org/mifos/mobile/util/RxSchedulersOverrideRule.kt
deleted file mode 100644
index 6da25d5ca..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/util/RxSchedulersOverrideRule.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package org.mifos.mobile.util
-
-import io.reactivex.Scheduler
-import io.reactivex.android.plugins.RxAndroidPlugins
-import io.reactivex.functions.Function
-import io.reactivex.plugins.RxJavaPlugins
-import io.reactivex.schedulers.Schedulers
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-import java.util.concurrent.Callable
-
-/**
- * This rule registers SchedulerHooks for RxJava and RxAndroid to ensure that subscriptions
- * always subscribeOn and observeOn Schedulers.immediate().
- * Warning, this rule will reset RxAndroidPlugins and RxJavaPlugins before and after each test so
- * if the application code uses RxJava plugins this may affect the behaviour of the testing method.
- */
-class RxSchedulersOverrideRule : TestRule {
-
- private val SCHEDULER_INSTANCE = Schedulers.trampoline()
-
- private val schedulerFunction: Function =
- Function { SCHEDULER_INSTANCE }
-
- private val schedulerFunctionLazy: Function?, Scheduler> =
- Function?, Scheduler> { SCHEDULER_INSTANCE }
-
- override fun apply(base: Statement, description: Description): Statement {
- return object : Statement() {
- @Throws(Throwable::class)
- override fun evaluate() {
- RxAndroidPlugins.reset()
- RxAndroidPlugins.setInitMainThreadSchedulerHandler(schedulerFunctionLazy)
- RxJavaPlugins.reset()
- RxJavaPlugins.setIoSchedulerHandler(schedulerFunction)
- RxJavaPlugins.setNewThreadSchedulerHandler(schedulerFunction)
- RxJavaPlugins.setComputationSchedulerHandler(schedulerFunction)
- base.evaluate()
- RxAndroidPlugins.reset()
- RxJavaPlugins.reset()
- }
- }
- }
-}
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/AccountsViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/AccountsViewModelTest.kt
deleted file mode 100644
index 9e4bff741..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/AccountsViewModelTest.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertEquals
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.core.data.repository.AccountsRepository
-import org.mifos.mobile.core.data.repositoryImpl.HomeRepositoryImp
-import org.mifos.mobile.core.model.entity.client.ClientAccounts
-import org.mifos.mobile.feature.account.utils.AccountState
-import org.mifos.mobile.feature.account.viewmodel.AccountsViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class AccountsViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @Mock
- lateinit var accountsRepositoryImp: AccountsRepository
-
- @Mock
- lateinit var homeRepositoryImp: HomeRepositoryImp
-
- private lateinit var mockClientAccounts: ClientAccounts
- private lateinit var accountsViewModel: AccountsViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- accountsViewModel = org.mifos.mobile.feature.account.viewmodel.AccountsViewModel(
- accountsRepositoryImp,
- homeRepositoryImp
- )
- mockClientAccounts = Mockito.mock(ClientAccounts::class.java)
- }
-
- @Test
- fun loadClientAccounts_Success() = runTest {
- val mockClientAccounts = mock(ClientAccounts::class.java)
- `when`(homeRepositoryImp.clientAccounts()).thenReturn(flowOf(mockClientAccounts))
- accountsViewModel.accountsUiState.test {
- accountsViewModel.loadClientAccounts()
- assertEquals(AccountState.Loading, awaitItem())
- assertEquals(
- AccountState.ShowSavingsAccounts(mockClientAccounts.savingsAccounts), awaitItem()
- )
- assertEquals(
- AccountState.ShowLoanAccounts(mockClientAccounts.loanAccounts), awaitItem()
- )
- assertEquals(
- AccountState.ShowShareAccounts(mockClientAccounts.shareAccounts), awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test
- fun loadAccountsUiState_Success() = runTest {
- val mockAccountType = "savingsAccounts"
- val mockClientAccounts = mock(ClientAccounts::class.java)
- `when`(accountsRepositoryImp.loadAccounts(mockAccountType)).thenReturn(
- flowOf(
- mockClientAccounts
- )
- )
- accountsViewModel.accountsUiState.test {
- accountsViewModel.loadAccounts(mockAccountType)
- assertEquals(AccountState.Loading, awaitItem())
- assertEquals(
- AccountState.ShowSavingsAccounts(mockClientAccounts.savingsAccounts), awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = RuntimeException::class)
- fun loadAccounts_Error() = runTest {
- val mockAccountType = "savings"
- `when`(accountsRepositoryImp.loadAccounts(anyString())).thenThrow(RuntimeException())
- accountsViewModel.accountsUiState.test {
- try {
- accountsViewModel.loadAccounts(mockAccountType)
- assertEquals(AccountState.Loading, awaitItem())
- } catch (e: RuntimeException) {
- assertEquals(AccountState.Error, awaitItem())
- }
- cancelAndIgnoreRemainingEvents()
- }
- }
-
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/AddGuarantorViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/AddGuarantorViewModelTest.kt
deleted file mode 100644
index 005d16b58..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/AddGuarantorViewModelTest.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import androidx.lifecycle.Observer
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import okhttp3.ResponseBody
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.models.guarantor.GuarantorApplicationPayload
-import org.mifos.mobile.models.guarantor.GuarantorTemplatePayload
-import org.mifos.mobile.repositories.GuarantorRepositoryImp
-import org.mifos.mobile.core.model.enums.GuarantorState
-import org.mifos.mobile.ui.guarantor.guarantor_add.AddGuarantorViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.GuarantorUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class AddGuarantorViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- private lateinit var guarantorRepositoryImp: GuarantorRepositoryImp
-
- @Mock
- lateinit var guarantorUiStateObserver: Observer
-
- private lateinit var viewModel: AddGuarantorViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = AddGuarantorViewModel(guarantorRepositoryImp)
- }
- fun TestScope.obserrveUiState(): MutableList {
- val uiStates = mutableListOf()
- viewModel.guarantorUiState.onEach {
- println(it)
- uiStates.add(it)
- }.launchIn(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
- return uiStates
- }
- @Test
- fun testGetGuarantorTemplate_Successful() = runTest {
- val response = mock(GuarantorTemplatePayload::class.java)
-
- `when`(guarantorRepositoryImp.getGuarantorTemplate(123L)).thenReturn(
- flowOf(response)
- )
- viewModel.guarantorUiState.test {
- viewModel.getGuarantorTemplate(org.mifos.mobile.core.model.enums.GuarantorState.UPDATE, 123L)
- assertEquals(GuarantorUiState.Loading, awaitItem())
- assertEquals(
- GuarantorUiState.ShowGuarantorUpdation(
- response
- ),
- awaitItem()
- )
- viewModel.getGuarantorTemplate(org.mifos.mobile.core.model.enums.GuarantorState.CREATE, 123L)
- assertEquals(GuarantorUiState.Loading, awaitItem())
- assertEquals(
- GuarantorUiState.ShowGuarantorApplication(
- response
- ),
- awaitItem()
- )
- }
- }
-
- @Test(expected = Exception::class)
- fun testGetGuarantorTemplate_Unsuccessful() = runTest {
- `when`(guarantorRepositoryImp.getGuarantorTemplate(123L))
- .thenThrow( Exception("Error occurred"))
- viewModel.guarantorUiState.test {
- try {
- viewModel.getGuarantorTemplate(org.mifos.mobile.core.model.enums.GuarantorState.UPDATE, 123L)
- assertEquals(GuarantorUiState.Loading, awaitItem())
- } catch (e: Exception) {
- assertEquals(
- GuarantorUiState.ShowError(Throwable().message),
- awaitItem()
- )
- }
- }
- }
-
- @Test
- fun testCreateGuarantor_Successful() = runTest {
- val payload = mock(GuarantorApplicationPayload::class.java)
- val response = mock(ResponseBody::class.java)
- `when`(guarantorRepositoryImp.createGuarantor(123L, payload)).thenReturn(
- flowOf(response)
- )
- viewModel.guarantorUiState.test {
- viewModel.createGuarantor(123L, payload)
- assertEquals(GuarantorUiState.Loading,awaitItem())
- assertEquals(
- GuarantorUiState.SubmittedSuccessfully(
- response.string(),
- payload
- ),
- awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testCreateGuarantor_Unsuccessful() = runTest {
- val payload = mock(GuarantorApplicationPayload::class.java)
- `when`(guarantorRepositoryImp.createGuarantor(123L, payload))
- .thenThrow( Exception("Error occurred"))
- viewModel.guarantorUiState.test {
- viewModel.createGuarantor(123L, payload)
- assertEquals(GuarantorUiState.Loading, awaitItem())
- assertEquals(GuarantorUiState.ShowError(Throwable().message), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test
- fun testUpdateGuarantor_Successful() = runTest{
- val payload = mock(GuarantorApplicationPayload::class.java)
- val response = mock(ResponseBody::class.java)
- `when`(
- guarantorRepositoryImp.updateGuarantor(
- payload,
- 11L,
- 22L
- )
- ).thenReturn(flowOf(response))
- viewModel.guarantorUiState.test {
- viewModel.updateGuarantor(payload, 11L, 22L)
- assertEquals(GuarantorUiState.Loading, awaitItem())
- assertEquals(
- GuarantorUiState.GuarantorUpdatedSuccessfully(
- response.string()
- ),
- awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testUpdateGuarantor_Unsuccessful() = runTest {
- val payload = mock(GuarantorApplicationPayload::class.java)
- `when`(guarantorRepositoryImp.updateGuarantor(payload, 11L, 22L))
- .thenThrow( Exception("Error occurred"))
- viewModel.guarantorUiState.test {
- viewModel.updateGuarantor(payload, 11L, 22L)
- assertEquals(GuarantorUiState.Loading, awaitItem())
- assertEquals(GuarantorUiState.ShowError(Throwable().message), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/BeneficiaryApplicationViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/BeneficiaryApplicationViewModelTest.kt
deleted file mode 100644
index a2000e708..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/BeneficiaryApplicationViewModelTest.kt
+++ /dev/null
@@ -1,206 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import android.view.View
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import androidx.lifecycle.Observer
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import okhttp3.ResponseBody
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.models.beneficiary.BeneficiaryPayload
-import org.mifos.mobile.models.beneficiary.BeneficiaryUpdatePayload
-import org.mifos.mobile.models.templates.beneficiary.BeneficiaryTemplate
-import org.mifos.mobile.repositories.BeneficiaryRepositoryImp
-import org.mifos.mobile.ui.beneficiary_application.BeneficiaryApplicationViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.BeneficiaryUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class BeneficiaryApplicationViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var beneficiaryRepositoryImp: BeneficiaryRepositoryImp
-
- @Mock
- lateinit var beneficiaryUiStateObserver: Observer
-
- private lateinit var viewModel: BeneficiaryApplicationViewModel
-
- // Test coroutine dispatcher and scope
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = BeneficiaryApplicationViewModel(beneficiaryRepositoryImp)
- }
-
- @Test
- fun testLoadBeneficiaryTemplate_Successful() = runTest {
- val response = mock(BeneficiaryTemplate::class.java)
- `when`(beneficiaryRepositoryImp.beneficiaryTemplate()).thenReturn(flowOf(response))
-
- viewModel.beneficiaryUiState.test {
- viewModel.loadBeneficiaryTemplate()
- assertEquals(BeneficiaryUiState.Initial, awaitItem())
- assertEquals(BeneficiaryUiState.Loading, awaitItem())
- assertEquals(
- BeneficiaryUiState.ShowBeneficiaryTemplate(response),
- awaitItem()
- )
- assertEquals(
- BeneficiaryUiState.SetVisibility(View.VISIBLE),
- awaitItem()
- )
- verifyNoMoreInteractions(beneficiaryUiStateObserver)
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- private fun TestScope.obserrveUiState(): MutableList {
- val uiStates = mutableListOf()
- viewModel.beneficiaryUiState.onEach {
- println(it)
- uiStates.add(it)
- }
- .launchIn(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
- return uiStates
- }
-
- @Test(expected = Exception::class)
- fun testLoadBeneficiaryTemplate_Unsuccessful() = runTest {
- val exception = Exception("Test exception")
- `when`(beneficiaryRepositoryImp.beneficiaryTemplate()).thenThrow(exception as Throwable)
-
- viewModel.beneficiaryUiState.test {
- viewModel.loadBeneficiaryTemplate()
- assertEquals(BeneficiaryUiState.Initial, awaitItem())
- assertEquals(BeneficiaryUiState.Loading, awaitItem())
- assertEquals(
- BeneficiaryUiState.ShowError(R.string.error_fetching_beneficiary_template),
- awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test
- fun testCreateBeneficiary_Successful() = runTest {
- val beneficiaryPayload = mock(BeneficiaryPayload::class.java)
- `when`(beneficiaryRepositoryImp.createBeneficiary(beneficiaryPayload)).thenReturn(
- flowOf(
- ResponseBody.create(null, "success")
- )
- )
-
- viewModel.beneficiaryUiState.test {
- viewModel.createBeneficiary(beneficiaryPayload)
- assertEquals(BeneficiaryUiState.Initial, awaitItem())
- assertEquals(BeneficiaryUiState.Loading, awaitItem())
- assertEquals(BeneficiaryUiState.CreatedSuccessfully, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testCreateBeneficiary_Unsuccessful() = runTest {
- val beneficiaryPayload = mock(BeneficiaryPayload::class.java)
- `when`(beneficiaryRepositoryImp.createBeneficiary(beneficiaryPayload))
- .thenThrow( Exception("Error Response"))
-
- viewModel.beneficiaryUiState.test {
- viewModel.createBeneficiary(beneficiaryPayload)
- assertEquals(BeneficiaryUiState.Initial, awaitItem())
- assertEquals(BeneficiaryUiState.Loading, awaitItem())
- assertEquals(
- BeneficiaryUiState.ShowError(R.string.error_creating_beneficiary),
- awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
-
- @Test
- fun testUpdateBeneficiary_Successful() = runTest {
- val response = mock(ResponseBody::class.java)
- val beneficiaryUpdatePayload = mock(BeneficiaryUpdatePayload::class.java)
- `when`(
- beneficiaryRepositoryImp.updateBeneficiary(
- 123L,
- beneficiaryUpdatePayload
- )
- ).thenReturn(
- flowOf(response)
- )
-
- viewModel.beneficiaryUiState.test {
-
- viewModel.updateBeneficiary(123L, beneficiaryUpdatePayload)
-
- assertEquals(BeneficiaryUiState.Initial, awaitItem())
- assertEquals(BeneficiaryUiState.Loading, awaitItem())
- assertEquals(BeneficiaryUiState.UpdatedSuccessfully, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testUpdateBeneficiary_Unsuccessful() = runTest {
- val beneficiaryUpdatePayload = mock(BeneficiaryUpdatePayload::class.java)
- `when`(
- beneficiaryRepositoryImp.updateBeneficiary(
- 123L,
- beneficiaryUpdatePayload
- )
- ).thenThrow( Exception("Error updating beneficiary") as Throwable)
-
- viewModel.beneficiaryUiState.test {
- viewModel.updateBeneficiary(123L, beneficiaryUpdatePayload)
- assertEquals(BeneficiaryUiState.Initial, awaitItem())
- assertEquals(BeneficiaryUiState.Loading, awaitItem())
- assertEquals(
- BeneficiaryUiState.ShowError(R.string.error_updating_beneficiary),
- awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @After
- fun tearDown() {
-// viewModel.beneficiaryUiState.removeObserver(beneficiaryUiStateObserver)
- }
-
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/BeneficiaryDetailViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/BeneficiaryDetailViewModelTest.kt
deleted file mode 100644
index f17e9f595..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/BeneficiaryDetailViewModelTest.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.test.resetMain
-import kotlinx.coroutines.test.setMain
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import okhttp3.ResponseBody
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.repositories.BeneficiaryRepositoryImp
-import org.mifos.mobile.ui.beneficiary_detail.BeneficiaryDetailViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.BeneficiaryUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-class BeneficiaryDetailViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @Mock
- lateinit var beneficiaryRepositoryImp: BeneficiaryRepositoryImp
-
- private lateinit var viewModel: BeneficiaryDetailViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = BeneficiaryDetailViewModel(beneficiaryRepositoryImp)
- }
- @Test
- fun testDeleteBeneficiary_Successful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val response = mock(ResponseBody::class.java)
- `when`(beneficiaryRepositoryImp.deleteBeneficiary(123L))
- .thenReturn(flowOf(response))
- viewModel.beneficiaryUiState.test {
- viewModel.deleteBeneficiary(123L)
- assertEquals(BeneficiaryUiState.Initial, awaitItem())
- assertEquals(BeneficiaryUiState.DeletedSuccessfully, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
- @Test(expected = Exception::class)
- fun testDeleteBeneficiary_Unsuccessful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- `when`(beneficiaryRepositoryImp.deleteBeneficiary(123L))
- .thenThrow(Exception("Error deleting beneficiary"))
- viewModel.beneficiaryUiState.test {
- viewModel.deleteBeneficiary(123L)
- assertEquals(BeneficiaryUiState.Loading, awaitItem())
- assertEquals(
- BeneficiaryUiState.ShowError(R.string.error_deleting_beneficiary),
- awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
- @After
- fun tearDown() {
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/BeneficiaryListViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/BeneficiaryListViewModelTest.kt
deleted file mode 100644
index 1238e3a23..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/BeneficiaryListViewModelTest.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import androidx.lifecycle.Observer
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.advanceUntilIdle
-import kotlinx.coroutines.test.runTest
-import okhttp3.ResponseBody
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.models.beneficiary.Beneficiary
-import org.mifos.mobile.repositories.BeneficiaryRepositoryImp
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.BeneficiaryUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-import retrofit2.Response
-import kotlin.time.ExperimentalTime
-
-@OptIn(ExperimentalTime::class)
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class BeneficiaryListViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var beneficiaryRepositoryImp: BeneficiaryRepositoryImp
-
-
- private lateinit var viewModel: BeneficiaryListViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = BeneficiaryListViewModel(beneficiaryRepositoryImp)
- }
-
-
-
- @Test
- fun testLoadBeneficiaries_Successful() = runTest {
- val list1 = mock(Beneficiary::class.java)
- val list2 = mock(Beneficiary::class.java)
- val list = listOf(list1, list2)
-
- `when`(beneficiaryRepositoryImp.beneficiaryList()).thenReturn(flowOf(list))
- viewModel.beneficiaryUiState.test {
- viewModel.loadBeneficiaries()
- assertEquals(BeneficiaryUiState.Initial, awaitItem())
- assertEquals(BeneficiaryUiState.Loading, awaitItem())
- assertEquals(BeneficiaryUiState.ShowBeneficiaryList(list), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testLoadBeneficiaries_Unsuccessful() = runTest {
- `when`(beneficiaryRepositoryImp.beneficiaryList()).thenThrow(Exception("Error occurred"))
- viewModel.beneficiaryUiState.test {
- viewModel.loadBeneficiaries()
- assertEquals(BeneficiaryUiState.Loading, awaitItem())
- assertEquals(BeneficiaryUiState.ShowError(R.string.beneficiaries), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @After
- fun tearDown() {
-
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/ClientChargeViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/ClientChargeViewModelTest.kt
deleted file mode 100644
index 8b9464357..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/ClientChargeViewModelTest.kt
+++ /dev/null
@@ -1,227 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import androidx.lifecycle.Observer
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.models.Charge
-import org.mifos.mobile.models.Page
-import org.mifos.mobile.repositories.ClientChargeRepositoryImp
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.ClientChargeUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class ClientChargeViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- private lateinit var clientChargeRepositoryImp: ClientChargeRepositoryImp
-
- @Mock
- private lateinit var clientChargeUiStateObserver: Observer
-
- private lateinit var viewModel: org.mifos.mobile.feature.charge.viewmodel.ClientChargeViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = org.mifos.mobile.feature.charge.viewmodel.ClientChargeViewModel(
- clientChargeRepositoryImp
- )
- }
-
- @Test
- fun testLoadClientCharges_Successful() = runTest {
- val charge1 = mock(Charge::class.java)
- val charge2 = mock(Charge::class.java)
- val mockChargePage = Page(1, listOf(charge1, charge2))
-
- `when`(clientChargeRepositoryImp.getClientCharges(123L)).thenReturn(
- flowOf(mockChargePage)
- )
-
- viewModel.clientChargeUiState.test {
- viewModel.loadClientCharges(123L)
- assertEquals(ClientChargeUiState.Initial, awaitItem())
- assertEquals(ClientChargeUiState.Loading,awaitItem())
- assertEquals(
- ClientChargeUiState.ShowClientCharges(
- mockChargePage.pageItems
- ), awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Throwable::class)
- fun testLoadClientCharges_Unsuccessful() = runTest {
- val errorMessageResId = R.string.client_charges
- val throwable = Throwable("Error occurred")
-
- `when`(clientChargeRepositoryImp.getClientCharges(123L))
- .thenThrow(throwable)
-
- viewModel.clientChargeUiState.test {
- viewModel.loadClientCharges(123L)
- assertEquals(ClientChargeUiState.Loading, awaitItem())
- assertEquals(
- ClientChargeUiState.ShowError(
- errorMessageResId
- ), awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test
- fun testLoadLoanAccountCharges_Successful() = runTest {
- val charge1 = mock(Charge::class.java)
- val charge2 = mock(Charge::class.java)
- val list = listOf(charge1, charge2)
-
- `when`(clientChargeRepositoryImp.getLoanCharges(123L))
- .thenReturn(flowOf(list))
-
- viewModel.clientChargeUiState.test {
- viewModel.loadLoanAccountCharges(123L)
- assertEquals(ClientChargeUiState.Initial, awaitItem())
- assertEquals(ClientChargeUiState.Loading, awaitItem())
- assertEquals(
- ClientChargeUiState.ShowClientCharges(
- list
- ), awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
- @Test(expected = Throwable::class)
- fun testLoadLoanAccountCharges_Unsuccessful() = runTest {
- val errorMessageResId = R.string.client_charges
- val throwable = Throwable("Error occurred")
-
- `when`(clientChargeRepositoryImp.getLoanCharges(123L))
- .thenThrow(throwable)
- viewModel.clientChargeUiState.test {
- viewModel.loadLoanAccountCharges(123L)
-
- assertEquals(ClientChargeUiState.Loading, awaitItem())
- assertEquals(
- ClientChargeUiState.ShowError(
- errorMessageResId
- ), awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test
- fun testLoadSavingsAccountCharges_Successful() = runTest {
- val charge1 = mock(Charge::class.java)
- val charge2 = mock(Charge::class.java)
- val list = listOf(charge1, charge2)
-
- `when`(clientChargeRepositoryImp.getSavingsCharges(123L))
- .thenReturn(flowOf(list))
-
- viewModel.clientChargeUiState.test {
- viewModel.loadSavingsAccountCharges(123L)
- assertEquals(ClientChargeUiState.Initial, awaitItem())
- assertEquals(ClientChargeUiState.Loading, awaitItem())
- assertEquals(
- ClientChargeUiState.ShowClientCharges(
- list
- ), awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Throwable::class)
- fun testLoadSavingsAccountCharges_Unsuccessful() = runTest {
- val errorMessageResId = R.string.client_charges
- val throwable = Throwable("Error occurred")
-
- `when`(clientChargeRepositoryImp.getSavingsCharges(123L))
- .thenThrow(throwable)
-
- viewModel.clientChargeUiState.test {
- viewModel.loadSavingsAccountCharges(123L)
- assertEquals(ClientChargeUiState.Loading, awaitItem())
- assertEquals(
- ClientChargeUiState.ShowError(
- errorMessageResId
- ), awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test
- fun loadClientLocalCharges_Successful() = runTest {
- val charge1 = mock(Charge::class.java)
- val charge2 = mock(Charge::class.java)
- val mockChargePage = Page(1, listOf(charge1, charge2))
-
- `when`(clientChargeRepositoryImp.clientLocalCharges()).thenReturn(
- flowOf(mockChargePage)
- )
-
- viewModel.clientChargeUiState.test {
- viewModel.loadClientLocalCharges()
- assertEquals(ClientChargeUiState.Initial, awaitItem())
- assertEquals(ClientChargeUiState.Loading, awaitItem())
- assertEquals(
- ClientChargeUiState.ShowClientCharges(
- mockChargePage.pageItems
- ), awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
- @Test(expected = Exception::class)
- fun loadClientLocalCharges_Unsuccessful() = runTest {
- val errorMessageResId = R.string.client_charges
- val throwable = Throwable("Error occurred")
- `when`(clientChargeRepositoryImp.clientLocalCharges()).thenThrow(throwable)
-
- viewModel.clientChargeUiState.test {
- viewModel.loadClientLocalCharges()
- assertEquals(ClientChargeUiState.Loading, awaitItem())
- assertEquals(
- ClientChargeUiState.ShowError(
- errorMessageResId
- ), awaitItem()
- )
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @After
- fun tearDown() {
- }
-
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/GuarantorDetailViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/GuarantorDetailViewModelTest.kt
deleted file mode 100644
index 735265254..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/GuarantorDetailViewModelTest.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.Assert.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import okhttp3.ResponseBody
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.repositories.GuarantorRepositoryImp
-import org.mifos.mobile.ui.guarantor.guarantor_details.GuarantorDetailViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.GuarantorUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class GuarantorDetailViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- private lateinit var guarantorRepositoryImp: GuarantorRepositoryImp
-
-
- lateinit var viewModel: GuarantorDetailViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = GuarantorDetailViewModel(guarantorRepositoryImp)
- }
-
- @Test
- fun testDeleteGuarantor_Successful() = runTest {
- val response = mock(ResponseBody::class.java)
-
- `when`(guarantorRepositoryImp.deleteGuarantor(1L, 2L)).thenReturn(flowOf(response))
- viewModel.guarantorDeleteState.test {
- viewModel.deleteGuarantor(1L, 2L)
- assertEquals(GuarantorUiState.Loading, awaitItem())
- assertEquals(GuarantorUiState.GuarantorDeletedSuccessfully(response.string()), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testDeleteGuarantor_Unsuccessful() = runTest {
- val error = Exception("Error")
- `when`(guarantorRepositoryImp.deleteGuarantor(1L, 2L))
- .thenThrow(error)
- viewModel.guarantorDeleteState.test {
- viewModel.deleteGuarantor(1L, 2L)
- assertEquals(GuarantorUiState.Loading, awaitItem())
- assertEquals(GuarantorUiState.ShowError(error.message), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/GuarantorListViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/GuarantorListViewModelTest.kt
deleted file mode 100644
index 977f20cbb..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/GuarantorListViewModelTest.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.models.guarantor.GuarantorPayload
-import org.mifos.mobile.repositories.GuarantorRepositoryImp
-import org.mifos.mobile.ui.guarantor.guarantor_list.GuarantorListViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.GuarantorUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class GuarantorListViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- private lateinit var guarantorRepositoryImp: GuarantorRepositoryImp
-
- private lateinit var viewModel: GuarantorListViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = GuarantorListViewModel(guarantorRepositoryImp)
- }
-
- @Test
- fun testGetGuarantorList_Successful() = runTest {
- val list1 = mock(GuarantorPayload::class.java)
- val list2 = mock(GuarantorPayload::class.java)
- val list = listOf(list1, list2)
-
- `when`(guarantorRepositoryImp.getGuarantorList(1L)).thenReturn(flowOf(list))
- viewModel.guarantorUiState.test {
- viewModel.getGuarantorList(1L)
- assertEquals(GuarantorUiState.Loading, awaitItem())
- assertEquals(GuarantorUiState.ShowGuarantorListSuccessfully(list), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testGetGuarantorList_Unsuccessful() = runTest {
- val error = Exception("Error")
- `when`(guarantorRepositoryImp.getGuarantorList(1L)).thenThrow(error)
- viewModel.guarantorUiState.test {
- viewModel.getGuarantorList(1L)
- assertEquals(GuarantorUiState.Loading, awaitItem())
- assertEquals(GuarantorUiState.ShowError(error.message), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/HelpViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/HelpViewModelTest.kt
deleted file mode 100644
index b91231e5d..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/HelpViewModelTest.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.Assert.assertEquals
-import kotlinx.coroutines.test.advanceUntilIdle
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.models.FAQ
-import org.mifos.mobile.utils.HelpUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-class HelpViewModelTest {
-
- @get:Rule
- val instantTaskExecutorRule = InstantTaskExecutorRule()
-
- @Mock
- private lateinit var mockFAQArrayList: ArrayList
-
- private lateinit var viewModel: HelpViewModel
-
- @Before
- fun setUp() {
- viewModel = HelpViewModel()
- }
-
- @Test
- fun testLoadFaq() = runTest {
- val qs = arrayOf("Question1", "Question2")
- val ans = arrayOf("Answer1", "Answer2")
-
- viewModel.loadFaq(qs, ans)
-
- viewModel.helpUiState.test{
- assertEquals(HelpUiState.ShowFaq(arrayListOf(FAQ("Question1", "Answer1", false), FAQ("Question2", "Answer2", false)))
- , awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test
- fun testFilterList() = runTest {
- val query = "app"
- val mockFAQ1 =FAQ("How to use the app?", "Answer1", false)
- val mockFAQ2 = FAQ("Is there a user guide available?", "Answer2", false)
- viewModel.loadFaq(arrayOf("How to use the app?", "Is there a user guide available?"), arrayOf("Answer1", "Answer2"))
- advanceUntilIdle()
- val filteredList = viewModel.filterList(query)
- viewModel.helpUiState.test{
- assertEquals(HelpUiState.ShowFaq(arrayListOf(mockFAQ1)), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-}
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/HomeViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/HomeViewModelTest.kt
deleted file mode 100644
index d6f02a214..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/HomeViewModelTest.kt
+++ /dev/null
@@ -1,149 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertTrue
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.api.local.PreferencesHelper
-import org.mifos.mobile.models.accounts.loan.LoanAccount
-import org.mifos.mobile.models.accounts.savings.SavingAccount
-import org.mifos.mobile.models.client.Client
-import org.mifos.mobile.models.client.ClientAccounts
-import org.mifos.mobile.repositories.HomeRepositoryImp
-import org.mifos.mobile.feature.home.viewmodels.HomeState
-import org.mifos.mobile.feature.home.viewmodels.HomeUiState
-import org.mifos.mobile.feature.home.viewmodels.HomeViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.mock
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class HomeViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var homeRepositoryImp: HomeRepositoryImp
-
- @Mock
- private lateinit var mockPreferencesHelper: PreferencesHelper
-
-
- private lateinit var viewModel: org.mifos.mobile.feature.home.viewmodels.HomeViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = org.mifos.mobile.feature.home.viewmodels.HomeViewModel(homeRepositoryImp)
- viewModel.preferencesHelper = mockPreferencesHelper
- }
-
- @Test
- fun testLoadingClientAccountDetails_Success(): Unit = runTest{
- val mockLoanAccounts = listOf(
- mock(LoanAccount::class.java).apply {
- `when`(loanBalance).thenReturn(100.0)
- }
- )
- val mockSavingsAccounts = listOf(
- mock(SavingAccount::class.java).apply {
- `when`(accountBalance).thenReturn(100.0)
- }
- )
- val expectedLoanBalance = 100.0
- val expectedSavingBalance = 100.0
- val mockClient = ClientAccounts(mockLoanAccounts, mockSavingsAccounts)
-
- `when`(homeRepositoryImp.clientAccounts()).thenReturn(flowOf(mockClient))
-
- viewModel.loadClientAccountDetails()
-
- assertEquals(viewModel.homeUiState.value,
- org.mifos.mobile.feature.home.viewmodels.HomeUiState.Success(
- org.mifos.mobile.feature.home.viewmodels.HomeState(
- loanAmount = expectedLoanBalance,
- savingsAmount = expectedSavingBalance
- )
- )
- )
-
- }
-
- @Test(expected = Exception::class)
- fun testLoadingClientAccountDetails_Error(): Unit = runTest{
- val errorMessageResId = R.string.error_fetching_accounts
-
- `when`(homeRepositoryImp.clientAccounts()).thenThrow( Exception())
-
- viewModel.loadClientAccountDetails()
-
- assertTrue(viewModel.homeUiState.value is org.mifos.mobile.feature.home.viewmodels.HomeUiState.Error)
- assertEquals(errorMessageResId, (viewModel.homeUiState.value as org.mifos.mobile.feature.home.viewmodels.HomeUiState.Error).errorMessage)
- }
-
- @Test
- fun testLoadingUserDetails_Success(): Unit = runTest{
- val mockClient = mock(Client::class.java)
-
- `when`(homeRepositoryImp.currentClient()).thenReturn(flowOf(mockClient))
-
- viewModel.getUserDetails()
- assertTrue(viewModel.homeUiState.value is org.mifos.mobile.feature.home.viewmodels.HomeUiState.Success)
- assertEquals(mockClient.officeName,
- (viewModel.homeUiState.value as org.mifos.mobile.feature.home.viewmodels.HomeUiState.Success).homeState.username)
- }
-
- @Test(expected = Exception::class)
- fun testLoadingUserDetails_Error(): Unit = runTest{
- val errorMessageResId = R.string.error_fetching_client
-
- `when`(homeRepositoryImp.currentClient()).thenThrow( Exception())
-
- viewModel.getUserDetails()
- assertTrue(viewModel.homeUiState.value is org.mifos.mobile.feature.home.viewmodels.HomeUiState.Error)
- assertEquals(errorMessageResId, org.mifos.mobile.feature.home.viewmodels.HomeUiState.Error(R.string.error_fetching_client))
-
- }
-
- @Test
- fun testLoadingUnreadNotificationsCount_Success(): Unit = runTest{
- val mockUnreadCount = 5
-
- `when`(homeRepositoryImp.unreadNotificationsCount()).thenReturn(flowOf(mockUnreadCount))
-
- viewModel.unreadNotificationsCount
-
- assertEquals(mockUnreadCount,viewModel.notificationsCount.value)
-
- }
-
- @Test(expected = Exception::class)
- fun testLoadingUnreadNotificationsCount_Error(): Unit = runTest{
- `when`(homeRepositoryImp.unreadNotificationsCount()).
- thenThrow( Exception("Error message "))
-
- viewModel.unreadNotificationsCount
-
- assertEquals(0, viewModel.notificationsCount.value)
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanAccountTransactionViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanAccountTransactionViewModelTest.kt
deleted file mode 100644
index 385a9296c..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanAccountTransactionViewModelTest.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.resetMain
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.models.Transaction
-import org.mifos.mobile.models.accounts.loan.LoanWithAssociations
-import org.mifos.mobile.repositories.LoanRepositoryImp
-import org.mifos.mobile.ui.loan_account_transaction.LoanAccountTransactionViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.core.common.Constants
-import org.mifos.mobile.utils.LoanUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-class LoanAccountTransactionViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @Mock
- lateinit var loanRepositoryImp: LoanRepositoryImp
-
-
- private lateinit var viewModel: LoanAccountTransactionViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = LoanAccountTransactionViewModel(loanRepositoryImp)
- }
-
- @Test
- fun testLoadLoanAccountDetails_Successful_WithEmptyTransaction() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val response = mock(LoanWithAssociations::class.java)
- `when`(
- loanRepositoryImp.getLoanWithAssociations(
- org.mifos.mobile.core.common.Constants.TRANSACTIONS,
- 1
- )
- ).thenReturn(flowOf(response))
- viewModel.loanUiState.test {
- viewModel.loadLoanAccountDetails(1)
- assertEquals(LoanUiState.Loading,awaitItem())
- assertEquals(LoanUiState.ShowEmpty(response),awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @Test
- fun testLoadLoanAccountDetails_Successful_WithNonEmptyTransaction() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val response = mock(LoanWithAssociations::class.java).apply {
- `when`(transactions).thenReturn(mutableListOf(mock(Transaction::class.java)))
- }
-
- `when`(
- loanRepositoryImp.getLoanWithAssociations(
- org.mifos.mobile.core.common.Constants.TRANSACTIONS,
- 1
- )
- ).thenReturn(flowOf(response))
- viewModel.loanUiState.test {
- viewModel.loadLoanAccountDetails(1)
- assertEquals(LoanUiState.Loading,awaitItem())
- assertEquals(LoanUiState.ShowLoan(response),awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @Test(expected = Exception::class)
- fun testLoadLoanAccountDetails_Unsuccessful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- `when`(
- loanRepositoryImp.getLoanWithAssociations(
- org.mifos.mobile.core.common.Constants.TRANSACTIONS,
- 1
- )
- ).thenThrow(Exception("Error occurred"))
- viewModel.loanUiState.test {
- viewModel.loadLoanAccountDetails(1)
- assertEquals(LoanUiState.Loading,awaitItem())
- assertEquals(LoanUiState.ShowError(R.string.loan_account_details),awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @After
- fun tearDown() {
- }
-
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanAccountWithdrawViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanAccountWithdrawViewModelTest.kt
deleted file mode 100644
index 717745595..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanAccountWithdrawViewModelTest.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.resetMain
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
-import okhttp3.ResponseBody
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.models.accounts.loan.LoanWithdraw
-import org.mifos.mobile.repositories.LoanRepositoryImp
-import org.mifos.mobile.ui.loan_account_withdraw.LoanAccountWithdrawViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.LoanUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-class LoanAccountWithdrawViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @Mock
- lateinit var loanRepositoryImp: LoanRepositoryImp
-
- private lateinit var viewModel: LoanAccountWithdrawViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = LoanAccountWithdrawViewModel(loanRepositoryImp)
-
- }
-
- @Test
- fun testWithdrawLoanAccount_Successful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val response = mock(ResponseBody::class.java)
- val mockLoanWithdraw = mock(LoanWithdraw::class.java)
- `when`(
- loanRepositoryImp.withdrawLoanAccount(
- 1,
- mockLoanWithdraw
- )
- ).thenReturn(flowOf(response))
- viewModel.loanUiState.test {
- viewModel.withdrawLoanAccount(1, mockLoanWithdraw)
- assertEquals( LoanUiState.Loading,awaitItem())
- assertEquals( LoanUiState.WithdrawSuccess,awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @Test(expected = Exception::class)
- fun testWithdrawLoanAccount_Unsuccessful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val mockLoanWithdraw = mock(LoanWithdraw::class.java)
- `when`(
- loanRepositoryImp.withdrawLoanAccount(
- 1,
- mockLoanWithdraw
- )
- ).thenThrow(Exception("Error occurred"))
- viewModel.loanUiState.test {
- viewModel.withdrawLoanAccount(1, mockLoanWithdraw)
- assertEquals( LoanUiState.Loading,awaitItem())
- assertEquals( LoanUiState.ShowError(R.string.error_loan_account_withdraw),awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @After
- fun tearDown() {
-
- }
-
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanAccountsDetailViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanAccountsDetailViewModelTest.kt
deleted file mode 100644
index c865fdd15..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanAccountsDetailViewModelTest.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.advanceUntilIdle
-import kotlinx.coroutines.test.resetMain
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.models.accounts.loan.LoanWithAssociations
-import org.mifos.mobile.repositories.LoanRepositoryImp
-import org.mifos.mobile.ui.loan_account.LoanAccountDetailUiState
-import org.mifos.mobile.ui.loan_account.LoanAccountsDetailViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.core.common.Constants
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-
-@RunWith(MockitoJUnitRunner::class)
-class LoanAccountsDetailViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @Mock
- lateinit var loanRepositoryImp: LoanRepositoryImp
-
- lateinit var viewModel: LoanAccountsDetailViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = LoanAccountsDetailViewModel(loanRepositoryImp)
-
- }
-
- @Test
- fun testLoadLoanAccountDetails_Successful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val response = mock(LoanWithAssociations::class.java)
-
- `when`(
- loanRepositoryImp.getLoanWithAssociations(
- org.mifos.mobile.core.common.Constants.REPAYMENT_SCHEDULE,
- 1
- )
- ).thenReturn(flowOf(response))
- viewModel.loadLoanAccountDetails(1)
- advanceUntilIdle()
- println(viewModel.loanUiState.value)
- assertEquals(viewModel.loanUiState.value, LoanAccountDetailUiState.Success(response))
- Dispatchers.resetMain()
- }
-
- @Test(expected = Exception::class)
- fun testLoadLoanAccountDetails_Unsuccessful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- `when`(
- loanRepositoryImp.getLoanWithAssociations(
- org.mifos.mobile.core.common.Constants.REPAYMENT_SCHEDULE,
- 1
- )
- ).thenThrow(Exception("Error occurred"))
- viewModel.loadLoanAccountDetails(1)
- advanceUntilIdle()
- assertEquals(viewModel.loanUiState.value,
- LoanAccountDetailUiState.Error)
- Dispatchers.resetMain()
- }
-
- @After
- fun tearDown() {
-
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanApplicationViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanApplicationViewModelTest.kt
deleted file mode 100644
index cf9a1ceb3..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanApplicationViewModelTest.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.resetMain
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.models.templates.loans.LoanTemplate
-import org.mifos.mobile.repositories.LoanRepositoryImp
-import org.mifos.mobile.core.model.enums.LoanState
-import org.mifos.mobile.ui.loan_account_application.LoanApplicationViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.LoanUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-class LoanApplicationViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @Mock
- lateinit var loanRepositoryImp: LoanRepositoryImp
-
- private lateinit var viewModel: LoanApplicationViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = LoanApplicationViewModel(loanRepositoryImp)
-
- }
-
- @Test
- fun testLoadLoanApplicationTemplate_Successful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val response = mock(LoanTemplate::class.java)
- val mockLoanState = mock(org.mifos.mobile.core.model.enums.LoanState::class.java)
- `when`(loanRepositoryImp.template()).thenReturn(flowOf(response))
- viewModel.loanUiState.test {
- viewModel.loadLoanApplicationTemplate(mockLoanState)
- assertEquals(LoanUiState.Loading, awaitItem())
- if (mockLoanState == org.mifos.mobile.core.model.enums.LoanState.CREATE) {
- assertEquals(LoanUiState.ShowLoanTemplateByProduct(response), awaitItem())
- } else {
- assertEquals(LoanUiState.ShowUpdateLoanTemplateByProduct(response), awaitItem())
- }
- }
- Dispatchers.resetMain()
- }
-
- @Test(expected = Exception::class)
- fun testLoadLoanApplicationTemplate_Unsuccessful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val loanState = mock(org.mifos.mobile.core.model.enums.LoanState::class.java)
- `when`(loanRepositoryImp.template())
- .thenThrow(Exception("Error occurred"))
- viewModel.loanUiState.test {
- viewModel.loadLoanApplicationTemplate(loanState)
- assertEquals(LoanUiState.Loading, awaitItem())
- assertEquals(LoanUiState.ShowError(R.string.error_fetching_template), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @Test
- fun loadLoanApplicationTemplateByProduct_Successful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val response = mock(LoanTemplate::class.java)
- val mockLoanState = mock(org.mifos.mobile.core.model.enums.LoanState::class.java)
-
- `when`(loanRepositoryImp.getLoanTemplateByProduct(1)).thenReturn(flowOf(response))
- viewModel.loanUiState.test {
- viewModel.loadLoanApplicationTemplateByProduct(1, mockLoanState)
- assertEquals(LoanUiState.Loading, awaitItem())
- if (mockLoanState == org.mifos.mobile.core.model.enums.LoanState.CREATE) {
- assertEquals(LoanUiState.ShowLoanTemplate(response), awaitItem())
- } else {
- assertEquals(LoanUiState.ShowUpdateLoanTemplate(response), awaitItem())
- }
- }
- Dispatchers.resetMain()
-
- }
-
- @Test(expected = Exception::class)
- fun loadLoanApplicationTemplateByProduct_Unsuccessful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val mockLoanState = mock(org.mifos.mobile.core.model.enums.LoanState::class.java)
- `when`(loanRepositoryImp.getLoanTemplateByProduct(1))
- .thenThrow(Exception("Error occurred"))
- viewModel.loanUiState.test {
- viewModel.loadLoanApplicationTemplateByProduct(1, mockLoanState)
- assertEquals(LoanUiState.Loading, awaitItem())
- assertEquals(LoanUiState.ShowError(R.string.error_fetching_template), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @After
- fun tearDown() {
-
- }
-
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanRepaymentScheduleViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanRepaymentScheduleViewModelTest.kt
deleted file mode 100644
index f057be0a8..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoanRepaymentScheduleViewModelTest.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.resetMain
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.models.accounts.loan.LoanWithAssociations
-import org.mifos.mobile.repositories.LoanRepositoryImp
-import org.mifos.mobile.ui.loan_repayment_schedule.LoanRepaymentScheduleViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.core.common.Constants
-import org.mifos.mobile.utils.LoanUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-class LoanRepaymentScheduleViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @Mock
- lateinit var loanRepositoryImp: LoanRepositoryImp
-
- private lateinit var viewModel: LoanRepaymentScheduleViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = LoanRepaymentScheduleViewModel(loanRepositoryImp)
-
- }
-
- @Test
- fun testLoanLoanWithAssociations_Successful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val response = mock(LoanWithAssociations::class.java)
- `when`(
- loanRepositoryImp.getLoanWithAssociations(
- org.mifos.mobile.core.common.Constants.REPAYMENT_SCHEDULE,
- 1
- )
- ).thenReturn(flowOf(response))
- viewModel.loanUiState.test {
- viewModel.loanLoanWithAssociations(1)
- assertEquals(LoanUiState.Loading, awaitItem())
- if (response.repaymentSchedule?.periods?.isNotEmpty() == true) {
- assertEquals(LoanUiState.ShowLoan(response), awaitItem())
- } else {
- assertEquals(LoanUiState.ShowEmpty(response), awaitItem())
- }
- }
- Dispatchers.resetMain()
- }
-
- @Test(expected = Exception::class)
- fun testLoanLoanWithAssociations_Unsuccessful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- `when`(
- loanRepositoryImp.getLoanWithAssociations(
- org.mifos.mobile.core.common.Constants.REPAYMENT_SCHEDULE,
- 1
- )
- ).thenThrow(Exception("Error occurred"))
- viewModel.loanUiState.test {
- viewModel.loanLoanWithAssociations(1)
- assertEquals(LoanUiState.Loading, awaitItem())
- assertEquals(LoanUiState.ShowError(R.string.repayment_schedule), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @After
- fun tearDown() {
-
- }
-
-
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoginViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/LoginViewModelTest.kt
deleted file mode 100644
index 1648ffe74..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/LoginViewModelTest.kt
+++ /dev/null
@@ -1,206 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.resetMain
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
-import org.junit.*
-import org.junit.runner.RunWith
-import org.mifos.mobile.core.data.repository.ClientRepository
-import org.mifos.mobile.core.data.repository.UserAuthRepository
-import org.mifos.mobile.core.model.entity.Page
-import org.mifos.mobile.core.model.entity.User
-import org.mifos.mobile.core.model.entity.client.Client
-import org.mifos.mobile.feature.login.utils.LoginUiState
-import org.mifos.mobile.feature.login.viewmodel.LoginViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.FakeRemoteDataSource
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-class LoginViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @Mock
- lateinit var userAuthRepositoryImp: UserAuthRepository
-
- @Mock
- lateinit var clientRepositoryImp: ClientRepository
-
- private lateinit var mockUser: User
- private lateinit var loginViewModel: LoginViewModel
- private var emptyClientPage: Page? = null
- private var clientPage: Page? = null
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- loginViewModel = LoginViewModel(
- userAuthRepositoryImp,
- clientRepositoryImp
- )
- mockUser = FakeRemoteDataSource.user
- emptyClientPage = FakeRemoteDataSource.noClients
- clientPage = FakeRemoteDataSource.clients
- }
-
- @Test
- fun testIsFieldEmpty_WithNonEmptyStringInput_ReturnsFalse() {
- val result = loginViewModel.isFieldEmpty("nonEmptyTestString")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testIsFieldEmpty_WithEmptyStringInput_ReturnsTrue() {
- val result = loginViewModel.isFieldEmpty("")
- Assert.assertTrue(result)
- }
-
- @Test
- fun testIsUsernameLengthInadequate_WithAdequateLengthInput_ReturnsFalse() {
- val result = loginViewModel.isUsernameLengthInadequate("username123")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testIsUsernameLengthInadequate_WithInadequateLengthInput_ReturnsTrue() {
- val result = loginViewModel.isUsernameLengthInadequate("user")
- Assert.assertTrue(result)
- }
-
- @Test
- fun testIsPasswordLengthInadequate_WithAdequateLengthInput_ReturnsFalse() {
- val result = loginViewModel.isUsernameLengthInadequate("password123")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testIsPasswordLengthInadequate_WithInadequateLengthInput_ReturnsTrue() {
- val result = loginViewModel.isUsernameLengthInadequate("pass")
- Assert.assertTrue(result)
- }
-
- @Test
- fun testUsernameHasSpaces_WithSpacesInput_ReturnsTrue() {
- val result = loginViewModel.usernameHasSpaces("username withSpace")
- Assert.assertTrue(result)
- }
-
- @Test
- fun testUsernameHasSpaces_WithNoSpacesInput_ReturnsFalse() {
- val result = loginViewModel.usernameHasSpaces("usernameNoSpaces")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testLogin_SuccessfulLoginReceivedFromRepository_ReturnsLoginSuccess() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- Mockito.`when`(
- userAuthRepositoryImp.login(Mockito.anyString(), Mockito.anyString())
- ).thenReturn(flowOf(mockUser))
- loginViewModel.loginUiState.test {
- loginViewModel.login("username", "password")
- Assert.assertEquals(LoginUiState.Initial, awaitItem())
- Assert.assertEquals(LoginUiState.LoginSuccess, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @Test(expected = Exception::class)
- fun testLogin_UnsuccessfulLoginReceivedFromRepository_ReturnsError() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- Mockito.`when`(
- userAuthRepositoryImp.login(Mockito.anyString(), Mockito.anyString())
- ).thenThrow(Exception("Error occurred"))
- loginViewModel.loginUiState.test {
- loginViewModel.login("username", "password")
- Assert.assertEquals(LoginUiState.Initial, awaitItem())
- Assert.assertEquals(LoginUiState.Error, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @Test(expected = Exception::class)
- fun testLoadClient_UnsuccessfulLoadClientReceivedFromRepository_ReturnsError() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- Mockito.`when`(
- clientRepositoryImp.loadClient()
- ).thenThrow(Exception("Error occurred"))
- loginViewModel.loginUiState.test {
- loginViewModel.loadClient()
- Assert.assertEquals(LoginUiState.Initial, awaitItem())
- Assert.assertEquals(LoginUiState.Loading, awaitItem())
- Assert.assertEquals(LoginUiState.Error, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Mockito.verify(clientRepositoryImp).clearPrefHelper()
- Mockito.verify(clientRepositoryImp).reInitializeService()
- Dispatchers.resetMain()
- }
-
- @Test(expected = Exception::class)
- fun testLoadClient_EmptyClientPageReceivedFromRepository_ReturnsError() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- Mockito.`when`(
- clientRepositoryImp.loadClient()
- ).thenThrow(Exception("Error occurred"))
- loginViewModel.loginUiState.test {
- loginViewModel.loadClient()
- Assert.assertEquals(LoginUiState.Initial, awaitItem())
- Assert.assertEquals(LoginUiState.Loading, awaitItem())
- Assert.assertEquals(LoginUiState.Error, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @Test
- fun testLoadClient_NonEmptyClientPageReceivedFromRepository_ReturnsLoadClientSuccess() =
- runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val clientId = clientPage?.pageItems?.get(0)?.id?.toLong()
- val clientName = clientPage?.pageItems?.get(0)?.displayName
- val client = mock(Client::class.java).apply {
- Mockito.`when`(id).thenReturn(clientId?.toInt())
- Mockito.`when`(displayName).thenReturn(clientName)
- }
- val clientPage1 = Page().apply {
- pageItems = listOf(client)
- }
- Mockito.`when`(
- clientRepositoryImp.loadClient()
- ).thenReturn(
- flow {
- emit(clientPage1 as Page)
- }
- )
- loginViewModel.loginUiState.test {
- loginViewModel.loadClient()
- Assert.assertEquals(LoginUiState.Initial, awaitItem())
- Assert.assertEquals(LoginUiState.LoadClientSuccess(clientName), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @After
- fun tearDown() {
-
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/NotificationViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/NotificationViewModelTest.kt
deleted file mode 100644
index 47d29daa3..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/NotificationViewModelTest.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.After
-import org.junit.Assert.*
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.models.notification.MifosNotification
-import org.mifos.mobile.repositories.NotificationRepository
-import org.mifos.mobile.ui.notification.NotificationViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.NotificationUiState
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class NotificationViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var notificationRepositoryImp: NotificationRepository
-
- private lateinit var notificationViewModel: NotificationViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- notificationViewModel = NotificationViewModel(notificationRepositoryImp)
- }
-
- @Test
- fun testLoadNotifications_NotificationsSuccessfullyReceivedFromRepository_ReturnsNotificationsSuccessfully() =
- runTest {
- val dummyNotifications = listOf(MifosNotification(), MifosNotification())
- `when`(notificationRepositoryImp.loadNotifications()).thenReturn(
- flowOf(
- dummyNotifications
- )
- )
- notificationViewModel.notificationUiState.test {
- notificationViewModel.loadNotifications()
- assertEquals(NotificationUiState.Initial, awaitItem())
- assertEquals(NotificationUiState.Loading, awaitItem())
- assertEquals(NotificationUiState.LoadNotificationsSuccessful(dummyNotifications), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
-}
-
- @Test(expected = Exception::class)
- fun testLoadNotifications_NotificationsNotReceivedFromRepository_ReturnsError() = runTest {
- `when`(notificationRepositoryImp.loadNotifications()).thenThrow(Exception("Dummy error"))
- notificationViewModel.notificationUiState.test {
- notificationViewModel.loadNotifications()
- assertEquals(NotificationUiState.Initial, awaitItem())
- assertEquals(NotificationUiState.Loading, awaitItem())
- assertEquals(NotificationUiState.Error, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @After
- fun tearDown() {
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/RecentTransactionViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/RecentTransactionViewModelTest.kt
deleted file mode 100644
index 7e4f65dc5..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/RecentTransactionViewModelTest.kt
+++ /dev/null
@@ -1,165 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.*
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.core.data.repository.RecentTransactionRepository
-import org.mifos.mobile.core.model.entity.Currency
-import org.mifos.mobile.core.model.entity.Page
-import org.mifos.mobile.core.model.entity.Transaction
-import org.mifos.mobile.core.model.entity.accounts.loan.calendardata.Type
-import org.mifos.mobile.feature.transaction.utils.RecentTransactionState
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-class RecentTransactionViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @OptIn(ExperimentalCoroutinesApi::class)
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var recentTransactionRepositoryImp: RecentTransactionRepository
-
-
- @Mock
- lateinit var type: Type
-
- @Mock
- lateinit var currency: Currency
-
- lateinit var viewModel: org.mifos.mobile.feature.transaction.viewmodel.RecentTransactionViewModel
-
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel =
- org.mifos.mobile.feature.transaction.viewmodel.RecentTransactionViewModel(
- recentTransactionRepositoryImp
- )
- }
-
- @Test
- fun loadRecentTransaction_success_with_no_empty_transactions() = runTest {
- val offset = 0
- val limit = 50
-
- val transaction = Transaction(
- id = 1L,
- officeId = 2L,
- officeName = "Office",
- type = type,
- date = listOf(2023, 7, 8),
- currency = currency,
- amount = 10.0,
- submittedOnDate = listOf(2023, 7, 9),
- reversed = false
- )
- val transactions: Page =
- Page(totalFilteredRecords = 1, pageItems = listOf(transaction))
- `when`(recentTransactionRepositoryImp.recentTransactions(offset, limit))
- .thenReturn(flowOf(transactions))
- viewModel.recentTransactionUiState.test {
- viewModel.loadRecentTransactions(loadMore = false, offset)
- assertEquals(RecentTransactionState.Initial, awaitItem())
- assertEquals(RecentTransactionState.Loading, awaitItem())
- assertEquals(transactions.pageItems.let { RecentTransactionState.RecentTransactions(it) }, awaitItem())
- }
- }
-
- @Test
- fun loadRecentTransaction_success_with_empty_transactions() = runTest {
- val offset = 0
- val limit = 50
-
- val transaction = Transaction(
- id = 1L,
- officeId = 2L,
- officeName = "Office",
- type = type,
- date = listOf(2023, 7, 8),
- currency = currency,
- amount = 10.0,
- submittedOnDate = listOf(2023, 7, 9),
- reversed = false
- )
- val transactions: Page =
- Page(totalFilteredRecords = 0, pageItems = listOf(transaction))
- `when`(recentTransactionRepositoryImp.recentTransactions(offset, limit))
- .thenReturn(flowOf(transactions))
- viewModel.recentTransactionUiState.test {
- viewModel.loadRecentTransactions(loadMore = false, offset)
- assertEquals(RecentTransactionState.Initial, awaitItem())
- assertEquals(RecentTransactionState.Loading, awaitItem())
- assertEquals(RecentTransactionState.EmptyTransaction, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test
- fun loadRecentTransaction_success_with_load_more_transactions() = runTest {
- val offset = 0
- val limit = 50
-
- val transaction = Transaction(
- id = 1L,
- officeId = 2L,
- officeName = "Office",
- type = type,
- date = listOf(2023, 7, 8),
- currency = currency,
- amount = 10.0,
- submittedOnDate = listOf(2023, 7, 9),
- reversed = false
- )
- val transactions: Page =
- Page(totalFilteredRecords = 1, pageItems = listOf(transaction))
- `when`(recentTransactionRepositoryImp.recentTransactions(offset, limit))
- .thenReturn(flowOf(transactions))
-
- viewModel.recentTransactionUiState.test {
- viewModel.loadRecentTransactions(loadMore = false, offset)
- assertEquals(RecentTransactionState.Initial, awaitItem())
- assertEquals(RecentTransactionState.Loading, awaitItem())
- assertEquals(transactions.pageItems.let { RecentTransactionState.RecentTransactions(it) }, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun loadRecentTransaction_unsuccessful() = runTest {
- `when`(recentTransactionRepositoryImp.recentTransactions(anyInt(), anyInt()))
- .thenThrow(Exception("Error occurred"))
- viewModel.recentTransactionUiState.test {
- viewModel.loadRecentTransactions(false, 0)
- assertEquals(RecentTransactionState.Initial, awaitItem())
- assertEquals(RecentTransactionState.Loading, awaitItem())
- assertEquals(RecentTransactionState.Error(R.string.recent_transactions), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @After
- fun tearDown() {
- }
-
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/RegistrationViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/RegistrationViewModelTest.kt
deleted file mode 100644
index 64095ec95..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/RegistrationViewModelTest.kt
+++ /dev/null
@@ -1,216 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import okhttp3.ResponseBody
-import org.junit.*
-import org.junit.runner.RunWith
-import org.mifos.mobile.core.data.repository.UserAuthRepository
-import org.mifos.mobile.feature.auth.registration.viewmodel.RegistrationViewModel
-import org.mifos.mobile.feature.registration.utils.RegistrationUiState
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-class RegistrationViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @OptIn(ExperimentalCoroutinesApi::class)
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @Mock
- lateinit var userAuthRepositoryImp: UserAuthRepository
-
- private lateinit var registrationViewModel: RegistrationViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- registrationViewModel =
- RegistrationViewModel(
- userAuthRepositoryImp
- )
-
- }
-
- @Test
- fun testIsInputFieldBlank_WithNonEmptyStringInput_ReturnsFalse() {
- val result = registrationViewModel.isInputFieldBlank("nonEmptyTestString")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testIsInputFieldBlank_WithEmptyStringInput_ReturnsTrue() {
- val result = registrationViewModel.isInputFieldBlank("")
- Assert.assertTrue(result)
- }
-
- @Test
- fun testIsInputLengthInadequate_WithAdequateLengthInput_ReturnsFalse() {
- val result = registrationViewModel.isInputLengthInadequate("Password123")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testIsInputLengthInadequate_WithInadequateLengthInput_ReturnsTrue() {
- val result = registrationViewModel.isInputLengthInadequate("")
- Assert.assertTrue(result)
- }
-
- @Test
- fun testInputHasSpaces_WithSpacesInput_ReturnsTrue() {
- val result = registrationViewModel.inputHasSpaces("testUpdateAuthenticationToken string")
- Assert.assertTrue(result)
- }
-
- @Test
- fun testInputHasSpaces_WithNoSpacesInput_ReturnsFalse() {
- val result = registrationViewModel.inputHasSpaces("testString")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testHasLeadingTrailingSpaces_WithLeadingTrailingSpacesInput_ReturnsTrue() {
- val result = registrationViewModel.hasLeadingTrailingSpaces(" Hello World ")
- Assert.assertTrue(result)
- }
-
- @Test
- fun testHasLeadingTrailingSpaces_WithoutLeadingTrailingSpacesInput_ReturnsFalse() {
- val result = registrationViewModel.hasLeadingTrailingSpaces("Hello World")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testIsEmailInvalid_WithValidEmailInput_ReturnsFalse() {
- val result =
- registrationViewModel.isEmailInvalid("testUpdateAuthenticationToken@example.com")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testIsEmailInvalid_WithInvalidEmailInput_ReturnsTrue() {
- val result = registrationViewModel.isEmailInvalid("testExample.com")
- Assert.assertTrue(result)
- }
-
-
- @Test
- fun testRegisterUser_SuccessfulRegistrationReceivedFromRepository_ReturnsRegistrationSuccessful() =
- runTest {
- val responseBody = Mockito.mock(ResponseBody::class.java)
- Mockito.`when`(
- userAuthRepositoryImp.registerUser(
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString(),
- )
- ).thenReturn(flowOf(responseBody))
- registrationViewModel.registrationUiState.test {
- registrationViewModel.registerUser(
- "accountNumber",
- "authMode",
- "email",
- "firstName",
- "lastName",
- "mobileNumber",
- "password",
- "username"
- )
- assertEquals(RegistrationUiState.Initial, awaitItem())
- assertEquals(RegistrationUiState.Loading, awaitItem())
- assertEquals(RegistrationUiState.Success, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testRegisterUser_UnsuccessfulRegistrationReceivedFromRepository_ReturnsRegistrationUnsuccessful() =
- runTest {
- Mockito.`when`(
- userAuthRepositoryImp.registerUser(
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString(),
- Mockito.anyString()
- )
- ).thenThrow(Exception("Error occurred"))
- registrationViewModel.registrationUiState.test {
- registrationViewModel.registerUser(
- "accountNumber",
- "authMode",
- "email",
- "firstName",
- "lastName",
- "mobileNumber",
- "password",
- "username"
- )
- assertEquals(RegistrationUiState.Initial, awaitItem())
- assertEquals(RegistrationUiState.Loading, awaitItem())
- assertEquals(RegistrationUiState.Error(0), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test
- fun testVerifyUser_SuccessfulRegistrationVerificationReceivedFromRepository_ReturnsRegistrationVerificationSuccessful() =
- runTest {
- Mockito.`when`(
- userAuthRepositoryImp.verifyUser(Mockito.anyString(), Mockito.anyString())
- ).thenReturn(flowOf(Mockito.mock(ResponseBody::class.java)))
-
- registrationViewModel.registrationVerificationUiState.test {
- registrationViewModel.verifyUser("authenticationToken", "requestId")
- assertEquals(RegistrationUiState.Initial,awaitItem())
- assertEquals(RegistrationUiState.Loading,awaitItem())
- assertEquals(RegistrationUiState.Success,awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testVerifyUser_UnsuccessfulRegistrationVerificationReceivedFromRepository_ReturnsRegistrationVerificationUnsuccessful() =
- runTest {
- val errorResponse = Exception("Error occurred")
- Mockito.`when`(
- userAuthRepositoryImp.verifyUser(Mockito.anyString(), Mockito.anyString())
- ).thenThrow(errorResponse)
- registrationViewModel.registrationVerificationUiState.test{
- registrationViewModel.verifyUser("authenticationToken", "requestId")
- assertEquals(RegistrationUiState.Initial,awaitItem())
- assertEquals(RegistrationUiState.Loading,awaitItem())
- assertEquals(RegistrationUiState.Error(0),awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @After
- fun tearDown() {
-
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingAccountsDetailViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingAccountsDetailViewModelTest.kt
deleted file mode 100644
index bbaf2c3a8..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingAccountsDetailViewModelTest.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.advanceUntilIdle
-import kotlinx.coroutines.test.runTest
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import com.mifos.mobile.core.data.utils.FakeRemoteDataSource
-import org.mifos.mobile.repositories.SavingsAccountRepository
-import org.mifos.mobile.ui.savings_account.SavingAccountsDetailViewModel
-import org.mifos.mobile.ui.savings_account.SavingsAccountDetailUiState
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.SavingsAccountUiState
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class SavingAccountsDetailViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var savingsAccountRepositoryImp: SavingsAccountRepository
-
-
- private lateinit var savingAccountsDetailViewModel: SavingAccountsDetailViewModel
- private val mockAccountId = 1L
- private val mockAssociationType = org.mifos.mobile.core.common.Constants.TRANSACTIONS
- private val mockSavingsWithAssociations = com.mifos.mobile.core.data.utils.FakeRemoteDataSource.savingsWithAssociations
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- savingAccountsDetailViewModel = SavingAccountsDetailViewModel(savingsAccountRepositoryImp)
-
- }
-
- @Test
- fun testLoadSavingsWithAssociations_SuccessResponseFromRepository_ReturnsSuccessLoadingSavingsWithAssociations() =
- runTest {
- Mockito.`when`(
- savingsAccountRepositoryImp.getSavingsWithAssociations(
- mockAccountId,
- mockAssociationType
- )
- ).thenReturn(flowOf(mockSavingsWithAssociations))
-
- savingAccountsDetailViewModel.loadSavingsWithAssociations(mockAccountId)
- advanceUntilIdle()
- assertEquals(
- SavingsAccountDetailUiState.Success(
- mockSavingsWithAssociations
- ),
- savingAccountsDetailViewModel.savingAccountsDetailUiState.value
- )
-
- }
-
- @Test(expected = Exception::class)
- fun testLoadSavingsWithAssociations_ErrorResponseFromRepository_ReturnsError() =
- runTest {
- Mockito.`when`(
- savingsAccountRepositoryImp.getSavingsWithAssociations(
- mockAccountId,
- mockAssociationType
- )
- ).thenThrow(Exception("Error occurred"))
- savingAccountsDetailViewModel.loadSavingsWithAssociations(mockAccountId)
- advanceUntilIdle()
- assertEquals(
- SavingsAccountUiState.Error,
- savingAccountsDetailViewModel.savingAccountsDetailUiState.value
- )
- }
-
-
- @After
- fun tearDown() {
-
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingAccountsTransactionViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingAccountsTransactionViewModelTest.kt
deleted file mode 100644
index 445283177..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingAccountsTransactionViewModelTest.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import com.mifos.mobile.core.data.utils.FakeRemoteDataSource
-import org.mifos.mobile.repositories.SavingsAccountRepository
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.SavingsAccountUiState
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class SavingAccountsTransactionViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var savingsAccountRepositoryImp: SavingsAccountRepository
-
- private lateinit var savingAccountsTransactionViewModel: SavingAccountsTransactionViewModel
- private val mockAccountId = 1L
- private val mockAssociationType = org.mifos.mobile.core.common.Constants.TRANSACTIONS
- private val mockSavingsWithAssociations = com.mifos.mobile.core.data.utils.FakeRemoteDataSource.savingsWithAssociations
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- savingAccountsTransactionViewModel =
- SavingAccountsTransactionViewModel(savingsAccountRepositoryImp)
-
- }
-
- @Test
- fun testLoadSavingsWithAssociations_SuccessReceivedFromRepository_ReturnSuccessLoadingSavingsWithAssociations() =
- runTest{
- Mockito.`when`(
- savingsAccountRepositoryImp.getSavingsWithAssociations(
- mockAccountId,
- mockAssociationType
- )
- ).thenReturn(flowOf(mockSavingsWithAssociations))
- savingAccountsTransactionViewModel.savingAccountsTransactionUiState.test {
- savingAccountsTransactionViewModel.loadSavingsWithAssociations(mockAccountId)
- assertEquals(SavingsAccountUiState.Initial,awaitItem())
- assertEquals(SavingsAccountUiState.Loading,awaitItem())
- assertEquals(SavingsAccountUiState.SuccessLoadingSavingsWithAssociations(mockSavingsWithAssociations),awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testLoadSavingsWithAssociations_ErrorResponseFromRepository_ReturnsError() = runTest{
- Mockito.`when`(
- savingsAccountRepositoryImp.getSavingsWithAssociations(
- mockAccountId,
- mockAssociationType
- )
- ).thenThrow(Exception("Error occurred while fetching data from server"))
- savingAccountsTransactionViewModel.savingAccountsTransactionUiState.test {
- savingAccountsTransactionViewModel.loadSavingsWithAssociations(mockAccountId)
- assertEquals(SavingsAccountUiState.Initial,awaitItem())
- assertEquals(SavingsAccountUiState.Loading,awaitItem())
- assertEquals(SavingsAccountUiState.Error,awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @After
- fun tearDown() {
-
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingsAccountApplicationViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingsAccountApplicationViewModelTest.kt
deleted file mode 100644
index badc2f53d..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingsAccountApplicationViewModelTest.kt
+++ /dev/null
@@ -1,268 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import androidx.lifecycle.Observer
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.advanceUntilIdle
-import kotlinx.coroutines.test.runTest
-import okhttp3.ResponseBody
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.api.local.PreferencesHelper
-import org.mifos.mobile.models.accounts.savings.SavingsAccountApplicationPayload
-import org.mifos.mobile.models.accounts.savings.SavingsAccountUpdatePayload
-import org.mifos.mobile.models.accounts.savings.SavingsWithAssociations
-import org.mifos.mobile.models.templates.savings.SavingsAccountTemplate
-import org.mifos.mobile.repositories.SavingsAccountRepository
-import org.mifos.mobile.core.model.enums.SavingsAccountState
-import org.mifos.mobile.ui.savings_account_application.SavingsAccountApplicationUiState
-import org.mifos.mobile.ui.savings_account_application.SavingsAccountApplicationViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.SavingsAccountUiState
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-import retrofit2.Response
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class SavingsAccountApplicationViewModelTest {
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var savingsAccountRepositoryImp: SavingsAccountRepository
-
- @Mock
- lateinit var preferenseHelper : PreferencesHelper
-
-
- private lateinit var savingsAccountApplicationViewModel: SavingsAccountApplicationViewModel
- private val mockClientId = 1L
- private val mockAccountId = 1L
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- savingsAccountApplicationViewModel =
- SavingsAccountApplicationViewModel(savingsAccountRepositoryImp,
- preferenseHelper)
-
- }
-
- @Test
- fun testLoadSavingsAccountApplicationTemplate_InputCreateStateSuccessResponseFromRepository_ReturnsShowUserInterfaceSavingAccountApplication() =
- runTest {
- val mockState = org.mifos.mobile.core.model.enums.SavingsAccountState.CREATE
- val clientId = 1L
- val mockTemplate = Mockito.mock(SavingsAccountTemplate::class.java)
- Mockito.`when`(
- savingsAccountRepositoryImp.getSavingAccountApplicationTemplate(mockClientId)
- ).thenReturn(flowOf(mockTemplate))
- `when`(preferenseHelper.clientId).thenReturn(clientId)
-
- savingsAccountApplicationViewModel.loadSavingsAccountApplicationTemplate()
- advanceUntilIdle()
- assertEquals(
- SavingsAccountApplicationUiState.ShowUserInterface(mockTemplate, mockState),
- savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value
- )
- }
-
- @Test
- fun testLoadSavingsAccountApplicationTemplate_InputUpdateStateSuccessResponseFromRepository_ReturnsShowUserInterfaceSavingAccountUpdate() =
- runTest {
- val mockState = org.mifos.mobile.core.model.enums.SavingsAccountState.UPDATE
- val mockTemplate = Mockito.mock(SavingsAccountTemplate::class.java)
- val mockPayload = Mockito.mock(SavingsAccountUpdatePayload::class.java)
- val mockResponse = Mockito.mock(ResponseBody::class.java)
- val mockSavingsWithAssociations = Mockito.mock(SavingsWithAssociations::class.java)
- Mockito.`when`(
- savingsAccountRepositoryImp.getSavingAccountApplicationTemplate(mockClientId)
- ).thenReturn(flowOf(mockTemplate))
- `when`(preferenseHelper.clientId).thenReturn(mockClientId)
- `when`(savingsAccountRepositoryImp.getSavingAccountApplicationTemplate(mockClientId))
- .thenReturn(flow { emit(mockTemplate) })
- // Set state to UPDATE
- savingsAccountApplicationViewModel.setSavingsAccountState(org.mifos.mobile.core.model.enums.SavingsAccountState.UPDATE)
- savingsAccountApplicationViewModel.setSavingsWithAssociations(mockSavingsWithAssociations)
- savingsAccountApplicationViewModel.loadSavingsAccountApplicationTemplate()
- advanceUntilIdle()
-
- assertEquals(
- SavingsAccountApplicationUiState.ShowUserInterface(mockTemplate,mockState),
- savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value
- )
- }
-
- @Test(expected = Exception::class)
- fun testLoadSavingsAccountApplicationTemplate_ErrorResponseFromRepository_ReturnsErrorMessage() =
- runTest {
- val errorResponse = Exception("Loading Failed")
- val mockState = org.mifos.mobile.core.model.enums.SavingsAccountState.UPDATE
- Mockito.`when`(
- savingsAccountRepositoryImp.getSavingAccountApplicationTemplate(mockClientId)
- ).thenThrow(errorResponse)
- savingsAccountApplicationViewModel.loadSavingsAccountApplicationTemplate()
- advanceUntilIdle()
- assertEquals(
- SavingsAccountUiState.ErrorMessage(errorResponse),
- savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value
- )
- }
-
- @Test
- fun testSubmitSavingsAccountApplication_SuccessResponseFromRepository_ReturnsHideProgress() =
- runTest {
- val responseBody = Mockito.mock(ResponseBody::class.java)
- val mockSavingsAccountApplicationPayload =
- Mockito.mock(SavingsAccountApplicationPayload::class.java)
- Mockito.`when`(
- savingsAccountRepositoryImp.submitSavingAccountApplication(
- mockSavingsAccountApplicationPayload
- )
- ).thenReturn(flowOf(responseBody))
-
-
- savingsAccountApplicationViewModel.submitSavingsAccountApplication(
- mockSavingsAccountApplicationPayload
- )
- advanceUntilIdle()
- assertEquals(
- SavingsAccountApplicationUiState.Success(org.mifos.mobile.core.model.enums.SavingsAccountState.CREATE),
- savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value
- )
-
- }
-
- @Test(expected = Exception::class)
- fun testSubmitSavingsAccountApplication_SuccessResponseFromRepository_ReturnsSavingsAccountUpdateSuccess() =
- runTest {
- val errorResponse = Exception("Submitting Failed")
- val mockSavingsAccountApplicationPayload =
- Mockito.mock(SavingsAccountApplicationPayload::class.java)
- Mockito.`when`(
- savingsAccountRepositoryImp.submitSavingAccountApplication(
- mockSavingsAccountApplicationPayload
- )
- ).thenThrow(errorResponse)
- savingsAccountApplicationViewModel.submitSavingsAccountApplication(
- mockSavingsAccountApplicationPayload
- )
- advanceUntilIdle()
- assertEquals(
- SavingsAccountUiState.ErrorMessage(errorResponse),
- savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value
- )
- }
-
- @Test(expected = Exception::class)
- fun testSubmitSavingsAccountApplication_ErrorResponseFromRepository_ReturnsErrorMessage() =
- runTest {
- val errorResponse = Exception("Submitting Failed")
- val mockSavingsAccountApplicationPayload =
- Mockito.mock(SavingsAccountApplicationPayload::class.java)
- Mockito.`when`(
- savingsAccountRepositoryImp.submitSavingAccountApplication(
- mockSavingsAccountApplicationPayload
- )
- ).thenThrow(errorResponse)
- savingsAccountApplicationViewModel.submitSavingsAccountApplication(
- mockSavingsAccountApplicationPayload
- )
- advanceUntilIdle()
- assertEquals(
- SavingsAccountUiState.ErrorMessage(errorResponse),
- savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value
- )
- }
-
- @Test
- fun testUpdateSavingsAccount_SuccessResponseFromRepository_ReturnsHideProgress() =runTest {
- val mockSavingsAccountUpdatePayload = Mockito.mock(SavingsAccountUpdatePayload::class.java)
- val responseBody = Mockito.mock(ResponseBody::class.java)
- Mockito.`when`(
- savingsAccountRepositoryImp.updateSavingsAccount(
- mockAccountId,
- mockSavingsAccountUpdatePayload
- )
- ).thenReturn(flowOf(responseBody))
- savingsAccountApplicationViewModel.setSavingsAccountState(org.mifos.mobile.core.model.enums.SavingsAccountState.UPDATE)
- savingsAccountApplicationViewModel.updateSavingsAccount(
- mockAccountId,
- mockSavingsAccountUpdatePayload
- )
- advanceUntilIdle()
- assertEquals(
- SavingsAccountApplicationUiState.Success(org.mifos.mobile.core.model.enums.SavingsAccountState.UPDATE),
- savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value
- )
- }
-
- @Test(expected = Exception::class)
- fun testUpdateSavingsAccount_SuccessResponseFromRepository_ReturnsSavingsAccountUpdateSuccess() =
- runTest {
- val errorResponse = Exception("Update Failed")
- val mockSavingsAccountUpdatePayload =
- Mockito.mock(SavingsAccountUpdatePayload::class.java)
- Mockito.`when`(
- savingsAccountRepositoryImp.updateSavingsAccount(
- mockAccountId,
- mockSavingsAccountUpdatePayload
- )
- ).thenThrow(errorResponse)
- savingsAccountApplicationViewModel.updateSavingsAccount(
- mockAccountId,
- mockSavingsAccountUpdatePayload
- )
- advanceUntilIdle()
- assertEquals(
- SavingsAccountUiState.ErrorMessage(errorResponse),
- savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value
- )
- }
-
- @Test(expected = Exception::class)
- fun testUpdateSavingsAccount_SuccessResponseFromRepository_ReturnsErrorMessage() =runTest {
- val errorResponse = Exception("Update Failed")
- val mockSavingsAccountUpdatePayload = Mockito.mock(SavingsAccountUpdatePayload::class.java)
- Mockito.`when`(
- savingsAccountRepositoryImp.updateSavingsAccount(
- mockAccountId,
- mockSavingsAccountUpdatePayload
- )
- ).thenThrow(errorResponse)
- savingsAccountApplicationViewModel.updateSavingsAccount(
- mockAccountId,
- mockSavingsAccountUpdatePayload
- )
- advanceUntilIdle()
- assertEquals(
- SavingsAccountUiState.ErrorMessage(errorResponse),
- savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value
- )
- }
-
- @After
- fun tearDown() {
-
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingsAccountWithdrawViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingsAccountWithdrawViewModelTest.kt
deleted file mode 100644
index 597c5d970..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingsAccountWithdrawViewModelTest.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import okhttp3.ResponseBody
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.models.accounts.savings.SavingsAccountWithdrawPayload
-import org.mifos.mobile.repositories.SavingsAccountRepository
-import org.mifos.mobile.ui.savings_account_withdraw.SavingsAccountWithdrawViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.SavingsAccountUiState
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class SavingsAccountWithdrawViewModelTest {
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var savingsAccountRepositoryImp: SavingsAccountRepository
-
- private lateinit var savingsAccountWithdrawViewModel: SavingsAccountWithdrawViewModel
- private val mockAccountId = "1"
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- savingsAccountWithdrawViewModel =
- SavingsAccountWithdrawViewModel(savingsAccountRepositoryImp)
-
- }
-
- @Test
- fun testSubmitWithdrawSavingsAccount_SuccessReceivedFromRepository_ReturnsSavingsAccountWithdrawSuccess() =
- runTest{
- val mockSavingsAccountWithdrawPayload =
- Mockito.mock(SavingsAccountWithdrawPayload::class.java)
- val responseBody = Mockito.mock(ResponseBody::class.java)
- Mockito.`when`(
- savingsAccountRepositoryImp.submitWithdrawSavingsAccount(
- mockAccountId,
- mockSavingsAccountWithdrawPayload
- )
- ).thenReturn(flowOf(responseBody))
- savingsAccountWithdrawViewModel.savingsAccountWithdrawUiState.test {
- savingsAccountWithdrawViewModel.submitWithdrawSavingsAccount(
- mockAccountId,
- mockSavingsAccountWithdrawPayload
- )
- assertEquals(SavingsAccountUiState.Initial, awaitItem())
- assertEquals(SavingsAccountUiState.Loading, awaitItem())
- assertEquals(SavingsAccountUiState.SavingsAccountWithdrawSuccess, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testSubmitWithdrawSavingsAccount_ErrorResponseFromRepository_ReturnsErrorMessage() =
- runTest{
- val mockSavingsAccountWithdrawPayload =
- Mockito.mock(SavingsAccountWithdrawPayload::class.java)
- val errorResponse = Exception("Submit Failed")
- Mockito.`when`(
- savingsAccountRepositoryImp.submitWithdrawSavingsAccount(
- mockAccountId,
- mockSavingsAccountWithdrawPayload
- )
- ).thenThrow(errorResponse )
- savingsAccountWithdrawViewModel.savingsAccountWithdrawUiState.test {
- savingsAccountWithdrawViewModel.submitWithdrawSavingsAccount(
- mockAccountId,
- mockSavingsAccountWithdrawPayload
- )
- assertEquals(SavingsAccountUiState.Initial, awaitItem())
- assertEquals(SavingsAccountUiState.Loading, awaitItem())
- assertEquals(SavingsAccountUiState.ErrorMessage(errorResponse), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @After
- fun tearDown() {
-
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingsMakeTransferViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingsMakeTransferViewModelTest.kt
deleted file mode 100644
index cda51f8f9..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/SavingsMakeTransferViewModelTest.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.models.templates.account.AccountOptionsTemplate
-import org.mifos.mobile.repositories.SavingsAccountRepository
-import org.mifos.mobile.ui.savings_make_transfer.SavingsMakeTransferViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.utils.SavingsAccountUiState
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class SavingsMakeTransferViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var savingsAccountRepositoryImp: SavingsAccountRepository
-
- private lateinit var savingsMakeTransferViewModel: SavingsMakeTransferViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- savingsMakeTransferViewModel = SavingsMakeTransferViewModel(savingsAccountRepositoryImp)
-
- }
-
- @Test
- fun testLoanAccountTransferTemplate_SuccessResponseFromRepository_ReturnsShowSavingsAccountTemplate() =
- runTest {
- val mockAccountOptionsTemplate = Mockito.mock(AccountOptionsTemplate::class.java)
- Mockito.`when`(
- savingsAccountRepositoryImp.loanAccountTransferTemplate()
- ).thenReturn(flowOf( mockAccountOptionsTemplate))
-
- savingsMakeTransferViewModel.savingsMakeTransferUiState.test {
-
- savingsMakeTransferViewModel.loanAccountTransferTemplate()
- assertEquals(SavingsAccountUiState.Initial, awaitItem())
- assertEquals(SavingsAccountUiState.Loading, awaitItem())
- assertEquals(SavingsAccountUiState.ShowSavingsAccountTemplate(mockAccountOptionsTemplate), awaitItem())
- cancelAndIgnoreRemainingEvents()
-
- }
- }
-
- @Test(expected = Exception::class)
- fun testLoanAccountTransferTemplate_ErrorResponseFromRepository_ReturnsErrorMessage() =
- runTest {
- val errorResponse = Exception("Loading Failed")
- Mockito.`when`(
- savingsAccountRepositoryImp.loanAccountTransferTemplate()
- ).thenThrow(errorResponse)
- savingsMakeTransferViewModel.savingsMakeTransferUiState.test {
- savingsMakeTransferViewModel.loanAccountTransferTemplate()
- assertEquals(SavingsAccountUiState.Initial, awaitItem())
- assertEquals(SavingsAccountUiState.Loading, awaitItem())
- assertEquals(SavingsAccountUiState.ErrorMessage(errorResponse), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @After
- fun tearDown() {
-
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/ThirdPartyTransferViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/ThirdPartyTransferViewModelTest.kt
deleted file mode 100644
index cad4097e3..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/ThirdPartyTransferViewModelTest.kt
+++ /dev/null
@@ -1,205 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.Assert.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.runBlocking
-import org.junit.After
-import org.junit.Assert.assertNotEquals
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.core.model.models.AccountOptionAndBeneficiary
-import org.mifos.mobile.models.beneficiary.Beneficiary
-import org.mifos.mobile.models.payload.AccountDetail
-import org.mifos.mobile.models.templates.account.AccountOption
-import org.mifos.mobile.models.templates.account.AccountOptionsTemplate
-import org.mifos.mobile.models.templates.account.AccountType
-import org.mifos.mobile.repositories.BeneficiaryRepositoryImp
-import org.mifos.mobile.repositories.ThirdPartyTransferRepositoryImp
-import org.mifos.mobile.ui.third_party_transfer.ThirdPartyTransferViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mockito.Mock
-import org.mockito.Mockito.*
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class ThirdPartyTransferViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var thirdPartyTransferRepositoryImp: ThirdPartyTransferRepositoryImp
-
- @Mock
- lateinit var beneficiaryRepositoryImp: BeneficiaryRepositoryImp
-
- private lateinit var thirdPartyTransferViewModel: ThirdPartyTransferViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- thirdPartyTransferViewModel =
- ThirdPartyTransferViewModel(
- thirdPartyTransferRepositoryImp,
- beneficiaryRepositoryImp,)
-
- }
-
- @Test
- fun testLoadTransferTemplate_Successful() = runBlocking {
- val templateResult = mock(AccountOptionsTemplate::class.java)
- val list1 = mock(Beneficiary::class.java)
- val list2 = mock(Beneficiary::class.java)
- val beneficiaryListResult = listOf(list1, list2)
-
- `when`(thirdPartyTransferRepositoryImp.thirdPartyTransferTemplate())
- .thenReturn(
- flowOf(
- templateResult
- )
- )
- `when`(beneficiaryRepositoryImp.beneficiaryList())
- .thenReturn(
- flowOf(
- beneficiaryListResult
- )
- )
- thirdPartyTransferViewModel.accountOptionAndBeneficiary =
- org.mifos.mobile.core.model.models.AccountOptionAndBeneficiary(
- templateResult,
- beneficiaryListResult
- )
- thirdPartyTransferViewModel.thirdPartyTransferUiState.test {
- thirdPartyTransferViewModel.loadTransferTemplate()
- assertEquals(ThirdPartyTransferUiState.Initial, awaitItem())
- assertEquals(ThirdPartyTransferUiState.Loading, awaitItem())
- assertEquals(ThirdPartyTransferUiState.ShowThirdPartyTransferTemplate(templateResult), awaitItem())
- assertEquals(ThirdPartyTransferUiState.ShowBeneficiaryList(beneficiaryListResult), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
-
- @Test(expected = Exception::class)
- fun testLoadTransferTemplate_Unsuccessful() = runBlocking {
- val errorMessage = R.string.error_fetching_third_party_transfer_template
- `when`(thirdPartyTransferRepositoryImp.thirdPartyTransferTemplate()).
- thenThrow(Exception("Error fetching third party transfer template"))
- `when`(thirdPartyTransferRepositoryImp.thirdPartyTransferTemplate())
- .thenThrow(Exception("Error fetching beneficiary list"))
- `when`(beneficiaryRepositoryImp.beneficiaryList())
- .thenThrow(Exception("Error fetching beneficiary list"))
- thirdPartyTransferViewModel.thirdPartyTransferUiState.test {
- thirdPartyTransferViewModel.loadTransferTemplate()
- assertEquals(ThirdPartyTransferUiState.Initial, awaitItem())
- assertEquals(ThirdPartyTransferUiState.Loading, awaitItem())
- assertEquals(ThirdPartyTransferUiState.Error(errorMessage), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test
- fun testGetAccountNumbersFromAccountOptions() {
- val accountTypeLoanString = "loan"
- val accountOptions = listOf(
- AccountOption(
- accountId = 1,
- accountNo = "123456789",
- accountType = AccountType(1, "savings", "SAV"),
- clientId = 1001,
- clientName = "John Doe",
- officeId = 101,
- officeName = "Main Office"
- ),
- AccountOption(
- accountId = 2,
- accountNo = "987654321",
- accountType = AccountType(2, "current", "CUR"),
- clientId = 1002,
- clientName = "Jane Smith",
- officeId = 102,
- officeName = "Branch Office"
- )
- )
-
- val result = thirdPartyTransferViewModel.getAccountNumbersFromAccountOptions(
- accountOptions,
- accountTypeLoanString
- )
-
- assertEquals(AccountDetail("123456789", "SAV"), result[0])
- assertEquals(AccountDetail("987654321", "CUR"), result[1])
- assertNotEquals(AccountDetail("987654321", "CUR"), result[0])
- assertNotEquals(AccountDetail("123456789", "SAV"), result[1])
- }
-
- @Test
- fun testGetAccountNumbersFromBeneficiaries() {
- val beneficiaries = listOf(
- Beneficiary(
- 1,
- "John Doe",
- "Main Office",
- "Client 1",
- AccountType(1, "savings", "SAV"),
- "123456789",
- 1000.0
- ),
- Beneficiary(
- 2,
- "Jane Smith",
- "Branch Office",
- "Client 2",
- AccountType(2, "current", "CUR"),
- "987654321",
- 2000.0
- )
- )
-
- val result = thirdPartyTransferViewModel.getAccountNumbersFromBeneficiaries(beneficiaries)
-
- assertEquals("123456789", result[0].accountNumber)
- assertEquals("John Doe", result[0].beneficiaryName)
- assertEquals("987654321", result[1].accountNumber)
- assertEquals("Jane Smith", result[1].beneficiaryName)
- assertNotEquals("123456789", result[1].accountNumber)
- assertNotEquals("John Doe", result[1].beneficiaryName)
- assertNotEquals("987654321", result[0].accountNumber)
- assertNotEquals("Jane Smith", result[0].beneficiaryName)
- }
-
- @Test
- fun testSearchAccount() {
- val accountNoToSearch = "123456789"
- val accountOptions = listOf(
- AccountOption(1, "123456789", AccountType(1, "savings", "SAV")),
- AccountOption(2, "987654321", AccountType(2, "current", "CUR")),
- AccountOption(3, "111111111", AccountType(3, "loan", "LOAN")),
- )
- val result = thirdPartyTransferViewModel.searchAccount(accountOptions, accountNoToSearch)
-
- assertEquals(AccountOption(1, "123456789", AccountType(1, "savings", "SAV")), result)
- assertNotEquals(AccountOption(), result)
- }
-
- @After
- fun tearDown() {
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/TransferProcessViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/TransferProcessViewModelTest.kt
deleted file mode 100644
index 8044eea35..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/TransferProcessViewModelTest.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import androidx.lifecycle.Observer
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.resetMain
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
-import okhttp3.ResponseBody
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.core.model.entity.payload.TransferPayload
-import org.mifos.mobile.core.model.enums.TransferType
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-import retrofit2.Response
-import org.mifos.mobile.ui.transfer_process.TransferProcessViewModel
-
-@RunWith(MockitoJUnitRunner::class)
-class TransferProcessViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @Mock
- lateinit var transferProcessImp: TransferRepositoryImp
-
- private lateinit var viewModel: TransferProcessViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = TransferProcessViewModel(transferProcessImp)
- }
-
- @Test
- fun makeTransfer_successful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val responseBody = Mockito.mock(ResponseBody::class.java)
- Mockito.`when`(
- transferProcessImp.makeTransfer(
- 1, 2, 3,
- 4, 5, 6, 7,
- 8, "06 July 2023 ", 100.0, "Transfer",
- "dd MMMM yyyy", "en", "0000001", "0000002",
- org.mifos.mobile.core.model.enums.TransferType.SELF
- )
- ).thenReturn(flowOf(responseBody))
- viewModel.transferUiState.test {
- viewModel.makeTransfer(
- TransferPayload(
- 1, 2, 3,
- 4, 5, 6, 7,
- 8, "06 July 2023 ", 100.0, "Transfer",
- "dd MMMM yyyy", "en", "0000001", "0000002"
- )
- )
- assertEquals(TransferUiState.Initial, awaitItem())
- assertEquals(TransferUiState.TransferSuccess, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- Dispatchers.resetMain()
- }
-
- @Test(expected = Exception::class)
- fun makeTransfer_unsuccessful() = runTest {
- Dispatchers.setMain(Dispatchers.Unconfined)
- val error = Exception("Savings Transfer Failed")
- Mockito.`when`(
- transferProcessImp.makeTransfer(
- 1, 2, 3,
- 4, 5, 6, 7,
- 8, "06 July 2023 ", 100.0, "Transfer",
- "dd MMMM yyyy", "en", "0000001", "0000002",
- TransferType.SELF
- )
- ).thenThrow(error)
- viewModel.transferUiState.test {
-
- viewModel.makeTransfer(
- TransferPayload(
- 1, 2, 3,
- 4, 5, 6, 7,
- 8, "06 July 2023 ", 100.0, "Transfer",
- "dd MMMM yyyy", "en", "0000001", "0000002"
- )
- )
- assertEquals(TransferUiState.Initial, awaitItem())
- assertEquals(TransferUiState.Error(error), awaitItem())
- cancelAndIgnoreRemainingEvents()
- Dispatchers.resetMain()
- }
- }
-
- @After
- fun tearDown() {
- }
-
-}
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/UpdatePasswordViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/UpdatePasswordViewModelTest.kt
deleted file mode 100644
index 875cca469..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/UpdatePasswordViewModelTest.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.runBlocking
-import okhttp3.ResponseBody
-import org.junit.*
-import org.junit.runner.RunWith
-import org.mifos.mobile.core.data.repository.ClientRepository
-import org.mifos.mobile.core.data.repository.UserAuthRepository
-import org.mifos.mobile.feature.registration.utils.RegistrationUiState
-import org.mifos.mobile.feature.update.password.UpdatePasswordViewModel
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-class UpdatePasswordViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @OptIn(ExperimentalCoroutinesApi::class)
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @Mock
- lateinit var userAuthRepositoryImp: UserAuthRepository
-
- @Mock
- lateinit var clientRepositoryImp: ClientRepository
-
- private lateinit var updatePasswordViewModel: UpdatePasswordViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- updatePasswordViewModel =
- UpdatePasswordViewModel(
- userAuthRepositoryImp,
- clientRepositoryImp
- )
- }
-
- @Test
- fun testIsInputFieldEmpty_WithEmptyStringInput_ReturnsTrue() {
- val result = updatePasswordViewModel.isInputFieldEmpty("")
- Assert.assertTrue(result)
- }
-
- @Test
- fun testIsInputFieldEmpty_WithNonEmptyStringInput_ReturnsFalse() {
- val result = updatePasswordViewModel.isInputFieldEmpty("nonEmptyStringInput")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testIsInputLengthInadequate_WithAdequateLengthInput_ReturnsFalse() {
- val result = updatePasswordViewModel.isInputLengthInadequate("Password123")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testIsInputLengthInadequate_WithInadequateLengthInput_ReturnsTrue() {
- val result = updatePasswordViewModel.isInputLengthInadequate("")
- Assert.assertTrue(result)
- }
-
- @Test
- fun testValidatePasswordMatch_WithSamePasswords_ReturnsTrue() {
- val result = updatePasswordViewModel.validatePasswordMatch("password", "password")
- Assert.assertTrue(result)
- }
-
- @Test
- fun testValidatePasswordMatch_WithDifferentPasswords_ReturnsFalse() {
- val result = updatePasswordViewModel.validatePasswordMatch("password1", "password2")
- Assert.assertFalse(result)
- }
-
- @Test
- fun testUpdateAccountPassword_SuccessReceivedFromRepository_ReturnsSuccess() = runBlocking {
- val responseBody = Mockito.mock(ResponseBody::class.java)
- Mockito.`when`(
- userAuthRepositoryImp.updateAccountPassword(Mockito.anyString(), Mockito.anyString())
- ).thenReturn(flowOf(responseBody))
- updatePasswordViewModel.updatePasswordUiState.test {
- updatePasswordViewModel.updateAccountPassword("newPassword", "newPassword")
- assertEquals(RegistrationUiState.Initial, awaitItem())
- assertEquals(RegistrationUiState.Loading, awaitItem())
- assertEquals(RegistrationUiState.Success, awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testUpdateAccountPassword_ErrorReceivedFromRepository_ReturnsError() = runBlocking {
- Mockito.`when`(
- userAuthRepositoryImp.updateAccountPassword(Mockito.anyString(), Mockito.anyString())
- ).thenThrow(Exception("Error updating password"))
-
- updatePasswordViewModel.updatePasswordUiState.test{
- updatePasswordViewModel.updateAccountPassword("newPassword", "newPassword")
- assertEquals(RegistrationUiState.Initial, awaitItem())
- assertEquals(RegistrationUiState.Loading, awaitItem())
- assertEquals(RegistrationUiState.Error(0), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @After
- fun tearDown() {
- }
-}
\ No newline at end of file
diff --git a/androidApp/src/test/java/org/mifos/mobile/viewModels/UserDetailViewModelTest.kt b/androidApp/src/test/java/org/mifos/mobile/viewModels/UserDetailViewModelTest.kt
deleted file mode 100644
index c7710740d..000000000
--- a/androidApp/src/test/java/org/mifos/mobile/viewModels/UserDetailViewModelTest.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-package org.mifos.mobile.viewModels
-
-import CoroutineTestRule
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import app.cash.turbine.test
-import junit.framework.Assert.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mifos.mobile.R
-import org.mifos.mobile.api.local.PreferencesHelper
-import org.mifos.mobile.feature.user.profile.viewmodel.UserDetailViewModel
-import org.mifos.mobile.models.client.Client
-import org.mifos.mobile.repositories.HomeRepositoryImp
-import org.mifos.mobile.repositories.UserDetailRepositoryImp
-import org.mifos.mobile.util.RxSchedulersOverrideRule
-import org.mifos.mobile.feature.user_profile.utils.UserDetailUiState
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-@ExperimentalCoroutinesApi
-class UserDetailViewModelTest {
-
- @JvmField
- @Rule
- val mOverrideSchedulersRule = RxSchedulersOverrideRule()
-
- @get:Rule
- val rule = InstantTaskExecutorRule()
-
- @get:Rule
- val coroutineTestRule = CoroutineTestRule()
-
- @Mock
- lateinit var userDetailRepositoryImp: UserDetailRepositoryImp
-
- @Mock
- lateinit var homeRepositoryImp: HomeRepositoryImp
-
- @Mock
- private lateinit var preferencesHelper: PreferencesHelper
-
-
- private lateinit var viewModel: UserDetailViewModel
-
- @Before
- fun setUp() {
- MockitoAnnotations.openMocks(this)
- viewModel = UserDetailViewModel(
- userDetailRepositoryImp,
- homeRepositoryImp
- )
- viewModel.preferencesHelper = preferencesHelper
- }
-
- @Test
- fun testLoadingUserDetails_Success(): Unit = runTest{
- val mockClient = Mockito.mock(Client::class.java)
-
- Mockito.`when`(homeRepositoryImp.currentClient()).thenReturn(flowOf(mockClient))
-
- viewModel.userDetailUiState.test {
- viewModel.userDetails
- assertEquals(UserDetailUiState.Loading ,awaitItem())
- assertEquals(UserDetailUiState.ShowUserDetails(mockClient), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
- @Test(expected = Exception::class)
- fun testLoadingUserDetails_Error(): Unit = runTest{
- val errorMessageResId = R.string.error_fetching_client
-
- Mockito.`when`(homeRepositoryImp.currentClient())
- .thenThrow( Exception("Error fetching client details"))
-
-
- viewModel.userDetailUiState.test {
- viewModel.userDetails
- assertEquals(UserDetailUiState.Loading ,awaitItem())
- assertEquals(UserDetailUiState.ShowError(errorMessageResId), awaitItem())
- cancelAndIgnoreRemainingEvents()
- }
- }
-
-}
\ No newline at end of file
diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts
index fce3f645e..abcfabda5 100644
--- a/build-logic/convention/build.gradle.kts
+++ b/build-logic/convention/build.gradle.kts
@@ -21,11 +21,15 @@ tasks.withType().configureEach {
dependencies {
compileOnly(libs.android.gradlePlugin)
compileOnly(libs.android.tools.common)
+ compileOnly(libs.compose.gradlePlugin)
compileOnly(libs.firebase.crashlytics.gradlePlugin)
compileOnly(libs.firebase.performance.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin)
compileOnly(libs.ksp.gradlePlugin)
compileOnly(libs.room.gradlePlugin)
+ compileOnly(libs.detekt.gradlePlugin)
+ compileOnly(libs.ktlint.gradlePlugin)
+ compileOnly(libs.spotless.gradlePlugin)
implementation(libs.truth)
}
@@ -46,6 +50,10 @@ gradlePlugin {
id = "mifos.android.application"
implementationClass = "AndroidApplicationConventionPlugin"
}
+ register("androidApplicationJacoco") {
+ id = "mifos.android.application.jacoco"
+ implementationClass = "AndroidApplicationJacocoConventionPlugin"
+ }
register("androidHilt") {
id = "mifos.android.hilt"
implementationClass = "AndroidHiltConventionPlugin"
@@ -62,6 +70,10 @@ gradlePlugin {
id = "mifos.android.feature"
implementationClass = "AndroidFeatureConventionPlugin"
}
+ register("androidLibraryJacoco") {
+ id = "mifos.android.library.jacoco"
+ implementationClass = "AndroidLibraryJacocoConventionPlugin"
+ }
register("androidTest") {
id = "mifos.android.test"
implementationClass = "AndroidTestConventionPlugin"
@@ -82,5 +94,25 @@ gradlePlugin {
id = "mifos.jvm.library"
implementationClass = "JvmLibraryConventionPlugin"
}
+ register("detekt") {
+ id = "mifos.detekt.plugin"
+ implementationClass = "MifosDetektConventionPlugin"
+ description = "Configures detekt for the project"
+ }
+ register("spotless") {
+ id = "mifos.spotless.plugin"
+ implementationClass = "MifosSpotlessConventionPlugin"
+ description = "Configures spotless for the project"
+ }
+ register("ktlint") {
+ id = "mifos.ktlint.plugin"
+ implementationClass = "MifosKtlintConventionPlugin"
+ description = "Configures kotlinter for the project"
+ }
+ register("gitHooks") {
+ id = "mifos.git.hooks"
+ implementationClass = "MifosGitHooksConventionPlugin"
+ description = "Installs git hooks for the project"
+ }
}
}
diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt
index c4a4f0bb2..2ec83e383 100644
--- a/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt
@@ -1,13 +1,15 @@
import com.android.build.api.dsl.ApplicationExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
+import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType
import org.mifos.mobile.configureAndroidCompose
class AndroidApplicationComposeConventionPlugin : Plugin {
override fun apply(target: Project) {
with(target) {
- pluginManager.apply("com.android.application")
+ apply(plugin = "com.android.application")
+ apply(plugin = "org.jetbrains.kotlin.plugin.compose")
val extension = extensions.getByType()
configureAndroidCompose(extension)
diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt
index 14897278b..36c1111da 100644
--- a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt
@@ -16,7 +16,12 @@ class AndroidApplicationConventionPlugin : Plugin {
apply("com.android.application")
apply("org.jetbrains.kotlin.android")
apply("mifos.android.lint")
+ apply("mifos.android.application.jacoco")
apply("com.dropbox.dependency-guard")
+ apply("mifos.detekt.plugin")
+ apply("mifos.spotless.plugin")
+ apply("mifos.ktlint.plugin")
+ apply("mifos.git.hooks")
}
extensions.configure {
diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt
new file mode 100644
index 000000000..4e81a0006
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt
@@ -0,0 +1,24 @@
+
+import com.android.build.api.variant.ApplicationAndroidComponentsExtension
+import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.getByType
+import org.mifos.mobile.configureJacoco
+
+class AndroidApplicationJacocoConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ pluginManager.apply("jacoco")
+ val androidExtension = extensions.getByType()
+
+ androidExtension.buildTypes.configureEach {
+ enableAndroidTestCoverage = true
+ enableUnitTestCoverage = true
+ }
+
+ configureJacoco(extensions.getByType())
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt
index b92960134..28ca369e2 100644
--- a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt
@@ -12,6 +12,7 @@ class AndroidFeatureConventionPlugin : Plugin {
pluginManager.apply {
apply("mifos.android.library")
apply("mifos.android.hilt")
+ apply("mifos.android.library.jacoco")
}
extensions.configure {
defaultConfig {
@@ -36,9 +37,13 @@ class AndroidFeatureConventionPlugin : Plugin {
add("androidTestImplementation", libs.findLibrary("androidx.lifecycle.runtimeTesting").get())
add("testImplementation", kotlin("test"))
+ add("testImplementation", project(":core:testing"))
add("testImplementation", libs.findLibrary("hilt.android.testing").get())
+ add("testImplementation", libs.findLibrary("squareup.okhttp").get())
add("debugImplementation", libs.findLibrary("androidx.compose.ui.test.manifest").get())
+
+ add("androidTestImplementation", project(":core:testing"))
add("androidTestImplementation", libs.findLibrary("androidx.navigation.testing").get())
add("androidTestImplementation", libs.findLibrary("androidx.compose.ui.test").get())
add("androidTestImplementation", libs.findLibrary("hilt.android.testing").get())
diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt
index 8230f220a..84c9fcdf3 100644
--- a/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt
@@ -1,13 +1,15 @@
import com.android.build.gradle.LibraryExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
+import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType
import org.mifos.mobile.configureAndroidCompose
class AndroidLibraryComposeConventionPlugin : Plugin {
override fun apply(target: Project) {
with(target) {
- pluginManager.apply("com.android.library")
+ apply(plugin = "com.android.library")
+ apply(plugin = "org.jetbrains.kotlin.plugin.compose")
val extension = extensions.getByType()
configureAndroidCompose(extension)
diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt
index 61fa7ed2d..5d050173e 100644
--- a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt
@@ -15,7 +15,11 @@ class AndroidLibraryConventionPlugin : Plugin {
with(pluginManager) {
apply("com.android.library")
apply("org.jetbrains.kotlin.android")
+ apply("mifos.android.library.jacoco")
apply("mifos.android.lint")
+ apply("mifos.detekt.plugin")
+ apply("mifos.spotless.plugin")
+ apply("mifos.ktlint.plugin")
}
extensions.configure {
diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt
new file mode 100644
index 000000000..c1fffea1e
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt
@@ -0,0 +1,24 @@
+
+import com.android.build.api.dsl.LibraryExtension
+import com.android.build.api.variant.LibraryAndroidComponentsExtension
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.getByType
+import org.mifos.mobile.configureJacoco
+
+class AndroidLibraryJacocoConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ pluginManager.apply("jacoco")
+ val androidExtension = extensions.getByType()
+
+ androidExtension.buildTypes.configureEach {
+ enableAndroidTestCoverage = true
+ enableUnitTestCoverage = true
+ }
+
+ configureJacoco(extensions.getByType())
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt
index 007ce8f34..af7beed6b 100644
--- a/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt
@@ -30,10 +30,8 @@ private fun Lint.configure() {
xmlReport = true
checkDependencies = true
abortOnError = false
- enable += "ComposeM2Api"
- error += "ComposeM2Api"
// Disable this rule until we ship the libraries to some maven.
-// disable += "ResourceName"
+ disable += "ResourceName"
baseline = File("lint-baseline.xml")
explainIssues = true
htmlReport = true
diff --git a/build-logic/convention/src/main/kotlin/MifosDetektConventionPlugin.kt b/build-logic/convention/src/main/kotlin/MifosDetektConventionPlugin.kt
new file mode 100644
index 000000000..b648e6859
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/MifosDetektConventionPlugin.kt
@@ -0,0 +1,22 @@
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.mifos.mobile.configureDetekt
+import org.mifos.mobile.detektGradle
+
+class MifosDetektConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ applyPlugins()
+
+ detektGradle {
+ configureDetekt(this)
+ }
+ }
+ }
+
+ private fun Project.applyPlugins() {
+ pluginManager.apply {
+ apply("io.gitlab.arturbosch.detekt")
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/MifosGitHooksConventionPlugin.kt b/build-logic/convention/src/main/kotlin/MifosGitHooksConventionPlugin.kt
new file mode 100644
index 000000000..18208729a
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/MifosGitHooksConventionPlugin.kt
@@ -0,0 +1,54 @@
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.Exec
+import org.gradle.kotlin.dsl.register
+import java.util.Locale
+
+class MifosGitHooksConventionPlugin : Plugin {
+ override fun apply(project: Project) {
+ // Define a function to check if the OS is Linux or MacOS
+ fun isLinuxOrMacOs(): Boolean {
+ val osName = System.getProperty("os.name").lowercase(Locale.getDefault())
+ return osName.contains("linux") || osName.contains("mac os") || osName.contains("macos")
+ }
+
+ // Define the copyGitHooks task
+ project.tasks.register("copyGitHooks") {
+ description = "Copies the git hooks from /scripts to the .git/hooks folder."
+ from("${project.rootDir}/scripts/") {
+ include("**/*.sh")
+ rename { it.removeSuffix(".sh") }
+ }
+ into("${project.rootDir}/.git/hooks")
+ }
+
+ // Define the installGitHooks task
+ project.tasks.register("installGitHooks") {
+ description = "Installs the pre-commit git hooks from the scripts directory."
+ group = "git hooks"
+ workingDir = project.rootDir
+
+ if (isLinuxOrMacOs()) {
+ commandLine("chmod", "-R", "+x", ".git/hooks/")
+ }else {
+ commandLine("cmd", "/c", "attrib", "-R", "+X", ".git/hooks/*.*")
+ }
+ dependsOn(project.tasks.named("copyGitHooks"))
+
+ doLast {
+ println("Git hooks installed successfully.")
+ }
+ }
+
+ // Configure task dependencies after evaluation
+ project.afterEvaluate {
+ project.tasks.matching {
+ it.name in listOf("preBuild", "build", "assembleDebug", "assembleRelease", "installDebug", "installRelease", "clean")
+ }.configureEach {
+ dependsOn(project.tasks.named("installGitHooks"))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/MifosKtlintConventionPlugin.kt b/build-logic/convention/src/main/kotlin/MifosKtlintConventionPlugin.kt
new file mode 100644
index 000000000..68afeb46e
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/MifosKtlintConventionPlugin.kt
@@ -0,0 +1,16 @@
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+class MifosKtlintConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ applyPlugins()
+ }
+ }
+
+ private fun Project.applyPlugins() {
+ pluginManager.apply {
+ apply("org.jlleitschuh.gradle.ktlint")
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/MifosSpotlessConventionPlugin.kt b/build-logic/convention/src/main/kotlin/MifosSpotlessConventionPlugin.kt
new file mode 100644
index 000000000..fcc502f73
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/MifosSpotlessConventionPlugin.kt
@@ -0,0 +1,22 @@
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.mifos.mobile.configureSpotless
+import org.mifos.mobile.spotlessGradle
+
+class MifosSpotlessConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ applyPlugins()
+
+ spotlessGradle {
+ configureSpotless(this)
+ }
+ }
+ }
+
+ private fun Project.applyPlugins() {
+ pluginManager.apply {
+ apply("com.diffplug.spotless")
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/org/mifos/mobile/AndroidCompose.kt b/build-logic/convention/src/main/kotlin/org/mifos/mobile/AndroidCompose.kt
index bfcf93b12..24886e875 100644
--- a/build-logic/convention/src/main/kotlin/org/mifos/mobile/AndroidCompose.kt
+++ b/build-logic/convention/src/main/kotlin/org/mifos/mobile/AndroidCompose.kt
@@ -2,25 +2,23 @@ package org.mifos.mobile
import com.android.build.api.dsl.CommonExtension
import org.gradle.api.Project
+import org.gradle.api.provider.Provider
+import org.gradle.kotlin.dsl.assign
+import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
-import org.gradle.kotlin.dsl.withType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension
/**
* Configure Compose-specific options
*/
internal fun Project.configureAndroidCompose(
- commonExtension: CommonExtension<*, *, *, *, *>,
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
) {
commonExtension.apply {
buildFeatures {
compose = true
}
- composeOptions {
- kotlinCompilerExtensionVersion = libs.findVersion("androidxComposeCompiler").get().toString()
- }
-
dependencies {
val bom = libs.findLibrary("androidx-compose-bom").get()
add("implementation", platform(bom))
@@ -38,35 +36,22 @@ internal fun Project.configureAndroidCompose(
}
}
- tasks.withType().configureEach {
- kotlinOptions {
- freeCompilerArgs = freeCompilerArgs + buildComposeMetricsParameters()
- }
- }
-}
+ extensions.configure {
+ fun Provider.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } }
+ fun Provider<*>.relativeToRootProject(dir: String) = flatMap {
+ rootProject.layout.buildDirectory.dir(projectDir.toRelativeString(rootDir))
+ }.map { it.dir(dir) }
-private fun Project.buildComposeMetricsParameters(): List {
- val metricParameters = mutableListOf()
- val enableMetricsProvider = project.providers.gradleProperty("enableComposeCompilerMetrics")
- val relativePath = projectDir.relativeTo(rootDir)
- val buildDir = layout.buildDirectory.get().asFile
- val enableMetrics = (enableMetricsProvider.orNull == "true")
- if (enableMetrics) {
- val metricsFolder = buildDir.resolve("compose-metrics").resolve(relativePath)
- metricParameters.add("-P")
- metricParameters.add(
- "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + metricsFolder.absolutePath
- )
- }
+ project.providers.gradleProperty("enableComposeCompilerMetrics").onlyIfTrue()
+ .relativeToRootProject("compose-metrics")
+ .let(metricsDestination::set)
+
+ project.providers.gradleProperty("enableComposeCompilerReports").onlyIfTrue()
+ .relativeToRootProject("compose-reports")
+ .let(reportsDestination::set)
+
+ stabilityConfigurationFile = rootProject.layout.projectDirectory.file("compose_compiler_config.conf")
- val enableReportsProvider = project.providers.gradleProperty("enableComposeCompilerReports")
- val enableReports = (enableReportsProvider.orNull == "true")
- if (enableReports) {
- val reportsFolder = buildDir.resolve("compose-reports").resolve(relativePath)
- metricParameters.add("-P")
- metricParameters.add(
- "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + reportsFolder.absolutePath
- )
+ enableStrongSkippingMode = true
}
- return metricParameters.toList()
}
diff --git a/build-logic/convention/src/main/kotlin/org/mifos/mobile/Detekt.kt b/build-logic/convention/src/main/kotlin/org/mifos/mobile/Detekt.kt
new file mode 100644
index 000000000..052569353
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/mifos/mobile/Detekt.kt
@@ -0,0 +1,23 @@
+package org.mifos.mobile
+
+import io.gitlab.arturbosch.detekt.Detekt
+import io.gitlab.arturbosch.detekt.extensions.DetektExtension
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+import org.gradle.kotlin.dsl.named
+
+internal fun Project.configureDetekt(extension: DetektExtension) = extension.apply {
+ tasks.named("detekt") {
+ reports {
+ xml.required.set(true)
+ html.required.set(true)
+ txt.required.set(true)
+ sarif.required.set(true)
+ md.required.set(true)
+ }
+ }
+ dependencies {
+ "detektPlugins"(libs.findLibrary("detekt-formatting").get())
+ "detektPlugins"(libs.findLibrary("twitter-detekt-compose").get())
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/org/mifos/mobile/Jacoco.kt b/build-logic/convention/src/main/kotlin/org/mifos/mobile/Jacoco.kt
new file mode 100644
index 000000000..71d29401d
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/mifos/mobile/Jacoco.kt
@@ -0,0 +1,104 @@
+package org.mifos.mobile
+
+
+import com.android.build.api.artifact.ScopedArtifact
+import com.android.build.api.variant.AndroidComponentsExtension
+import com.android.build.api.variant.ScopedArtifacts
+import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.file.RegularFile
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.tasks.testing.Test
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.register
+import org.gradle.kotlin.dsl.withType
+import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
+import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
+import org.gradle.testing.jacoco.tasks.JacocoReport
+import java.util.Locale
+
+private val coverageExclusions = listOf(
+ // Android
+ "**/R.class",
+ "**/R\$*.class",
+ "**/BuildConfig.*",
+ "**/Manifest*.*",
+ "**/*_Hilt*.class",
+ "**/Hilt_*.class",
+)
+
+private fun String.capitalize() = replaceFirstChar {
+ if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
+}
+
+/**
+ * Creates a new task that generates a combined coverage report with data from local and
+ * instrumented tests.
+ *
+ * `create{variant}CombinedCoverageReport`
+ *
+ * Note that coverage data must exist before running the task. This allows us to run device
+ * tests on CI using a different Github Action or an external device farm.
+ */
+internal fun Project.configureJacoco(
+ androidComponentsExtension: AndroidComponentsExtension<*, *, *>,
+) {
+ configure {
+ toolVersion = libs.findVersion("jacoco").get().toString()
+ }
+
+ androidComponentsExtension.onVariants { variant ->
+ val myObjFactory = project.objects
+ val buildDir = layout.buildDirectory.get().asFile
+ val allJars: ListProperty = myObjFactory.listProperty(RegularFile::class.java)
+ val allDirectories: ListProperty = myObjFactory.listProperty(Directory::class.java)
+ val reportTask =
+ tasks.register("create${variant.name.capitalize()}CombinedCoverageReport", JacocoReport::class) {
+
+ classDirectories.setFrom(
+ allJars,
+ allDirectories.map { dirs ->
+ dirs.map { dir ->
+ myObjFactory.fileTree().setDir(dir).exclude(coverageExclusions)
+ }
+ }
+ )
+ reports {
+ xml.required.set(true)
+ html.required.set(true)
+ }
+
+ // TODO: This is missing files in src/debug/, src/prod, src/demo, src/demoDebug...
+ sourceDirectories.setFrom(files("$projectDir/src/main/java", "$projectDir/src/main/kotlin"))
+
+ executionData.setFrom(
+ project.fileTree("$buildDir/outputs/unit_test_code_coverage/${variant.name}UnitTest")
+ .matching { include("**/*.exec") },
+
+ project.fileTree("$buildDir/outputs/code_coverage/${variant.name}AndroidTest")
+ .matching { include("**/*.ec") }
+ )
+ }
+
+
+ variant.artifacts.forScope(ScopedArtifacts.Scope.PROJECT)
+ .use(reportTask)
+ .toGet(
+ ScopedArtifact.CLASSES,
+ { _ -> allJars },
+ { _ -> allDirectories },
+ )
+ }
+
+ tasks.withType().configureEach {
+ configure {
+ // Required for JaCoCo + Robolectric
+ // https://github.com/robolectric/robolectric/issues/2230
+ isIncludeNoLocationClasses = true
+
+ // Required for JDK 11 with the above
+ // https://github.com/gradle/gradle/issues/5184#issuecomment-391982009
+ excludes = listOf("jdk.internal.*")
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/kotlin/org/mifos/mobile/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/org/mifos/mobile/KotlinAndroid.kt
index 35720b5ce..e4cc23a61 100644
--- a/build-logic/convention/src/main/kotlin/org/mifos/mobile/KotlinAndroid.kt
+++ b/build-logic/convention/src/main/kotlin/org/mifos/mobile/KotlinAndroid.kt
@@ -4,23 +4,26 @@ import com.android.build.api.dsl.CommonExtension
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
+import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.provideDelegate
-import org.gradle.kotlin.dsl.withType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
+import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
+import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
/**
* Configure base Kotlin with Android options
*/
internal fun Project.configureKotlinAndroid(
- commonExtension: CommonExtension<*, *, *, *, *>,
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
) {
commonExtension.apply {
compileSdk = 34
defaultConfig {
- minSdk = 24
+ minSdk = 26
}
compileOptions {
@@ -32,7 +35,7 @@ internal fun Project.configureKotlinAndroid(
}
}
- configureKotlin()
+ configureKotlin()
dependencies {
add("coreLibraryDesugaring", libs.findLibrary("android.desugarJdkLibs").get())
@@ -50,26 +53,30 @@ internal fun Project.configureKotlinJvm() {
targetCompatibility = JavaVersion.VERSION_17
}
- configureKotlin()
+ configureKotlin()
}
/**
* Configure base Kotlin options
*/
-private fun Project.configureKotlin() {
- // Use withType to workaround https://youtrack.jetbrains.com/issue/KT-55947
- tasks.withType().configureEach {
- kotlinOptions {
- // Set JVM target to 11
- jvmTarget = JavaVersion.VERSION_17.toString()
- // Treat all Kotlin warnings as errors (disabled by default)
- // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
- val warningsAsErrors: String? by project
- allWarningsAsErrors = warningsAsErrors.toBoolean()
- freeCompilerArgs = freeCompilerArgs + listOf(
- // Enable experimental coroutines APIs, including Flow
- "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
- )
- }
+private inline fun Project.configureKotlin() = configure {
+ // Treat all Kotlin warnings as errors (disabled by default)
+ // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
+ val warningsAsErrors: String? by project
+ when (this) {
+ is KotlinAndroidProjectExtension -> compilerOptions
+ is KotlinJvmProjectExtension -> compilerOptions
+ else -> TODO("Unsupported project extension $this ${T::class}")
+ }.apply {
+ jvmTarget = JvmTarget.JVM_17
+ allWarningsAsErrors = warningsAsErrors.toBoolean()
+ freeCompilerArgs.add(
+ // Enable experimental coroutines APIs, including Flow
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ )
+ freeCompilerArgs.add(
+ "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api"
+ )
}
}
+
diff --git a/build-logic/convention/src/main/kotlin/org/mifos/mobile/ProjectExtensions.kt b/build-logic/convention/src/main/kotlin/org/mifos/mobile/ProjectExtensions.kt
index 797c2a5be..b66f8b9dd 100644
--- a/build-logic/convention/src/main/kotlin/org/mifos/mobile/ProjectExtensions.kt
+++ b/build-logic/convention/src/main/kotlin/org/mifos/mobile/ProjectExtensions.kt
@@ -1,9 +1,22 @@
package org.mifos.mobile
+import com.diffplug.gradle.spotless.SpotlessExtension
+import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
+import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.getByType
val Project.libs
get(): VersionCatalog = extensions.getByType().named("libs")
+
+inline fun Project.detektGradle(crossinline configure: DetektExtension.() -> Unit) =
+ extensions.configure {
+ configure()
+ }
+
+inline fun Project.spotlessGradle(crossinline configure: SpotlessExtension.() -> Unit) =
+ extensions.configure {
+ configure()
+ }
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/org/mifos/mobile/Spotless.kt b/build-logic/convention/src/main/kotlin/org/mifos/mobile/Spotless.kt
new file mode 100644
index 000000000..f6a2c6225
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/mifos/mobile/Spotless.kt
@@ -0,0 +1,31 @@
+package org.mifos.mobile
+
+import com.diffplug.gradle.spotless.SpotlessExtension
+import org.gradle.api.Project
+
+val ktlintVersion = "1.0.1"
+
+internal fun Project.configureSpotless(extension: SpotlessExtension) = extension.apply {
+ kotlin {
+ target("**/*.kt")
+ targetExclude("**/build/**/*.kt")
+ ktlint(ktlintVersion).editorConfigOverride(
+ mapOf("android" to "true"),
+ )
+ licenseHeaderFile(rootProject.file("spotless/copyright.kt"))
+ }
+
+ format("kts") {
+ target("**/*.kts")
+ targetExclude("**/build/**/*.kts")
+ // Look for the first line that doesn't have a block comment (assumed to be the license)
+ licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)")
+ }
+
+ format("xml") {
+ target("**/*.xml")
+ targetExclude("**/build/**/*.xml")
+ // Look for the first XML tag that isn't a comment (
-
+
+
+
\ No newline at end of file
diff --git a/core/data/src/main/java/org/mifos/mobile/core/data/di/RepositoryModule.kt b/core/data/src/main/java/org/mifos/mobile/core/data/di/RepositoryModule.kt
index aa5c26389..e8f92feb5 100644
--- a/core/data/src/main/java/org/mifos/mobile/core/data/di/RepositoryModule.kt
+++ b/core/data/src/main/java/org/mifos/mobile/core/data/di/RepositoryModule.kt
@@ -27,8 +27,10 @@ import org.mifos.mobile.core.data.repository.SavingsAccountRepository
import org.mifos.mobile.core.data.repository.ThirdPartyTransferRepository
import org.mifos.mobile.core.data.repository.TransferRepository
import org.mifos.mobile.core.data.repository.UserAuthRepository
+import org.mifos.mobile.core.data.repository.UserDataRepository
import org.mifos.mobile.core.data.repository.UserDetailRepository
import org.mifos.mobile.core.data.repositoryImpl.AccountsRepositoryImp
+import org.mifos.mobile.core.data.repositoryImpl.AuthenticationUserRepository
import org.mifos.mobile.core.data.repositoryImpl.BeneficiaryRepositoryImp
import org.mifos.mobile.core.data.repositoryImpl.ClientChargeRepositoryImp
import org.mifos.mobile.core.data.repositoryImpl.ClientRepositoryImp
@@ -43,11 +45,23 @@ import org.mifos.mobile.core.data.repositoryImpl.ThirdPartyTransferRepositoryImp
import org.mifos.mobile.core.data.repositoryImpl.TransferRepositoryImp
import org.mifos.mobile.core.data.repositoryImpl.UserAuthRepositoryImp
import org.mifos.mobile.core.data.repositoryImpl.UserDetailRepositoryImp
+import org.mifos.mobile.core.data.utils.ConnectivityManagerNetworkMonitor
+import org.mifos.mobile.core.data.utils.NetworkMonitor
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
+ @Binds
+ internal abstract fun bindsUserDataRepository(
+ authenticationUserRepository: AuthenticationUserRepository,
+ ): UserDataRepository
+
+ @Binds
+ internal abstract fun bindsNetworkMonitor(
+ networkMonitor: ConnectivityManagerNetworkMonitor,
+ ): NetworkMonitor
+
@Binds
internal abstract fun providesUserAuthRepository(
repository: UserAuthRepositoryImp,
diff --git a/core/data/src/main/java/org/mifos/mobile/core/data/repository/UserDataRepository.kt b/core/data/src/main/java/org/mifos/mobile/core/data/repository/UserDataRepository.kt
new file mode 100644
index 000000000..9fe5c7a68
--- /dev/null
+++ b/core/data/src/main/java/org/mifos/mobile/core/data/repository/UserDataRepository.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.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import org.mifos.mobile.core.model.UserData
+
+interface UserDataRepository {
+ /**
+ * Stream of [UserData]
+ */
+ val userData: Flow
+
+ fun logOut(): Unit
+}
diff --git a/core/data/src/main/java/org/mifos/mobile/core/data/repositoryImpl/AuthenticationUserRepository.kt b/core/data/src/main/java/org/mifos/mobile/core/data/repositoryImpl/AuthenticationUserRepository.kt
new file mode 100644
index 000000000..739ab6b1d
--- /dev/null
+++ b/core/data/src/main/java/org/mifos/mobile/core/data/repositoryImpl/AuthenticationUserRepository.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.data.repositoryImpl
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+import org.mifos.mobile.core.data.repository.UserDataRepository
+import org.mifos.mobile.core.datastore.PreferencesHelper
+import org.mifos.mobile.core.model.UserData
+import javax.inject.Inject
+
+class AuthenticationUserRepository @Inject constructor(
+ private val preferencesHelper: PreferencesHelper,
+) : UserDataRepository {
+
+ override val userData: Flow = flow {
+ emit(
+ UserData(
+ isAuthenticated = !preferencesHelper.token.isNullOrEmpty(),
+ userName = preferencesHelper.userName ?: "",
+ clientId = preferencesHelper.clientId ?: 0,
+ ),
+ )
+ }
+
+ override fun logOut() {
+ preferencesHelper.clear()
+ }
+}
diff --git a/core/data/src/main/java/org/mifos/mobile/core/data/repositoryImpl/ReviewLoanApplicationRepositoryImpl.kt b/core/data/src/main/java/org/mifos/mobile/core/data/repositoryImpl/ReviewLoanApplicationRepositoryImpl.kt
index f20e9ba24..be06aa042 100644
--- a/core/data/src/main/java/org/mifos/mobile/core/data/repositoryImpl/ReviewLoanApplicationRepositoryImpl.kt
+++ b/core/data/src/main/java/org/mifos/mobile/core/data/repositoryImpl/ReviewLoanApplicationRepositoryImpl.kt
@@ -29,7 +29,7 @@ class ReviewLoanApplicationRepositoryImpl @Inject constructor(
): Flow {
return flow {
emit(
- if (loanState == org.mifos.mobile.core.model.enums.LoanState.CREATE) {
+ if (loanState == LoanState.CREATE) {
dataManager.createLoansAccount(loansPayload)
} else {
dataManager.updateLoanAccount(loanId, loansPayload)
diff --git a/core/data/src/main/java/org/mifos/mobile/core/data/utils/ConnectivityManagerNetworkMonitor.kt b/core/data/src/main/java/org/mifos/mobile/core/data/utils/ConnectivityManagerNetworkMonitor.kt
new file mode 100644
index 000000000..ee1aa6a23
--- /dev/null
+++ b/core/data/src/main/java/org/mifos/mobile/core/data/utils/ConnectivityManagerNetworkMonitor.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.data.utils
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.ConnectivityManager.NetworkCallback
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkRequest
+import android.net.NetworkRequest.Builder
+import androidx.core.content.getSystemService
+import dagger.hilt.android.qualifiers.ApplicationContext
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import javax.inject.Inject
+
+class ConnectivityManagerNetworkMonitor @Inject constructor(
+ @ApplicationContext private val context: Context,
+) : NetworkMonitor {
+ override val isOnline: Flow = callbackFlow {
+ val connectivityManager = context.getSystemService()
+ if (connectivityManager == null) {
+ channel.trySend(false)
+ channel.close()
+ return@callbackFlow
+ }
+
+ /**
+ * The callback's methods are invoked on changes to *any* network matching the [NetworkRequest],
+ * not just the active network. So we can simply track the presence (or absence) of such [Network].
+ */
+ val callback = object : NetworkCallback() {
+
+ private val networks = mutableSetOf()
+
+ override fun onAvailable(network: Network) {
+ networks += network
+ channel.trySend(true)
+ }
+
+ override fun onLost(network: Network) {
+ networks -= network
+ channel.trySend(networks.isNotEmpty())
+ }
+ }
+
+ val request = Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build()
+ connectivityManager.registerNetworkCallback(request, callback)
+
+ /**
+ * Sends the latest connectivity status to the underlying channel.
+ */
+ channel.trySend(connectivityManager.isCurrentlyConnected())
+
+ awaitClose {
+ connectivityManager.unregisterNetworkCallback(callback)
+ }
+ }
+ .conflate()
+
+ private fun ConnectivityManager.isCurrentlyConnected() = activeNetwork
+ ?.let(::getNetworkCapabilities)
+ ?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
+}
diff --git a/core/data/src/main/java/org/mifos/mobile/core/data/utils/NetworkMonitor.kt b/core/data/src/main/java/org/mifos/mobile/core/data/utils/NetworkMonitor.kt
new file mode 100644
index 000000000..9f2326c9c
--- /dev/null
+++ b/core/data/src/main/java/org/mifos/mobile/core/data/utils/NetworkMonitor.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.data.utils
+
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Utility for reporting app connectivity status
+ */
+interface NetworkMonitor {
+ val isOnline: Flow
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/AccountsRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/AccountsRepositoryImpTest.kt
similarity index 69%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/AccountsRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/AccountsRepositoryImpTest.kt
index e9b4293f5..fbd8a7f71 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/AccountsRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/AccountsRepositoryImpTest.kt
@@ -1,31 +1,39 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
-import CoroutineTestRule
import app.cash.turbine.test
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.client.ClientAccounts
-import org.mifos.mobile.util.checkForUnsuccessfulOperation
+import org.mifos.mobile.core.data.repository.AccountsRepository
+import org.mifos.mobile.core.data.repositoryImpl.AccountsRepositoryImp
+import org.mifos.mobile.core.model.entity.client.ClientAccounts
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
-import java.io.IOException
@RunWith(MockitoJUnitRunner::class)
@ExperimentalCoroutinesApi
class AccountsRepositoryImpTest {
@get:Rule
- val coroutineTestRule = CoroutineTestRule()
+ val coroutineTestRule = MainDispatcherRule()
@Mock
lateinit var dataManager: DataManager
@@ -61,5 +69,4 @@ class AccountsRepositoryImpTest {
assert(Throwable("Error occurred") == awaitError())
}
}
-
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/BeneficiaryRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/BeneficiaryRepositoryImpTest.kt
similarity index 80%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/BeneficiaryRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/BeneficiaryRepositoryImpTest.kt
index 4005ab8d9..61d65080b 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/BeneficiaryRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/BeneficiaryRepositoryImpTest.kt
@@ -1,37 +1,43 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
-import CoroutineTestRule
import app.cash.turbine.test
import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import okhttp3.ResponseBody
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.beneficiary.Beneficiary
-import org.mifos.mobile.models.beneficiary.BeneficiaryPayload
-import org.mifos.mobile.models.beneficiary.BeneficiaryUpdatePayload
-import org.mifos.mobile.models.templates.beneficiary.BeneficiaryTemplate
-import org.mifos.mobile.util.checkForUnsuccessfulOperation
+import org.mifos.mobile.core.data.repositoryImpl.BeneficiaryRepositoryImp
+import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary
+import org.mifos.mobile.core.model.entity.beneficiary.BeneficiaryPayload
+import org.mifos.mobile.core.model.entity.beneficiary.BeneficiaryUpdatePayload
+import org.mifos.mobile.core.model.entity.templates.beneficiary.BeneficiaryTemplate
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
-import org.mockito.Mockito.*
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
-import retrofit2.Response
@RunWith(MockitoJUnitRunner::class)
@ExperimentalCoroutinesApi
class BeneficiaryRepositoryImpTest {
@get:Rule
- val coroutineTestRule = CoroutineTestRule()
+ val coroutineTestRule = MainDispatcherRule()
@Mock
lateinit var dataManager: DataManager
@@ -46,7 +52,7 @@ class BeneficiaryRepositoryImpTest {
@Test
fun testBeneficiaryTemplate_Successful() = runTest {
- val success = mock(BeneficiaryTemplate::class.java)
+ val success = mock(BeneficiaryTemplate::class.java)
`when`(dataManager.beneficiaryTemplate())
.thenReturn(success)
@@ -57,18 +63,18 @@ class BeneficiaryRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).beneficiaryTemplate()
- }
+ }
@Test(expected = Exception::class)
fun testBeneficiaryTemplate_Unsuccessful() = runTest {
- `when`(dataManager.beneficiaryTemplate())
- .thenThrow( Exception("Error occurred"))
- val resultFlow= beneficiaryRepositoryImp.beneficiaryTemplate()
+ `when`(dataManager.beneficiaryTemplate())
+ .thenThrow(Exception("Error occurred"))
+ val resultFlow = beneficiaryRepositoryImp.beneficiaryTemplate()
resultFlow.test {
assert(Throwable("Error occurred") == awaitError())
}
verify(dataManager).beneficiaryTemplate()
- }
+ }
@Test
fun testCreateBeneficiary_Successful() = runTest {
@@ -115,16 +121,14 @@ class BeneficiaryRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).updateBeneficiary(123L, beneficiaryUpdatePayload)
-
}
@Test
fun testUpdateBeneficiary_Unsuccessful() = runTest {
-
val beneficiaryUpdatePayload = mock(BeneficiaryUpdatePayload::class.java)
`when`(dataManager.updateBeneficiary(123L, beneficiaryUpdatePayload)).thenThrow(
- Exception("Error occurred")
+ Exception("Error occurred"),
)
val result = beneficiaryRepositoryImp.updateBeneficiary(123L, beneficiaryUpdatePayload)
@@ -133,7 +137,6 @@ class BeneficiaryRepositoryImpTest {
}
}
-
@Test
fun testDeleteBeneficiary_Successful() = runTest {
val success = mock(ResponseBody::class.java)
@@ -180,4 +183,4 @@ class BeneficiaryRepositoryImpTest {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/ClientChargeRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/ClientChargeRepositoryImpTest.kt
similarity index 70%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/ClientChargeRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/ClientChargeRepositoryImpTest.kt
index 56285f63d..d805e9a06 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/ClientChargeRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/ClientChargeRepositoryImpTest.kt
@@ -1,33 +1,39 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
-import CoroutineTestRule
import app.cash.turbine.test
import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.Charge
-import org.mifos.mobile.models.Page
-import org.mifos.mobile.util.checkForUnsuccessfulOperation
+import org.mifos.mobile.core.data.repositoryImpl.ClientChargeRepositoryImp
+import org.mifos.mobile.core.datastore.model.Charge
+import org.mifos.mobile.core.model.entity.Page
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
-
@RunWith(MockitoJUnitRunner::class)
@ExperimentalCoroutinesApi
class ClientChargeRepositoryImpTest {
@get:Rule
- val coroutineTestRule = CoroutineTestRule()
+ val coroutineTestRule = MainDispatcherRule()
@Mock
lateinit var dataManager: DataManager
@@ -42,9 +48,9 @@ class ClientChargeRepositoryImpTest {
@Test
fun testGetClientCharges_Successful() = runTest {
- val clientChargeMock = List(5) { mock(Charge::class.java)}
+ val clientChargeMock = List(5) { mock(Charge::class.java) }
val chargeList = clientChargeMock.toList()
- val success = Page(5,chargeList)
+ val success = Page(5, chargeList)
`when`(dataManager.getClientCharges(123L))
.thenReturn(success)
val resultFlow = clientChargeRepositoryImp.getClientCharges(123L)
@@ -56,19 +62,18 @@ class ClientChargeRepositoryImpTest {
@Test(expected = Exception::class)
fun testGetClientCharges_Unsuccessful() = runTest {
- `when`(dataManager.getClientCharges(123L))
- .thenThrow( Exception("Error occurred"))
+ `when`(dataManager.getClientCharges(123L))
+ .thenThrow(Exception("Error occurred"))
val result = clientChargeRepositoryImp.getClientCharges(123L)
- result.test {
+ result.test {
assert(Throwable("Error occurred") == awaitError())
}
-
}
@Test
fun testGetLoanCharges_Successful() = runTest {
- val loanChargeMock = mock(Charge::class.java)
- val success = List(5) { loanChargeMock }.toList()
+ val loanChargeMock = mock(Charge::class.java)
+ val success = List(5) { loanChargeMock }.toList()
`when`(dataManager.getLoanCharges(123L)).thenReturn(success)
val resultFlow = clientChargeRepositoryImp.getLoanCharges(123L)
resultFlow.test {
@@ -79,18 +84,18 @@ class ClientChargeRepositoryImpTest {
@Test(expected = Exception::class)
fun testGetLoanCharges_Unsuccessful() = runTest {
- `when`(dataManager.getLoanCharges(123L))
- .thenThrow( Exception("Error occurred"))
+ `when`(dataManager.getLoanCharges(123L))
+ .thenThrow(Exception("Error occurred"))
val result = clientChargeRepositoryImp.getLoanCharges(123L)
- result.test {
+ result.test {
assert(Throwable("Error occurred") == awaitError())
}
}
@Test
fun testGetSavingsCharges_Successful() = runTest {
- val savingChargeMock = mock(Charge::class.java)
- val success = List(5) { savingChargeMock }.toList()
+ val savingChargeMock = mock(Charge::class.java)
+ val success = List(5) { savingChargeMock }.toList()
`when`(dataManager.getSavingsCharges(123L)).thenReturn(success)
val resultFlow = clientChargeRepositoryImp.getSavingsCharges(123L)
resultFlow.test {
@@ -101,20 +106,19 @@ class ClientChargeRepositoryImpTest {
@Test(expected = Exception::class)
fun testGetSavingsCharges_Unsuccessful() = runTest {
- `when`(dataManager.getSavingsCharges(123L))
- .thenThrow( Exception("Error occurred"))
+ `when`(dataManager.getSavingsCharges(123L))
+ .thenThrow(Exception("Error occurred"))
val result = clientChargeRepositoryImp.getSavingsCharges(123L)
- result.test {
+ result.test {
assert(Throwable("Error occurred") == awaitError())
}
-
}
@Test
fun testClientLocalCharges_Successful() = runTest {
- val clientLocalChargeMock = List(5) { mock(Charge::class.java)}
+ val clientLocalChargeMock = List(5) { mock(Charge::class.java) }
val chargeList = clientLocalChargeMock.toList()
- val success = Page(5,chargeList)
+ val success = Page(5, chargeList)
`when`(dataManager.clientLocalCharges()).thenReturn(success)
val resultFlow = clientChargeRepositoryImp.clientLocalCharges()
resultFlow.test {
@@ -126,11 +130,10 @@ class ClientChargeRepositoryImpTest {
@Test(expected = Exception::class)
fun testClientLocalCharges_Unsuccessful() = runTest {
`when`(dataManager.clientLocalCharges())
- .thenThrow( Exception("Error occurred"))
+ .thenThrow(Exception("Error occurred"))
val result = clientChargeRepositoryImp.clientLocalCharges()
result.test {
assert(Throwable("Error occurred") == awaitError())
}
-
}
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/ClientRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/ClientRepositoryImpTest.kt
similarity index 66%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/ClientRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/ClientRepositoryImpTest.kt
index a583a8986..c1d39cf37 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/ClientRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/ClientRepositoryImpTest.kt
@@ -1,4 +1,13 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
import app.cash.turbine.test
import kotlinx.coroutines.Dispatchers
@@ -8,13 +17,16 @@ import kotlinx.coroutines.test.setMain
import okhttp3.Credentials
import org.junit.Assert
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import com.mifos.mobile.core.data.utils.FakeRemoteDataSource
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.api.local.PreferencesHelper
-import org.mifos.mobile.models.Page
-import org.mifos.mobile.models.client.Client
+import org.mifos.mobile.core.data.repositoryImpl.ClientRepositoryImp
+import org.mifos.mobile.core.datastore.PreferencesHelper
+import org.mifos.mobile.core.model.entity.Page
+import org.mifos.mobile.core.model.entity.client.Client
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.FakeRemoteDataSource
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito
@@ -27,6 +39,9 @@ import retrofit2.Retrofit
@RunWith(MockitoJUnitRunner::class)
class ClientRepositoryImpTest {
+ @get:Rule
+ val coroutineTestRule = MainDispatcherRule()
+
@Mock
lateinit var dataManager: DataManager
@@ -48,21 +63,23 @@ class ClientRepositoryImpTest {
clientRepositoryImp = ClientRepositoryImp(
dataManager,
preferencesHelper,
- retrofit
+ retrofit,
)
- mockClientPage = com.mifos.mobile.core.data.utils.FakeRemoteDataSource.clients
+ mockClientPage = FakeRemoteDataSource.clients
}
@Test
fun testLoadClient_SuccessResponseReceivedFromDataManager_ReturnsClientPageSuccessfully() =
- runTest {
+ runTest {
Dispatchers.setMain(Dispatchers.Unconfined)
- val successResponse:Page
- = Page(5, List(5) {
- (mock(Client::class.java) as Client)
- })
- Mockito.`when`(
- dataManager.clients()
+ val successResponse: Page = Page(
+ 5,
+ List(5) {
+ (mock(Client::class.java) as Client)
+ },
+ )
+ `when`(
+ dataManager.clients(),
).thenReturn(successResponse)
val resultFlow = clientRepositoryImp.loadClient()
@@ -70,19 +87,19 @@ class ClientRepositoryImpTest {
Assert.assertEquals(successResponse, awaitItem())
cancelAndIgnoreRemainingEvents()
}
- Mockito.verify(dataManager).clients()
+ Mockito.verify(dataManager).clients()
Dispatchers.resetMain()
}
@Test(expected = Exception::class)
- fun testLoadClient_ErrorResponseReceivedFromDataManager_ReturnsError() =runTest{
+ fun testLoadClient_ErrorResponseReceivedFromDataManager_ReturnsError() = runTest {
Dispatchers.setMain(Dispatchers.Unconfined)
- Mockito.`when`(
- dataManager.clients()
+ Mockito.`when`(
+ dataManager.clients(),
).thenThrow(Exception("Error occurred"))
val result = clientRepositoryImp.loadClient()
- result.test{
+ result.test {
assert(Throwable("Error occurred") == awaitError())
}
Mockito.verify(dataManager).clients()
@@ -101,4 +118,4 @@ class ClientRepositoryImpTest {
Mockito.verify(preferencesHelper).saveToken(authenticationToken)
}
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/GuarantorRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/GuarantorRepositoryImpTest.kt
similarity index 83%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/GuarantorRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/GuarantorRepositoryImpTest.kt
index af236d4a0..ff1b5e52c 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/GuarantorRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/GuarantorRepositoryImpTest.kt
@@ -1,6 +1,14 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
-import CoroutineTestRule
import app.cash.turbine.test
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -10,12 +18,16 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.guarantor.GuarantorApplicationPayload
-import org.mifos.mobile.models.guarantor.GuarantorPayload
-import org.mifos.mobile.models.guarantor.GuarantorTemplatePayload
+import org.mifos.mobile.core.data.repositoryImpl.GuarantorRepositoryImp
+import org.mifos.mobile.core.model.entity.guarantor.GuarantorApplicationPayload
+import org.mifos.mobile.core.model.entity.guarantor.GuarantorPayload
+import org.mifos.mobile.core.model.entity.guarantor.GuarantorTemplatePayload
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
-import org.mockito.Mockito.*
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
@@ -24,7 +36,7 @@ import org.mockito.junit.MockitoJUnitRunner
class GuarantorRepositoryImpTest {
@get:Rule
- val coroutineTestRule = CoroutineTestRule()
+ val coroutineTestRule = MainDispatcherRule()
@Mock
private lateinit var dataManager: DataManager
@@ -44,18 +56,17 @@ class GuarantorRepositoryImpTest {
`when`(dataManager.getGuarantorTemplate(123L)).thenReturn(success)
val result = guarantorRepositoryImp.getGuarantorTemplate(123L)
-
- result.test {
+
+ result.test {
assertEquals(success, awaitItem())
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).getGuarantorTemplate(123L)
-
}
@Test(expected = Exception::class)
fun testGetGuarantorTemplate_Unsuccessful() = runTest {
- val error = Exception("error")
+ val error = Exception("error")
`when`(dataManager.getGuarantorTemplate(123L)).thenThrow(error)
@@ -65,7 +76,6 @@ class GuarantorRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).getGuarantorTemplate(123L)
-
}
@Test
@@ -81,12 +91,11 @@ class GuarantorRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).createGuarantor(123L, payload)
-
}
@Test(expected = Exception::class)
fun testCreateGuarantor_Unsuccessful() = runTest {
- val error = Exception("Error")
+ val error = Exception("Error")
val payload = mock(GuarantorApplicationPayload::class.java)
`when`(dataManager.createGuarantor(123L, payload)).thenThrow(error)
@@ -97,7 +106,6 @@ class GuarantorRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).createGuarantor(123L, payload)
-
}
@Test
@@ -113,12 +121,11 @@ class GuarantorRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).updateGuarantor(payload, 11L, 22L)
-
}
@Test(expected = Exception::class)
fun testUpdateGuarantor_Unsuccessful() = runTest {
- val error = Exception("Error")
+ val error = Exception("Error")
val payload = mock(GuarantorApplicationPayload::class.java)
`when`(dataManager.updateGuarantor(payload, 11L, 22L)).thenThrow(error)
@@ -129,7 +136,6 @@ class GuarantorRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).updateGuarantor(payload, 11L, 22L)
-
}
@Test
@@ -143,12 +149,11 @@ class GuarantorRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).deleteGuarantor(1L, 2L)
-
}
@Test(expected = Exception::class)
fun testDeleteGuarantor_Unsuccessful() = runTest {
- val error = Exception("Error")
+ val error = Exception("Error")
`when`(dataManager.deleteGuarantor(1L, 2L)).thenThrow(error)
@@ -158,7 +163,6 @@ class GuarantorRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).deleteGuarantor(1L, 2L)
-
}
@Test
@@ -174,12 +178,11 @@ class GuarantorRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).getGuarantorList(123L)
-
}
@Test(expected = Exception::class)
fun testGetGuarantorList_Unsuccessful() = runTest {
- val error = Exception("Error")
+ val error = Exception("Error")
`when`(dataManager.getGuarantorList(123L)).thenThrow(error)
@@ -189,6 +192,5 @@ class GuarantorRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).getGuarantorList(123L)
-
}
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/HomeRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/HomeRepositoryImpTest.kt
similarity index 83%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/HomeRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/HomeRepositoryImpTest.kt
index 9c45fb72a..154f15c4f 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/HomeRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/HomeRepositoryImpTest.kt
@@ -1,9 +1,16 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
-import CoroutineTestRule
import app.cash.turbine.test
import junit.framework.Assert.assertEquals
-import junit.framework.Assert.fail
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import okhttp3.ResponseBody
@@ -11,12 +18,14 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.client.Client
-import org.mifos.mobile.models.client.ClientAccounts
+import org.mifos.mobile.core.data.repositoryImpl.HomeRepositoryImp
+import org.mifos.mobile.core.model.entity.client.Client
+import org.mifos.mobile.core.model.entity.client.ClientAccounts
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
@@ -25,7 +34,7 @@ import org.mockito.junit.MockitoJUnitRunner
class HomeRepositoryImpTest {
@get:Rule
- val coroutineTestRule = CoroutineTestRule()
+ val coroutineTestRule = MainDispatcherRule()
@Mock
lateinit var dataManager: DataManager
@@ -97,8 +106,8 @@ class HomeRepositoryImpTest {
@Test(expected = Exception::class)
fun testClientAccounts_Error() = runTest {
val errorMessage = "Failed to fetch client accounts"
- `when`(dataManager.clientAccounts()).
- thenThrow(Exception(errorMessage))
+ `when`(dataManager.clientAccounts())
+ .thenThrow(Exception(errorMessage))
val flow = homeRepositoryImp.clientAccounts()
flow.test {
@@ -148,4 +157,4 @@ class HomeRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
}
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/LoanRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/LoanRepositoryImpTest.kt
similarity index 79%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/LoanRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/LoanRepositoryImpTest.kt
index 811ca8d0e..ecaa38d1a 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/LoanRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/LoanRepositoryImpTest.kt
@@ -1,4 +1,13 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
import app.cash.turbine.test
import junit.framework.Assert.assertEquals
@@ -8,23 +17,30 @@ import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import okhttp3.ResponseBody
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.accounts.loan.LoanWithAssociations
-import org.mifos.mobile.models.accounts.loan.LoanWithdraw
-import org.mifos.mobile.models.templates.loans.LoanTemplate
+import org.mifos.mobile.core.data.repositoryImpl.LoanRepositoryImp
+import org.mifos.mobile.core.model.entity.accounts.loan.LoanWithAssociations
+import org.mifos.mobile.core.model.entity.accounts.loan.LoanWithdraw
+import org.mifos.mobile.core.model.entity.templates.loans.LoanTemplate
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
import retrofit2.Response
@RunWith(MockitoJUnitRunner::class)
class LoanRepositoryImpTest {
+
+ @get:Rule
+ val coroutineTestRule = MainDispatcherRule()
+
@Mock
lateinit var dataManager: DataManager
@@ -42,20 +58,20 @@ class LoanRepositoryImpTest {
@Test
fun testGetLoanWithAssociations_Successful() = runTest {
Dispatchers.setMain(Dispatchers.Unconfined)
- val success= mock(LoanWithAssociations::class.java)
+ val success = mock(LoanWithAssociations::class.java)
`when`(
dataManager.getLoanWithAssociations(
Mockito.anyString(),
- Mockito.anyLong()
- )
+ Mockito.anyLong(),
+ ),
).thenReturn(success)
val result = loanRepositoryImp.getLoanWithAssociations(
"associationType",
- 1
+ 1,
)
- result?.test {
+ result.test {
assertEquals(success, awaitItem())
cancelAndIgnoreRemainingEvents()
}
@@ -69,12 +85,12 @@ class LoanRepositoryImpTest {
`when`(
dataManager.getLoanWithAssociations(
Mockito.anyString(),
- Mockito.anyLong()
- )
+ Mockito.anyLong(),
+ ),
).thenThrow(Exception("Error occurred"))
val result = loanRepositoryImp.getLoanWithAssociations(
"associationType",
- 1
+ 1,
)
result!!.test {
assert(Throwable("Error occurred") == awaitError())
@@ -96,7 +112,7 @@ class LoanRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
verify(dataManager).withdrawLoanAccount(1, loanWithdraw)
- }
+ }
@Test(expected = Exception::class)
fun testWithdrawLoanAccount_Unsuccessful() = runTest {
@@ -114,12 +130,12 @@ class LoanRepositoryImpTest {
@Test
fun testTemplate_Successful() = runTest {
Dispatchers.setMain(Dispatchers.Unconfined)
- val success= mock(LoanTemplate::class.java)
+ val success = mock(LoanTemplate::class.java)
`when`(dataManager.loanTemplate()).thenReturn(success)
val result = loanRepositoryImp.template()
- result?.test {
+ result.test {
assertEquals(success, awaitItem())
cancelAndIgnoreRemainingEvents()
}
@@ -134,7 +150,7 @@ class LoanRepositoryImpTest {
.thenThrow(Exception("Error occurred"))
val result = loanRepositoryImp.template()
- result!!.test {
+ result.test {
assert(Throwable("Error occurred") == awaitError())
}
verify(dataManager).loanTemplate()
@@ -148,7 +164,7 @@ class LoanRepositoryImpTest {
`when`(dataManager.getLoanTemplateByProduct(1)).thenReturn(success)
val result = loanRepositoryImp.getLoanTemplateByProduct(1)
- result?.test {
+ result.test {
assertEquals(success, awaitItem())
cancelAndIgnoreRemainingEvents()
}
@@ -161,14 +177,14 @@ class LoanRepositoryImpTest {
Dispatchers.setMain(Dispatchers.Unconfined)
val error: Response =
Response.error(404, ResponseBody.create(null, "error"))
- `when`(dataManager.getLoanTemplateByProduct(1)).
- thenThrow(Exception("Error occurred"))
+ `when`(dataManager.getLoanTemplateByProduct(1))
+ .thenThrow(Exception("Error occurred"))
val result = loanRepositoryImp.getLoanTemplateByProduct(1)
- result!!.test {
+ result.test {
assert(Throwable("Error occurred") == awaitError())
}
verify(dataManager).getLoanTemplateByProduct(1)
Dispatchers.resetMain()
}
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/NotificationRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/NotificationRepositoryImpTest.kt
similarity index 66%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/NotificationRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/NotificationRepositoryImpTest.kt
index eea5d6874..17b032e56 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/NotificationRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/NotificationRepositoryImpTest.kt
@@ -1,20 +1,28 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
-import CoroutineTestRule
import app.cash.turbine.test
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.notification.MifosNotification
+import org.mifos.mobile.core.data.repository.NotificationRepository
+import org.mifos.mobile.core.data.repositoryImpl.NotificationRepositoryImp
+import org.mifos.mobile.core.datastore.model.MifosNotification
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@@ -25,7 +33,7 @@ import org.mockito.junit.MockitoJUnitRunner
class NotificationRepositoryImpTest {
@get:Rule
- val coroutineTestRule = CoroutineTestRule()
+ val coroutineTestRule = MainDispatcherRule()
@Mock
lateinit var dataManager: DataManager
@@ -41,9 +49,9 @@ class NotificationRepositoryImpTest {
@Test
fun testLoadNotifications_SuccessResponseReceivedFromDataManager_ReturnsSuccess() = runTest {
val notification = mock(MifosNotification::class.java)
- val notificationList = List(5){ notification}
- Mockito.`when`(
- dataManager.notifications()
+ val notificationList = List(5) { notification }
+ `when`(
+ dataManager.notifications(),
).thenReturn(notificationList)
val notifications = notificationRepositoryImp.loadNotifications()
@@ -65,5 +73,4 @@ class NotificationRepositoryImpTest {
assert(Throwable("Dummy error") == awaitItem())
}
}
-
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/RecentTransactionRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/RecentTransactionRepositoryImpTest.kt
similarity index 71%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/RecentTransactionRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/RecentTransactionRepositoryImpTest.kt
index 636a1b3cd..269988f34 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/RecentTransactionRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/RecentTransactionRepositoryImpTest.kt
@@ -1,33 +1,37 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
-import CoroutineTestRule
import app.cash.turbine.test
import junit.framework.TestCase.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
-import okhttp3.ResponseBody
-import org.junit.Assert
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.Page
-import org.mifos.mobile.models.Transaction
+import org.mifos.mobile.core.data.repositoryImpl.RecentTransactionRepositoryImp
+import org.mifos.mobile.core.model.entity.Page
+import org.mifos.mobile.core.model.entity.Transaction
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
-import retrofit2.Response
-
@RunWith(MockitoJUnitRunner::class)
class RecentTransactionRepositoryImpTest {
- @OptIn(ExperimentalCoroutinesApi::class)
@get:Rule
- val coroutineTestRule = CoroutineTestRule()
+ val coroutineTestRule = MainDispatcherRule()
@Mock
lateinit var dataManager: DataManager
@@ -42,7 +46,7 @@ class RecentTransactionRepositoryImpTest {
@Test
fun recentTransaction_successful_response_from_dataManger() = runTest {
- val success= mock(Page()::class.java)
+ val success = mock(Page()::class.java)
val offset = 0
val limit = 50
@@ -71,5 +75,4 @@ class RecentTransactionRepositoryImpTest {
}
Mockito.verify(dataManager).getRecentTransactions(offset, limit)
}
-
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/SavingsAccountRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/SavingsAccountRepositoryImpTest.kt
similarity index 76%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/SavingsAccountRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/SavingsAccountRepositoryImpTest.kt
index 55ededd17..de490b754 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/SavingsAccountRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/SavingsAccountRepositoryImpTest.kt
@@ -1,6 +1,14 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
-import CoroutineTestRule
import app.cash.turbine.test
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -11,14 +19,16 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.accounts.savings.SavingsAccountApplicationPayload
-import org.mifos.mobile.models.accounts.savings.SavingsAccountUpdatePayload
-import org.mifos.mobile.models.accounts.savings.SavingsAccountWithdrawPayload
-import org.mifos.mobile.models.accounts.savings.SavingsWithAssociations
-import org.mifos.mobile.models.templates.account.AccountOptionsTemplate
-import org.mifos.mobile.models.templates.savings.SavingsAccountTemplate
-import org.mifos.mobile.core.common.Constants
+import org.mifos.mobile.core.data.repository.SavingsAccountRepository
+import org.mifos.mobile.core.data.repositoryImpl.SavingsAccountRepositoryImp
+import org.mifos.mobile.core.model.entity.accounts.savings.SavingsAccountApplicationPayload
+import org.mifos.mobile.core.model.entity.accounts.savings.SavingsAccountUpdatePayload
+import org.mifos.mobile.core.model.entity.accounts.savings.SavingsAccountWithdrawPayload
+import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations
+import org.mifos.mobile.core.model.entity.templates.account.AccountOptionsTemplate
+import org.mifos.mobile.core.model.entity.templates.savings.SavingsAccountTemplate
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
@@ -30,7 +40,7 @@ import org.mockito.junit.MockitoJUnitRunner
class SavingsAccountRepositoryImpTest {
@get:Rule
- val coroutineTestRule = CoroutineTestRule()
+ val coroutineTestRule = MainDispatcherRule()
@Mock
lateinit var dataManager: DataManager
@@ -51,12 +61,12 @@ class SavingsAccountRepositoryImpTest {
runTest {
val mockSavingsWithAssociations = mock(SavingsWithAssociations::class.java)
Mockito.`when`(
- dataManager.getSavingsWithAssociations(mockAccountId, mockAssociationType)
+ dataManager.getSavingsWithAssociations(mockAccountId, mockAssociationType),
).thenReturn(mockSavingsWithAssociations)
val result = savingsAccountRepositoryImp.getSavingsWithAssociations(
mockAccountId,
- mockAssociationType
+ mockAssociationType,
)
result.test {
Assert.assertEquals(mockSavingsWithAssociations, awaitItem())
@@ -70,12 +80,12 @@ class SavingsAccountRepositoryImpTest {
fun testGetSavingsWithAssociations_ErrorResponseReceivedFromDataManager_ReturnsError() =
runTest {
Mockito.`when`(
- dataManager.getSavingsWithAssociations(mockAccountId, mockAssociationType)
+ dataManager.getSavingsWithAssociations(mockAccountId, mockAssociationType),
).thenThrow(Exception("Error occurred"))
val result = savingsAccountRepositoryImp.getSavingsWithAssociations(
mockAccountId,
- mockAssociationType
+ mockAssociationType,
)
result.test {
assertEquals(Throwable("Error occurred"), awaitItem())
@@ -83,7 +93,6 @@ class SavingsAccountRepositoryImpTest {
}
Mockito.verify(dataManager)
.getSavingsWithAssociations(mockAccountId, mockAssociationType)
-
}
@Test
@@ -91,7 +100,7 @@ class SavingsAccountRepositoryImpTest {
runTest {
val mockSavingsAccountTemplate = mock(SavingsAccountTemplate::class.java)
Mockito.`when`(
- dataManager.getSavingAccountApplicationTemplate(mockClientId)
+ dataManager.getSavingAccountApplicationTemplate(mockClientId),
).thenReturn(mockSavingsAccountTemplate)
val result =
@@ -107,7 +116,7 @@ class SavingsAccountRepositoryImpTest {
fun testGetSavingsAccountApplicationTemplate_ErrorResponseFromDataManager_ReturnsError() =
runTest {
Mockito.`when`(
- dataManager.getSavingAccountApplicationTemplate(mockClientId)
+ dataManager.getSavingAccountApplicationTemplate(mockClientId),
).thenThrow(Exception("Error occurred"))
val result =
@@ -123,14 +132,14 @@ class SavingsAccountRepositoryImpTest {
fun testSubmitSavingAccountApplication_SuccessResponseFromDataManager_ReturnsSuccess() =
runTest {
val mockSavingsAccountApplicationPayload =
- Mockito.mock(SavingsAccountApplicationPayload::class.java)
+ mock(SavingsAccountApplicationPayload::class.java)
val responseBody = mock(ResponseBody::class.java)
Mockito.`when`(
- dataManager.submitSavingAccountApplication(mockSavingsAccountApplicationPayload)
+ dataManager.submitSavingAccountApplication(mockSavingsAccountApplicationPayload),
).thenReturn(responseBody)
val result = savingsAccountRepositoryImp.submitSavingAccountApplication(
- mockSavingsAccountApplicationPayload
+ mockSavingsAccountApplicationPayload,
)
result.test {
assertEquals(responseBody, awaitItem())
@@ -144,35 +153,33 @@ class SavingsAccountRepositoryImpTest {
fun testSubmitSavingAccountApplication_ErrorResponseFromDataManager_ReturnsError() =
runTest {
val mockSavingsAccountApplicationPayload =
- Mockito.mock(SavingsAccountApplicationPayload::class.java)
+ mock(SavingsAccountApplicationPayload::class.java)
Mockito.`when`(
- dataManager.submitSavingAccountApplication(mockSavingsAccountApplicationPayload)
+ dataManager.submitSavingAccountApplication(mockSavingsAccountApplicationPayload),
).thenThrow(Exception("Error occurred"))
val result = savingsAccountRepositoryImp.submitSavingAccountApplication(
- mockSavingsAccountApplicationPayload
+ mockSavingsAccountApplicationPayload,
)
result.test {
assertEquals(Throwable("Error occurred"), awaitItem())
cancelAndIgnoreRemainingEvents()
-
}
Mockito.verify(dataManager)
.submitSavingAccountApplication(mockSavingsAccountApplicationPayload)
-
}
@Test
fun testUpdateSavingsAccount_SuccessResponseFromDataManager_ReturnsSuccess() = runTest {
- val mockSavingsAccountUpdatePayload = Mockito.mock(SavingsAccountUpdatePayload::class.java)
+ val mockSavingsAccountUpdatePayload = mock(SavingsAccountUpdatePayload::class.java)
val responseBody = mock(ResponseBody::class.java)
Mockito.`when`(
- dataManager.updateSavingsAccount(mockAccountId, mockSavingsAccountUpdatePayload)
+ dataManager.updateSavingsAccount(mockAccountId, mockSavingsAccountUpdatePayload),
).thenReturn(responseBody)
val result = savingsAccountRepositoryImp.updateSavingsAccount(
mockAccountId,
- mockSavingsAccountUpdatePayload
+ mockSavingsAccountUpdatePayload,
)
result.test {
assertEquals(responseBody, awaitItem())
@@ -184,14 +191,14 @@ class SavingsAccountRepositoryImpTest {
@Test(expected = Exception::class)
fun testUpdateSavingsAccount_ErrorResponseFromDataManager_ReturnsError() = runTest {
- val mockSavingsAccountUpdatePayload = Mockito.mock(SavingsAccountUpdatePayload::class.java)
+ val mockSavingsAccountUpdatePayload = mock(SavingsAccountUpdatePayload::class.java)
Mockito.`when`(
- dataManager.updateSavingsAccount(mockAccountId, mockSavingsAccountUpdatePayload)
+ dataManager.updateSavingsAccount(mockAccountId, mockSavingsAccountUpdatePayload),
).thenThrow(Exception("Error occurred"))
val result = savingsAccountRepositoryImp.updateSavingsAccount(
mockAccountId,
- mockSavingsAccountUpdatePayload
+ mockSavingsAccountUpdatePayload,
)
result.test {
assertEquals(Throwable("Error occurred"), awaitItem())
@@ -199,7 +206,6 @@ class SavingsAccountRepositoryImpTest {
}
Mockito.verify(dataManager)
.updateSavingsAccount(mockAccountId, mockSavingsAccountUpdatePayload)
-
}
@Test
@@ -207,23 +213,22 @@ class SavingsAccountRepositoryImpTest {
runTest {
val mockAccountId = "1"
val mockSavingsAccountWithdrawPayload =
- Mockito.mock(SavingsAccountWithdrawPayload::class.java)
+ mock(SavingsAccountWithdrawPayload::class.java)
val responseBody = mock(ResponseBody::class.java)
Mockito.`when`(
dataManager.submitWithdrawSavingsAccount(
mockAccountId,
- mockSavingsAccountWithdrawPayload
- )
+ mockSavingsAccountWithdrawPayload,
+ ),
).thenReturn(responseBody)
val result = savingsAccountRepositoryImp.submitWithdrawSavingsAccount(
mockAccountId,
- mockSavingsAccountWithdrawPayload
+ mockSavingsAccountWithdrawPayload,
)
result.test {
assertEquals(responseBody, awaitItem())
cancelAndIgnoreRemainingEvents()
-
}
Mockito.verify(dataManager)
.submitWithdrawSavingsAccount(mockAccountId, mockSavingsAccountWithdrawPayload)
@@ -233,17 +238,17 @@ class SavingsAccountRepositoryImpTest {
fun testSubmitWithdrawSavingsAccount_ErrorResponseFromDataManager_ReturnsError() = runTest {
val mockAccountId = "1"
val mockSavingsAccountWithdrawPayload =
- Mockito.mock(SavingsAccountWithdrawPayload::class.java)
- Mockito.`when`(
+ mock(SavingsAccountWithdrawPayload::class.java)
+ Mockito.`when`(
dataManager.submitWithdrawSavingsAccount(
mockAccountId,
- mockSavingsAccountWithdrawPayload
- )
+ mockSavingsAccountWithdrawPayload,
+ ),
).thenThrow(Exception("Error occurred"))
val result = savingsAccountRepositoryImp.submitWithdrawSavingsAccount(
mockAccountId,
- mockSavingsAccountWithdrawPayload
+ mockSavingsAccountWithdrawPayload,
)
result.test {
assertEquals(Throwable("Error occurred"), awaitItem())
@@ -251,37 +256,35 @@ class SavingsAccountRepositoryImpTest {
}
Mockito.verify(dataManager)
.submitWithdrawSavingsAccount(mockAccountId, mockSavingsAccountWithdrawPayload)
-
}
@Test
fun testLoanAccountTransferTemplate_SuccessResponseFromDataManager_ReturnsSuccess() =
runTest {
- val responseBody=mock(AccountOptionsTemplate::class.java)
+ val responseBody = mock(AccountOptionsTemplate::class.java)
Mockito.`when`(
- dataManager.accountTransferTemplate()
+ dataManager.accountTransferTemplate(null, null),
).thenReturn(responseBody)
- val result = savingsAccountRepositoryImp.loanAccountTransferTemplate()
+ val result = savingsAccountRepositoryImp.accountTransferTemplate(null, null)
result.test {
assertEquals(responseBody, awaitItem())
cancelAndIgnoreRemainingEvents()
}
- Mockito.verify(dataManager).accountTransferTemplate()
+ Mockito.verify(dataManager).accountTransferTemplate(null, null)
}
@Test(expected = Exception::class)
fun testLoanAccountTransferTemplate_ErrorResponseFromDataManager_ReturnsError() = runTest {
- Mockito.`when`(
- dataManager.accountTransferTemplate()
+ Mockito.`when`(
+ dataManager.accountTransferTemplate(null, null),
).thenThrow(Exception("Error occurred"))
- val result = savingsAccountRepositoryImp.loanAccountTransferTemplate()
+ val result = savingsAccountRepositoryImp.accountTransferTemplate(null, null)
result.test {
assertEquals(Throwable("Error occurred"), awaitItem())
cancelAndIgnoreRemainingEvents()
}
- Mockito.verify(dataManager).accountTransferTemplate()
-
+ Mockito.verify(dataManager).accountTransferTemplate(null, null)
}
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/ThirdPartyTransferRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/ThirdPartyTransferRepositoryImpTest.kt
similarity index 64%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/ThirdPartyTransferRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/ThirdPartyTransferRepositoryImpTest.kt
index bf9f68ed2..862f8ee56 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/ThirdPartyTransferRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/ThirdPartyTransferRepositoryImpTest.kt
@@ -1,36 +1,43 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
-import CoroutineTestRule
import app.cash.turbine.test
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
-import okhttp3.ResponseBody
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.beneficiary.Beneficiary
-import org.mifos.mobile.models.templates.account.AccountOptionsTemplate
+import org.mifos.mobile.core.data.repositoryImpl.ThirdPartyTransferRepositoryImp
+import org.mifos.mobile.core.model.entity.templates.account.AccountOptionsTemplate
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
-import retrofit2.Response
@RunWith(MockitoJUnitRunner::class)
@ExperimentalCoroutinesApi
class ThirdPartyTransferRepositoryImpTest {
@get:Rule
- val coroutineTestRule = CoroutineTestRule()
+ val coroutineTestRule = MainDispatcherRule()
@Mock
lateinit var dataManager: DataManager
- lateinit var transferRepositoryImp: ThirdPartyTransferRepositoryImp
+ private lateinit var transferRepositoryImp: ThirdPartyTransferRepositoryImp
@Before
fun setUp() {
@@ -40,7 +47,7 @@ class ThirdPartyTransferRepositoryImpTest {
@Test
fun testThirdPartyTransferTemplate_Successful() = runTest {
- val response= Mockito.mock(AccountOptionsTemplate::class.java)
+ val response = Mockito.mock(AccountOptionsTemplate::class.java)
`when`(dataManager.thirdPartyTransferTemplate()).thenReturn(response)
@@ -61,5 +68,4 @@ class ThirdPartyTransferRepositoryImpTest {
cancelAndIgnoreRemainingEvents()
}
}
-
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/TransferRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/TransferRepositoryImpTest.kt
similarity index 90%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/TransferRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/TransferRepositoryImpTest.kt
index ef2b8ecdd..81fd597ac 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/TransferRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/TransferRepositoryImpTest.kt
@@ -1,4 +1,13 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
import app.cash.turbine.test
import kotlinx.coroutines.Dispatchers
@@ -8,21 +17,25 @@ import kotlinx.coroutines.test.setMain
import okhttp3.ResponseBody
import org.junit.Assert
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.payload.TransferPayload
+import org.mifos.mobile.core.data.repositoryImpl.TransferRepositoryImp
+import org.mifos.mobile.core.model.entity.payload.TransferPayload
import org.mifos.mobile.core.model.enums.TransferType
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
-import retrofit2.Response
-
@RunWith(MockitoJUnitRunner::class)
class TransferRepositoryImpTest {
+ @get:Rule
+ val coroutineTestRule = MainDispatcherRule()
+
@Mock
lateinit var dataManager: DataManager
@@ -75,7 +88,7 @@ class TransferRepositoryImpTest {
transferPayload.locale,
transferPayload.fromAccountNumber,
transferPayload.toAccountNumber,
- org.mifos.mobile.core.model.enums.TransferType.TPT
+ TransferType.TPT,
)
result.test {
Assert.assertEquals(success, awaitItem())
@@ -125,7 +138,7 @@ class TransferRepositoryImpTest {
transferPayload.locale,
transferPayload.fromAccountNumber,
transferPayload.toAccountNumber,
- org.mifos.mobile.core.model.enums.TransferType.SELF
+ TransferType.SELF,
)
result.test {
Assert.assertEquals(success, awaitItem())
@@ -174,7 +187,7 @@ class TransferRepositoryImpTest {
transferPayload.locale,
transferPayload.fromAccountNumber,
transferPayload.toAccountNumber,
- org.mifos.mobile.core.model.enums.TransferType.TPT
+ TransferType.TPT,
)
result.test {
Assert.assertEquals(Throwable("Error occurred"), awaitError())
@@ -204,8 +217,8 @@ class TransferRepositoryImpTest {
this.fromAccountNumber = "0000001"
this.toAccountNumber = "0000002"
}
- Mockito.`when`(dataManager.makeTransfer(transferPayload)).
- thenThrow(Exception("Error occurred"))
+ Mockito.`when`(dataManager.makeTransfer(transferPayload))
+ .thenThrow(Exception("Error occurred"))
val result = transferProcessImp.makeTransfer(
transferPayload.fromOfficeId,
@@ -223,7 +236,7 @@ class TransferRepositoryImpTest {
transferPayload.locale,
transferPayload.fromAccountNumber,
transferPayload.toAccountNumber,
- org.mifos.mobile.core.model.enums.TransferType.SELF
+ TransferType.SELF,
)
result.test {
Assert.assertEquals(Throwable("Error occurred"), awaitError())
@@ -232,4 +245,4 @@ class TransferRepositoryImpTest {
Mockito.verify(dataManager).makeTransfer(transferPayload)
Dispatchers.resetMain()
}
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/UserAuthRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/UserAuthRepositoryImpTest.kt
similarity index 76%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/UserAuthRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/UserAuthRepositoryImpTest.kt
index e4a15ddb2..266e9b51e 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/UserAuthRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/UserAuthRepositoryImpTest.kt
@@ -1,6 +1,14 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
-import CoroutineTestRule
import app.cash.turbine.test
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -10,12 +18,15 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import com.mifos.mobile.core.data.utils.FakeRemoteDataSource
-import com.mifos.mobile.core.data.utils.FakeRemoteDataSource.userVerify
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.User
-import org.mifos.mobile.models.payload.LoginPayload
-import org.mifos.mobile.models.register.RegisterPayload
+import org.mifos.mobile.core.data.repository.UserAuthRepository
+import org.mifos.mobile.core.data.repositoryImpl.UserAuthRepositoryImp
+import org.mifos.mobile.core.model.entity.User
+import org.mifos.mobile.core.model.entity.payload.LoginPayload
+import org.mifos.mobile.core.model.entity.register.RegisterPayload
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.FakeRemoteDataSource
+import org.mifos.mobile.core.testing.util.FakeRemoteDataSource.userVerify
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
@@ -27,7 +38,7 @@ import org.mockito.junit.MockitoJUnitRunner
class UserAuthRepositoryImpTest {
@get:Rule
- val coroutineTestRule = CoroutineTestRule()
+ val coroutineTestRule = MainDispatcherRule()
@Mock
lateinit var dataManager: DataManager
@@ -35,18 +46,17 @@ class UserAuthRepositoryImpTest {
private lateinit var userAuthRepositoryImp: UserAuthRepository
private lateinit var mockUser: User
-
@Before
fun setUp() {
MockitoAnnotations.openMocks(this)
userAuthRepositoryImp = UserAuthRepositoryImp(dataManager)
- mockUser = com.mifos.mobile.core.data.utils.FakeRemoteDataSource.user
+ mockUser = FakeRemoteDataSource.user
}
@Test
fun testRegisterUser_SuccessResponseReceivedFromDataManager_ReturnSuccessfulRegistration() =
- runTest{
- val successResponse= mock(ResponseBody::class.java)
+ runTest {
+ val successResponse = mock(ResponseBody::class.java)
val registerPayload = RegisterPayload().apply {
this.accountNumber = "accountNumber"
this.authenticationMode = "authenticationMode"
@@ -68,20 +78,19 @@ class UserAuthRepositoryImpTest {
registerPayload.lastName,
registerPayload.mobileNumber,
registerPayload.password,
- registerPayload.username
+ registerPayload.username,
)
result.test {
assertEquals(successResponse, awaitItem())
cancelAndIgnoreRemainingEvents()
}
Mockito.verify(dataManager).registerUser(registerPayload)
-
}
@Test(expected = Exception::class)
fun testRegisterUser_ErrorResponseReceivedFromDataManager_ReturnsUnsuccessfulRegistration() =
- runTest{
- val registerPayload = RegisterPayload().apply {
+ runTest {
+ val registerPayload = RegisterPayload().apply {
this.accountNumber = "accountNumber"
this.authenticationMode = "authenticationMode"
this.email = "email"
@@ -103,7 +112,7 @@ class UserAuthRepositoryImpTest {
registerPayload.lastName,
registerPayload.mobileNumber,
registerPayload.password,
- registerPayload.username
+ registerPayload.username,
)
result.test {
assertEquals(Throwable("Error occurred"), awaitError())
@@ -113,38 +122,37 @@ class UserAuthRepositoryImpTest {
}
@Test
- fun testLogin_SuccessResponseReceivedFromDataManager_ReturnsUserSuccessfully() = runTest{
+ fun testLogin_SuccessResponseReceivedFromDataManager_ReturnsUserSuccessfully() = runTest {
val mockLoginPayload = LoginPayload().apply {
this.username = "username"
this.password = "password"
}
-
+
Mockito.`when`(
- dataManager.login(mockLoginPayload)
+ dataManager.login(mockLoginPayload),
).thenReturn(mockUser)
val result = userAuthRepositoryImp.login("username", "password")
-
+
result.test {
assertEquals(mockUser, awaitItem())
cancelAndIgnoreRemainingEvents()
}
Mockito.verify(dataManager).login(mockLoginPayload)
-
}
@Test(expected = Exception::class)
- fun testLogin_ErrorResponseReceivedFromDataManager_ReturnsError() = runTest{
+ fun testLogin_ErrorResponseReceivedFromDataManager_ReturnsError() = runTest {
val mockLoginPayload = LoginPayload().apply {
this.username = "username"
this.password = "password"
}
- Mockito.`when`(
- dataManager.login(mockLoginPayload)
+ Mockito.`when`(
+ dataManager.login(mockLoginPayload),
).thenThrow(Exception("Error occurred"))
val result = userAuthRepositoryImp.login("username", "password")
- result.test {
+ result.test {
assertEquals(Throwable("Error occurred"), awaitError())
cancelAndIgnoreRemainingEvents()
}
@@ -153,16 +161,16 @@ class UserAuthRepositoryImpTest {
@Test
fun testVerifyUser_SuccessResponseReceivedFromDataManager_ReturnsSuccessfulRegistrationVerification() =
- runTest{
- val successResponse= mock(ResponseBody::class.java)
+ runTest {
+ val successResponse = mock(ResponseBody::class.java)
Mockito.`when`(
- dataManager.verifyUser(userVerify)
+ dataManager.verifyUser(userVerify),
).thenReturn(successResponse)
val result =
userAuthRepositoryImp.verifyUser(
userVerify.authenticationToken,
- userVerify.requestId
+ userVerify.requestId,
)
result.test {
assertEquals(successResponse, awaitItem())
@@ -173,21 +181,20 @@ class UserAuthRepositoryImpTest {
@Test(expected = Exception::class)
fun testVerifyUser_ErrorResponseReceivedFromDataManager_ReturnsUnsuccessfulRegistrationVerification() =
- runTest{
- Mockito.`when`(
- dataManager.verifyUser(userVerify)
+ runTest {
+ Mockito.`when`(
+ dataManager.verifyUser(userVerify),
).thenThrow(Exception("Error occurred"))
val result =
userAuthRepositoryImp.verifyUser(
userVerify.authenticationToken,
- userVerify.requestId
+ userVerify.requestId,
)
result.test {
assert(Throwable("Error occurred") == awaitError())
cancelAndIgnoreRemainingEvents()
}
Mockito.verify(dataManager).verifyUser(userVerify)
-
}
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/test/java/org/mifos/mobile/repositories/UserDetailRepositoryImpTest.kt b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/UserDetailRepositoryImpTest.kt
similarity index 78%
rename from androidApp/src/test/java/org/mifos/mobile/repositories/UserDetailRepositoryImpTest.kt
rename to core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/UserDetailRepositoryImpTest.kt
index a9cca802f..c4cb3d679 100644
--- a/androidApp/src/test/java/org/mifos/mobile/repositories/UserDetailRepositoryImpTest.kt
+++ b/core/data/src/test/kotlin/org/mifos/mobile/core/data/repositories/UserDetailRepositoryImpTest.kt
@@ -1,23 +1,37 @@
-package org.mifos.mobile.repositories
+/*
+ * 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.data.repositories
import app.cash.turbine.test
import junit.framework.Assert.assertEquals
-import junit.framework.Assert.fail
-import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import okhttp3.ResponseBody
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
-import org.mifos.mobile.api.DataManager
-import org.mifos.mobile.models.notification.NotificationRegisterPayload
-import org.mifos.mobile.models.notification.NotificationUserDetail
+import org.mifos.mobile.core.data.repository.UserDetailRepository
+import org.mifos.mobile.core.data.repositoryImpl.UserDetailRepositoryImp
+import org.mifos.mobile.core.model.entity.notification.NotificationRegisterPayload
+import org.mifos.mobile.core.model.entity.notification.NotificationUserDetail
+import org.mifos.mobile.core.network.DataManager
+import org.mifos.mobile.core.testing.util.MainDispatcherRule
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
class UserDetailRepositoryImpTest {
+ @get:Rule
+ val coroutineTestRule = MainDispatcherRule()
+
@Mock
private lateinit var mockDataManager: DataManager
@@ -48,9 +62,9 @@ class UserDetailRepositoryImpTest {
val errorMessage = "Failed to register notification"
val mockPayload: NotificationRegisterPayload = mock(NotificationRegisterPayload::class.java)
`when`(mockDataManager.registerNotification(mockPayload)).thenThrow(
- Exception(
- errorMessage
- )
+ Exception(
+ errorMessage,
+ ),
)
val flow = userDetailRepository.registerNotification(mockPayload)
@@ -81,9 +95,9 @@ class UserDetailRepositoryImpTest {
val errorMessage = "Failed to get user notification"
val mockId = 123L
`when`(mockDataManager.getUserNotificationId(mockId)).thenThrow(
- Exception(
- errorMessage
- )
+ Exception(
+ errorMessage,
+ ),
)
val flow = userDetailRepository.getUserNotificationId(mockId)
@@ -100,7 +114,7 @@ class UserDetailRepositoryImpTest {
val mockPayload: NotificationRegisterPayload = mock(NotificationRegisterPayload::class.java)
val mockId = 123L
`when`(mockDataManager.updateRegisterNotification(mockId, mockPayload)).thenReturn(
- mockResponseBody
+ mockResponseBody,
)
val flow = userDetailRepository.updateRegisterNotification(mockId, mockPayload)
@@ -117,7 +131,7 @@ class UserDetailRepositoryImpTest {
val mockPayload: NotificationRegisterPayload = mock(NotificationRegisterPayload::class.java)
val mockId = 123L
`when`(mockDataManager.updateRegisterNotification(mockId, mockPayload)).thenThrow(
- Exception(errorMessage)
+ Exception(errorMessage),
)
val flow = userDetailRepository.updateRegisterNotification(mockId, mockPayload)
@@ -126,6 +140,5 @@ class UserDetailRepositoryImpTest {
assertEquals(Throwable(errorMessage), awaitError())
cancelAndIgnoreRemainingEvents()
}
-
}
}
diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts
index 1ddc25004..0ddb46915 100644
--- a/core/datastore/build.gradle.kts
+++ b/core/datastore/build.gradle.kts
@@ -37,5 +37,5 @@ dependencies {
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
- androidTestImplementation(libs.espresso.core)
+ androidTestImplementation(libs.androidx.test.espresso.core)
}
\ No newline at end of file
diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts
index d1b606897..15067bda7 100644
--- a/core/designsystem/build.gradle.kts
+++ b/core/designsystem/build.gradle.kts
@@ -31,6 +31,9 @@ dependencies {
api(libs.androidx.compose.ui.util)
api(libs.androidx.activity.compose)
+ // Accompanist Pager Library
+ implementation(libs.accompanist.pager)
+
testImplementation(libs.androidx.compose.ui.test)
androidTestImplementation(libs.androidx.compose.ui.test)
}
diff --git a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosTabPager.kt b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosTabPager.kt
index 4132729e2..f7d16f63e 100644
--- a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosTabPager.kt
+++ b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosTabPager.kt
@@ -9,13 +9,10 @@
*/
package org.mifos.mobile.core.designsystem.components
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.pager.HorizontalPager
-import androidx.compose.foundation.pager.PagerState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
@@ -26,8 +23,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
+import com.google.accompanist.pager.HorizontalPager
+import com.google.accompanist.pager.PagerState
-@OptIn(ExperimentalFoundationApi::class)
+@Suppress("DEPRECATION")
@Composable
fun MifosTabPager(
pagerState: PagerState,
@@ -67,8 +66,9 @@ fun MifosTabPager(
HorizontalPager(
state = pagerState,
+ count = tabs.size,
modifier = Modifier.fillMaxWidth(),
- pageContent = { page ->
+ content = { page ->
content(page)
},
)
diff --git a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/icons/MifosIcons.kt b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/icons/MifosIcons.kt
index dfe21dc02..5738de8be 100644
--- a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/icons/MifosIcons.kt
+++ b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/icons/MifosIcons.kt
@@ -11,6 +11,13 @@ package org.mifos.mobile.core.designsystem.icons
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.automirrored.filled.Assignment
+import androidx.compose.material.icons.automirrored.filled.CompareArrows
+import androidx.compose.material.icons.automirrored.filled.Help
+import androidx.compose.material.icons.automirrored.filled.Label
+import androidx.compose.material.icons.automirrored.filled.Logout
+import androidx.compose.material.icons.filled.AccountBalance
+import androidx.compose.material.icons.filled.AccountBalanceWallet
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowDropUp
@@ -24,8 +31,12 @@ import androidx.compose.material.icons.filled.LocationOn
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Notifications
+import androidx.compose.material.icons.filled.Paid
+import androidx.compose.material.icons.filled.People
import androidx.compose.material.icons.filled.Phone
+import androidx.compose.material.icons.filled.RealEstateAgent
import androidx.compose.material.icons.filled.Search
+import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Share
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
@@ -34,10 +45,21 @@ import androidx.compose.material.icons.outlined.Mail
import androidx.compose.ui.graphics.vector.ImageVector
object MifosIcons {
+ val Paid: ImageVector = Icons.Default.Paid
+ val Logout: ImageVector = Icons.AutoMirrored.Filled.Logout
+ val Help: ImageVector = Icons.AutoMirrored.Filled.Help
+ val Settings: ImageVector = Icons.Default.Settings
+ val Label: ImageVector = Icons.AutoMirrored.Filled.Label
+ val Assignment: ImageVector = Icons.AutoMirrored.Filled.Assignment
+ val People: ImageVector = Icons.Filled.People
+ val RealEstateAgent: ImageVector = Icons.Filled.RealEstateAgent
+ val AccountBalanceWallet: ImageVector = Icons.Filled.AccountBalanceWallet
+ val CompareArrows: ImageVector = Icons.AutoMirrored.Filled.CompareArrows
+ val AccountBalance: ImageVector = Icons.Filled.AccountBalance
val Share: ImageVector = Icons.Default.Share
val Mail: ImageVector = Icons.Outlined.Mail
val LocationOn: ImageVector = Icons.Filled.LocationOn
- val Phone: ImageVector= Icons.Default.Phone
+ val Phone: ImageVector = Icons.Default.Phone
val MoreVert: ImageVector = Icons.Filled.MoreVert
val VisibilityOff: ImageVector = Icons.Filled.VisibilityOff
val Visibility: ImageVector = Icons.Filled.Visibility
diff --git a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/theme/BackgroundTheme.kt b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/theme/BackgroundTheme.kt
new file mode 100644
index 000000000..f6354b5c2
--- /dev/null
+++ b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/theme/BackgroundTheme.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.designsystem.theme
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.Dp
+
+/**
+ * A class to model background color and tonal elevation values for Now in Android.
+ */
+@Immutable
+data class BackgroundTheme(
+ val color: Color = Color.Unspecified,
+ val tonalElevation: Dp = Dp.Unspecified,
+)
+
+/**
+ * A composition local for [BackgroundTheme].
+ */
+val LocalBackgroundTheme = staticCompositionLocalOf { BackgroundTheme() }
diff --git a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/theme/MifosBackground.kt b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/theme/MifosBackground.kt
new file mode 100644
index 000000000..8559cee6c
--- /dev/null
+++ b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/theme/MifosBackground.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.designsystem.theme
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.LocalAbsoluteTonalElevation
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+/**
+ * The main background for the app.
+ * Uses [LocalBackgroundTheme] to set the color and tonal elevation of a [Surface].
+ *
+ * @param modifier Modifier to be applied to the background.
+ * @param content The background content.
+ */
+@Composable
+fun MifosBackground(
+ modifier: Modifier = Modifier,
+ content: @Composable () -> Unit,
+) {
+ val color = LocalBackgroundTheme.current.color
+ val tonalElevation = LocalBackgroundTheme.current.tonalElevation
+ Surface(
+ color = if (color == Color.Unspecified) Color.Transparent else color,
+ tonalElevation = if (tonalElevation == Dp.Unspecified) 0.dp else tonalElevation,
+ modifier = modifier.fillMaxSize(),
+ ) {
+ CompositionLocalProvider(LocalAbsoluteTonalElevation provides 0.dp) {
+ content()
+ }
+ }
+}
+
+/**
+ * Multipreview annotation that represents light and dark themes. Add this annotation to a
+ * composable to render the both themes.
+ */
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light theme")
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark theme")
+annotation class ThemePreviews
+
+@ThemePreviews
+@Composable
+fun BackgroundDefault() {
+ MifosMobileTheme {
+ MifosBackground(Modifier.size(100.dp), content = {})
+ }
+}
+
+@ThemePreviews
+@Composable
+fun BackgroundDynamic() {
+ MifosMobileTheme {
+ MifosBackground(Modifier.size(100.dp), content = {})
+ }
+}
+
+@ThemePreviews
+@Composable
+fun BackgroundAndroid() {
+ MifosMobileTheme {
+ MifosBackground(Modifier.size(100.dp), content = {})
+ }
+}
diff --git a/core/logs/build.gradle.kts b/core/logs/build.gradle.kts
index 5409ae825..56154cc60 100644
--- a/core/logs/build.gradle.kts
+++ b/core/logs/build.gradle.kts
@@ -26,5 +26,5 @@ dependencies {
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
- androidTestImplementation(libs.espresso.core)
+ androidTestImplementation(libs.androidx.test.espresso.core)
}
\ No newline at end of file
diff --git a/core/logs/src/main/java/org/mifos/mobile/core/logs/di/FirebaseAnalyticsHelper.kt b/core/logs/src/main/java/org/mifos/mobile/core/logs/di/FirebaseAnalyticsHelper.kt
index 81221920b..f038eed63 100644
--- a/core/logs/src/main/java/org/mifos/mobile/core/logs/di/FirebaseAnalyticsHelper.kt
+++ b/core/logs/src/main/java/org/mifos/mobile/core/logs/di/FirebaseAnalyticsHelper.kt
@@ -10,7 +10,7 @@
package org.mifos.mobile.core.logs.di
import com.google.firebase.analytics.FirebaseAnalytics
-import com.google.firebase.analytics.ktx.logEvent
+import com.google.firebase.analytics.logEvent
import org.mifos.mobile.core.logs.AnalyticsEvent
import org.mifos.mobile.core.logs.AnalyticsHelper
import javax.inject.Inject
diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts
index a0ad276c6..0ec00d707 100644
--- a/core/model/build.gradle.kts
+++ b/core/model/build.gradle.kts
@@ -29,5 +29,5 @@ dependencies {
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
- androidTestImplementation(libs.espresso.core)
+ androidTestImplementation(libs.androidx.test.espresso.core)
}
\ No newline at end of file
diff --git a/core/model/src/main/java/org/mifos/mobile/core/model/UserData.kt b/core/model/src/main/java/org/mifos/mobile/core/model/UserData.kt
new file mode 100644
index 000000000..3b8787c08
--- /dev/null
+++ b/core/model/src/main/java/org/mifos/mobile/core/model/UserData.kt
@@ -0,0 +1,16 @@
+/*
+ * 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.model
+
+data class UserData(
+ val isAuthenticated: Boolean,
+ val userName: String,
+ val clientId: Long,
+)
diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts
index 812db0897..068b32ea9 100644
--- a/core/network/build.gradle.kts
+++ b/core/network/build.gradle.kts
@@ -13,14 +13,11 @@ plugins {
id("kotlinx-serialization")
}
-apply(from = "${project.rootDir}/config/quality/quality.gradle")
-
android {
namespace = "org.mifos.mobile.core.network"
}
dependencies {
-
api(projects.core.common)
api(projects.core.model)
api(projects.core.datastore)
@@ -32,12 +29,12 @@ dependencies {
}
implementation(libs.squareup.retrofit.adapter.rxjava)
implementation(libs.squareup.retrofit.converter.gson)
- implementation(libs.squareup.okhttp)
implementation(libs.squareup.logging.interceptor)
+ implementation(libs.squareup.okhttp)
implementation(libs.jetbrains.kotlin.jdk7)
testImplementation(libs.junit)
- androidTestImplementation(libs.androidx.test.ext.junit)
- androidTestImplementation(libs.espresso.core)
+ testImplementation(libs.mockito.core)
+ implementation(libs.mockito.core)
}
\ No newline at end of file
diff --git a/core/qrcode/build.gradle.kts b/core/qrcode/build.gradle.kts
index 9005ee0c1..2345ca418 100644
--- a/core/qrcode/build.gradle.kts
+++ b/core/qrcode/build.gradle.kts
@@ -1,3 +1,12 @@
+/*
+ * 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
+ */
plugins {
alias(libs.plugins.mifos.android.library)
@@ -10,13 +19,14 @@ android {
dependencies {
implementation(projects.core.model)
+ implementation(libs.androidx.compose.ui)
api(libs.zxing.core)
api(libs.squareup.retrofit.converter.gson)
- //cameraX
+ // cameraX
implementation(libs.androidx.camera.camera2)
implementation(libs.androidx.camera.lifecycle)
implementation(libs.androidx.camera.view)
implementation(libs.androidx.camera.core)
-}
\ No newline at end of file
+}
diff --git a/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/BarcodeCamera.kt b/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/BarcodeCamera.kt
index ecbba627e..b71f3e37b 100644
--- a/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/BarcodeCamera.kt
+++ b/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/BarcodeCamera.kt
@@ -1,3 +1,12 @@
+/*
+ * 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.qr
import android.content.ContentValues.TAG
@@ -23,7 +32,6 @@ import androidx.lifecycle.LifecycleOwner
@ExperimentalGetImage
class BarcodeCamera {
-
private var camera: Camera? = null
@Composable
@@ -44,11 +52,10 @@ class BarcodeCamera {
AndroidView(
factory = { context ->
PreviewView(context).apply {
- layoutParams =
- LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
- )
+ layoutParams = LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ )
scaleType = PreviewView.ScaleType.FILL_START
startCamera(
@@ -56,10 +63,10 @@ class BarcodeCamera {
previewView = this,
imageCapture = imageCapture,
lifecycleOwner = lifecycleOwner,
- onBarcodeScanned = onBarcodeScanned
+ onBarcodeScanned = onBarcodeScanned,
)
}
- }
+ },
)
}
@@ -68,7 +75,7 @@ class BarcodeCamera {
previewView: PreviewView,
lifecycleOwner: LifecycleOwner,
imageCapture: ImageCapture,
- onBarcodeScanned: (String) -> Unit
+ onBarcodeScanned: (String) -> Unit,
) {
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
@@ -86,26 +93,30 @@ class BarcodeCamera {
imageAnalysis.setAnalyzer(
ContextCompat.getMainExecutor(context),
QrCodeAnalyzer(
- onBarcodeScanned = onBarcodeScanned
- )
+ onBarcodeScanned = onBarcodeScanned,
+ ),
)
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
cameraProvider.unbindAll()
- camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, imageCapture, imageAnalysis)
+ camera = cameraProvider.bindToLifecycle(
+ lifecycleOwner,
+ cameraSelector,
+ preview,
+ imageCapture,
+ imageAnalysis,
+ )
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
},
- ContextCompat.getMainExecutor(context)
+ ContextCompat.getMainExecutor(context),
)
}
- private fun toggleFlash(
- isOn: Boolean,
- ) {
+ private fun toggleFlash(isOn: Boolean) {
camera?.cameraControl?.enableTorch(isOn)
}
-}
\ No newline at end of file
+}
diff --git a/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/QrCodeAnalyzer.kt b/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/QrCodeAnalyzer.kt
index 15beeb29f..7b20ec437 100644
--- a/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/QrCodeAnalyzer.kt
+++ b/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/QrCodeAnalyzer.kt
@@ -1,6 +1,16 @@
+/*
+ * 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.qr
import android.graphics.ImageFormat
+import android.util.Log
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import com.google.zxing.BarcodeFormat
@@ -12,7 +22,7 @@ import com.google.zxing.common.HybridBinarizer
import java.nio.ByteBuffer
class QrCodeAnalyzer(
- private val onBarcodeScanned: (String) -> Unit
+ private val onBarcodeScanned: (String) -> Unit,
) : ImageAnalysis.Analyzer {
private val supportedImageFormats = listOf(
@@ -32,7 +42,7 @@ class QrCodeAnalyzer(
0,
image.width,
image.height,
- false
+ false,
)
val binaryBmp = BinaryBitmap(HybridBinarizer(source))
try {
@@ -40,14 +50,14 @@ class QrCodeAnalyzer(
setHints(
mapOf(
DecodeHintType.POSSIBLE_FORMATS to arrayListOf(
- BarcodeFormat.QR_CODE
- )
- )
+ BarcodeFormat.QR_CODE,
+ ),
+ ),
)
}.decode(binaryBmp)
onBarcodeScanned(result.text)
} catch (e: Exception) {
- e.printStackTrace()
+ Log.d("QrCodeAnalyzer", "analyze: ${e.message}")
} finally {
image.close()
}
@@ -60,4 +70,4 @@ class QrCodeAnalyzer(
get(it)
}
}
-}
\ No newline at end of file
+}
diff --git a/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/QrCodeGenerator.kt b/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/QrCodeGenerator.kt
index aa17fd3e8..0cf943bf9 100644
--- a/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/QrCodeGenerator.kt
+++ b/core/qrcode/src/main/kotlin/org/mifos/mobile/core/qr/QrCodeGenerator.kt
@@ -1,3 +1,12 @@
+/*
+ * 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.qr
import android.graphics.Bitmap
diff --git a/feature/login/.gitignore b/core/testing/.gitignore
similarity index 100%
rename from feature/login/.gitignore
rename to core/testing/.gitignore
diff --git a/core/testing/README.md b/core/testing/README.md
new file mode 100644
index 000000000..e360ba867
--- /dev/null
+++ b/core/testing/README.md
@@ -0,0 +1 @@
+# :core:testing module
diff --git a/core/testing/build.gradle.kts b/core/testing/build.gradle.kts
new file mode 100644
index 000000000..9dc5d5eaf
--- /dev/null
+++ b/core/testing/build.gradle.kts
@@ -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
+ */
+plugins {
+ alias(libs.plugins.mifos.android.library)
+ alias(libs.plugins.mifos.android.library.compose)
+ alias(libs.plugins.mifos.android.hilt)
+}
+
+android {
+ namespace = "org.mifos.mobile.core.testing"
+}
+
+dependencies {
+ api(libs.bundles.androidx.compose.ui.test)
+ api(libs.kotlinx.coroutines.test)
+ api(projects.core.data)
+ api(projects.core.logs)
+ api(projects.core.model)
+ api(libs.turbine)
+ api(libs.mockito.core)
+
+ implementation(kotlin("test"))
+ implementation(libs.squareup.retrofit.converter.gson)
+ implementation(libs.androidx.navigation.testing)
+ implementation(libs.kotlinx.collections.immutable)
+ implementation(libs.androidx.test.espresso.core)
+ implementation(libs.androidx.test.rules)
+ implementation(libs.hilt.android.testing)
+}
\ No newline at end of file
diff --git a/core/testing/src/main/AndroidManifest.xml b/core/testing/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..4ee22a4fb
--- /dev/null
+++ b/core/testing/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/core/testing/src/main/java/org/mifos/mobile/core/testing/MifosTestRunner.kt b/core/testing/src/main/java/org/mifos/mobile/core/testing/MifosTestRunner.kt
new file mode 100644
index 000000000..1dd6d4ca1
--- /dev/null
+++ b/core/testing/src/main/java/org/mifos/mobile/core/testing/MifosTestRunner.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.core.testing
+
+import android.app.Application
+import android.content.Context
+import androidx.test.runner.AndroidJUnitRunner
+import dagger.hilt.android.testing.HiltTestApplication
+
+class MifosTestRunner : AndroidJUnitRunner() {
+ override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
+ return super.newApplication(cl, HiltTestApplication::class.java.name, context)
+ }
+}
diff --git a/core/testing/src/main/java/org/mifos/mobile/core/testing/di/TestDispatcherModule.kt b/core/testing/src/main/java/org/mifos/mobile/core/testing/di/TestDispatcherModule.kt
new file mode 100644
index 000000000..b93826ec6
--- /dev/null
+++ b/core/testing/src/main/java/org/mifos/mobile/core/testing/di/TestDispatcherModule.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.testing.di
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal object TestDispatcherModule {
+ @Provides
+ @Singleton
+ fun providesTestDispatcher(): TestDispatcher = UnconfinedTestDispatcher()
+}
diff --git a/core/testing/src/main/java/org/mifos/mobile/core/testing/di/TestDispatchersModule.kt b/core/testing/src/main/java/org/mifos/mobile/core/testing/di/TestDispatchersModule.kt
new file mode 100644
index 000000000..3bd0ac79d
--- /dev/null
+++ b/core/testing/src/main/java/org/mifos/mobile/core/testing/di/TestDispatchersModule.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.testing.di
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.components.SingletonComponent
+import dagger.hilt.testing.TestInstallIn
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import org.mifos.mobile.core.common.network.Dispatcher
+import org.mifos.mobile.core.common.network.MifosDispatchers
+import org.mifos.mobile.core.common.network.di.DispatchersModule
+
+@Module
+@TestInstallIn(
+ components = [SingletonComponent::class],
+ replaces = [DispatchersModule::class],
+)
+internal object TestDispatchersModule {
+ @Provides
+ @Dispatcher(MifosDispatchers.IO)
+ fun providesIODispatcher(testDispatcher: TestDispatcher): CoroutineDispatcher = testDispatcher
+
+ @Provides
+ @Dispatcher(MifosDispatchers.Default)
+ fun providesDefaultDispatcher(
+ testDispatcher: TestDispatcher,
+ ): CoroutineDispatcher = testDispatcher
+}
diff --git a/core/testing/src/main/java/org/mifos/mobile/core/testing/rules/GrantPostNotificationsPermissionRule.kt b/core/testing/src/main/java/org/mifos/mobile/core/testing/rules/GrantPostNotificationsPermissionRule.kt
new file mode 100644
index 000000000..7fa8622d2
--- /dev/null
+++ b/core/testing/src/main/java/org/mifos/mobile/core/testing/rules/GrantPostNotificationsPermissionRule.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.testing.rules
+
+import android.Manifest.permission.POST_NOTIFICATIONS
+import android.os.Build.VERSION.SDK_INT
+import android.os.Build.VERSION_CODES.TIRAMISU
+import androidx.test.rule.GrantPermissionRule.grant
+import org.junit.rules.TestRule
+
+/**
+ * [TestRule] granting [POST_NOTIFICATIONS] permission if running on [SDK_INT] greater than [TIRAMISU].
+ */
+class GrantPostNotificationsPermissionRule :
+ TestRule by if (SDK_INT >= TIRAMISU) grant(POST_NOTIFICATIONS) else grant()
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/FakeJsonName.kt b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/FakeJsonName.kt
similarity index 85%
rename from androidApp/src/main/java/org/mifos/mobile/utils/FakeJsonName.kt
rename to core/testing/src/main/java/org/mifos/mobile/core/testing/util/FakeJsonName.kt
index 2f12f6ab0..35363f2f5 100644
--- a/androidApp/src/main/java/org/mifos/mobile/utils/FakeJsonName.kt
+++ b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/FakeJsonName.kt
@@ -1,8 +1,14 @@
-package org.mifos.mobile.utils
-
-/**
- * Created by dilpreet on 26/6/17.
+/*
+ * 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.testing.util
+
object FakeJsonName {
const val ACCOUNT_OPTION_TEMPLATE = "accountOptionTemplate.json"
const val CLIENT_ACCOUNTS_JSON = "clientAccounts.json"
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/FakeRemoteDataSource.kt b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/FakeRemoteDataSource.kt
similarity index 83%
rename from androidApp/src/main/java/org/mifos/mobile/utils/FakeRemoteDataSource.kt
rename to core/testing/src/main/java/org/mifos/mobile/core/testing/util/FakeRemoteDataSource.kt
index 9082e0735..904e4e469 100644
--- a/androidApp/src/main/java/org/mifos/mobile/utils/FakeRemoteDataSource.kt
+++ b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/FakeRemoteDataSource.kt
@@ -1,12 +1,28 @@
-package org.mifos.mobile.utils
+/*
+ * 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.testing.util
import com.google.gson.reflect.TypeToken
-import org.mifos.mobile.core.model.entity.*
+import org.mifos.mobile.core.datastore.model.Charge
+import org.mifos.mobile.core.model.entity.Page
+import org.mifos.mobile.core.model.entity.Transaction
+import org.mifos.mobile.core.model.entity.UpdatePasswordPayload
+import org.mifos.mobile.core.model.entity.User
import org.mifos.mobile.core.model.entity.accounts.loan.LoanAccount
import org.mifos.mobile.core.model.entity.accounts.loan.LoanWithAssociations
import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations
-import org.mifos.mobile.core.model.entity.client.*
-import org.mifos.mobile.core.model.entity.beneficiary.*
+import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary
+import org.mifos.mobile.core.model.entity.beneficiary.BeneficiaryPayload
+import org.mifos.mobile.core.model.entity.beneficiary.BeneficiaryUpdatePayload
+import org.mifos.mobile.core.model.entity.client.Client
+import org.mifos.mobile.core.model.entity.client.ClientAccounts
import org.mifos.mobile.core.model.entity.guarantor.GuarantorPayload
import org.mifos.mobile.core.model.entity.guarantor.GuarantorTemplatePayload
import org.mifos.mobile.core.model.entity.payload.LoansPayload
@@ -19,13 +35,10 @@ import org.mifos.mobile.core.model.entity.templates.beneficiary.BeneficiaryTempl
import org.mifos.mobile.core.model.entity.templates.loans.LoanTemplate
import org.mifos.mobile.core.model.entity.templates.savings.SavingsAccountTemplate
-/**
- * Created by dilpreet on 26/6/17.
- */
object FakeRemoteDataSource {
private val mTestDataFactory = TestDataFactory()
val clientAccounts: ClientAccounts
- get() = mTestDataFactory.getListTypePojo(
+ get() = mTestDataFactory.getListTypePojo(
object :
TypeToken() {},
FakeJsonName.CLIENT_ACCOUNTS_JSON,
@@ -51,7 +64,7 @@ object FakeRemoteDataSource {
FakeJsonName.CLIENT_SHARE_ACCOUNT_JSON,
)
val beneficiaries: List
- get() = mTestDataFactory.getListTypePojo>(
+ get() = mTestDataFactory.getListTypePojo(
object :
TypeToken?>() {},
FakeJsonName.BENEFICIARIES_JSON,
@@ -164,22 +177,22 @@ object FakeRemoteDataSource {
LoginPayload::class.java,
FakeJsonName.LOGIN,
)
- val charge: Page?
- get() = mTestDataFactory.getListTypePojo?>(
+ val charge: Page?
+ get() = mTestDataFactory.getListTypePojo?>(
object :
- TypeToken?>() {},
+ TypeToken?>() {},
FakeJsonName.CHARGE,
)
- val savingsCharge: List
- get() = mTestDataFactory.getListTypePojo>(
+ val savingsCharge: List
+ get() = mTestDataFactory.getListTypePojo(
object :
- TypeToken?>() {},
+ TypeToken?>() {},
FakeJsonName.SAVING_CHARGE,
)
- val loanCharge: List
- get() = mTestDataFactory.getListTypePojo>(
+ val loanCharge: List
+ get() = mTestDataFactory.getListTypePojo(
object :
- TypeToken?>() {},
+ TypeToken?>() {},
FakeJsonName.LOAN_CHARGE,
)
val userVerify: UserVerify
@@ -193,7 +206,7 @@ object FakeRemoteDataSource {
FakeJsonName.GUARANTOR_TEMPLATE,
)
val guarantorsList: List
- get() = mTestDataFactory.getListTypePojo>(
+ get() = mTestDataFactory.getListTypePojo(
object :
TypeToken?>() {},
FakeJsonName.GUARANTOR_LIST,
diff --git a/core/testing/src/main/java/org/mifos/mobile/core/testing/util/MainDispatcherRule.kt b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/MainDispatcherRule.kt
new file mode 100644
index 000000000..83b3505da
--- /dev/null
+++ b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/MainDispatcherRule.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.testing.util
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.setMain
+import org.junit.rules.TestRule
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+
+/**
+ * A JUnit [TestRule] that sets the Main dispatcher to [testDispatcher]
+ * for the duration of the test.
+ */
+class MainDispatcherRule(
+ private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher(),
+) : TestWatcher() {
+ override fun starting(description: Description) = Dispatchers.setMain(testDispatcher)
+
+ override fun finished(description: Description) = Dispatchers.resetMain()
+}
diff --git a/core/testing/src/main/java/org/mifos/mobile/core/testing/util/TestAnalyticsHelper.kt b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/TestAnalyticsHelper.kt
new file mode 100644
index 000000000..debd4a44a
--- /dev/null
+++ b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/TestAnalyticsHelper.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.testing.util
+
+import org.mifos.mobile.core.logs.AnalyticsEvent
+import org.mifos.mobile.core.logs.AnalyticsHelper
+
+class TestAnalyticsHelper : AnalyticsHelper {
+
+ private val events = mutableListOf()
+
+ override fun logEvent(event: AnalyticsEvent) {
+ events.add(event)
+ }
+
+ fun hasLogged(event: AnalyticsEvent) = event in events
+}
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/TestDataFactory.kt b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/TestDataFactory.kt
similarity index 75%
rename from androidApp/src/main/java/org/mifos/mobile/utils/TestDataFactory.kt
rename to core/testing/src/main/java/org/mifos/mobile/core/testing/util/TestDataFactory.kt
index 83c65278a..cce019969 100644
--- a/androidApp/src/main/java/org/mifos/mobile/utils/TestDataFactory.kt
+++ b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/TestDataFactory.kt
@@ -1,13 +1,19 @@
-package org.mifos.mobile.utils
+/*
+ * 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.testing.util
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import java.io.InputStreamReader
-/**
- * Created by dilpreet on 26/6/17.
- */
class TestDataFactory {
/**
* Note : This Generic Method DeSerialize Only Json Object in POJO
@@ -27,8 +33,8 @@ class TestDataFactory {
* new TypeToken
*/
fun getObjectTypePojo(model: Class?, jsonName: String?): T {
- val `in` = javaClass.classLoader?.getResourceAsStream(jsonName)
- val reader = JsonReader(InputStreamReader(`in`))
+ val inputStream = javaClass.classLoader?.getResourceAsStream(jsonName)
+ val reader = JsonReader(InputStreamReader(inputStream))
return Gson().fromJson(reader, model)
}
@@ -58,8 +64,8 @@ class TestDataFactory {
* new TypeToken */
fun getListTypePojo(listModel: TypeToken?, jsonName: String?): T {
- val `in` = javaClass.classLoader?.getResourceAsStream(jsonName)
- val reader = JsonReader(InputStreamReader(`in`))
+ val inputStream = javaClass.classLoader?.getResourceAsStream(jsonName)
+ val reader = JsonReader(InputStreamReader(inputStream))
return Gson().fromJson(reader, listModel?.type)
}
}
diff --git a/core/testing/src/main/java/org/mifos/mobile/core/testing/util/TestNetworkMonitor.kt b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/TestNetworkMonitor.kt
new file mode 100644
index 000000000..142dbdf99
--- /dev/null
+++ b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/TestNetworkMonitor.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.testing.util
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import org.mifos.mobile.core.data.utils.NetworkMonitor
+
+class TestNetworkMonitor : NetworkMonitor {
+
+ private val connectivityFlow = MutableStateFlow(true)
+
+ override val isOnline: Flow = connectivityFlow
+
+ /**
+ * A test-only API to set the connectivity state from tests.
+ */
+ fun setConnected(isConnected: Boolean) {
+ connectivityFlow.value = isConnected
+ }
+}
diff --git a/core/testing/src/main/java/org/mifos/mobile/core/testing/util/Util.kt b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/Util.kt
new file mode 100644
index 000000000..3f8ca7419
--- /dev/null
+++ b/core/testing/src/main/java/org/mifos/mobile/core/testing/util/Util.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.testing.util
+
+import androidx.compose.ui.test.junit4.ComposeTestRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.printToLog
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+/**
+ * Used to debug the semantic tree.
+ */
+fun ComposeTestRule.dumpSemanticNodes() {
+ this.onRoot().printToLog(tag = "MifosLog")
+}
+
+fun runTestWithLogging(
+ context: CoroutineContext = EmptyCoroutineContext,
+ timeout: Duration = 30.seconds,
+ testBody: suspend TestScope.() -> Unit,
+) = runTest(context, timeout) {
+ runCatching {
+ testBody()
+ }.onFailure { exception ->
+ exception.printStackTrace()
+ throw exception
+ }
+}
diff --git a/androidApp/src/test/resources/SavingsAccountWithAssociations.json b/core/testing/src/main/resources/SavingsAccountWithAssociations.json
similarity index 100%
rename from androidApp/src/test/resources/SavingsAccountWithAssociations.json
rename to core/testing/src/main/resources/SavingsAccountWithAssociations.json
diff --git a/androidApp/src/test/resources/accountOptionTemplate.json b/core/testing/src/main/resources/accountOptionTemplate.json
similarity index 100%
rename from androidApp/src/test/resources/accountOptionTemplate.json
rename to core/testing/src/main/resources/accountOptionTemplate.json
diff --git a/androidApp/src/test/resources/beneficiaries.json b/core/testing/src/main/resources/beneficiaries.json
similarity index 100%
rename from androidApp/src/test/resources/beneficiaries.json
rename to core/testing/src/main/resources/beneficiaries.json
diff --git a/androidApp/src/test/resources/beneficiaryApplication.json b/core/testing/src/main/resources/beneficiaryApplication.json
similarity index 100%
rename from androidApp/src/test/resources/beneficiaryApplication.json
rename to core/testing/src/main/resources/beneficiaryApplication.json
diff --git a/androidApp/src/test/resources/beneficiaryPayload.json b/core/testing/src/main/resources/beneficiaryPayload.json
similarity index 100%
rename from androidApp/src/test/resources/beneficiaryPayload.json
rename to core/testing/src/main/resources/beneficiaryPayload.json
diff --git a/androidApp/src/test/resources/beneficiaryTemplate.json b/core/testing/src/main/resources/beneficiaryTemplate.json
similarity index 100%
rename from androidApp/src/test/resources/beneficiaryTemplate.json
rename to core/testing/src/main/resources/beneficiaryTemplate.json
diff --git a/androidApp/src/test/resources/beneficiaryUpdatePayload.json b/core/testing/src/main/resources/beneficiaryUpdatePayload.json
similarity index 100%
rename from androidApp/src/test/resources/beneficiaryUpdatePayload.json
rename to core/testing/src/main/resources/beneficiaryUpdatePayload.json
diff --git a/androidApp/src/test/resources/charge.json b/core/testing/src/main/resources/charge.json
similarity index 100%
rename from androidApp/src/test/resources/charge.json
rename to core/testing/src/main/resources/charge.json
diff --git a/androidApp/src/test/resources/client.json b/core/testing/src/main/resources/client.json
similarity index 100%
rename from androidApp/src/test/resources/client.json
rename to core/testing/src/main/resources/client.json
diff --git a/androidApp/src/test/resources/clientAccounts.json b/core/testing/src/main/resources/clientAccounts.json
similarity index 100%
rename from androidApp/src/test/resources/clientAccounts.json
rename to core/testing/src/main/resources/clientAccounts.json
diff --git a/androidApp/src/test/resources/clientLoanAccounts.json b/core/testing/src/main/resources/clientLoanAccounts.json
similarity index 100%
rename from androidApp/src/test/resources/clientLoanAccounts.json
rename to core/testing/src/main/resources/clientLoanAccounts.json
diff --git a/androidApp/src/test/resources/clientSavingsAccounts.json b/core/testing/src/main/resources/clientSavingsAccounts.json
similarity index 100%
rename from androidApp/src/test/resources/clientSavingsAccounts.json
rename to core/testing/src/main/resources/clientSavingsAccounts.json
diff --git a/androidApp/src/test/resources/clientShareAccounts.json b/core/testing/src/main/resources/clientShareAccounts.json
similarity index 100%
rename from androidApp/src/test/resources/clientShareAccounts.json
rename to core/testing/src/main/resources/clientShareAccounts.json
diff --git a/androidApp/src/test/resources/clients.json b/core/testing/src/main/resources/clients.json
similarity index 100%
rename from androidApp/src/test/resources/clients.json
rename to core/testing/src/main/resources/clients.json
diff --git a/androidApp/src/test/resources/clientsNotFound.json b/core/testing/src/main/resources/clientsNotFound.json
similarity index 100%
rename from androidApp/src/test/resources/clientsNotFound.json
rename to core/testing/src/main/resources/clientsNotFound.json
diff --git a/androidApp/src/main/resources/guarantorList.json b/core/testing/src/main/resources/guarantorList.json
similarity index 100%
rename from androidApp/src/main/resources/guarantorList.json
rename to core/testing/src/main/resources/guarantorList.json
diff --git a/androidApp/src/main/resources/guarantorTemplate.json b/core/testing/src/main/resources/guarantorTemplate.json
similarity index 100%
rename from androidApp/src/main/resources/guarantorTemplate.json
rename to core/testing/src/main/resources/guarantorTemplate.json
diff --git a/androidApp/src/test/resources/loanAccount.json b/core/testing/src/main/resources/loanAccount.json
similarity index 100%
rename from androidApp/src/test/resources/loanAccount.json
rename to core/testing/src/main/resources/loanAccount.json
diff --git a/androidApp/src/test/resources/loanAccountWithEmptyRepaymentSchedule.json b/core/testing/src/main/resources/loanAccountWithEmptyRepaymentSchedule.json
similarity index 100%
rename from androidApp/src/test/resources/loanAccountWithEmptyRepaymentSchedule.json
rename to core/testing/src/main/resources/loanAccountWithEmptyRepaymentSchedule.json
diff --git a/androidApp/src/test/resources/loanAccountWithEmptyTransactions.json b/core/testing/src/main/resources/loanAccountWithEmptyTransactions.json
similarity index 100%
rename from androidApp/src/test/resources/loanAccountWithEmptyTransactions.json
rename to core/testing/src/main/resources/loanAccountWithEmptyTransactions.json
diff --git a/androidApp/src/test/resources/loanAccountWithRepaymentSchedule.json b/core/testing/src/main/resources/loanAccountWithRepaymentSchedule.json
similarity index 100%
rename from androidApp/src/test/resources/loanAccountWithRepaymentSchedule.json
rename to core/testing/src/main/resources/loanAccountWithRepaymentSchedule.json
diff --git a/androidApp/src/test/resources/loanAccountWithTransactions.json b/core/testing/src/main/resources/loanAccountWithTransactions.json
similarity index 100%
rename from androidApp/src/test/resources/loanAccountWithTransactions.json
rename to core/testing/src/main/resources/loanAccountWithTransactions.json
diff --git a/androidApp/src/test/resources/loanCharge.json b/core/testing/src/main/resources/loanCharge.json
similarity index 100%
rename from androidApp/src/test/resources/loanCharge.json
rename to core/testing/src/main/resources/loanCharge.json
diff --git a/androidApp/src/test/resources/loanPayload.json b/core/testing/src/main/resources/loanPayload.json
similarity index 100%
rename from androidApp/src/test/resources/loanPayload.json
rename to core/testing/src/main/resources/loanPayload.json
diff --git a/androidApp/src/test/resources/loanTemplate.json b/core/testing/src/main/resources/loanTemplate.json
similarity index 100%
rename from androidApp/src/test/resources/loanTemplate.json
rename to core/testing/src/main/resources/loanTemplate.json
diff --git a/androidApp/src/test/resources/loanTemplateByProduct.json b/core/testing/src/main/resources/loanTemplateByProduct.json
similarity index 100%
rename from androidApp/src/test/resources/loanTemplateByProduct.json
rename to core/testing/src/main/resources/loanTemplateByProduct.json
diff --git a/androidApp/src/test/resources/login.json b/core/testing/src/main/resources/login.json
similarity index 100%
rename from androidApp/src/test/resources/login.json
rename to core/testing/src/main/resources/login.json
diff --git a/androidApp/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/core/testing/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker
similarity index 100%
rename from androidApp/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
rename to core/testing/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker
diff --git a/androidApp/src/test/resources/register.json b/core/testing/src/main/resources/register.json
similarity index 100%
rename from androidApp/src/test/resources/register.json
rename to core/testing/src/main/resources/register.json
diff --git a/androidApp/src/test/resources/savingCharge.json b/core/testing/src/main/resources/savingCharge.json
similarity index 100%
rename from androidApp/src/test/resources/savingCharge.json
rename to core/testing/src/main/resources/savingCharge.json
diff --git a/androidApp/src/main/resources/savingsAccountTemplate.json b/core/testing/src/main/resources/savingsAccountTemplate.json
similarity index 100%
rename from androidApp/src/main/resources/savingsAccountTemplate.json
rename to core/testing/src/main/resources/savingsAccountTemplate.json
diff --git a/androidApp/src/test/resources/transactions.json b/core/testing/src/main/resources/transactions.json
similarity index 100%
rename from androidApp/src/test/resources/transactions.json
rename to core/testing/src/main/resources/transactions.json
diff --git a/androidApp/src/test/resources/transferPayload.json b/core/testing/src/main/resources/transferPayload.json
similarity index 100%
rename from androidApp/src/test/resources/transferPayload.json
rename to core/testing/src/main/resources/transferPayload.json
diff --git a/androidApp/src/test/resources/updatePasswordPayload.json b/core/testing/src/main/resources/updatePasswordPayload.json
similarity index 100%
rename from androidApp/src/test/resources/updatePasswordPayload.json
rename to core/testing/src/main/resources/updatePasswordPayload.json
diff --git a/androidApp/src/test/resources/user.json b/core/testing/src/main/resources/user.json
similarity index 100%
rename from androidApp/src/test/resources/user.json
rename to core/testing/src/main/resources/user.json
diff --git a/androidApp/src/test/resources/userVerify.json b/core/testing/src/main/resources/userVerify.json
similarity index 100%
rename from androidApp/src/test/resources/userVerify.json
rename to core/testing/src/main/resources/userVerify.json
diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts
index 46f16bd0c..fb1be6485 100644
--- a/core/ui/build.gradle.kts
+++ b/core/ui/build.gradle.kts
@@ -23,10 +23,7 @@ dependencies {
api(projects.core.designsystem)
api(projects.core.model)
api(projects.core.common)
-
- implementation("androidx.core:core-ktx:1.12.0")
- implementation("androidx.appcompat:appcompat:1.6.1")
- implementation("com.google.android.material:material:1.9.0")
+ api(libs.androidx.metrics)
testImplementation(libs.androidx.compose.ui.test)
androidTestImplementation(libs.bundles.androidx.compose.ui.test)
diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosComposeView.kt b/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosComposeView.kt
deleted file mode 100644
index f0e02cac6..000000000
--- a/core/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosComposeView.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2024 Mifos Initiative
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- *
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
- */
-package org.mifos.mobile.core.ui.component
-
-import android.content.Context
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.fragment.app.Fragment
-import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
-
-fun Fragment.mifosComposeView(context: Context, content: @Composable () -> Unit): ComposeView {
- return ComposeView(context).apply {
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- MifosMobileTheme {
- content()
- }
- }
- }
-}
diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ColorUtils.kt b/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ColorUtils.kt
index f4dedc245..b7d9e9b99 100644
--- a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ColorUtils.kt
+++ b/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ColorUtils.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.core.ui.utils
import android.app.Activity
@@ -21,4 +30,4 @@ fun Context.getThemeAttributeColor(@AttrRes colorAttribute: Int): Int {
val typedValue = TypedValue()
theme.resolveAttribute(colorAttribute, typedValue, true)
return typedValue.data
-}
\ No newline at end of file
+}
diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ImageUtil.kt b/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ImageUtil.kt
index cacf2425e..9ba20c089 100644
--- a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ImageUtil.kt
+++ b/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ImageUtil.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.core.ui.utils
import android.graphics.Bitmap
@@ -129,4 +138,4 @@ private fun createScaledBitmap(
}
return scaledBitmap
-}
\ No newline at end of file
+}
diff --git a/core/ui/src/main/res/values/colors.xml b/core/ui/src/main/res/values/colors.xml
new file mode 100644
index 000000000..d7ccaf034
--- /dev/null
+++ b/core/ui/src/main/res/values/colors.xml
@@ -0,0 +1,13 @@
+
+
+
+ #FF325ca8
+
\ No newline at end of file
diff --git a/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsHeader.kt b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsHeader.kt
index 685640604..2b24dcda9 100644
--- a/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsHeader.kt
+++ b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsHeader.kt
@@ -33,7 +33,7 @@ internal fun AboutUsHeader(
) {
Column(modifier) {
Image(
- painter = painterResource(id = R.mipmap.core_common_mifos_icon),
+ painter = painterResource(id = R.drawable.mifos_logo),
contentDescription = null,
modifier = Modifier
.size(100.dp)
diff --git a/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt
index 0b7a009de..f8c6aff83 100644
--- a/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt
+++ b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt
@@ -63,7 +63,8 @@ internal fun AboutUsScreen(
private fun getAboutUsItem(context: Context): List {
val currentYear = Calendar.getInstance().get(Calendar.YEAR)
- val copyrightText = context.getString(R.string.feature_about_copyright_mifos).replace("%1\$s", currentYear.toString())
+ val copyrightText = context.getString(R.string.feature_about_copyright_mifos)
+ .replace("%1\$s", currentYear.toString())
return listOf(
AboutUsItem(
diff --git a/feature/about/src/main/res/drawable/feature_about_law_icon.xml b/feature/about/src/main/res/drawable/feature_about_law_icon.xml
index e905b9c30..ec0e3648a 100644
--- a/feature/about/src/main/res/drawable/feature_about_law_icon.xml
+++ b/feature/about/src/main/res/drawable/feature_about_law_icon.xml
@@ -11,7 +11,6 @@
#ffffffff
#000000
diff --git a/feature/auth/build.gradle.kts b/feature/auth/build.gradle.kts
index bdd37214e..383dabd97 100644
--- a/feature/auth/build.gradle.kts
+++ b/feature/auth/build.gradle.kts
@@ -18,4 +18,5 @@ android {
dependencies {
implementation(projects.libs.countryCodePicker)
+ implementation(libs.squareup.okhttp)
}
\ No newline at end of file
diff --git a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavGraph.kt b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavGraph.kt
index 8d3637dd2..cc2c0d51c 100644
--- a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavGraph.kt
+++ b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavGraph.kt
@@ -26,20 +26,25 @@ fun NavController.navigateToLoginScreen() {
fun NavGraphBuilder.authenticationNavGraph(
navController: NavHostController,
+ route: String,
navigateToPasscodeScreen: () -> Unit,
) {
navigation(
+ route = route,
startDestination = AuthenticationNavigation.Login.route,
- route = AuthenticationNavigation.AuthenticationBase.route,
) {
loginRoute(
- navigateToRegisterScreen = { navController.navigate(AuthenticationNavigation.Registration.route) },
+ navigateToRegisterScreen = {
+ navController.navigate(AuthenticationNavigation.Registration.route)
+ },
navigateToPasscodeScreen = navigateToPasscodeScreen,
)
registrationRoute(
navigateBack = navController::popBackStack,
- onRegistered = { navController.navigate(AuthenticationNavigation.RegistrationVerification.route) },
+ onRegistered = {
+ navController.navigate(AuthenticationNavigation.RegistrationVerification.route)
+ },
)
registrationVerificationRoute(
@@ -49,7 +54,7 @@ fun NavGraphBuilder.authenticationNavGraph(
}
}
-fun NavGraphBuilder.loginRoute(
+private fun NavGraphBuilder.loginRoute(
navigateToRegisterScreen: () -> Unit,
navigateToPasscodeScreen: () -> Unit,
) {
@@ -61,7 +66,7 @@ fun NavGraphBuilder.loginRoute(
}
}
-fun NavGraphBuilder.registrationRoute(
+private fun NavGraphBuilder.registrationRoute(
navigateBack: () -> Unit,
onRegistered: () -> Unit,
) {
@@ -73,7 +78,7 @@ fun NavGraphBuilder.registrationRoute(
}
}
-fun NavGraphBuilder.registrationVerificationRoute(
+private fun NavGraphBuilder.registrationVerificationRoute(
navigateBack: () -> Unit,
onRegistrationVerified: () -> Unit,
) {
diff --git a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavigationScreen.kt b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavigationScreen.kt
index 4c261bdaf..9b5b58a5c 100644
--- a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavigationScreen.kt
+++ b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavigationScreen.kt
@@ -9,13 +9,11 @@
*/
package org.mifos.mobile.feature.auth.navigation
-const val AUTH_NAVIGATION_ROUTE = "auth_route"
const val LOGIN_SCREEN_ROUTE = "login_screen"
const val REGISTRATION_SCREEN_ROUTE = "registration_screen"
const val REGISTRATION_VERIFICATION_SCREEN_ROUTE = "registration_verification_screen"
sealed class AuthenticationNavigation(val route: String) {
- data object AuthenticationBase : AuthenticationNavigation(route = AUTH_NAVIGATION_ROUTE)
data object Login : AuthenticationNavigation(route = LOGIN_SCREEN_ROUTE)
data object Registration : AuthenticationNavigation(route = REGISTRATION_SCREEN_ROUTE)
data object RegistrationVerification :
diff --git a/feature/auth/src/main/res/drawable/feature_auth_lock.xml b/feature/auth/src/main/res/drawable/feature_auth_lock.xml
index 8658fd5eb..a457b9e96 100644
--- a/feature/auth/src/main/res/drawable/feature_auth_lock.xml
+++ b/feature/auth/src/main/res/drawable/feature_auth_lock.xml
@@ -11,7 +11,6 @@
diff --git a/feature/client-charge/src/main/res/values/colors.xml b/feature/client-charge/src/main/res/values/colors.xml
new file mode 100644
index 000000000..d7ccaf034
--- /dev/null
+++ b/feature/client-charge/src/main/res/values/colors.xml
@@ -0,0 +1,13 @@
+
+
+
+ #FF325ca8
+
\ No newline at end of file
diff --git a/feature/guarantor/build.gradle.kts b/feature/guarantor/build.gradle.kts
index 890455979..bfac1677f 100644
--- a/feature/guarantor/build.gradle.kts
+++ b/feature/guarantor/build.gradle.kts
@@ -16,4 +16,6 @@ android {
namespace = "org.mifos.mobile.feature.guarantor"
}
-dependencies { }
\ No newline at end of file
+dependencies {
+ implementation(libs.squareup.okhttp)
+}
\ No newline at end of file
diff --git a/feature/home/src/main/java/org/mifos/mobile/feature/home/components/HomeNavigationDrawer.kt b/feature/home/src/main/java/org/mifos/mobile/feature/home/components/HomeNavigationDrawer.kt
index 469d7c699..396b0fa54 100644
--- a/feature/home/src/main/java/org/mifos/mobile/feature/home/components/HomeNavigationDrawer.kt
+++ b/feature/home/src/main/java/org/mifos/mobile/feature/home/components/HomeNavigationDrawer.kt
@@ -30,9 +30,7 @@ import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.unit.dp
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.ui.component.MifosUserImage
@@ -84,7 +82,7 @@ internal fun HomeNavigationDrawer(
label = {
Row {
Icon(
- imageVector = ImageVector.vectorResource(id = item.iconResId),
+ imageVector = item.imageVector,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
)
diff --git a/feature/home/src/main/java/org/mifos/mobile/feature/home/screens/HomeContent.kt b/feature/home/src/main/java/org/mifos/mobile/feature/home/screens/HomeContent.kt
index 93146703e..318a075a0 100644
--- a/feature/home/src/main/java/org/mifos/mobile/feature/home/screens/HomeContent.kt
+++ b/feature/home/src/main/java/org/mifos/mobile/feature/home/screens/HomeContent.kt
@@ -10,7 +10,6 @@
package org.mifos.mobile.feature.home.screens
import android.graphics.Bitmap
-import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -44,10 +43,10 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.colorResource
-import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
@@ -213,7 +212,7 @@ private fun HomeCards(
.weight(1f)
.padding(bottom = 8.dp),
titleId = card.titleId,
- drawableResId = card.drawableResId,
+ imageVector = card.imageVector,
onClick = {
if (card == HomeCardItem.TransferCard) {
showTransferDialog = true
@@ -272,7 +271,7 @@ private fun UserDetailsRow(
@Composable
private fun HomeCard(
@StringRes titleId: Int,
- @DrawableRes drawableResId: Int,
+ imageVector: ImageVector,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
@@ -288,9 +287,9 @@ private fun HomeCard(
verticalArrangement = Arrangement.Center,
) {
Icon(
- painter = painterResource(id = drawableResId),
+ imageVector = imageVector,
contentDescription = null,
- modifier = Modifier.size(56.dp),
+ modifier = Modifier.size(48.dp),
tint = MaterialTheme.colorScheme.primary,
)
Text(
diff --git a/feature/home/src/main/java/org/mifos/mobile/feature/home/viewmodel/HomeViewModel.kt b/feature/home/src/main/java/org/mifos/mobile/feature/home/viewmodel/HomeViewModel.kt
index 7c57ed918..714ed0b7b 100644
--- a/feature/home/src/main/java/org/mifos/mobile/feature/home/viewmodel/HomeViewModel.kt
+++ b/feature/home/src/main/java/org/mifos/mobile/feature/home/viewmodel/HomeViewModel.kt
@@ -11,6 +11,7 @@ package org.mifos.mobile.feature.home.viewmodel
import android.util.Base64
import android.util.Log
+import androidx.compose.ui.graphics.vector.ImageVector
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -20,6 +21,7 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.launch
import org.mifos.mobile.core.data.repository.HomeRepository
import org.mifos.mobile.core.datastore.PreferencesHelper
+import org.mifos.mobile.core.designsystem.icons.MifosIcons
import org.mifos.mobile.core.model.entity.accounts.loan.LoanAccount
import org.mifos.mobile.core.model.entity.accounts.savings.SavingAccount
import org.mifos.mobile.core.ui.utils.ImageUtil
@@ -163,85 +165,100 @@ internal class HomeViewModel @Inject constructor(
sealed class HomeCardItem(
val titleId: Int,
- val drawableResId: Int,
+ val imageVector: ImageVector,
) {
- data object AccountCard :
- HomeCardItem(R.string.accounts, R.drawable.ic_account_balance_black_24dp)
+ data object AccountCard : HomeCardItem(
+ titleId = R.string.accounts,
+ imageVector = MifosIcons.AccountBalance,
+ )
- data object TransferCard :
- HomeCardItem(R.string.transfer, R.drawable.ic_compare_arrows_black_24dp)
+ data object TransferCard : HomeCardItem(
+ titleId = R.string.transfer,
+ imageVector = MifosIcons.CompareArrows,
+ )
- data object ChargesCard :
- HomeCardItem(R.string.charges, R.drawable.ic_account_balance_wallet_black_24dp)
+ data object ChargesCard : HomeCardItem(
+ titleId = R.string.charges,
+ imageVector = MifosIcons.AccountBalanceWallet,
+ )
- data object LoanCard : HomeCardItem(R.string.apply_for_loan, R.drawable.ic_loan)
- data object BeneficiariesCard :
- HomeCardItem(R.string.beneficiaries, R.drawable.ic_beneficiaries_48px)
+ data object LoanCard : HomeCardItem(
+ titleId = R.string.apply_for_loan,
+ imageVector = MifosIcons.RealEstateAgent,
+ )
- data object SurveyCard : HomeCardItem(R.string.survey, R.drawable.ic_surveys_48px)
+ data object BeneficiariesCard : HomeCardItem(
+ titleId = R.string.beneficiaries,
+ imageVector = MifosIcons.People,
+ )
+
+ data object SurveyCard : HomeCardItem(
+ R.string.survey,
+ MifosIcons.Assignment,
+ )
}
enum class HomeNavigationItems(
val nameResId: Int,
- val iconResId: Int,
+ val imageVector: ImageVector,
) {
Home(
nameResId = R.string.home,
- iconResId = R.drawable.ic_account_balance_black_24dp,
+ imageVector = MifosIcons.AccountBalance,
),
Accounts(
nameResId = R.string.accounts,
- iconResId = R.drawable.ic_account_balance_wallet_black_24dp,
+ imageVector = MifosIcons.AccountBalanceWallet,
),
RecentTransactions(
nameResId = R.string.recent_transactions,
- iconResId = R.drawable.ic_label_black_24dp,
+ imageVector = MifosIcons.Label,
),
Charges(
nameResId = R.string.charges,
- iconResId = R.drawable.ic_charges,
+ imageVector = MifosIcons.Paid,
),
ThirdPartyTransfer(
nameResId = R.string.third_party_transfer,
- iconResId = R.drawable.ic_compare_arrows_black_24dp,
+ imageVector = MifosIcons.CompareArrows,
),
ManageBeneficiaries(
nameResId = R.string.manage_beneficiaries,
- iconResId = R.drawable.ic_beneficiaries_48px,
+ imageVector = MifosIcons.People,
),
Settings(
nameResId = R.string.settings,
- iconResId = R.drawable.ic_settings,
+ imageVector = MifosIcons.Settings,
),
AboutUs(
nameResId = R.string.about_us,
- iconResId = R.drawable.ic_about_us_black_24dp,
+ imageVector = MifosIcons.People,
),
Help(
nameResId = R.string.help,
- iconResId = R.drawable.ic_help_black_24dp,
+ imageVector = MifosIcons.Help,
),
Share(
nameResId = R.string.share,
- iconResId = R.drawable.ic_share_black_24dp,
+ imageVector = MifosIcons.Share,
),
AppInfo(
nameResId = R.string.app_info,
- iconResId = R.drawable.ic_info_black_24dp,
+ imageVector = MifosIcons.Info,
),
Logout(
nameResId = R.string.logout,
- iconResId = R.drawable.ic_logout,
+ imageVector = MifosIcons.Logout,
),
}
diff --git a/feature/home/src/main/res/drawable/ic_account_balance_black_24dp.xml b/feature/home/src/main/res/drawable/ic_account_balance_black_24dp.xml
index 759996009..96c65d736 100644
--- a/feature/home/src/main/res/drawable/ic_account_balance_black_24dp.xml
+++ b/feature/home/src/main/res/drawable/ic_account_balance_black_24dp.xml
@@ -14,6 +14,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
diff --git a/feature/home/src/main/res/drawable/ic_account_balance_wallet_black_24dp.xml b/feature/home/src/main/res/drawable/ic_account_balance_wallet_black_24dp.xml
index 4ee317f3e..2c5889326 100644
--- a/feature/home/src/main/res/drawable/ic_account_balance_wallet_black_24dp.xml
+++ b/feature/home/src/main/res/drawable/ic_account_balance_wallet_black_24dp.xml
@@ -14,6 +14,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
diff --git a/feature/home/src/main/res/drawable/ic_beneficiaries_48px.xml b/feature/home/src/main/res/drawable/ic_beneficiaries_48px.xml
index d420c9e1c..0371e2838 100644
--- a/feature/home/src/main/res/drawable/ic_beneficiaries_48px.xml
+++ b/feature/home/src/main/res/drawable/ic_beneficiaries_48px.xml
@@ -14,6 +14,6 @@
android:viewportWidth="48.0"
android:viewportHeight="48.0">
diff --git a/feature/home/src/main/res/drawable/ic_charges.xml b/feature/home/src/main/res/drawable/ic_charges.xml
index ad76ab446..d48a1cbcb 100644
--- a/feature/home/src/main/res/drawable/ic_charges.xml
+++ b/feature/home/src/main/res/drawable/ic_charges.xml
@@ -14,42 +14,42 @@
android:viewportWidth="147.0"
android:viewportHeight="146.0">
diff --git a/feature/home/src/main/res/drawable/ic_compare_arrows_black_24dp.xml b/feature/home/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
index 90e1df30d..884d9365b 100644
--- a/feature/home/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
+++ b/feature/home/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
@@ -14,6 +14,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
diff --git a/feature/home/src/main/res/drawable/ic_loan.xml b/feature/home/src/main/res/drawable/ic_loan.xml
index 87c0173ba..9b6155a7e 100644
--- a/feature/home/src/main/res/drawable/ic_loan.xml
+++ b/feature/home/src/main/res/drawable/ic_loan.xml
@@ -14,7 +14,7 @@
android:viewportWidth="192.0"
android:viewportHeight="192.0">
diff --git a/feature/home/src/main/res/drawable/ic_logout.xml b/feature/home/src/main/res/drawable/ic_logout.xml
index 27f586f3a..4d7522faa 100644
--- a/feature/home/src/main/res/drawable/ic_logout.xml
+++ b/feature/home/src/main/res/drawable/ic_logout.xml
@@ -11,7 +11,6 @@
diff --git a/feature/home/src/main/res/drawable/ic_surveys_48px.xml b/feature/home/src/main/res/drawable/ic_surveys_48px.xml
index facc532bb..9ef95ef8b 100644
--- a/feature/home/src/main/res/drawable/ic_surveys_48px.xml
+++ b/feature/home/src/main/res/drawable/ic_surveys_48px.xml
@@ -14,6 +14,6 @@
android:viewportWidth="48.0"
android:viewportHeight="48.0">
diff --git a/feature/home/src/main/res/drawable/ic_visibility_24px.xml b/feature/home/src/main/res/drawable/ic_visibility_24px.xml
index 4741a7fe7..f6a9a3c6a 100644
--- a/feature/home/src/main/res/drawable/ic_visibility_24px.xml
+++ b/feature/home/src/main/res/drawable/ic_visibility_24px.xml
@@ -11,7 +11,7 @@
diff --git a/feature/home/src/main/res/drawable/ic_visibility_off_24px.xml b/feature/home/src/main/res/drawable/ic_visibility_off_24px.xml
index 57495b870..fb25f37eb 100644
--- a/feature/home/src/main/res/drawable/ic_visibility_off_24px.xml
+++ b/feature/home/src/main/res/drawable/ic_visibility_off_24px.xml
@@ -11,7 +11,7 @@
diff --git a/feature/loan/src/main/java/org/mifos/mobile/feature/loan/loanRepaymentSchedule/LoanRepaymentScheduleScreen.kt b/feature/loan/src/main/java/org/mifos/mobile/feature/loan/loanRepaymentSchedule/LoanRepaymentScheduleScreen.kt
index 38d2a7eeb..55ed6382c 100644
--- a/feature/loan/src/main/java/org/mifos/mobile/feature/loan/loanRepaymentSchedule/LoanRepaymentScheduleScreen.kt
+++ b/feature/loan/src/main/java/org/mifos/mobile/feature/loan/loanRepaymentSchedule/LoanRepaymentScheduleScreen.kt
@@ -175,8 +175,11 @@ private fun RepaymentScheduleTable(
TableCell(text = "${periods.indexOf(period) + 1}", weight = 0.5f)
TableCell(text = DateHelper.getDateAsString(period.dueDate), weight = 1f)
TableCell(
- text = if (period.principalOriginalDue == null) "$currency 0.00"
- else "$currency ${period.principalOriginalDue}",
+ text = if (period.principalOriginalDue == null) {
+ "$currency 0.00"
+ } else {
+ "$currency ${period.principalOriginalDue}"
+ },
weight = 1f,
)
TableCell(
diff --git a/feature/loan/src/main/res/drawable/ic_assignment_turned_in_black_24dp.xml b/feature/loan/src/main/res/drawable/ic_assignment_turned_in_black_24dp.xml
index e0a066387..772843ee1 100644
--- a/feature/loan/src/main/res/drawable/ic_assignment_turned_in_black_24dp.xml
+++ b/feature/loan/src/main/res/drawable/ic_assignment_turned_in_black_24dp.xml
@@ -11,7 +11,6 @@
diff --git a/feature/loan/src/main/res/drawable/ic_compare_arrows_black_24dp.xml b/feature/loan/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
index 90e1df30d..884d9365b 100644
--- a/feature/loan/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
+++ b/feature/loan/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
@@ -14,6 +14,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
diff --git a/feature/loan/src/main/res/drawable/ic_qrcode_scan.xml b/feature/loan/src/main/res/drawable/ic_qrcode_scan.xml
index 9b5a08a67..cef83f53e 100644
--- a/feature/loan/src/main/res/drawable/ic_qrcode_scan.xml
+++ b/feature/loan/src/main/res/drawable/ic_qrcode_scan.xml
@@ -14,6 +14,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
diff --git a/feature/loan/src/main/res/drawable/ic_surveys_48px.xml b/feature/loan/src/main/res/drawable/ic_surveys_48px.xml
index facc532bb..9ef95ef8b 100644
--- a/feature/loan/src/main/res/drawable/ic_surveys_48px.xml
+++ b/feature/loan/src/main/res/drawable/ic_surveys_48px.xml
@@ -14,6 +14,6 @@
android:viewportWidth="48.0"
android:viewportHeight="48.0">
diff --git a/feature/login/build.gradle.kts b/feature/login/build.gradle.kts
deleted file mode 100644
index 41dd4f420..000000000
--- a/feature/login/build.gradle.kts
+++ /dev/null
@@ -1,19 +0,0 @@
-plugins {
- alias(libs.plugins.mifos.android.feature)
- alias(libs.plugins.mifos.android.library.compose)
-}
-
-android {
- namespace = "org.mifos.mobile.feature.login"
-}
-
-dependencies {
- implementation(projects.ui)
- implementation(projects.core.common)
- implementation(projects.core.model)
- implementation(projects.core.data)
-
- testImplementation(libs.junit)
- androidTestImplementation(libs.androidx.test.ext.junit)
- androidTestImplementation(libs.espresso.core)
-}
\ No newline at end of file
diff --git a/feature/login/consumer-rules.pro b/feature/login/consumer-rules.pro
deleted file mode 100644
index e69de29bb..000000000
diff --git a/feature/login/proguard-rules.pro b/feature/login/proguard-rules.pro
deleted file mode 100644
index 481bb4348..000000000
--- a/feature/login/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/feature/login/src/androidTest/java/org/mifos/mobile/feature/login/ExampleInstrumentedTest.kt b/feature/login/src/androidTest/java/org/mifos/mobile/feature/login/ExampleInstrumentedTest.kt
deleted file mode 100644
index c1e24c3c3..000000000
--- a/feature/login/src/androidTest/java/org/mifos/mobile/feature/login/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.mifos.mobile.feature.login
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("org.mifos.mobile.feature.login.test", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/feature/login/src/main/AndroidManifest.xml b/feature/login/src/main/AndroidManifest.xml
deleted file mode 100644
index a5918e68a..000000000
--- a/feature/login/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/feature/login/src/main/res/values/strings.xml b/feature/login/src/main/res/values/strings.xml
deleted file mode 100644
index a15011cd6..000000000
--- a/feature/login/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- No Internet Connection
- Register
- Login Failed, Please Try Again Later.
- Welcome %1$s
- Username
- Password
- Login
- Create an account
- %1$s cannot be blank
- %1$s cannot be less than %2$d characters
- %1$s cannot contain %2$s
- Spaces
-
\ No newline at end of file
diff --git a/feature/login/src/test/java/org/mifos/mobile/feature/login/ExampleUnitTest.kt b/feature/login/src/test/java/org/mifos/mobile/feature/login/ExampleUnitTest.kt
deleted file mode 100644
index 337817e5b..000000000
--- a/feature/login/src/test/java/org/mifos/mobile/feature/login/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.mifos.mobile.feature.login
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file
diff --git a/feature/notification/src/main/res/drawable/ic_notifications.xml b/feature/notification/src/main/res/drawable/ic_notifications.xml
index 5f3a73aef..ca8b6a1ae 100644
--- a/feature/notification/src/main/res/drawable/ic_notifications.xml
+++ b/feature/notification/src/main/res/drawable/ic_notifications.xml
@@ -11,7 +11,6 @@
Unit,
+ onProceed: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ if (cropState != null) {
+ ImageCropperDialog(state = cropState)
+ }
+
+ if (cropState == null && loadingStatus != null) {
+ LoadingDialog(status = loadingStatus)
+ }
+
+ Column(
+ modifier = modifier.padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ if (selectedImage != null) {
+ Text(
+ modifier = Modifier
+ .fillMaxWidth(),
+ text = stringResource(id = R.string.seleted_qr_image),
+ textAlign = TextAlign.Center,
+ )
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ Image(
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxWidth()
+ .padding(16.dp),
+ bitmap = selectedImage,
+ contentDescription = null,
+ )
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ MifosOutlinedButton(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ textResId = R.string.import_another_qr,
+ onClick = onPick,
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ MifosButton(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ textResId = R.string.proceed,
+ onClick = onProceed,
+ )
+ } else {
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = Modifier.weight(1f),
+ ) {
+ Text("No QR Code selected!")
+ }
+
+ MifosButton(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ textResId = R.string.import_qr,
+ onClick = onPick,
+ )
+ }
+ }
+}
+
+@DevicePreviews
+@Composable
+private fun CropContentPreview(
+ modifier: Modifier = Modifier,
+) {
+ MifosMobileTheme {
+ CropContent(
+ cropState = rememberImageCropper().cropState,
+ modifier = modifier,
+ loadingStatus = null,
+ selectedImage = null,
+ onPick = {},
+ onProceed = {},
+ )
+ }
+}
diff --git a/feature/qr/src/main/java/org/mifos/mobile/feature/qr/components/CropErrorDialog.kt b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/components/CropErrorDialog.kt
new file mode 100644
index 000000000..ebeedf312
--- /dev/null
+++ b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/components/CropErrorDialog.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.feature.qr.components
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.BasicAlertDialog
+import androidx.compose.material3.Button
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.mr0xf00.easycrop.CropError
+import com.mr0xf00.easycrop.CropperLoading
+
+@Composable
+fun CropErrorDialog(
+ error: CropError,
+ onDismiss: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ AlertDialog(
+ onDismissRequest = onDismiss,
+ modifier = modifier,
+ confirmButton = { Button(onClick = onDismiss) { Text("Ok") } },
+ text = { Text(error.getMessage()) },
+ )
+}
+
+@Composable
+fun CropError.getMessage(): String = remember(this) {
+ when (this) {
+ CropError.LoadingError -> "Error while opening the image !"
+ CropError.SavingError -> "Error while saving the image !"
+ }
+}
+
+@Composable
+fun LoadingDialog(
+ status: CropperLoading,
+ modifier: Modifier = Modifier,
+) {
+ var dismissed by remember(status) { mutableStateOf(false) }
+
+ if (!dismissed) {
+ BasicAlertDialog(
+ onDismissRequest = { dismissed = true },
+ modifier = modifier,
+ ) {
+ Surface(
+ shape = MaterialTheme.shapes.small,
+ tonalElevation = 2.dp,
+ shadowElevation = 2.dp,
+ ) {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(6.dp, Alignment.CenterVertically),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.padding(16.dp),
+ ) {
+ CircularProgressIndicator()
+ Text(text = status.getMessage())
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun CropperLoading.getMessage(): String {
+ return remember(this) {
+ when (this) {
+ CropperLoading.PreparingImage -> "Preparing Image"
+ CropperLoading.SavingResult -> "Saving Result"
+ }
+ }
+}
diff --git a/feature/qr/src/main/java/org/mifos/mobile/feature/qr/navigation/QrNavGraph.kt b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/navigation/QrNavGraph.kt
index 9061299c8..0cd21903c 100644
--- a/feature/qr/src/main/java/org/mifos/mobile/feature/qr/navigation/QrNavGraph.kt
+++ b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/navigation/QrNavGraph.kt
@@ -23,9 +23,7 @@ import org.mifos.mobile.feature.qr.qrCodeDisplay.QrCodeDisplayScreen
import org.mifos.mobile.feature.qr.qrCodeImport.QrCodeImportScreen
fun NavController.navigateToQrDisplayScreen(qrString: String) {
- navigate(
- QrNavigation.QrDisplayScreen.passArguments(qrString = qrString),
- )
+ navigate(QrNavigation.QrDisplayScreen.passArguments(qrString = qrString))
}
fun NavController.navigateToQrImportScreen() {
diff --git a/feature/qr/src/main/java/org/mifos/mobile/feature/qr/qrCodeImport/QrCodeImportScreen.kt b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/qrCodeImport/QrCodeImportScreen.kt
index 75c594fa7..a433b6b93 100644
--- a/feature/qr/src/main/java/org/mifos/mobile/feature/qr/qrCodeImport/QrCodeImportScreen.kt
+++ b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/qrCodeImport/QrCodeImportScreen.kt
@@ -10,55 +10,44 @@
package org.mifos.mobile.feature.qr.qrCodeImport
import android.graphics.Bitmap
-import android.graphics.ImageDecoder
-import android.net.Uri
-import android.os.Build
-import android.provider.MediaStore
import android.widget.Toast
-import androidx.activity.compose.rememberLauncherForActivityResult
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
-import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.canhub.cropper.CropImageContract
-import com.canhub.cropper.CropImageContractOptions
-import com.canhub.cropper.CropImageOptions
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import com.google.zxing.BarcodeFormat
import com.google.zxing.Result
-import org.mifos.mobile.core.common.Network
-import org.mifos.mobile.core.designsystem.components.MifosButton
+import com.mr0xf00.easycrop.CropError
+import com.mr0xf00.easycrop.CropResult
+import com.mr0xf00.easycrop.crop
+import com.mr0xf00.easycrop.rememberImageCropper
+import com.mr0xf00.easycrop.rememberImagePicker
+import kotlinx.coroutines.launch
import org.mifos.mobile.core.designsystem.components.MifosScaffold
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary
import org.mifos.mobile.core.model.enums.BeneficiaryState
-import org.mifos.mobile.core.ui.component.MifosErrorComponent
import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay
import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.qr.R
+import org.mifos.mobile.feature.qr.components.CropContent
+import org.mifos.mobile.feature.qr.components.CropErrorDialog
@Composable
internal fun QrCodeImportScreen(
@@ -143,126 +132,37 @@ private fun QrCodeImportContent(
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
-
- var bitmap: Bitmap? by rememberSaveable {
- mutableStateOf(null)
- }
- var showErrorScreen by rememberSaveable {
- mutableStateOf(false)
- }
-
- Column(
- modifier = modifier
- .fillMaxSize(),
- ) {
- /**
- * responsible for image picking,
- * & cropping image
- */
- QrCodeImportFunctions(
- setImageBitmap = { bitmap = it },
- showErrorScreen = { showErrorScreen = it },
- )
-
- if (showErrorScreen) {
- MifosErrorComponent(
- isNetworkConnected = Network.isConnected(context),
- isRetryEnabled = false,
- isEmptyData = true,
- message = stringResource(
- id = R.string.no_image_selected_or_something_went_wrong,
- ),
- )
- } else if (bitmap != null) {
- Text(
- modifier = Modifier
- .fillMaxWidth(),
- text = stringResource(id = R.string.seleted_qr_image),
- textAlign = TextAlign.Center,
- )
-
- Spacer(modifier = Modifier.height(16.dp))
-
- Image(
- modifier = Modifier
- .weight(1f)
- .fillMaxWidth()
- .padding(16.dp),
- bitmap = bitmap!!.asImageBitmap(),
- contentDescription = null,
- )
-
- Spacer(modifier = Modifier.height(16.dp))
-
- MifosButton(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- textResId = R.string.proceed,
- onClick = { proceedClicked.invoke(bitmap!!) },
- )
- }
- }
-}
-
-@Composable
-@Suppress("ModifierMissing")
-private fun QrCodeImportFunctions(
- setImageBitmap: (Bitmap) -> Unit,
- showErrorScreen: (error: Boolean) -> Unit,
-) {
- val context = LocalContext.current
-
- var imageUri: Uri? by rememberSaveable { mutableStateOf(null) }
- var bitmap: Bitmap? by rememberSaveable { mutableStateOf(null) }
- var hasImagePicked by rememberSaveable { mutableStateOf(false) }
-
- /**
- * responsible for image cropping
- * [https://github.com/CanHub/Android-Image-Cropper]
- */
- val imageCropLauncher = rememberLauncherForActivityResult(CropImageContract()) { result ->
- if (result.isSuccessful) {
- hasImagePicked = true
- val uri = result.uriContent
- bitmap = if (Build.VERSION.SDK_INT < 28) {
- MediaStore.Images.Media.getBitmap(context.contentResolver, uri)
- } else {
- val source = uri?.let { ImageDecoder.createSource(context.contentResolver, it) }
- val hardwareBitmap = source?.let { ImageDecoder.decodeBitmap(it) }
- hardwareBitmap?.let { convertToMutableBitmap(it) }
+ val imageCropper = rememberImageCropper()
+ val scope = rememberCoroutineScope()
+
+ var selectedImage by remember { mutableStateOf(null) }
+ var error by remember { mutableStateOf(null) }
+
+ val imagePicker = rememberImagePicker(
+ onImage = { uri ->
+ scope.launch {
+ when (val result = imageCropper.crop(uri, context)) {
+ is CropResult.Cancelled -> {}
+ is CropError -> error = result
+ is CropResult.Success -> {
+ selectedImage = result.bitmap
+ }
+ }
}
- } else {
- /**
- * handles if the user presses back button on top-left (that comes with the cropLauncher)
- * instead of cropping the image
- */
- showErrorScreen.invoke(true)
- Toast.makeText(context, R.string.something_went_wrong, Toast.LENGTH_SHORT).show()
- }
- bitmap?.let { setImageBitmap.invoke(it) }
- }
+ },
+ )
- /**
- * uri is null when the user presses back button instead of
- * selecting images when on gallery. so we have to handle it as well
- */
- val imagePickerLauncher = rememberLauncherForActivityResult(
- contract = ActivityResultContracts.GetContent(),
- ) { uri: Uri? ->
- imageUri = uri
- if (imageUri == null) {
- Toast.makeText(context, R.string.something_went_wrong, Toast.LENGTH_SHORT).show()
- showErrorScreen.invoke(true)
- } else {
- imageCropLauncher.launch(CropImageContractOptions(imageUri, CropImageOptions()))
- }
- }
+ CropContent(
+ cropState = imageCropper.cropState,
+ loadingStatus = imageCropper.loadingStatus,
+ selectedImage = selectedImage,
+ onPick = { imagePicker.pick() },
+ onProceed = { selectedImage?.let { proceedClicked.invoke(it.asAndroidBitmap()) } },
+ modifier = modifier,
+ )
- LaunchedEffect(Unit) {
- if (!hasImagePicked) {
- imagePickerLauncher.launch("image/*")
- }
+ error?.let {
+ CropErrorDialog(it, onDismiss = { error = null })
}
}
diff --git a/feature/qr/src/main/res/values/strings.xml b/feature/qr/src/main/res/values/strings.xml
index 5393282ed..c7f0b74e0 100644
--- a/feature/qr/src/main/res/values/strings.xml
+++ b/feature/qr/src/main/res/values/strings.xml
@@ -25,6 +25,7 @@
Proceed
Import QR
+ Import Another QR
Error while reading QR, make sure you select proper region
You can\'t make action into your account, please scan other user
diff --git a/feature/recent-transaction/src/main/res/drawable/ic_local_atm_black_24dp.xml b/feature/recent-transaction/src/main/res/drawable/ic_local_atm_black_24dp.xml
index 64849554a..457c96740 100644
--- a/feature/recent-transaction/src/main/res/drawable/ic_local_atm_black_24dp.xml
+++ b/feature/recent-transaction/src/main/res/drawable/ic_local_atm_black_24dp.xml
@@ -11,7 +11,6 @@
-
-
-
\ No newline at end of file
diff --git a/feature/registration/src/main/res/values/strings.xml b/feature/registration/src/main/res/values/strings.xml
deleted file mode 100644
index ea95349d6..000000000
--- a/feature/registration/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
- mobile
- email
- Account Number
- Username
- First Name
- Last Name
- Phone Number
- Email
- Password
- Confirm Password
- Verification Mode
- Register
- Do you want to cancel registering New Account ?
- Cancel Registration
- Request Id
- Request ID cannot be empty
- Authentication Token
- Authentication Token cannot be empty
- Verify
- User has been verified successfully
- Yes
- No
- We were unable to register the user.
- Weak
- Medium
- Strong
- Very Strong
- Error loading response from server
- %1$s cannot be blank
- %1$s cannot be less than %2$d characters
- %1$s cannot contain %2$s
- %1$s cannot begin or
- end with blank space
-
- Username should be greater than 6 alphabets
-
- Spaces
- Invalid Phone Number
- Invalid email id
- Password does not match.
-
\ No newline at end of file
diff --git a/feature/registration/src/main/res/values/validations.xml b/feature/registration/src/main/res/values/validations.xml
deleted file mode 100644
index 37e3b680a..000000000
--- a/feature/registration/src/main/res/values/validations.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- 5
- 6
-
\ No newline at end of file
diff --git a/feature/registration/src/main/res/values/values.xml b/feature/registration/src/main/res/values/values.xml
deleted file mode 100644
index d8be40f3f..000000000
--- a/feature/registration/src/main/res/values/values.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- 16dp
- 100dp
-
\ No newline at end of file
diff --git a/feature/registration/src/test/java/org/mifos/mobile/feature/registration/ExampleUnitTest.kt b/feature/registration/src/test/java/org/mifos/mobile/feature/registration/ExampleUnitTest.kt
deleted file mode 100644
index 6f5898ed3..000000000
--- a/feature/registration/src/test/java/org/mifos/mobile/feature/registration/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.mifos.mobile.feature.registration
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file
diff --git a/feature/savings/src/main/res/drawable/ic_charges.xml b/feature/savings/src/main/res/drawable/ic_charges.xml
index ad76ab446..dc0f4f5c1 100644
--- a/feature/savings/src/main/res/drawable/ic_charges.xml
+++ b/feature/savings/src/main/res/drawable/ic_charges.xml
@@ -14,42 +14,42 @@
android:viewportWidth="147.0"
android:viewportHeight="146.0">
diff --git a/feature/savings/src/main/res/drawable/ic_compare_arrows_black_24dp.xml b/feature/savings/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
index 90e1df30d..eb3432e44 100644
--- a/feature/savings/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
+++ b/feature/savings/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
@@ -14,6 +14,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
diff --git a/feature/savings/src/main/res/drawable/ic_qrcode_scan.xml b/feature/savings/src/main/res/drawable/ic_qrcode_scan.xml
index 9b5a08a67..72ae29efc 100644
--- a/feature/savings/src/main/res/drawable/ic_qrcode_scan.xml
+++ b/feature/savings/src/main/res/drawable/ic_qrcode_scan.xml
@@ -14,6 +14,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
diff --git a/feature/settings/src/main/res/drawable/ic_baseline_dark_mode_24.xml b/feature/settings/src/main/res/drawable/ic_baseline_dark_mode_24.xml
index 01bb33722..81968e4c6 100644
--- a/feature/settings/src/main/res/drawable/ic_baseline_dark_mode_24.xml
+++ b/feature/settings/src/main/res/drawable/ic_baseline_dark_mode_24.xml
@@ -11,7 +11,6 @@
diff --git a/feature/user-profile/src/main/res/drawable/ic_gender_24dp.xml b/feature/user-profile/src/main/res/drawable/ic_gender_24dp.xml
index e06dc90fd..22bf928dc 100644
--- a/feature/user-profile/src/main/res/drawable/ic_gender_24dp.xml
+++ b/feature/user-profile/src/main/res/drawable/ic_gender_24dp.xml
@@ -14,42 +14,42 @@
android:viewportWidth="512"
android:viewportHeight="512">
diff --git a/feature/user-profile/src/main/res/drawable/ic_phone_24dp.xml b/feature/user-profile/src/main/res/drawable/ic_phone_24dp.xml
index 4d17a6cd4..14e0908c8 100644
--- a/feature/user-profile/src/main/res/drawable/ic_phone_24dp.xml
+++ b/feature/user-profile/src/main/res/drawable/ic_phone_24dp.xml
@@ -15,6 +15,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 45b103bc9..d23564b01 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,150 +1,121 @@
[versions]
-accompanistPermissionsVersion = "0.34.0"
-androidImageCropperVersion = "4.0.0"
-
-activityVersion = "1.5.0"
-annotationVersion = "1.1.0"
-appcompatVersion = "1.6.1"
-androidDesugarJdkLibs = "2.0.4"
-androidx-test-ext-junit = "1.1.5"
-androidGradlePlugin = "8.2.2"
-androidTools = "31.2.1"
-androidxActivity = "1.7.2"
-androidxComposeBom = "2023.10.01"
-androidxComposeCompiler = "1.5.14"
-androidxCore = "1.10.1"
-androidxHilt = "1.1.0-alpha01"
-androidxLifecycle = "2.6.1"
+accompanistVersion = "0.34.0"
+androidGradlePlugin = "8.5.2"
+androidTools = "31.5.2"
+activityVersion = "1.9.1"
+annotationVersion = "1.8.2"
+appcompatVersion = "1.7.0"
+androidDesugarJdkLibs = "2.1.1"
+androidx-test-ext-junit = "1.2.1"
+androidxTestRules = "1.6.1"
+androidxTestExt = "1.2.1"
+androidxTestRunner = "1.6.2"
+androidxEspresso = "3.6.1"
+androidxUiAutomator = "2.3.0"
+androidxTracing = "1.2.0"
+androidxProfileinstaller = "1.3.1"
+androidxMetrics = "1.0.0-beta01"
+androidxActivity = "1.9.1"
+androidxComposeBom = "2024.08.00"
+androidxComposeCompiler = "1.5.15"
+androidxHilt = "1.2.0"
+androidxLifecycle = "2.8.4"
androidxNavigation = "2.8.0-rc01"
androidxCoreSplashscreen = "1.0.1"
-biometricVersion = "1.1.0"
-butterknifePlugin = "10.2.3"
-butterKnifeVersion = "10.2.3"
-
-ccpVersion = "2.7.2"
-constraintlayoutVersion = "2.1.4"
-
-compileSdk = "34"
-
-coreKtxVersion = "1.12.0"
-coreTestingVersion = "2.2.0"
-cameraxVersion = "1.3.1"
+coreKtxVersion = "1.13.1"
+cameraxVersion = "1.3.4"
compose-material = "1.6.8"
-countrycodechooserVersion = "1.0"
-
-# Multiplatform Dependencies
-compose-plugin = "1.6.11"
-
detekt = "1.23.5"
-dependencyGuard = "0.4.3"
+dependencyGuard = "0.5.0"
dbflowVersion = "4.2.4"
-espressoContribVersion = "3.5.1"
-espresso-core = "3.5.1"
+easycropVersion = "0.1.1"
-fragmentVersion = "1.5.0"
-firebaseCrashlyticsPlugin = "2.9.2"
+firebaseCrashlyticsPlugin = "3.0.2"
firebasePerfPlugin = "1.4.2"
-firebaseBom = "32.7.1"
+firebaseBom = "33.2.0"
-gmsPlugin = "4.3.14"
-googleOss = "17.0.1"
+gmsPlugin = "4.4.2"
+googleOss = "17.1.0"
googleOssPlugin = "0.10.6"
googleMaps = "4.4.1"
-googleAppCodeScanner = "17.2.0"
+googleAppCodeScanner = "17.3.0"
-hilt = "2.50"
+hilt = "2.52"
-junit = "4.13.2"
-junitVersion = "4.12"
+junitVersion = "4.13.2"
+jacoco = "0.8.7"
-kotlin = "1.9.24"
-ksp = "1.9.21-1.0.16"
-kotlinxCoroutinesAndroidVersion = "1.6.4"
-kotlinxCoroutinesTestVersion = "1.7.3"
+kotlin = "2.0.20"
+ksp = "2.0.20-1.0.24"
+ktlint = "12.1.1"
+kotlinxCoroutinesAndroidVersion = "1.8.1"
+kotlinxCoroutinesTestVersion = "1.8.1"
kotlinxImmutable = "0.3.7"
-legacySupportV4Version = "1.0.0"
-lifecycleVersion = "2.6.2"
+lifecycleVersion = "2.8.4"
lifecycleExtensionsVersion = "2.2.0"
libphonenumberAndroidVersion = "8.13.35"
-minSdk = "24"
-material = "1.11.0"
material3 = "1.2.1"
-mifosPasscodeVersion = "1.0.3"
mockitoAndroidVersion = "5.4.0"
mockitoCoreVersion = "5.4.0"
-mockitoCore = "5.4.0"
multidexVersion = "2.0.1"
-spotbugs = "4.8.0"
spotlessVersion = "6.23.3"
-okHttp3Version = "3.14.9"
+okHttp3Version = "4.12.0"
-preference = "1.0.0"
-playServicesVersion = "17.0.1"
+playServicesVersion = "19.0.0"
playServicesCodeScanner = "16.1.0"
-recyclerviewVersion = "1.2.1"
-rulesVersion = "1.6.0-alpha01"
-runnerVersion = "1.6.0-alpha04"
-retrofitVersion = "2.9.0"
+retrofitVersion = "2.11.0"
rxandroidVersion = "2.1.1"
rxjavaVersion = "2.2.21"
room = "2.6.1"
-roborazzi = "1.6.0"
+roborazzi = "1.26.0"
-simplecropviewVersion = "1.1.8"
-sweetErrorVersion = "1.0.0"
secrets = "2.0.1"
slackComposeLint = "1.3.1"
-tableviewVersion = "0.8.9.4"
-targetSdk = "34"
-
-truth = "1.1.5"
+truth = "1.4.2"
turbineVersion = "1.1.0"
twitter-detekt-compose = "0.0.26"
-uihouseVersion = "alpha-2.1"
-
-vectordrawableVersion = "1.1.0"
-
zxingVersion = "3.5.3"
cameraCoreVersion = "1.3.4"
+# Multiplatform Dependencies
+# Add the version of the multiplatform dependencies here
+compose-plugin = "1.6.11"
+
koin = "3.6.0-Beta4"
koinComposeMultiplatform = "1.2.0-Beta4"
-navigationCompose = "2.8.0-alpha02"
-lifecycleViewModel = "2.8.2"
[bundles]
androidx-compose-ui-test = ["androidx-compose-ui-test", "androidx-compose-ui-test-manifest"]
[libraries]
-accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissionsVersion" }
-android-image-cropper = { module = "com.github.CanHub:Android-Image-Cropper", version.ref = "androidImageCropperVersion" }
+accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistVersion" }
+accompanist-pager = { module = "com.google.accompanist:accompanist-pager", version.ref = "accompanistVersion" }
androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityVersion" }
+androidx-metrics = { group = "androidx.metrics", name = "metrics-performance", version.ref = "androidxMetrics" }
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "annotationVersion" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompatVersion" }
-androidx-biometric = { module = "androidx.biometric:biometric", version.ref = "biometricVersion" }
-androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayoutVersion" }
-androidx-core-testing = { module = "androidx.arch.core:core-testing", version.ref = "coreTestingVersion" }
-androidx-espresso-contrib = { module = "androidx.test.espresso:espresso-contrib", version.ref = "espressoContribVersion" }
-androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentVersion" }
-androidx-legacy-support-v4 = { module = "androidx.legacy:legacy-support-v4", version.ref = "legacySupportV4Version" }
androidx-multidex = { module = "androidx.multidex:multidex", version.ref = "multidexVersion" }
-androidx-rules = { module = "androidx.test:rules", version.ref = "rulesVersion" }
-androidx-runner = { module = "androidx.test:runner", version.ref = "runnerVersion" }
-androidx-vectordrawable = { module = "androidx.vectordrawable:vectordrawable", version.ref = "vectordrawableVersion" }
-androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerviewVersion" }
-androidx-preference = { group = "androidx.preference", name = "preference", version.ref = "preference" }
+
+androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidxEspresso" }
+androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
+androidx-test-ext = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxTestExt" }
+androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidxTestRules" }
+androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTestRunner" }
+androidx-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "androidxUiAutomator" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtxVersion" }
+androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "androidxProfileinstaller" }
+androidx-tracing-ktx = { group = "androidx.tracing", name = "tracing-ktx", version.ref = "androidxTracing" }
androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" }
android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" }
@@ -162,20 +133,22 @@ androidx-compose-animation = { group = "androidx.compose.animation", name = "ani
androidx-compose-compiler = { module = "androidx.compose.compiler:compiler", version.ref = "androidxComposeCompiler" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" }
-androidx-compose-material = { group = "androidx.compose.material", name = "material", version.ref = "compose-material"}
-androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3"}
+androidx-compose-material = { group = "androidx.compose.material", name = "material", version.ref = "compose-material" }
+androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" }
-androidx-compose-ui = { group = "androidx.compose.ui", name = "ui"}
-androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util"}
-androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4"}
-androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest"}
-androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling"}
-androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview"}
+androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util" }
+androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" }
androidx-navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "androidxNavigation" }
+easycrop-compose = { module = "io.github.mr0xf00:easycrop", version.ref = "easycropVersion" }
+
kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroidVersion" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesTestVersion" }
@@ -210,14 +183,14 @@ dbflow-processor = { group = "com.github.Raizlabs.DBFlow", name = "dbflow-proces
dbflow-core = { group = "com.github.Raizlabs.DBFlow", name = "dbflow-core", version.ref = "dbflowVersion" }
dbflow = { group = "com.github.Raizlabs.DBFlow", name = "dbflow", version.ref = "dbflowVersion" }
-androidx-camera-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "cameraxVersion"}
-androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "cameraxVersion"}
-androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraxVersion"}
+androidx-camera-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "cameraxVersion" }
+androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "cameraxVersion" }
+androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraxVersion" }
androidx-camera-core = { group = "androidx.camera", name = "camera-core", version.ref = "cameraxVersion" }
play-services-maps = { group = "com.google.android.gms", name = "play-services-maps", version.ref = "playServicesVersion" }
google-play-services-code-scanner = { group = "com.google.android.gms", name = "play-services-code-scanner", version.ref = "playServicesCodeScanner" }
-google-app-code-scanner = { group = "com.google.mlkit", name = "barcode-scanning", version.ref = "googleAppCodeScanner"}
+google-app-code-scanner = { group = "com.google.mlkit", name = "barcode-scanning", version.ref = "googleAppCodeScanner" }
google-map-compose = { group = "com.google.maps.android", name = "maps-compose", version.ref = "googleMaps" }
mockito-android = { module = "org.mockito:mockito-android", version.ref = "mockitoAndroidVersion" }
@@ -228,7 +201,7 @@ truth = { group = "com.google.truth", name = "truth", version.ref = "truth" }
google-oss-licenses = { group = "com.google.android.gms", name = "play-services-oss-licenses", version.ref = "googleOss" }
google-oss-licenses-plugin = { group = "com.google.android.gms", name = "oss-licenses-plugin", version.ref = "googleOssPlugin" }
-junit = { module = "junit:junit", version.ref = "junitVersion"}
+junit = { module = "junit:junit", version.ref = "junitVersion" }
jetbrains-kotlin-jdk7 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" }
libphonenumber-android = { module = "io.michaelrocks:libphonenumber-android", version.ref = "libphonenumberAndroidVersion" }
@@ -236,76 +209,69 @@ libphonenumber-android = { module = "io.michaelrocks:libphonenumber-android", ve
lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "androidTools" }
lint-checks = { group = "com.android.tools.lint", name = "lint-checks", version.ref = "androidTools" }
lint-tests = { group = "com.android.tools.lint", name = "lint-tests", version.ref = "androidTools" }
-slack-compose-lint = {group="com.slack.lint.compose",name="compose-lint-checks",version.ref="slackComposeLint"}
+slack-compose-lint = { group = "com.slack.lint.compose", name = "compose-lint-checks", version.ref = "slackComposeLint" }
-#Detekt
+#Static Analysis & Formatting
+detekt-gradlePlugin = { group = "io.gitlab.arturbosch.detekt", name = "detekt-gradle-plugin", version.ref = "detekt" }
detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" }
twitter-detekt-compose = { group = "com.twitter.compose.rules", name = "detekt", version.ref = "twitter-detekt-compose" }
+ktlint-gradlePlugin = { group = "org.jlleitschuh.gradle", name = "ktlint-gradle", version.ref = "ktlint" }
+spotless-gradlePlugin = { group = "com.diffplug.spotless", name = "spotless-plugin-gradle", version.ref = "spotlessVersion" }
zxing-core = { module = "com.google.zxing:core", version.ref = "zxingVersion" }
-ccp = { module = "com.hbb20:ccp", version.ref = "ccpVersion" }
-countrycodechooser = { module = "com.github.ParveshSandila:CountryCodeChooser", version.ref = "countrycodechooserVersion" }
-mifos-passcode = { module = "com.github.openMF.mifos-passcode:compose", version.ref = "mifosPasscodeVersion" }
-
-jakewharton-butterknife = { module = "com.jakewharton:butterknife", version.ref = "butterKnifeVersion" }
-jakewharton-compiler = { module = "com.jakewharton:butterknife-compiler", version.ref = "butterKnifeVersion" }
-
-simplecropview = { module = "com.isseiaoki:simplecropview", version.ref = "simplecropviewVersion" }
-sweet-error = { module = "com.github.therajanmaurya:Sweet-Error", version.ref = "sweetErrorVersion" }
-
-tableview = { module = "com.evrencoskun.library:tableview", version.ref = "tableviewVersion" }
-
# Dependencies of the included build-logic
android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
android-tools-common = { group = "com.android.tools", name = "common", version.ref = "androidTools" }
+compose-gradlePlugin = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" }
firebase-crashlytics-gradlePlugin = { group = "com.google.firebase", name = "firebase-crashlytics-gradle", version.ref = "firebaseCrashlyticsPlugin" }
firebase-performance-gradlePlugin = { group = "com.google.firebase", name = "perf-plugin", version.ref = "firebasePerfPlugin" }
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
room-gradlePlugin = { group = "androidx.room", name = "room-gradle-plugin", version.ref = "room" }
turbine = { module = "app.cash.turbine:turbine", version.ref = "turbineVersion" }
-uihouse = { module = "com.github.rahul-gill.mifos-ui-library:uihouse", version.ref = "uihouseVersion" }
work-testing = { group = "androidx.work", name = "work-testing", version = "2.8.1" }
-androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
-material = { group = "com.google.android.material", name = "material", version.ref = "material" }
-spotless-gradle = { group = "com.diffplug.spotless", name = "spotless-plugin-gradle", version.ref = "spotlessVersion" }
-camera-core = { group = "androidx.camera", name = "camera-core", version.ref = "cameraCoreVersion" }
[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
+android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" }
+compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+dependencyGuard = { id = "com.dropbox.dependency-guard", version.ref = "dependencyGuard" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
-gms = { id = "com.google.gms.google-services", version.ref = "gmsPlugin" }
-hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsPlugin" }
firebase-perf = { id = "com.google.firebase.firebase-perf", version.ref = "firebasePerfPlugin" }
-dependencyGuard = { id = "com.dropbox.dependency-guard", version.ref = "dependencyGuard" }
-secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" }
+gms = { id = "com.google.gms.google-services", version.ref = "gmsPlugin" }
+hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
-android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
+kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
+ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
+ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" }
-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp"}
room = { id = "androidx.room", version.ref = "room" }
-spotbugs = { id = "com.github.spotbugs", version.ref = "spotbugs" }
+secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" }
spotless = { id = "com.diffplug.spotless", version.ref = "spotlessVersion" }
-kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
# Plugins defined by this project
mifos-android-application = { id = "mifos.android.application", version = "unspecified" }
+mifos-android-application-jacoco = { id = "mifos.android.application.jacoco", version = "unspecified" }
mifos-android-application-compose = { id = "mifos.android.application.compose", version = "unspecified" }
mifos-android-application-firebase = { id = "mifos.android.application.firebase", version = "unspecified" }
mifos-android-feature = { id = "mifos.android.feature", version = "unspecified" }
mifos-android-hilt = { id = "mifos.android.hilt", version = "unspecified" }
mifos-android-library = { id = "mifos.android.library", version = "unspecified" }
+mifos-android-library-jacoco = { id = "mifos.android.library.jacoco", version = "unspecified" }
mifos-android-library-compose = { id = "mifos.android.library.compose", version = "unspecified" }
mifos-android-lint = { id = "mifos.android.lint", version = "unspecified" }
mifos-android-room = { id = "mifos.android.room", version = "unspecified" }
mifos-android-test = { id = "mifos.android.test", version = "unspecified" }
mifos-jvm-library = { id = "mifos.jvm.library", version = "unspecified" }
+mifos-detekt-plugin = { id = "mifos.detekt.plugin", version = "unspecified" }
+mifos-spotless-plugin = { id = "mifos.spotless.plugin", version = "unspecified" }
+mifos-ktlint-plugin = { id = "mifos.ktlint.plugin", version = "unspecified" }
+mifos-git-hooks = { id = "mifos.git.hooks", version = "unspecified" }
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 60b08099b..a9a478406 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
diff --git a/libs/country-code-picker/build.gradle.kts b/libs/country-code-picker/build.gradle.kts
index 69740c1e1..f6b28408e 100644
--- a/libs/country-code-picker/build.gradle.kts
+++ b/libs/country-code-picker/build.gradle.kts
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
plugins {
alias(libs.plugins.mifos.android.library)
diff --git a/libs/country-code-picker/src/main/AndroidManifest.xml b/libs/country-code-picker/src/main/AndroidManifest.xml
index 3c84395b9..4ee22a4fb 100644
--- a/libs/country-code-picker/src/main/AndroidManifest.xml
+++ b/libs/country-code-picker/src/main/AndroidManifest.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
diff --git a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/CountryCodePicker.kt b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/CountryCodePicker.kt
index 86da45a5d..133b7dd9a 100644
--- a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/CountryCodePicker.kt
+++ b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/CountryCodePicker.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.countrycodepicker
diff --git a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/Autofill.kt b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/Autofill.kt
index 97b7c89ef..d52d6d751 100644
--- a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/Autofill.kt
+++ b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/Autofill.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.countrycodepicker.component
diff --git a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/CountryCodeDialog.kt b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/CountryCodeDialog.kt
index de99fc90c..e15e92a6e 100644
--- a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/CountryCodeDialog.kt
+++ b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/CountryCodeDialog.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.countrycodepicker.component
diff --git a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/CountryDialog.kt b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/CountryDialog.kt
index 2b2d2a328..f9490bd71 100644
--- a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/CountryDialog.kt
+++ b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/component/CountryDialog.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.countrycodepicker.component
diff --git a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/CountryData.kt b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/CountryData.kt
index f7264d5d7..b54877c9d 100644
--- a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/CountryData.kt
+++ b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/CountryData.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.countrycodepicker.data
diff --git a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/CountryCodeUtils.kt b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/CountryCodeUtils.kt
index 77c1ac361..7a6bab352 100644
--- a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/CountryCodeUtils.kt
+++ b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/CountryCodeUtils.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.countrycodepicker.data.utils
diff --git a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/CountryNameMap.kt b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/CountryNameMap.kt
index 89caad9ce..bd5311b54 100644
--- a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/CountryNameMap.kt
+++ b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/CountryNameMap.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.countrycodepicker.data.utils
diff --git a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/NumberHintMap.kt b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/NumberHintMap.kt
index 1bcfa425a..da6520338 100644
--- a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/NumberHintMap.kt
+++ b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/NumberHintMap.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.countrycodepicker.data.utils
diff --git a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/SearchCountryList.kt b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/SearchCountryList.kt
index 347e2d1bc..fe6517da1 100644
--- a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/SearchCountryList.kt
+++ b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/SearchCountryList.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.countrycodepicker.data.utils
diff --git a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/ValidatePhoneNumber.kt b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/ValidatePhoneNumber.kt
index 8070000c5..accc7ac1a 100644
--- a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/ValidatePhoneNumber.kt
+++ b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/data/utils/ValidatePhoneNumber.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.countrycodepicker.data.utils
diff --git a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/transformation/PhoneNumberTransformation.kt b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/transformation/PhoneNumberTransformation.kt
index 9af61b140..22071a696 100644
--- a/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/transformation/PhoneNumberTransformation.kt
+++ b/libs/country-code-picker/src/main/kotlin/com/mifos/library/countrycodepicker/transformation/PhoneNumberTransformation.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.countrycodepicker.transformation
diff --git a/libs/country-code-picker/src/main/res/values-ar/strings.xml b/libs/country-code-picker/src/main/res/values-ar/strings.xml
index e6296394d..1d50880d3 100644
--- a/libs/country-code-picker/src/main/res/values-ar/strings.xml
+++ b/libs/country-code-picker/src/main/res/values-ar/strings.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
أفغانستان
diff --git a/libs/country-code-picker/src/main/res/values-es/strings.xml b/libs/country-code-picker/src/main/res/values-es/strings.xml
index 1ee645316..5c42e6d72 100644
--- a/libs/country-code-picker/src/main/res/values-es/strings.xml
+++ b/libs/country-code-picker/src/main/res/values-es/strings.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
Afganistán
diff --git a/libs/country-code-picker/src/main/res/values-fr/strings.xml b/libs/country-code-picker/src/main/res/values-fr/strings.xml
index 26e4169cc..dcc9a0075 100644
--- a/libs/country-code-picker/src/main/res/values-fr/strings.xml
+++ b/libs/country-code-picker/src/main/res/values-fr/strings.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
Afghanistan
diff --git a/libs/country-code-picker/src/main/res/values-hi/strings.xml b/libs/country-code-picker/src/main/res/values-hi/strings.xml
index 6314ab908..a5ac82bbf 100644
--- a/libs/country-code-picker/src/main/res/values-hi/strings.xml
+++ b/libs/country-code-picker/src/main/res/values-hi/strings.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
अफ़ग़ानिस्तान
diff --git a/libs/country-code-picker/src/main/res/values-it-rIT/strings.xml b/libs/country-code-picker/src/main/res/values-it-rIT/strings.xml
index 32f4a8364..2fea5982c 100644
--- a/libs/country-code-picker/src/main/res/values-it-rIT/strings.xml
+++ b/libs/country-code-picker/src/main/res/values-it-rIT/strings.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
"Afghanistan "
diff --git a/libs/country-code-picker/src/main/res/values-nl/strings.xml b/libs/country-code-picker/src/main/res/values-nl/strings.xml
index 2655b61bf..4d40e24dd 100644
--- a/libs/country-code-picker/src/main/res/values-nl/strings.xml
+++ b/libs/country-code-picker/src/main/res/values-nl/strings.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
Afghanistan
diff --git a/libs/country-code-picker/src/main/res/values-ru-rRU/strings.xml b/libs/country-code-picker/src/main/res/values-ru-rRU/strings.xml
index 5b56af0f6..6c7705708 100644
--- a/libs/country-code-picker/src/main/res/values-ru-rRU/strings.xml
+++ b/libs/country-code-picker/src/main/res/values-ru-rRU/strings.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
Афганистан
diff --git a/libs/country-code-picker/src/main/res/values-so/strings.xml b/libs/country-code-picker/src/main/res/values-so/strings.xml
index 098a763b9..acb641a76 100644
--- a/libs/country-code-picker/src/main/res/values-so/strings.xml
+++ b/libs/country-code-picker/src/main/res/values-so/strings.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
Afgaanistaan
diff --git a/libs/country-code-picker/src/main/res/values-tr-rTR/strings.xml b/libs/country-code-picker/src/main/res/values-tr-rTR/strings.xml
index 972b0f93a..5f1109544 100644
--- a/libs/country-code-picker/src/main/res/values-tr-rTR/strings.xml
+++ b/libs/country-code-picker/src/main/res/values-tr-rTR/strings.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
Afganistan
diff --git a/libs/country-code-picker/src/main/res/values-zh/strings.xml b/libs/country-code-picker/src/main/res/values-zh/strings.xml
index 210bac47f..cfb6193a7 100644
--- a/libs/country-code-picker/src/main/res/values-zh/strings.xml
+++ b/libs/country-code-picker/src/main/res/values-zh/strings.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
阿富汗
diff --git a/libs/country-code-picker/src/main/res/values/strings.xml b/libs/country-code-picker/src/main/res/values/strings.xml
index c92eef4f6..bbf2d124c 100644
--- a/libs/country-code-picker/src/main/res/values/strings.xml
+++ b/libs/country-code-picker/src/main/res/values/strings.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
Afghanistan
diff --git a/libs/material3-navigation/build.gradle.kts b/libs/material3-navigation/build.gradle.kts
index 16f46693a..32eb8f65b 100644
--- a/libs/material3-navigation/build.gradle.kts
+++ b/libs/material3-navigation/build.gradle.kts
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
plugins {
alias(libs.plugins.mifos.android.library)
diff --git a/libs/material3-navigation/src/main/AndroidManifest.xml b/libs/material3-navigation/src/main/AndroidManifest.xml
index 367affcc3..c04f75ad0 100644
--- a/libs/material3-navigation/src/main/AndroidManifest.xml
+++ b/libs/material3-navigation/src/main/AndroidManifest.xml
@@ -6,6 +6,6 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
\ No newline at end of file
diff --git a/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheet.kt b/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheet.kt
index 27389dac3..3affa4cf9 100644
--- a/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheet.kt
+++ b/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheet.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.material3.navigation
diff --git a/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheetNavigator.kt b/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheetNavigator.kt
index 6bd0d056f..7ada55ff6 100644
--- a/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheetNavigator.kt
+++ b/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheetNavigator.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
@file:OptIn(ExperimentalMaterial3Api::class)
@file:Suppress("ktlint:standard:discouraged-comment-location")
diff --git a/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheetNavigatorDestinationBuilder.kt b/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheetNavigatorDestinationBuilder.kt
index 33b3e74ee..0525211aa 100644
--- a/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheetNavigatorDestinationBuilder.kt
+++ b/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/BottomSheetNavigatorDestinationBuilder.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.material3.navigation
diff --git a/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/NavGraphBuilder.kt b/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/NavGraphBuilder.kt
index 5793a19dd..1ff1a0b5b 100644
--- a/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/NavGraphBuilder.kt
+++ b/libs/material3-navigation/src/main/kotlin/com/mifos/library/material3/navigation/NavGraphBuilder.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package com.mifos.library.material3.navigation
diff --git a/libs/mifos-passcode/build.gradle.kts b/libs/mifos-passcode/build.gradle.kts
index 324c50300..daf33a0fb 100644
--- a/libs/mifos-passcode/build.gradle.kts
+++ b/libs/mifos-passcode/build.gradle.kts
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
plugins {
alias(libs.plugins.mifos.android.library)
diff --git a/libs/mifos-passcode/src/main/AndroidManifest.xml b/libs/mifos-passcode/src/main/AndroidManifest.xml
index b9b046d65..8951201ec 100644
--- a/libs/mifos-passcode/src/main/AndroidManifest.xml
+++ b/libs/mifos-passcode/src/main/AndroidManifest.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/PassCodeScreen.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/PassCodeScreen.kt
index c20e54ad4..06178d818 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/PassCodeScreen.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/PassCodeScreen.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/PasscodeNavigation.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/PasscodeNavigation.kt
index eab00eefd..40e13b5d2 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/PasscodeNavigation.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/PasscodeNavigation.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/MifosIcon.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/MifosIcon.kt
index 5b5e8b778..c5f5de8ab 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/MifosIcon.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/MifosIcon.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.component
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeButton.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeButton.kt
index f1b4d31ac..682742439 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeButton.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeButton.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.component
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeHeader.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeHeader.kt
index 1a08a37a9..18a745104 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeHeader.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeHeader.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.component
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeKeys.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeKeys.kt
index abaf8c3e3..a3c737903 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeKeys.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeKeys.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.component
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeMismatchedDialog.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeMismatchedDialog.kt
index 228221117..aea895fe7 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeMismatchedDialog.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeMismatchedDialog.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.component
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeStepIndicator.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeStepIndicator.kt
index 43d582a7f..5fd7c24d2 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeStepIndicator.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeStepIndicator.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.component
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeToolbar.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeToolbar.kt
index fc5bda091..2bbd418a5 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeToolbar.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/PasscodeToolbar.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.component
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeManager.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeManager.kt
index f2436f02d..3c2bebba5 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeManager.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeManager.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.data
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeRepository.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeRepository.kt
index 4311e6ade..fc6bbf1d2 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeRepository.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeRepository.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.data
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeRepositoryImpl.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeRepositoryImpl.kt
index 8f4cbd863..49a8c676b 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeRepositoryImpl.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/data/PasscodeRepositoryImpl.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.data
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/di/ApplicationModule.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/di/ApplicationModule.kt
index c5a5e821d..3631545e3 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/di/ApplicationModule.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/di/ApplicationModule.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.di
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Color.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Color.kt
index 87e379858..ff877a70d 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Color.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Color.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.theme
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Font.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Font.kt
index e948eca0a..9cbfba4d5 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Font.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Font.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.theme
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Theme.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Theme.kt
index 121bfcbd3..11a7a24ae 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Theme.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Theme.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.theme
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Type.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Type.kt
index b1bdb5ecb..b0cd4c1ca 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Type.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/theme/Type.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.theme
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/Constants.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/Constants.kt
index 4cb95a4e5..3e548137e 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/Constants.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/Constants.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.utility
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/PreferenceManager.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/PreferenceManager.kt
index 89d729d92..39a9890f6 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/PreferenceManager.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/PreferenceManager.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.utility
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/ShakeAnimation.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/ShakeAnimation.kt
index 88e75cf82..d67c772a4 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/ShakeAnimation.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/ShakeAnimation.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.utility
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/Step.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/Step.kt
index e5782451a..205a96b14 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/Step.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/Step.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.utility
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/VibrationFeedback.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/VibrationFeedback.kt
index 62ca18df1..5ae884255 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/VibrationFeedback.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/utility/VibrationFeedback.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.utility
diff --git a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/viewmodels/PasscodeViewModel.kt b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/viewmodels/PasscodeViewModel.kt
index 4842a5800..fd1ab4d96 100644
--- a/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/viewmodels/PasscodeViewModel.kt
+++ b/libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/viewmodels/PasscodeViewModel.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.library.passcode.viewmodels
diff --git a/libs/mifos-passcode/src/main/res/drawable/lib_mifos_passcode_delete_forever.xml b/libs/mifos-passcode/src/main/res/drawable/lib_mifos_passcode_delete_forever.xml
index cc0810db2..ff49c209c 100644
--- a/libs/mifos-passcode/src/main/res/drawable/lib_mifos_passcode_delete_forever.xml
+++ b/libs/mifos-passcode/src/main/res/drawable/lib_mifos_passcode_delete_forever.xml
@@ -6,7 +6,7 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
Passcode will be reset, are you sure?
diff --git a/libs/pullrefresh/build.gradle.kts b/libs/pullrefresh/build.gradle.kts
index e7c69ca10..49b96c380 100644
--- a/libs/pullrefresh/build.gradle.kts
+++ b/libs/pullrefresh/build.gradle.kts
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
plugins {
alias(libs.plugins.mifos.android.library)
diff --git a/libs/pullrefresh/src/main/AndroidManifest.xml b/libs/pullrefresh/src/main/AndroidManifest.xml
index 367affcc3..c04f75ad0 100644
--- a/libs/pullrefresh/src/main/AndroidManifest.xml
+++ b/libs/pullrefresh/src/main/AndroidManifest.xml
@@ -6,6 +6,6 @@
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-wallet/blob/master/LICENSE.md
+ See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
\ No newline at end of file
diff --git a/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefresh.kt b/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefresh.kt
index dd15d09b4..0f015468a 100644
--- a/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefresh.kt
+++ b/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefresh.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
@file:Suppress("InvalidPackageDeclaration")
diff --git a/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshIndicator.kt b/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshIndicator.kt
index cfae879f2..420774be5 100644
--- a/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshIndicator.kt
+++ b/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshIndicator.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
@file:Suppress("InvalidPackageDeclaration")
diff --git a/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshIndicatorTransform.kt b/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshIndicatorTransform.kt
index d586986be..08b24bcf0 100644
--- a/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshIndicatorTransform.kt
+++ b/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshIndicatorTransform.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
@file:Suppress("InvalidPackageDeclaration")
diff --git a/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshState.kt b/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshState.kt
index 076e26844..cae2b925f 100644
--- a/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshState.kt
+++ b/libs/pullrefresh/src/main/kotlin/com.mifos.library.pullrefresh/PullRefreshState.kt
@@ -5,7 +5,7 @@
* 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-wallet/blob/master/LICENSE.md
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
@file:Suppress("InvalidPackageDeclaration")
diff --git a/lint/build.gradle.kts b/lint/build.gradle.kts
index 964c67a53..19650c9de 100644
--- a/lint/build.gradle.kts
+++ b/lint/build.gradle.kts
@@ -29,8 +29,6 @@ kotlin {
}
dependencies {
- lintChecks(libs.slack.compose.lint)
-
compileOnly(libs.kotlin.stdlib)
compileOnly(libs.lint.api)
diff --git a/scripts/pre-commit.sh b/scripts/pre-commit.sh
new file mode 100644
index 000000000..622a1d04c
--- /dev/null
+++ b/scripts/pre-commit.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+# Function to check the current branch
+check_current_branch() {
+ echo "\n🚀 Checking the current git branch..."
+ CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
+ if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "develop" ]; then
+ echo "🛑 Hold it right there! Committing directly to the '$CURRENT_BRANCH' branch? That's a big no-no!"
+ echo "🚫 Direct commits to '$CURRENT_BRANCH' are like trying to use a wrench to write code—doesn't work! 😜"
+ echo "\nABORTING COMMIT: You must navigate to a feature branch or create a new one to save the day! 🦸♂️🦸♀️\n"
+ exit 1
+ else
+ echo "✅ Fantastic! You're on the '$CURRENT_BRANCH' branch, which is perfect for commits. Let's keep this awesome momentum going! 🚀✨"
+ fi
+}
+
+# Function to run Spotless checks
+run_spotless_checks() {
+ echo "\n🚀 Spotless is now analyzing and formatting your code!"
+ ./gradlew spotlessApply --no-configuration-cache --daemon > /tmp/spotless-result
+ SPOTLESS_EXIT_CODE=$?
+
+ if [ ${SPOTLESS_EXIT_CODE} -ne 0 ]; then
+ cat /tmp/spotless-result
+ rm /tmp/spotless-result
+ echo "\n*********************************************************************************"
+ echo " 💥 Uh-oh! Spotless found formatting issues in the code! Time to tidy up! 💥"
+ echo " 💡 Tip: Check the reported issues and fix formatting errors. 🛠️"
+ echo "*********************************************************************************"
+ exit ${SPOTLESS_EXIT_CODE}
+ else
+ rm /tmp/spotless-result
+ echo "🎉 Stellar job! Your code is pristine and has passed Spotless's formatting checks without a hitch! Keep shining bright! ✨🚀"
+ fi
+}
+
+# Function to run ktlint checks
+run_dependency_guard() {
+ printf "\n🚀 Brace yourself! We're about to generate dependency guard baseline!"
+ ./gradlew dependencyGuardBaseline
+ KT_EXIT_CODE=$?
+
+ if [ ${KT_EXIT_CODE} -ne 0 ]; then
+ printf "\n*********************************************************************************"
+ echo " 💥 Oh no! Something went wrong! 💥"
+ echo " 💡 Unable to generate dependency baseline. 🛠️"
+ printf "*********************************************************************************\n"
+ exit ${KT_EXIT_CODE}
+ else
+ echo "🎉 Bravo! Dependency baseline has been generated successfully! Keep rocking that clean code! 🚀💫"
+ fi
+}
+
+# Function to run Detekt checks
+run_detekt_checks() {
+ echo "\n🚀 Detekt is now analyzing your Kotlin code for potential issues!"
+ ./gradlew detekt > /tmp/detekt-result
+ DETEKT_EXIT_CODE=$?
+
+ if [ ${DETEKT_EXIT_CODE} -ne 0 ]; then
+ cat /tmp/detekt-result
+ rm /tmp/detekt-result
+ echo "\n*********************************************************************************"
+ echo " 💥 Oh no! Detekt found issues in the code! Time to fix those issues! 💥"
+ echo " 💡 Tip: Review the Detekt report to resolve these issues. 🛠️"
+ echo "*********************************************************************************"
+ exit ${DETEKT_EXIT_CODE}
+ else
+ rm /tmp/detekt-result
+ echo "🎉 Fantastic work! Your Kotlin code has sailed through Detekt's analysis with ease! Onward to greatness! 🚀🌟"
+ fi
+}
+
+# Function to print success message
+print_success_message() {
+ GIT_USERNAME=$(git config user.name)
+ echo "\n *******************************************************************************"
+ echo "🚀🎉 Huzzah, $GIT_USERNAME! Your code has triumphed through the Style Checker Dragon unscathed! 🐉"
+ echo "Your code shines brighter than a supernova and sparkles like a constellation of stars! ✨🌌"
+ echo "*******************************************************************************"
+ echo "\n🚀🎉 Hold tight, $GIT_USERNAME! Your code is ready to commit and conquer new heights! 🌟✨ Keep up the amazing work! 💪\n"
+}
+
+# Main script execution
+check_current_branch
+run_spotless_checks
+run_detekt_checks
+run_dependency_guard
+print_success_message
+
+exit 0
diff --git a/scripts/pre-push.sh b/scripts/pre-push.sh
new file mode 100644
index 000000000..c3db0f7e0
--- /dev/null
+++ b/scripts/pre-push.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+# Function to check the current branch
+check_current_branch() {
+ printf "\n🚀 Checking the current git branch..."
+ CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
+ if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "develop" ]; then
+ echo "🛑 Hold it right there! Committing directly to the '$CURRENT_BRANCH' branch? That's a big no-no!"
+ echo "🚫 Direct commits to '$CURRENT_BRANCH' are like trying to use a wrench to write code—doesn't work! 😜"
+ printf "\nABORTING COMMIT: You must navigate to a feature branch or create a new one to save the day! 🦸♂️🦸♀️\n"
+ exit 1
+ else
+ echo "✅ Fantastic! You're on the '$CURRENT_BRANCH' branch, which is perfect for commits. Let's keep this awesome momentum going! 🚀✨"
+ fi
+}
+
+# Function to run Spotless checks
+run_spotless_checks() {
+ printf "\n🚀 Spotless is now analyzing and formatting your code!"
+ ./gradlew spotlessCheck --daemon > /tmp/spotless-result
+ SPOTLESS_EXIT_CODE=$?
+
+ if [ ${SPOTLESS_EXIT_CODE} -ne 0 ]; then
+ cat /tmp/spotless-result
+ rm /tmp/spotless-result
+ printf "\n*********************************************************************************"
+ echo " 💥 Uh-oh! Spotless found formatting issues in the code! Time to tidy up! 💥"
+ echo " 💡 Tip: Check the reported issues and fix formatting errors. 🛠️"
+ echo "*********************************************************************************"
+ echo "🚀 Attempting to apply Spotless formatting fixes..."
+ ./gradlew spotlessApply --daemon
+ else
+ rm /tmp/spotless-result
+ echo "🎉 Stellar job! Your code is pristine and has passed Spotless's formatting checks without a hitch! Keep shining bright! ✨🚀"
+ fi
+}
+
+# Function to run Detekt checks
+run_detekt_checks() {
+ printf "\n🚀 Detekt is now analyzing your Kotlin code for potential issues!"
+ ./gradlew detekt > /tmp/detekt-result
+ DETEKT_EXIT_CODE=$?
+
+ if [ ${DETEKT_EXIT_CODE} -ne 0 ]; then
+ cat /tmp/detekt-result
+ rm /tmp/detekt-result
+ printf "\n*********************************************************************************"
+ echo " 💥 Oh no! Detekt found issues in the code! Time to fix those issues! 💥"
+ echo " 💡 Tip: Review the Detekt report to resolve these issues. 🛠️"
+ echo "*********************************************************************************"
+ exit ${DETEKT_EXIT_CODE}
+ else
+ rm /tmp/detekt-result
+ echo "🎉 Fantastic work! Your Kotlin code has sailed through Detekt's analysis with ease! Onward to greatness! 🚀🌟"
+ fi
+}
+
+# Function to run ktlint checks
+run_dependency_guard() {
+ printf "\n🚀 Brace yourself! We're about to generate dependency guard baseline!"
+ ./gradlew dependencyGuard
+ KT_EXIT_CODE=$?
+
+ if [ ${KT_EXIT_CODE} -ne 0 ]; then
+ printf "\n*********************************************************************************"
+ echo " 💥 Oh no! Something went wrong! 💥"
+ echo " 💡 Unable to generate dependency baseline. 🛠️"
+ printf "*********************************************************************************\n"
+ echo "🚀 Attempting to generate dependency baseline again..."
+ ./gradlew dependencyGuardBaseline
+ else
+ echo "🎉 Bravo! Dependency baseline has been generated successfully! Keep rocking that clean code! 🚀💫"
+ fi
+}
+
+# Function to print success message
+print_success_message() {
+ GIT_USERNAME=$(git config user.name)
+ printf "\n *******************************************************************************"
+ echo "🚀🎉 Huzzah, $GIT_USERNAME! Your code has triumphed through the Style Checker Dragon unscathed! 🐉"
+ echo "Your code shines brighter than a supernova and sparkles like a constellation of stars! ✨🌌"
+ echo "*******************************************************************************"
+ echo "\n🚀🎉 Get ready, $GIT_USERNAME! Your code is about to take flight into the repository cosmos! 🌟✨ Fasten your seatbelt for a stellar push! 🚀💫\n"
+}
+
+# Main script execution
+check_current_branch
+run_spotless_checks
+run_detekt_checks
+run_dependency_guard
+print_success_message
+
+exit 0
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 0dc9df9ee..d4125305f 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,9 +1,9 @@
pluginManagement {
includeBuild("build-logic")
repositories {
- gradlePluginPortal()
google()
mavenCentral()
+ gradlePluginPortal()
}
}
dependencyResolutionManagement {
@@ -32,6 +32,7 @@ include(":core:data")
include(":core:network")
include(":core:datastore")
include(":core:qrcode")
+include(":core:testing")
// Feature Modules
include(":feature:loan")
diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts
index 417bcc1d9..5f4856a37 100644
--- a/shared/build.gradle.kts
+++ b/shared/build.gradle.kts
@@ -1,6 +1,7 @@
plugins {
- kotlin("multiplatform")
+ alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.android.library)
+ alias(libs.plugins.compose.compiler)
alias(libs.plugins.jetbrainsCompose)
}