Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cmp-android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ dependencies {
testImplementation(kotlin("test"))
testImplementation(libs.koin.test)
testImplementation(libs.koin.test.junit4)

implementation("androidx.glance:glance-appwidget:1.1.0")
implementation("androidx.glance:glance-material3:1.1.0")

implementation("androidx.work:work-runtime-ktx:2.9.1")
implementation("io.insert-koin:koin-android:3.5.6")
implementation("io.insert-koin:koin-androidx-workmanager:3.5.6")
Comment on lines +135 to +137

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find and examine the build.gradle.kts file
find . -name "build.gradle.kts" -path "*cmp-android*" | head -5

Repository: openMF/mifos-pay

Length of output: 185


🏁 Script executed:

# Get file size and check specific lines
wc -l cmp-android/build.gradle.kts

Repository: openMF/mifos-pay

Length of output: 187


🏁 Script executed:

# Read the relevant section around lines 114-137
sed -n '110,145p' cmp-android/build.gradle.kts

Repository: openMF/mifos-pay

Length of output: 1334


🏁 Script executed:

# Check for version catalog file
find . -name "libs.versions.toml" -o -name "gradle.properties" | head -10

Repository: openMF/mifos-pay

Length of output: 234


🏁 Script executed:

# Search for all koin dependency declarations in the build file
rg "koin" cmp-android/build.gradle.kts -B 1 -A 1

Repository: openMF/mifos-pay

Length of output: 631


🏁 Script executed:

# Examine the version catalog
cat gradle/libs.versions.toml

Repository: openMF/mifos-pay

Length of output: 29711


🏁 Script executed:

# Search for koin versions in the catalog
rg "koin" gradle/libs.versions.toml -B 1 -A 1

Repository: openMF/mifos-pay

Length of output: 2077


Remove the hardcoded koin-android duplicate—it conflicts with the catalog version.

Lines 135-136 redeclare io.insert-koin:koin-android:3.5.6 which conflicts with the catalog-managed libs.koin.android (version 4.1.1, declared at line 114). Remove the hardcoded declarations and add missing entries (Glance, WorkManager, koin-androidx-workmanager) to the version catalog instead to maintain a single source of truth.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmp-android/build.gradle.kts` around lines 135 - 137, The hardcoded
implementation of `io.insert-koin:koin-android:3.5.6` on line 136 conflicts with
the catalog-managed version at line 114. Remove this hardcoded declaration and
replace it with a reference to the catalog-managed version (libs.koin.android).
Additionally, instead of hardcoding
`io.insert-koin:koin-androidx-workmanager:3.5.6`, add this dependency to the
version catalog and use the catalog reference to maintain a single source of
truth for all dependency versions.


}

dependencyGuard {
Expand Down
2,194 changes: 1,155 additions & 1,039 deletions cmp-android/dependencies/prodReleaseRuntimeClasspath.tree.txt

Large diffs are not rendered by default.

136 changes: 77 additions & 59 deletions cmp-android/dependencies/prodReleaseRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ androidx.activity:activity-compose:1.12.2
androidx.activity:activity-ktx:1.12.2
androidx.activity:activity:1.12.2
androidx.annotation:annotation-experimental:1.5.1
androidx.annotation:annotation-jvm:1.9.1
androidx.annotation:annotation:1.9.1
androidx.annotation:annotation-jvm:1.10.0
androidx.annotation:annotation:1.10.0
androidx.appcompat:appcompat-resources:1.7.1
androidx.appcompat:appcompat:1.7.1
androidx.arch.core:core-common:2.2.0
Expand All @@ -62,14 +62,14 @@ androidx.camera:camera-view:1.4.2
androidx.collection:collection-jvm:1.5.0
androidx.collection:collection-ktx:1.5.0
androidx.collection:collection:1.5.0
androidx.compose.animation:animation-android:1.10.2
androidx.compose.animation:animation-core-android:1.10.2
androidx.compose.animation:animation-core:1.10.2
androidx.compose.animation:animation:1.10.2
androidx.compose.foundation:foundation-android:1.10.2
androidx.compose.foundation:foundation-layout-android:1.10.2
androidx.compose.foundation:foundation-layout:1.10.2
androidx.compose.foundation:foundation:1.10.2
androidx.compose.animation:animation-android:1.11.1
androidx.compose.animation:animation-core-android:1.11.1
androidx.compose.animation:animation-core:1.11.1
androidx.compose.animation:animation:1.11.1
androidx.compose.foundation:foundation-android:1.11.1
androidx.compose.foundation:foundation-layout-android:1.11.1
androidx.compose.foundation:foundation-layout:1.11.1
androidx.compose.foundation:foundation:1.11.1
androidx.compose.material3.adaptive:adaptive-android:1.2.0
androidx.compose.material3.adaptive:adaptive-layout-android:1.2.0
androidx.compose.material3.adaptive:adaptive-layout:1.2.0
Expand All @@ -88,37 +88,38 @@ androidx.compose.material:material-icons-extended:1.7.8
androidx.compose.material:material-ripple-android:1.10.0
androidx.compose.material:material-ripple:1.10.0
androidx.compose.material:material:1.10.0
androidx.compose.runtime:runtime-android:1.10.2
androidx.compose.runtime:runtime-annotation-android:1.10.2
androidx.compose.runtime:runtime-annotation:1.10.2
androidx.compose.runtime:runtime-retain-android:1.10.2
androidx.compose.runtime:runtime-retain:1.10.2
androidx.compose.runtime:runtime-saveable-android:1.10.2
androidx.compose.runtime:runtime-saveable:1.10.2
androidx.compose.runtime:runtime-tracing:1.10.2
androidx.compose.runtime:runtime:1.10.2
androidx.compose.ui:ui-android:1.10.2
androidx.compose.ui:ui-geometry-android:1.10.2
androidx.compose.ui:ui-geometry:1.10.2
androidx.compose.ui:ui-graphics-android:1.10.2
androidx.compose.ui:ui-graphics:1.10.2
androidx.compose.ui:ui-text-android:1.10.2
androidx.compose.ui:ui-text:1.10.2
androidx.compose.ui:ui-tooling-android:1.10.2
androidx.compose.ui:ui-tooling-data-android:1.10.2
androidx.compose.ui:ui-tooling-data:1.10.2
androidx.compose.ui:ui-tooling-preview-android:1.10.2
androidx.compose.ui:ui-tooling-preview:1.10.2
androidx.compose.ui:ui-tooling:1.10.2
androidx.compose.ui:ui-unit-android:1.10.2
androidx.compose.ui:ui-unit:1.10.2
androidx.compose.ui:ui-util-android:1.10.2
androidx.compose.ui:ui-util:1.10.2
androidx.compose.ui:ui:1.10.2
androidx.compose.runtime:runtime-android:1.11.1
androidx.compose.runtime:runtime-annotation-android:1.11.1
androidx.compose.runtime:runtime-annotation:1.11.1
androidx.compose.runtime:runtime-retain-android:1.11.1
androidx.compose.runtime:runtime-retain:1.11.1
androidx.compose.runtime:runtime-saveable-android:1.11.1
androidx.compose.runtime:runtime-saveable:1.11.1
androidx.compose.runtime:runtime-tracing:1.11.1
androidx.compose.runtime:runtime:1.11.1
androidx.compose.ui:ui-android:1.11.1
androidx.compose.ui:ui-geometry-android:1.11.1
androidx.compose.ui:ui-geometry:1.11.1
androidx.compose.ui:ui-graphics-android:1.11.1
androidx.compose.ui:ui-graphics:1.11.1
androidx.compose.ui:ui-text-android:1.11.1
androidx.compose.ui:ui-text:1.11.1
androidx.compose.ui:ui-tooling-android:1.11.1
androidx.compose.ui:ui-tooling-data-android:1.11.1
androidx.compose.ui:ui-tooling-data:1.11.1
androidx.compose.ui:ui-tooling-preview-android:1.11.1
androidx.compose.ui:ui-tooling-preview:1.11.1
androidx.compose.ui:ui-tooling:1.11.1
androidx.compose.ui:ui-unit-android:1.11.1
androidx.compose.ui:ui-unit:1.11.1
androidx.compose.ui:ui-util-android:1.11.1
androidx.compose.ui:ui-util:1.11.1
androidx.compose.ui:ui:1.11.1
androidx.compose:compose-bom:2025.12.01
androidx.concurrent:concurrent-futures-ktx:1.1.0
androidx.concurrent:concurrent-futures:1.1.0
androidx.core:core-ktx:1.17.0
androidx.core:core-remoteviews:1.1.0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Security vulnerabilities for androidx.glance 1.1.0, androidx.work 2.10.3, androidx.room 2.6.1, androidx.sqlite 2.4.0

💡 Result:

Security vulnerabilities for these exact versions (evidence-backed from the sources I found): A) androidx.glance 1.1.0 The official Glance release notes indicate that version 1.1.0 has a vulnerability risk related to CVE-2024-7254: Glance “compiles against protobuf 4.28.2 in order to address CVE-2024-7254,” and the notes instruct upgrading from version 1.1.0 to 1.1.1 “to address the vulnerability risk.” [1][2] B) androidx.work 2.10.3 In the sources retrieved for the WorkManager 2.10.3 release notes, I did not find an explicit CVE/security vulnerability associated with version 2.10.3 (only bug-fix text appeared in the retrieved snippet). [3] C) androidx.room 2.6.1 In the sources retrieved for the Room 2.6.1 release notes, I did not find an explicit CVE/security vulnerability associated with version 2.6.1 (only bug-fix text appeared in the retrieved snippet). [4] D) androidx.sqlite 2.4.0 In the sources retrieved for the SQLite 2.4.0 release notes, I did not find an explicit CVE/security vulnerability associated with version 2.4.0 (the retrieved snippet discussed bug fixes and a change-log, without mentioning CVEs). [5][6] Recommendation (based on the explicit evidence found): - For androidx.glance, upgrade to 1.1.1+ to address the protobuf vulnerability risk referenced as CVE-2024-7254 in the official release notes. [1][2] Note: To produce a fully complete “all CVEs that affect these artifacts/versions” list, I would normally also cross-check AndroidX/security advisory databases and/or dependency-advisory feeds for each specific artifact (and its sub-artifacts). If you share your exact Gradle coordinates for each library (some artifacts—especially Room/SQLite—have multiple modules), I can narrow the search to the correct components and try again.

Citations:


Upgrade androidx.glance from 1.1.0 to 1.1.1 or later to address CVE-2024-7254.

The resolved dependency lock file includes androidx.glance:1.1.0 (line 157 and related), which has a known vulnerability related to an unsafe protobuf version. According to the official AndroidX release notes, version 1.1.0 compiles against protobuf 4.28.2 which has CVE-2024-7254, and the fix requires upgrading to androidx.glance:1.1.1 or later.

The remaining dependencies align with PR objectives:

  • androidx.core:core-remoteviews (1.1.0) and upgraded androidx.glance (1.1.1+) for Glance widget UI
  • androidx.work (2.10.3) + io.insert-koin:koin-androidx-workmanager (4.1.1) for WorkManager-based refresh scheduling
  • androidx.room (2.6.1) + androidx.sqlite (2.4.0) for persistence (no CVE found)

All other versions are stable and compatible with the existing Compose BOM (2025.12.01) and Koin BOM (4.1.1).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmp-android/dependencies/prodReleaseRuntimeClasspath.txt` at line 121, The
androidx.glance dependency is currently pinned to version 1.1.0, which has a
known CVE-2024-7254 vulnerability due to its use of protobuf 4.28.2. Locate all
occurrences of androidx.glance:1.1.0 in the dependency configuration (mentioned
as being present at multiple locations including line 157 and related areas) and
upgrade them to androidx.glance:1.1.1 or a later stable version to resolve the
security vulnerability.

androidx.core:core-splashscreen:1.2.0
androidx.core:core-viewtree:1.0.0
androidx.core:core:1.17.0
Expand Down Expand Up @@ -152,6 +153,11 @@ androidx.emoji2:emoji2:1.4.0
androidx.exifinterface:exifinterface:1.4.1
androidx.fragment:fragment-ktx:1.8.9
androidx.fragment:fragment:1.8.9
androidx.glance:glance-appwidget-external-protobuf:1.1.0
androidx.glance:glance-appwidget-proto:1.1.0
androidx.glance:glance-appwidget:1.1.0
androidx.glance:glance-material3:1.1.0
androidx.glance:glance:1.1.0
androidx.graphics:graphics-path:1.0.1
androidx.interpolator:interpolator:1.0.0
androidx.legacy:legacy-support-core-utils:1.0.0
Expand Down Expand Up @@ -197,16 +203,21 @@ androidx.privacysandbox.ads:ads-adservices-java:1.1.0-beta11
androidx.privacysandbox.ads:ads-adservices:1.1.0-beta11
androidx.profileinstaller:profileinstaller:1.4.1
androidx.resourceinspection:resourceinspection-annotation:1.0.1
androidx.room:room-common:2.6.1
androidx.room:room-ktx:2.6.1
androidx.room:room-runtime:2.6.1
androidx.savedstate:savedstate-android:1.4.0
androidx.savedstate:savedstate-compose-android:1.4.0
androidx.savedstate:savedstate-compose:1.4.0
androidx.savedstate:savedstate-ktx:1.4.0
androidx.savedstate:savedstate:1.4.0
androidx.slidingpanelayout:slidingpanelayout:1.2.0
androidx.sqlite:sqlite-framework:2.4.0
androidx.sqlite:sqlite:2.4.0
androidx.startup:startup-runtime:1.2.0
androidx.tracing:tracing-android:1.3.0
androidx.tracing:tracing-ktx:1.3.0
androidx.tracing:tracing-perfetto:1.0.0
androidx.tracing:tracing-perfetto:1.0.1
androidx.tracing:tracing:1.3.0
androidx.transition:transition:1.6.0
androidx.vectordrawable:vectordrawable-animated:1.1.0
Expand All @@ -216,6 +227,8 @@ androidx.viewpager:viewpager:1.0.0
androidx.window:window-core-android:1.5.0
androidx.window:window-core:1.5.0
androidx.window:window:1.5.0
androidx.work:work-runtime-ktx:2.10.3
androidx.work:work-runtime:2.10.3
co.touchlab:kermit-android:2.0.8
co.touchlab:kermit-core-android:2.0.8
co.touchlab:kermit-core:2.0.8
Expand Down Expand Up @@ -292,6 +305,10 @@ com.google.mlkit:vision-interfaces:16.3.0
com.google.zxing:core:3.5.3
com.mohamedrejeb.calf:calf-permissions-android:0.8.0
com.mohamedrejeb.calf:calf-permissions:0.8.0
com.patrykandpatrick.vico:compose-android:3.2.2
com.patrykandpatrick.vico:compose-m3-android:3.2.2
com.patrykandpatrick.vico:compose-m3:3.2.2
com.patrykandpatrick.vico:compose:3.2.2
com.russhwolf:multiplatform-settings-android:1.3.0
com.russhwolf:multiplatform-settings-coroutines-android:1.3.0
com.russhwolf:multiplatform-settings-coroutines:1.3.0
Expand Down Expand Up @@ -362,6 +379,7 @@ io.github.vinceglb:filekit-dialogs:0.12.0
io.insert-koin:koin-android:4.1.1
io.insert-koin:koin-androidx-compose:4.1.1
io.insert-koin:koin-androidx-navigation:4.1.1
io.insert-koin:koin-androidx-workmanager:4.1.1
io.insert-koin:koin-annotations-jvm:2.1.0
io.insert-koin:koin-annotations:2.1.0
io.insert-koin:koin-bom:4.1.1
Expand Down Expand Up @@ -429,16 +447,16 @@ org.jetbrains.androidx.navigationevent:navigationevent-compose:1.0.1
org.jetbrains.androidx.savedstate:savedstate-compose:1.3.6
org.jetbrains.androidx.savedstate:savedstate:1.4.0
org.jetbrains.androidx.window:window-core:1.4.0-beta01
org.jetbrains.compose.animation:animation-core:1.10.0
org.jetbrains.compose.animation:animation:1.10.0
org.jetbrains.compose.annotation-internal:annotation:1.10.1
org.jetbrains.compose.collection-internal:collection:1.10.1
org.jetbrains.compose.animation:animation-core:1.11.0
org.jetbrains.compose.animation:animation:1.11.0
org.jetbrains.compose.annotation-internal:annotation:1.9.3
org.jetbrains.compose.collection-internal:collection:1.9.3
org.jetbrains.compose.components:components-resources-android:1.10.0
org.jetbrains.compose.components:components-resources:1.10.0
org.jetbrains.compose.components:components-ui-tooling-preview-android:1.9.3
org.jetbrains.compose.components:components-ui-tooling-preview:1.9.3
org.jetbrains.compose.foundation:foundation-layout:1.10.0
org.jetbrains.compose.foundation:foundation:1.10.0
org.jetbrains.compose.foundation:foundation-layout:1.11.0
org.jetbrains.compose.foundation:foundation:1.11.0
org.jetbrains.compose.material3.adaptive:adaptive-layout:1.1.2
org.jetbrains.compose.material3.adaptive:adaptive-navigation:1.1.2
org.jetbrains.compose.material3.adaptive:adaptive:1.2.0-alpha05
Expand All @@ -448,32 +466,32 @@ org.jetbrains.compose.material:material-icons-core:1.7.3
org.jetbrains.compose.material:material-icons-extended:1.7.3
org.jetbrains.compose.material:material-ripple:1.9.3
org.jetbrains.compose.material:material:1.9.3
org.jetbrains.compose.runtime:runtime-saveable:1.10.1
org.jetbrains.compose.runtime:runtime:1.10.1
org.jetbrains.compose.runtime:runtime-saveable:1.11.0
org.jetbrains.compose.runtime:runtime:1.11.0
org.jetbrains.compose.ui:ui-backhandler-android:1.9.3
org.jetbrains.compose.ui:ui-backhandler:1.9.3
org.jetbrains.compose.ui:ui-geometry:1.10.1
org.jetbrains.compose.ui:ui-graphics:1.10.1
org.jetbrains.compose.ui:ui-text:1.10.1
org.jetbrains.compose.ui:ui-geometry:1.11.0
org.jetbrains.compose.ui:ui-graphics:1.11.0
org.jetbrains.compose.ui:ui-text:1.11.0
org.jetbrains.compose.ui:ui-tooling-preview:1.10.0
org.jetbrains.compose.ui:ui-unit:1.10.1
org.jetbrains.compose.ui:ui-util:1.10.1
org.jetbrains.compose.ui:ui:1.10.1
org.jetbrains.compose.ui:ui-unit:1.11.0
org.jetbrains.compose.ui:ui-util:1.11.0
org.jetbrains.compose.ui:ui:1.11.0
org.jetbrains.kotlin:kotlin-parcelize-runtime:2.2.21
org.jetbrains.kotlin:kotlin-reflect:2.2.21
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22
org.jetbrains.kotlin:kotlin-stdlib:2.2.21
org.jetbrains.kotlin:kotlin-stdlib:2.3.21
org.jetbrains.kotlinx:atomicfu-jvm:0.27.0
org.jetbrains.kotlinx:atomicfu:0.27.0
org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.4.0
org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2
org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.10.2
org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.10.2
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.11.0
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.11.0
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0
org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.11.0
org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.11.0
org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.7.1
org.jetbrains.kotlinx:kotlinx-datetime:0.7.1
org.jetbrains.kotlinx:kotlinx-io-bytestring-jvm:0.8.0
Expand Down
33 changes: 33 additions & 0 deletions cmp-android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="mifospay" />
</intent-filter>
Comment on lines +54 to +61

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the AndroidManifest.xml file
find . -name "AndroidManifest.xml" -type f

Repository: openMF/mifos-pay

Length of output: 1748


🏁 Script executed:

# Search for all occurrences of "mifospay" in the codebase to understand actual usage
rg "mifospay" --type kotlin --type java --type xml -i

Repository: openMF/mifos-pay

Length of output: 154


🏁 Script executed:

# Look for intent-filter and deep-link configurations in the Android manifest
cat cmp-android/src/main/AndroidManifest.xml | head -100

Repository: openMF/mifos-pay

Length of output: 3933


🏁 Script executed:

# Search for "mifospay" without type restrictions to find all usages
rg "mifospay" -i --no-heading

Repository: openMF/mifos-pay

Length of output: 154


🏁 Script executed:

# Search for navigation patterns that might use the mifospay scheme
rg "navigate.*mifospay|mifospay.*navigate" -i

Repository: openMF/mifos-pay

Length of output: 154


🏁 Script executed:

# Look for URI or Intent construction related to deep-links
rg "uri|intent|deeplink" cmp-android/src/main --type-list

Repository: openMF/mifos-pay

Length of output: 544


🏁 Script executed:

# Search for "dashboard", "add", "expense" as potential hosts in navigation code
rg "dashboard|\"add\"|expense" cmp-android/src --type-list -i

Repository: openMF/mifos-pay

Length of output: 544


🏁 Script executed:

# Simple search for mifospay in all files
rg "mifospay" .

Repository: openMF/mifos-pay

Length of output: 50373


🏁 Script executed:

# List Kotlin and Java files in cmp-android to understand code structure
find cmp-android -name "*.kt" -o -name "*.java" | head -20

Repository: openMF/mifos-pay

Length of output: 824


🏁 Script executed:

# Search for navigation patterns and deep-link URIs
rg "deeplink|DeepLink|NavDeepLink|navDeepLink" cmp-android --type-list

Repository: openMF/mifos-pay

Length of output: 544


🏁 Script executed:

# Look for strings that might contain the scheme or hosts
rg "dashboard|add.*expense" cmp-android -i

Repository: openMF/mifos-pay

Length of output: 2024


🏁 Script executed:

# Read the WidgetDeepLink.kt file to see all URI constants
cat cmp-android/src/main/kotlin/org/mifospay/widget/WidgetDeepLink.kt

Repository: openMF/mifos-pay

Length of output: 716


🏁 Script executed:

# Search for any other deep-link URI patterns that might use mifospay scheme
rg "mifospay://" cmp-android

Repository: openMF/mifos-pay

Length of output: 154


Restrict deep-link hosts to only those used by the widget.

The manifest currently accepts any host under the mifospay:// scheme. The app only uses three specific hosts (add, expense, and dashboard) for widget actions. Restricting the intent-filter to these hosts reduces the app's externally triggerable entry points to only those needed for widget functionality.

Suggested hardening
-                <data android:scheme="mifospay" />
+                <data android:scheme="mifospay" android:host="dashboard" />
+                <data android:scheme="mifospay" android:host="add" />
+                <data android:scheme="mifospay" android:host="expense" />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmp-android/src/main/AndroidManifest.xml` around lines 54 - 61, The
intent-filter in the AndroidManifest.xml currently accepts any host under the
`mifospay://` scheme by only specifying the scheme in the `<data>` element. To
restrict deep-linking to only the hosts actually used by the widget, replace the
single `<data android:scheme="mifospay" />` element with multiple `<data>`
elements that each specify both the `android:scheme` attribute set to "mifospay"
and the `android:host` attribute set to one of the three allowed hosts: "add",
"expense", and "dashboard". This will limit the app's externally triggerable
entry points to only those hosts needed for widget functionality.

</activity>

<meta-data
Expand All @@ -67,6 +75,31 @@
android:resource="@xml/provider_paths" />
</provider>

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>


<receiver
android:name=".widget.FinanceWidgetReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/finance_widget_info" />
</receiver>



<!-- Disable collection of AD_ID for all build variants -->
<meta-data
android:name="google_analytics_adid_collection_enabled"
Expand Down
6 changes: 6 additions & 0 deletions cmp-android/src/main/kotlin/org/mifospay/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
package org.mifospay

import android.content.Intent
import android.content.res.Resources
import android.os.Bundle
import androidx.activity.compose.setContent
Expand Down Expand Up @@ -106,4 +107,9 @@ class MainActivity : AppCompatActivity() {
)
}
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
}
}
27 changes: 25 additions & 2 deletions cmp-android/src/main/kotlin/org/mifospay/MifosPayApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,53 @@ package org.mifospay
import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.work.Configuration
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.androidx.workmanager.factory.KoinWorkerFactory
import org.koin.androidx.workmanager.koin.workManagerFactory
import org.koin.core.context.GlobalContext.startKoin
import org.mifospay.core.data.repository.WidgetManagerRepository
import org.mifospay.core.datastore.UserPreferencesRepository
import org.mifospay.shared.di.KoinModules
import org.mifospay.widget.WidgetRefreshWorker
import org.mifospay.widget.androidWidgetModule

