Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ThemeChanging Functionality Added #2723

Open
wants to merge 6 commits into
base: development
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion androidApp/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<activity
android:name=".HomeActivity"
android:exported="true"
android:theme="@style/Theme.MifosSplash"
android:theme="@style/Theme.Mifos.Splash"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
32 changes: 25 additions & 7 deletions androidApp/src/main/kotlin/org/mifos/mobile/HomeActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
*/
package org.mifos.mobile

import android.graphics.Color
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
Expand All @@ -35,6 +37,7 @@ import org.mifos.mobile.core.data.utils.NetworkMonitor
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.designsystem.theme.darkScrim
import org.mifos.mobile.core.designsystem.theme.lightScrim
import org.mifos.mobile.core.model.enums.AppTheme
import org.mifos.mobile.navigation.MifosNavGraph.AUTH_GRAPH
import org.mifos.mobile.navigation.MifosNavGraph.PASSCODE_GRAPH
import org.mifos.mobile.navigation.RootNavGraph
Expand All @@ -54,7 +57,6 @@ class HomeActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
var uiState: HomeActivityUiState by mutableStateOf(HomeActivityUiState.Loading)

// Update the uiState
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState
Expand All @@ -76,25 +78,41 @@ class HomeActivity : ComponentActivity() {
val navController = rememberNavController()

val appState = rememberMifosMobileState(networkMonitor = networkMonitor)
val darkTheme = isSystemInDarkTheme()
val navDestination = when (uiState) {
is Success -> if ((uiState as Success).userData.isAuthenticated) {
PASSCODE_GRAPH
} else {
AUTH_GRAPH
}

else -> AUTH_GRAPH
}

DisposableEffect(darkTheme) {
window?.statusBarColor = if (darkTheme) darkScrim.toArgb() else lightScrim.toArgb()
window?.navigationBarColor = if (darkTheme) darkScrim.toArgb() else lightScrim.toArgb()
val isSystemInDarkMode = isSystemInDarkTheme()
DisposableEffect(isSystemInDarkMode) {
enableEdgeToEdge(
statusBarStyle = SystemBarStyle.auto(
Color.TRANSPARENT,
Color.TRANSPARENT,
) { isSystemInDarkMode },
navigationBarStyle = SystemBarStyle.auto(
lightScrim.toArgb(),
darkScrim.toArgb(),
) { isSystemInDarkMode },
)
onDispose {}
}

val isDarkMode = when (uiState) {
is Success -> when ((uiState as Success).themeState) {
AppTheme.DARK -> true
AppTheme.LIGHT -> false
AppTheme.SYSTEM -> isSystemInDarkMode
}
else -> isSystemInDarkMode
}

CompositionLocalProvider {
MifosMobileTheme {
MifosMobileTheme(isDarkMode) {
RootNavGraph(
appState = appState,
navHostController = navController,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,28 @@ 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.combine
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.datastore.PreferencesHelper
import org.mifos.mobile.core.model.UserData
import org.mifos.mobile.core.model.enums.AppTheme
import javax.inject.Inject

@HiltViewModel
class HomeActivityViewModel @Inject constructor(
private val userDataRepository: UserDataRepository,
private val passcodeManager: PasscodeManager,
private val preferencesHelper: PreferencesHelper,
) : ViewModel() {

val uiState: StateFlow<HomeActivityUiState> = userDataRepository.userData.map {
HomeActivityUiState.Success(it)
val uiState: StateFlow<HomeActivityUiState> = combine(
userDataRepository.userData,
preferencesHelper.themeFlow,
) { userData, themeState ->
HomeActivityUiState.Success(userData, themeState)
}.stateIn(
scope = viewModelScope,
initialValue = HomeActivityUiState.Loading,
Expand All @@ -46,5 +52,5 @@ class HomeActivityViewModel @Inject constructor(

sealed interface HomeActivityUiState {
data object Loading : HomeActivityUiState
data class Success(val userData: UserData) : HomeActivityUiState
data class Success(val userData: UserData, val themeState: AppTheme) : HomeActivityUiState
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import androidx.multidex.MultiDexApplication
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.raizlabs.android.dbflow.config.FlowManager
import dagger.hilt.android.HiltAndroidApp
import org.mifos.mobile.core.datastore.PreferencesHelper
import org.mifos.mobile.feature.settings.applySavedTheme

@HiltAndroidApp
class MifosSelfServiceApp : MultiDexApplication() {
Expand All @@ -24,7 +22,6 @@ class MifosSelfServiceApp : MultiDexApplication() {
MultiDex.install(this)
FlowManager.init(this)
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true)
PreferencesHelper(this).applySavedTheme()
}

override fun onTerminate() {
Expand Down
13 changes: 13 additions & 0 deletions androidApp/src/main/res/values-night/color.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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
-->
<resources>
<color name="status_bar">#FF1B1B1F</color> // dark/color.xml
</resources>
21 changes: 21 additions & 0 deletions androidApp/src/main/res/values-night/theme.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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
-->
<resources xmlns:tools="http://schemas.android.com/tools">

<style name="NightAdjusted.Theme" parent="android:Theme.Material.NoActionBar">
<item name="android:statusBarColor">@color/status_bar</item>
</style>

<style name="NightAdjusted.Theme.Splash" parent="Theme.SplashScreen">
<item name="android:windowLightNavigationBar" tools:targetApi="27">false</item>
</style>

</resources>
2 changes: 1 addition & 1 deletion androidApp/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<color name="gray_bright">#33CCCCCC</color>

<!-- Material design colors -->

<color name="status_bar">#FFFEFBFF</color>
<!-- New UI color palette -->
<!--<color name="primary">#068799</color>-->
<!--<color name="primary_dark">#006978</color>-->
Expand Down
35 changes: 24 additions & 11 deletions androidApp/src/main/res/values/splash.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,31 @@

See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
<resources>
<style name="Theme.MifosSplash" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">#FFFDFDF5</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
<item name="postSplashScreenTheme">@style/AppTheme</item>
<item name="windowSplashScreenAnimationDuration">10</item>
<resources xmlns:tools="http://schemas.android.com/tools">

<!-- Base Theme for Night Mode -->
<style name="NightAdjusted.Theme" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:statusBarColor">@color/status_bar</item>
</style>

<!-- Final Theme for the Application -->
<style name="Theme.MifosSplash" parent="NightAdjusted.Theme">
<!-- Splash background color -->
<item name="windowSplashScreenBackground">@color/white</item>
</style>

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:statusBarColor">@color/background</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowIsTranslucent">true</item>
<!-- Splash Screen Theme -->
<style name="NightAdjusted.Theme.Splash" parent="Theme.SplashScreen">
<item name="android:windowLightNavigationBar" tools:targetApi="27">true</item>
<!-- Splash screen animation duration -->
<item name="windowSplashScreenAnimationDuration">500</item>
</style>

<!-- Final Splash Theme -->
<style name="Theme.Mifos.Splash" parent="NightAdjusted.Theme.Splash">
<!-- Icon for splash screen -->
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
<!-- Post splash screen theme -->
<item name="postSplashScreenTheme">@style/Theme.MifosSplash</item>
</style>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import android.text.TextUtils
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.callbackFlow
import org.mifos.mobile.core.model.enums.AppTheme
Expand All @@ -29,9 +32,23 @@ import javax.inject.Singleton
*/
@Singleton
class PreferencesHelper @Inject constructor(@ApplicationContext context: Context?) {
private val themeFlowState: MutableStateFlow<AppTheme>
val themeFlow: StateFlow<AppTheme> get() = themeFlowState.asStateFlow()

private val sharedPreferences: SharedPreferences? =
PreferenceManager.getDefaultSharedPreferences(context)

init {
if (!sharedPreferences?.contains(APPLICATION_THEME)!!) {
putInt(APPLICATION_THEME, AppTheme.SYSTEM.ordinal)
}
themeFlowState = MutableStateFlow(
AppTheme.entries.getOrNull(
sharedPreferences.getInt(APPLICATION_THEME, AppTheme.SYSTEM.ordinal),
) ?: AppTheme.SYSTEM,
)
}

fun clear() {
val editor = sharedPreferences?.edit()
// prevent deletion of url and tenant
Expand Down Expand Up @@ -178,6 +195,7 @@ class PreferencesHelper @Inject constructor(@ApplicationContext context: Context
get() = getInt(APPLICATION_THEME, AppTheme.SYSTEM.ordinal) ?: AppTheme.SYSTEM.ordinal
set(value) {
putInt(APPLICATION_THEME, value)
themeFlowState.value = AppTheme.entries[value]
}

var language
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ private val DarkThemeColors = darkColorScheme(
secondary = Black1,
error = RedErrorDark,
background = BackgroundDark,
surface = Black1,
surface = BackgroundDark,
onSurface = Color.White,
onSecondary = Color.White,
outlineVariant = Color.White,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
*/
package org.mifos.mobile.feature.settings

import android.os.Build
import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
Expand Down Expand Up @@ -70,15 +68,7 @@ internal class SettingsViewModel @Inject constructor(
}

fun updateTheme(theme: AppTheme) {
AppCompatDelegate.setDefaultNightMode(
when (theme) {
AppTheme.DARK -> AppCompatDelegate.MODE_NIGHT_YES
AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
},
)
preferencesHelper.appTheme = theme.ordinal
preferencesHelper.applyTheme(theme)
}
}

Expand Down Expand Up @@ -124,27 +114,3 @@ internal enum class SettingsCardItem(
subclassOf = R.string.other,
),
}

fun PreferencesHelper.applySavedTheme() {
val applicationTheme = AppTheme.entries.find { it.ordinal == this.appTheme }
AppCompatDelegate.setDefaultNightMode(
when {
applicationTheme == AppTheme.DARK -> AppCompatDelegate.MODE_NIGHT_YES
applicationTheme == AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
Build.VERSION.SDK_INT > Build.VERSION_CODES.P -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
else -> AppCompatDelegate.MODE_NIGHT_NO
},
)
}

internal fun PreferencesHelper.applyTheme(applicationTheme: AppTheme) {
this.appTheme = applicationTheme.ordinal
AppCompatDelegate.setDefaultNightMode(
when {
applicationTheme == AppTheme.DARK -> AppCompatDelegate.MODE_NIGHT_YES
applicationTheme == AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
Build.VERSION.SDK_INT > Build.VERSION_CODES.P -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
else -> AppCompatDelegate.MODE_NIGHT_NO
},
)
}
Loading