Skip to content

Commit 9a99a33

Browse files
Merge branch 'development' into recurringAccount
2 parents f1097f7 + c2c2d8f commit 9a99a33

129 files changed

Lines changed: 3286 additions & 1934 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
9+
*/
10+
package cmp.android.app
11+
12+
import android.content.res.Configuration
13+
import android.graphics.Color
14+
import androidx.activity.ComponentActivity
15+
import androidx.activity.SystemBarStyle
16+
import androidx.activity.enableEdgeToEdge
17+
import androidx.annotation.ColorInt
18+
import androidx.appcompat.app.AppCompatDelegate
19+
import androidx.core.util.Consumer
20+
import androidx.lifecycle.Lifecycle
21+
import androidx.lifecycle.lifecycleScope
22+
import androidx.lifecycle.repeatOnLifecycle
23+
import com.mifos.core.datastore.model.DarkThemeConfig
24+
import kotlinx.coroutines.channels.awaitClose
25+
import kotlinx.coroutines.flow.Flow
26+
import kotlinx.coroutines.flow.callbackFlow
27+
import kotlinx.coroutines.flow.combine
28+
import kotlinx.coroutines.flow.conflate
29+
import kotlinx.coroutines.flow.distinctUntilChanged
30+
import kotlinx.coroutines.launch
31+
32+
@ColorInt
33+
private val SCRIM_COLOR: Int = Color.TRANSPARENT
34+
35+
/**
36+
* Helper method to handle edge-to-edge logic for dark mode.
37+
*
38+
* This logic is from the Now-In-Android app found
39+
* [here](https://github.com/android/nowinandroid/blob/689ef92e41427ab70f82e2c9fe59755441deae92/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt#L94).
40+
*/
41+
@Suppress("MaxLineLength")
42+
fun ComponentActivity.setupEdgeToEdge(
43+
appThemeFlow: Flow<DarkThemeConfig>,
44+
) {
45+
lifecycleScope.launch {
46+
lifecycle.repeatOnLifecycle(state = Lifecycle.State.STARTED) {
47+
combine(
48+
isSystemInDarkModeFlow(),
49+
appThemeFlow,
50+
) { isSystemDarkMode, appTheme ->
51+
AppCompatDelegate.setDefaultNightMode(appTheme.osValue)
52+
}
53+
.distinctUntilChanged()
54+
.collect { isDarkMode ->
55+
// This handles all the settings to go edge-to-edge. We are using a transparent
56+
// scrim for system bars and switching between "light" and "dark" based on the
57+
// system and internal app theme settings.
58+
val style = SystemBarStyle.auto(
59+
darkScrim = SCRIM_COLOR,
60+
lightScrim = SCRIM_COLOR,
61+
// Disabling Dark Mode for this app
62+
detectDarkMode = { false },
63+
)
64+
enableEdgeToEdge(statusBarStyle = style, navigationBarStyle = style)
65+
}
66+
}
67+
}
68+
}
69+
70+
/**
71+
* Adds a configuration change listener to retrieve whether system is in
72+
* dark theme or not. This will emit current status immediately and then
73+
* will emit changes as needed.
74+
*/
75+
private fun ComponentActivity.isSystemInDarkModeFlow(): Flow<Boolean> =
76+
callbackFlow {
77+
channel.trySend(element = resources.configuration.isSystemInDarkMode)
78+
val listener = Consumer<Configuration> {
79+
channel.trySend(element = it.isSystemInDarkMode)
80+
}
81+
addOnConfigurationChangedListener(listener = listener)
82+
awaitClose { removeOnConfigurationChangedListener(listener = listener) }
83+
}
84+
.distinctUntilChanged()
85+
.conflate()

cmp-navigation/src/commonMain/kotlin/cmp/navigation/navigation/NavGraphRoute.kt renamed to cmp-android/src/main/kotlin/cmp/android/app/ConfigurationExtension.kt

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@
77
*
88
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
99
*/
10-
package cmp.navigation.navigation
10+
package cmp.android.app
1111