class MifosPayApp : Application() {
class MifosPayApp : Application(), Configuration.Provider {
private val userDataRepository: UserPreferencesRepository by inject()
private val widgetManager: WidgetManagerRepository by inject()

override fun onCreate() {
super.onCreate()

startKoin {
androidContext(this@MifosPayApp)
androidLogger()
modules(KoinModules.allModules)
workManagerFactory()
modules(KoinModules.allModules + androidWidgetModule)
}

widgetManager.schedule()

WorkManager.getInstance(this)
.enqueue(
OneTimeWorkRequestBuilder<WidgetRefreshWorker>()
.build(),
)
Comment on lines +48 to +52

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n cmp-android/src/main/kotlin/org/mifospay/MifosPayApp.kt | head -60

Repository: openMF/mifos-pay

Length of output: 2677


🏁 Script executed:

# Check for any imports related to WorkManager
grep -n "import.*work\|WorkManager\|ExistingWorkPolicy" cmp-android/src/main/kotlin/org/mifospay/MifosPayApp.kt

Repository: openMF/mifos-pay

Length of output: 449


🏁 Script executed:

# Search for other uses of enqueueUniqueWork in the codebase to see if this pattern is used elsewhere
rg "enqueueUniqueWork" cmp-android/ --type kotlin

Repository: openMF/mifos-pay

Length of output: 154


🏁 Script executed:

# Check what widgetManager.schedule() does
rg "fun schedule\|\.schedule\(\)" cmp-android/src/main/kotlin/org/mifospay/core/data/repository/ --type kotlin -A 5

Repository: openMF/mifos-pay

Length of output: 257


🌐 Web query:

WorkManager enqueue vs enqueueUniqueWork behavior Android duplicate jobs on app startup

💡 Result:

Using WorkManager.enqueue() at app startup—such as inside an Activity's onCreate()—will cause duplicate jobs to be scheduled every time that code path executes [1][2][3]. Because enqueue() does not check for existing work, it simply adds a new instance of the WorkRequest to the queue [4][5][2]. To prevent duplicate jobs on app startup, you should use unique work APIs instead [4][6]. These methods identify work by a string name and allow you to define a policy for handling existing work with that same name [7][6]. The recommended approach is to use: 1. WorkManager.enqueueUniqueWork() for one-time work [4][6]. 2. WorkManager.enqueueUniquePeriodicWork() for periodic work [4][6]. When using these methods, you must provide an ExistingWorkPolicy (for one-time work) or ExistingPeriodicWorkPolicy (for periodic work) to determine how WorkManager should resolve collisions [4][6]. Common policies include: - KEEP: If work with the given name already exists, ignore the new request and keep the existing work [4][8]. - REPLACE: Cancel and delete the existing work, then enqueue the new work [4][8]. - APPEND: Add the new work as a prerequisite to the existing work sequence [4][8]. By choosing the appropriate policy—most commonly KEEP—you ensure that your startup logic only schedules the task if it is not already pending or running, effectively preventing duplicates [4][9].

Citations:


🏁 Script executed:

# Check if there are any other WorkManager enqueue calls in the codebase
rg "\.enqueue\(|\.enqueueUniqueWork\(" cmp-android/ --type kotlin -B 2 -A 2

Repository: openMF/mifos-pay

Length of output: 590


Use enqueueUniqueWork() to prevent duplicate startup refresh jobs.

enqueue() in Application.onCreate() schedules a new job every time the app starts. Use enqueueUniqueWork() with ExistingWorkPolicy.KEEP to ensure the refresh job is only scheduled once, preventing duplicate work from consuming resources and impacting performance.

Suggested fix
+import androidx.work.ExistingWorkPolicy
...
         WorkManager.getInstance(this)
-            .enqueue(
+            .enqueueUniqueWork(
+                "mifospay_widget_refresh_startup",
+                ExistingWorkPolicy.KEEP,
                 OneTimeWorkRequestBuilder<WidgetRefreshWorker>()
                     .build(),
             )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmp-android/src/main/kotlin/org/mifospay/MifosPayApp.kt` around lines 48 -
52, In the MifosPayApp class where WorkManager.getInstance(this).enqueue() is
called with OneTimeWorkRequestBuilder for WidgetRefreshWorker, replace the
enqueue() method with enqueueUniqueWork() instead. This method requires two
additional parameters: a unique work name string identifier for this refresh
job, and ExistingWorkPolicy.KEEP to prevent scheduling duplicate work if the job
already exists. This ensures the widget refresh worker is only scheduled once
during the app lifecycle rather than on every app startup.


restoreSavedLanguage()
}

override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(KoinWorkerFactory())
.build()

private fun restoreSavedLanguage() {
runBlocking {
val language = userDataRepository.language.first()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2026 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-wallet/blob/master/LICENSE.md
*/
package org.mifospay.widget

import org.koin.dsl.module
import org.mifospay.core.data.util.WidgetSyncService

val androidWidgetModule = module {

single<WidgetSyncService> {
AndroidWidgetSyncService(context = get(), widgetDataProvider = get())
}
}
Loading
Loading