Skip to content

Commit

Permalink
Save app settings to Proto DataStore (#30)
Browse files Browse the repository at this point in the history
* Added a new module to sore app settings

* Refactoring

* Save the dark mode setting

* + new module for main activity

* Changes dark mode in the main activity based on the settings
  • Loading branch information
dmitriy-chernysh authored Jan 4, 2025
1 parent 765399e commit 7d0dc26
Show file tree
Hide file tree
Showing 39 changed files with 570 additions and 61 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ dependencies {
androidTestImplementation(libs.firebase.crashlytics) {
exclude(group = "com.google.firebase", module = "firebase-crashlytics")
}

implementation(projects.feature.main)
}


Expand Down
3 changes: 2 additions & 1 deletion app/src/main/kotlin/com/mobiledevpro/app/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package com.mobiledevpro.app
import android.app.Application
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.mobiledevpro.app.di.coreModule
import com.mobiledevpro.apptemplate.compose.BuildConfig
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
Expand All @@ -36,7 +37,7 @@ class App : Application() {
// Reference Android context
androidContext(this@App)
// Load common modules (feature modules will be loaded on demand)
// modules(myAppModules)
modules(coreModule)
}

//Stop sending crashes and analytics in debug mode
Expand Down
28 changes: 25 additions & 3 deletions app/src/main/kotlin/com/mobiledevpro/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,30 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.mobiledevpro.app.di.mainModule
import com.mobiledevpro.app.ui.MainApp
import com.mobiledevpro.di.koinScope
import com.mobiledevpro.main.view.state.MainUIState
import com.mobiledevpro.main.view.vm.MainViewModel
import com.mobiledevpro.ui.theme.AppTheme
import com.mobiledevpro.ui.theme.darkModeState
import org.koin.compose.KoinContext
import org.koin.core.context.loadKoinModules
import kotlin.getValue

class MainActivity : ComponentActivity() {

private val viewModel: MainViewModel by koinScope<MainActivity>().inject()

init {
loadKoinModules(mainModule)
}

override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
Expand All @@ -22,9 +36,17 @@ class MainActivity : ComponentActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false)

setContent {
val darkModeState by darkModeState.collectAsStateWithLifecycle()
val uiState by viewModel.uiState.collectAsStateWithLifecycle()

var darkMode by remember { mutableStateOf(true) }

if (uiState is MainUIState.Success) {
(uiState as MainUIState.Success).settings.let {
darkMode = it.darkMode
}
}

AppTheme(darkTheme = darkModeState) {
AppTheme(darkTheme = darkMode) {
KoinContext {
MainApp()
}
Expand Down
40 changes: 40 additions & 0 deletions app/src/main/kotlin/com/mobiledevpro/app/di/Module.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2025 | Dmitri Chernysh | https://github.com/dmitriy-chernysh
*
*
* 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 com.mobiledevpro.app.di

import com.mobiledevpro.app.MainActivity
import com.mobiledevpro.main.view.vm.MainViewModel
import com.mobiledevpro.settings.core.datastore.AppSettingsManager
import com.mobiledevpro.settings.core.datastore.ImplAppSettingsManager
import com.mobiledevpro.settings.core.usecase.GetAppSettingsUseCase
import org.koin.core.module.dsl.bind
import org.koin.core.module.dsl.scopedOf
import org.koin.core.module.dsl.singleOf
import org.koin.core.module.dsl.viewModelOf
import org.koin.dsl.module

val coreModule = module {
singleOf(::ImplAppSettingsManager) { bind<AppSettingsManager>() }
}

val mainModule = module {
scope<MainActivity> {
viewModelOf(::MainViewModel)
scopedOf(::GetAppSettingsUseCase)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import kotlinx.coroutines.withContext
* Created on Sep 12, 2022.
*
*/
abstract class BaseCoroutinesFLowUseCase<Results, in Params>(
abstract class BaseCoroutinesFLowUseCase<in Params, Results>(
executionDispatcher: CoroutineDispatcher
) : BaseUseCase(executionDispatcher) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import kotlinx.coroutines.withContext
* Created on Sep 12, 2022.
*
*/
abstract class BaseCoroutinesUseCase<Results, in Params>(
abstract class BaseCoroutinesUseCase<in Params, Results>(
executionDispatcher: CoroutineDispatcher
) : BaseUseCase(executionDispatcher) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import android.net.Uri
*/

data class UserProfile(
val name : String,
val nickname: String,
val name: String = "",
val nickname: String = "",
val status: Boolean = false,
val photo : Uri = Uri.EMPTY
)
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ fun NavGraphBuilder.userProfileScreen(onNavigateTo: (Screen) -> Unit) {

ProfileScreen(
state = viewModel.uiState,
onDarkModeSwitched = { isDarkMode ->
viewModel.onDarkModeSwitched(isDarkMode)
},
onNavigateToSubscription = {
onNavigateTo(Screen.Subscription)
}
Expand Down
9 changes: 1 addition & 8 deletions core/ui/src/main/kotlin/com/mobiledevpro/ui/theme/Theme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

private val LightColorScheme = lightColorScheme(
primary = md_theme_light_primary,
Expand Down Expand Up @@ -134,8 +131,4 @@ fun AppTheme(
content()
}
}
}

//TODO: it's temporary implementation. Dark mode value should be saved into preferences.
val _darkModeState = MutableStateFlow(true)
val darkModeState: StateFlow<Boolean> = _darkModeState.asStateFlow()
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import kotlinx.coroutines.flow.flowOf

class GetChatListUseCase(

) : BaseCoroutinesFLowUseCase<List<Chat>, None>(Dispatchers.IO) {
) : BaseCoroutinesFLowUseCase<None, List<Chat>>(Dispatchers.IO) {

override suspend fun buildUseCaseFlow(params: None?): Flow<List<Chat>> =
flowOf(fakeChatList)
Expand Down
1 change: 1 addition & 0 deletions feature/main/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
7 changes: 7 additions & 0 deletions feature/main/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
id("feature-module")
}

dependencies {
api(projects.feature.settingsCore)
}
Empty file added feature/main/consumer-rules.pro
Empty file.
21 changes: 21 additions & 0 deletions feature/main/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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
2 changes: 2 additions & 0 deletions feature/main/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2023 | Dmitri Chernysh | https://mobile-dev.pro
*
*
* 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 com.mobiledevpro.main.view.state

import com.mobiledevpro.settings.core.model.Settings
import com.mobiledevpro.ui.state.UIState


sealed interface MainUIState : UIState {
data object Empty : MainUIState

data class Success(
val settings: Settings
) : MainUIState
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2023 | Dmitri Chernysh | https://mobile-dev.pro
*
*
* 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 com.mobiledevpro.main.view.vm

import androidx.lifecycle.viewModelScope
import com.mobiledevpro.main.view.state.MainUIState
import com.mobiledevpro.settings.core.usecase.GetAppSettingsUseCase
import com.mobiledevpro.ui.vm.BaseViewModel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlin.onSuccess


class MainViewModel(
private val getAppSettingsUseCase: GetAppSettingsUseCase
) : BaseViewModel<MainUIState>() {

override fun initUIState(): MainUIState = MainUIState.Empty

init {
observeSettings()
}

private fun observeSettings() {
viewModelScope.launch {
getAppSettingsUseCase.execute()
.collectLatest { result ->
result.onSuccess { settings ->
_uiState.value = MainUIState.Success(settings)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import kotlinx.coroutines.flow.flowOf

class GetPeopleListUseCase(

) : BaseCoroutinesFLowUseCase<List<PeopleProfile>, None>(Dispatchers.IO) {
) : BaseCoroutinesFLowUseCase<None, List<PeopleProfile>>(Dispatchers.IO) {

override suspend fun buildUseCaseFlow(params: None?): Flow<List<PeopleProfile>> {
Log.d("testing", "buildUseCaseFlow: Thread - ${Thread.currentThread().name}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import kotlinx.coroutines.flow.flowOf

class GetPeopleProfileUseCase(

) : BaseCoroutinesFLowUseCase<PeopleProfile?, Int>(Dispatchers.IO) {
) : BaseCoroutinesFLowUseCase<Int, PeopleProfile?>(Dispatchers.IO) {

override suspend fun buildUseCaseFlow(params: Int?): Flow<PeopleProfile?> =
params?.let { peopleProfileId ->
Expand Down
1 change: 1 addition & 0 deletions feature/settings_core/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
33 changes: 33 additions & 0 deletions feature/settings_core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
plugins {
id("feature-module")
alias(libs.plugins.google.protobuf)
}


dependencies {
api(libs.data.store)
api(libs.protobuf.kotlin)
}

protobuf {
protoc {
artifact = "com.google.protobuf:protoc:${libs.versions.protobuf.get()}"
}

generateProtoTasks {
all().forEach { task ->
task.builtins {
create("java") {
option("lite")
}
/*
create("kotlin") {
option("lite")
}
*/
}
}
}

}
Empty file.
21 changes: 21 additions & 0 deletions feature/settings_core/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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
2 changes: 2 additions & 0 deletions feature/settings_core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />
Loading

0 comments on commit 7d0dc26

Please sign in to comment.