12-
internal object NavGraphRoute {
13-
const val ROOT_GRAPH = "root_graph"
14-
const val AUTH_GRAPH = "auth_graph"
15-
const val PASSCODE_GRAPH = "passcode_graph"
16-
const val MAIN_GRAPH = "main_graph"
17-
}
12+
import android.content.res.Configuration
13+
14+
val Configuration.isSystemInDarkMode
15+
get() = (uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES

cmp-android/src/main/kotlin/cmp/android/app/MainActivity.kt

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,18 @@ package cmp.android.app
1212
import android.os.Bundle
1313
import androidx.activity.ComponentActivity
1414
import androidx.activity.compose.setContent
15-
import androidx.activity.enableEdgeToEdge
15+
import androidx.appcompat.app.AppCompatDelegate
16+
import androidx.core.os.LocaleListCompat
1617
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
1718
import androidx.core.view.WindowCompat
1819
import cmp.shared.SharedApp
20+
import com.mifos.core.datastore.UserPreferencesRepository
1921
import com.mifos.core.ui.util.ShareUtils
2022
import io.github.vinceglb.filekit.FileKit
2123
import io.github.vinceglb.filekit.dialogs.init
24+
import org.koin.android.ext.android.inject
25+
import java.util.Locale
26+
import kotlin.getValue
2227

2328
/**
2429
* Main activity class.
@@ -32,22 +37,42 @@ class MainActivity : ComponentActivity() {
3237
* Called when the activity is starting.
3338
* This is where most initialization should go: calling [setContentView(int)] to inflate the activity's UI,
3439
*/
40+
41+
private val userPreferencesRepository: UserPreferencesRepository by inject()
42+
3543
override fun onCreate(savedInstanceState: Bundle?) {
3644
super.onCreate(savedInstanceState)
45+
var shouldShowSplashScreen = true
46+
installSplashScreen().setKeepOnScreenCondition { shouldShowSplashScreen }
3747

38-
installSplashScreen()
48+
val darkThemeConfigFlow = userPreferencesRepository.appTheme
3949

40-
ShareUtils.setActivityProvider { this }
50+
WindowCompat.setDecorFitsSystemWindows(window, false)
51+
setupEdgeToEdge(darkThemeConfigFlow)
52+
ShareUtils.setActivityProvider { return@setActivityProvider this }
4153
FileKit.init(this)
4254

43-
WindowCompat.setDecorFitsSystemWindows(window, false)
44-
enableEdgeToEdge()
4555
/**
4656
* Set the content view of the activity.
4757
* @see setContent
4858
*/
4959
setContent {
50-
SharedApp()
60+
SharedApp(
61+
handleThemeMode = {
62+
AppCompatDelegate.setDefaultNightMode(it)
63+
},
64+
handleAppLocale = {
65+
it?.let {
66+
AppCompatDelegate.setApplicationLocales(
67+
LocaleListCompat.forLanguageTags(it),
68+
)
69+
Locale.setDefault(Locale(it))
70+
}
71+
},
72+
onSplashScreenRemoved = {
73+
shouldShowSplashScreen = false
74+
},
75+
)
5176
}
5277
}
5378
}

cmp-navigation/src/commonMain/composeResources/values/strings.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,18 @@
1010
-->
1111
<resources>
1212
<string name="cmp_navigation_no_internet">You are not connected to the Internet!</string>
13+
14+
<string name="cmp_navigation_mifos">Mifos</string>
15+
<string name="cmp_navigation_search">Search</string>
16+
<string name="cmp_navigation_clients">Clients</string>
17+
<string name="cmp_navigation_center">Centers</string>
18+
<string name="cmp_navigation_groups">Groups</string>
19+
20+
<string name="checker_inbox_tasks">Checker Inbox Tasks</string>
21+
<string name="collection_sheet">Collection Sheet</string>
22+
<string name="run_reports">Run Reports</string>
23+
<string name="path_tracker">Path Tracker</string>
24+
<string name="settings">Settings</string>
25+
<string name="about">About</string>
26+
<string name="offline_sync">Offline Sync</string>
1327
</resources>

0 commit comments

Comments
 (0)