Skip to content

Commit

Permalink
feat: Migrated :core:datastore module to KMP (openMF#2737)
Browse files Browse the repository at this point in the history
  • Loading branch information
revanthkumarJ authored Jan 14, 2025
1 parent d88e08f commit 7520dd3
Show file tree
Hide file tree
Showing 15 changed files with 504 additions and 243 deletions.
8 changes: 8 additions & 0 deletions androidApp/dependencies/demoDebugRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ 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.zxing:core:3.5.3
com.russhwolf:multiplatform-settings-android-debug:1.2.0
com.russhwolf:multiplatform-settings-coroutines-android-debug:1.2.0
com.russhwolf:multiplatform-settings-coroutines:1.2.0
com.russhwolf:multiplatform-settings-no-arg-android-debug:1.2.0
com.russhwolf:multiplatform-settings-no-arg:1.2.0
com.russhwolf:multiplatform-settings-serialization-android-debug:1.2.0
com.russhwolf:multiplatform-settings-serialization:1.2.0
com.russhwolf:multiplatform-settings:1.2.0
com.squareup.okhttp3:logging-interceptor:4.12.0
com.squareup.okhttp3:okhttp:4.12.0
com.squareup.okio:okio-jvm:3.9.1
Expand Down
8 changes: 8 additions & 0 deletions androidApp/dependencies/demoReleaseRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@ 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.zxing:core:3.5.3
com.russhwolf:multiplatform-settings-android:1.2.0
com.russhwolf:multiplatform-settings-coroutines-android:1.2.0
com.russhwolf:multiplatform-settings-coroutines:1.2.0
com.russhwolf:multiplatform-settings-no-arg-android:1.2.0
com.russhwolf:multiplatform-settings-no-arg:1.2.0
com.russhwolf:multiplatform-settings-serialization-android:1.2.0
com.russhwolf:multiplatform-settings-serialization:1.2.0
com.russhwolf:multiplatform-settings:1.2.0
com.squareup.okhttp3:logging-interceptor:4.12.0
com.squareup.okhttp3:okhttp:4.12.0
com.squareup.okio:okio-jvm:3.9.1
Expand Down
8 changes: 8 additions & 0 deletions androidApp/dependencies/prodDebugRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ 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.zxing:core:3.5.3
com.russhwolf:multiplatform-settings-android-debug:1.2.0
com.russhwolf:multiplatform-settings-coroutines-android-debug:1.2.0
com.russhwolf:multiplatform-settings-coroutines:1.2.0
com.russhwolf:multiplatform-settings-no-arg-android-debug:1.2.0
com.russhwolf:multiplatform-settings-no-arg:1.2.0
com.russhwolf:multiplatform-settings-serialization-android-debug:1.2.0
com.russhwolf:multiplatform-settings-serialization:1.2.0
com.russhwolf:multiplatform-settings:1.2.0
com.squareup.okhttp3:logging-interceptor:4.12.0
com.squareup.okhttp3:okhttp:4.12.0
com.squareup.okio:okio-jvm:3.9.1
Expand Down
8 changes: 8 additions & 0 deletions androidApp/dependencies/prodReleaseRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@ 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.zxing:core:3.5.3
com.russhwolf:multiplatform-settings-android:1.2.0
com.russhwolf:multiplatform-settings-coroutines-android:1.2.0
com.russhwolf:multiplatform-settings-coroutines:1.2.0
com.russhwolf:multiplatform-settings-no-arg-android:1.2.0
com.russhwolf:multiplatform-settings-no-arg:1.2.0
com.russhwolf:multiplatform-settings-serialization-android:1.2.0
com.russhwolf:multiplatform-settings-serialization:1.2.0
com.russhwolf:multiplatform-settings:1.2.0
com.squareup.okhttp3:logging-interceptor:4.12.0
com.squareup.okhttp3:okhttp:4.12.0
com.squareup.okio:okio-jvm:3.9.1
Expand Down
24 changes: 16 additions & 8 deletions core/datastore/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
*
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/

plugins {
alias(libs.plugins.mifos.android.library)
alias(libs.plugins.mifos.android.hilt)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.mifos.kmp.library)
id("kotlinx-serialization")
}

android {
Expand All @@ -22,9 +22,17 @@ android {

}

dependencies {
implementation(projects.core.common)
implementation(projects.core.model)
kotlin{

sourceSets{
commonMain.dependencies {
implementation(libs.multiplatform.settings)
implementation(libs.multiplatform.settings.serialization)
implementation(libs.multiplatform.settings.coroutines)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.core)
implementation(projects.core.common)
}
}
}


}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Copyright 2025 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
*/
@file:OptIn(ExperimentalSerializationApi::class, ExperimentalSettingsApi::class)

package org.mifos.mobile.core.datastore

import com.russhwolf.settings.ExperimentalSettingsApi
import com.russhwolf.settings.Settings
import com.russhwolf.settings.serialization.decodeValue
import com.russhwolf.settings.serialization.decodeValueOrNull
import com.russhwolf.settings.serialization.encodeValue
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import org.mifos.mobile.core.datastore.model.AppSettings
import org.mifos.mobile.core.datastore.model.AppTheme
import org.mifos.mobile.core.datastore.model.UserData

private const val USER_DATA = "userData"
private const val APP_SETTINGS = "appSettings"

class UserPreferencesDataSource(
private val settings: Settings,
private val dispatcher: CoroutineDispatcher,
) {

private val _userInfo = MutableStateFlow(
settings.decodeValue(
key = USER_DATA,
serializer = UserData.serializer(),
defaultValue = settings.decodeValueOrNull(
key = USER_DATA,
serializer = UserData.serializer(),
) ?: UserData.DEFAULT,
),
)

private val _settingsInfo = MutableStateFlow(
settings.decodeValue(
key = APP_SETTINGS,
serializer = AppSettings.serializer(),
defaultValue = settings.decodeValueOrNull(
key = APP_SETTINGS,
serializer = AppSettings.serializer(),
) ?: AppSettings.DEFAULT,
),
)

val token = _userInfo.map {
it.base64EncodedAuthenticationKey
}

val userInfo = _userInfo

val settingsInfo = _settingsInfo

val clientId = _userInfo.map { it.clientId }

val appTheme = _settingsInfo.map { it.appTheme }

suspend fun updateSettingsInfo(appSettings: AppSettings) {
withContext(dispatcher) {
settings.putSettingsPreference(appSettings)
_settingsInfo.value = appSettings
}
}

suspend fun updateUserInfo(user: UserData) {
withContext(dispatcher) {
settings.putUserPreference(user)
_userInfo.value = user
}
}

suspend fun updateToken(token: String) {
withContext(dispatcher) {
settings.putUserPreference(
UserData.DEFAULT.copy(
base64EncodedAuthenticationKey = token,
),
)
_userInfo.value = UserData.DEFAULT.copy(
base64EncodedAuthenticationKey = token,
)
}
}

suspend fun updateTheme(theme: AppTheme) {
withContext(dispatcher) {
settings.putSettingsPreference(
AppSettings.DEFAULT.copy(
appTheme = theme,
),
)
_settingsInfo.value = AppSettings.DEFAULT.copy(
appTheme = theme,
)
}
}

fun updateProfileImage(image: String) {
settings.putString(PROFILE_IMAGE, image)
}

fun getProfileImage(): String? {
return settings.getString(PROFILE_IMAGE, "").ifEmpty { null }
}

suspend fun clearInfo() {
withContext(dispatcher) {
settings.clear()
}
}

companion object {
private const val PROFILE_IMAGE = "preferences_profile_image"
}
}

@OptIn(ExperimentalSerializationApi::class)
private fun Settings.putUserPreference(user: UserData) {
encodeValue(
key = USER_DATA,
serializer = UserData.serializer(),
value = user,
)
}

private fun Settings.putSettingsPreference(settings: AppSettings) {
encodeValue(
key = APP_SETTINGS,
serializer = AppSettings.serializer(),
value = settings,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2025 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.datastore

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import org.mifos.mobile.core.datastore.model.AppSettings
import org.mifos.mobile.core.datastore.model.AppTheme
import org.mifos.mobile.core.datastore.model.UserData
import org.mifospay.core.common.DataState

interface UserPreferencesRepository {
val userInfo: Flow<UserData>

val settingsInfo: Flow<AppSettings>

val token: StateFlow<String?>

val clientId: StateFlow<Long?>

val appTheme: StateFlow<AppTheme?>

val profileImage: String?

suspend fun updateToken(token: String): DataState<Unit>

suspend fun updateTheme(theme: AppTheme): DataState<Unit>

suspend fun updateUser(user: UserData): DataState<Unit>

suspend fun updateSettings(appSettings: AppSettings): DataState<Unit>

suspend fun updateProfileImage(image: String): DataState<Unit>

suspend fun logOut(): Unit
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2025 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.datastore

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import org.mifos.mobile.core.datastore.model.AppSettings
import org.mifos.mobile.core.datastore.model.AppTheme
import org.mifos.mobile.core.datastore.model.UserData
import org.mifospay.core.common.DataState

class UserPreferencesRepositoryImpl(
private val preferenceManager: UserPreferencesDataSource,
private val ioDispatcher: CoroutineDispatcher,
unconfinedDispatcher: CoroutineDispatcher,
) : UserPreferencesRepository {
private val unconfinedScope = CoroutineScope(unconfinedDispatcher)

override val userInfo: Flow<UserData>
get() = preferenceManager.userInfo

override val settingsInfo: Flow<AppSettings>
get() = preferenceManager.settingsInfo

override val appTheme: StateFlow<AppTheme?>
get() = preferenceManager.appTheme.stateIn(
scope = unconfinedScope,
initialValue = null,
started = SharingStarted.Eagerly,
)
override val token: StateFlow<String?>
get() = preferenceManager.token.stateIn(
scope = unconfinedScope,
initialValue = null,
started = SharingStarted.Eagerly,
)

override val clientId: StateFlow<Long?>
get() = preferenceManager.clientId.stateIn(
scope = unconfinedScope,
initialValue = null,
started = SharingStarted.Eagerly,
)

override val profileImage: String?
get() = preferenceManager.getProfileImage()

override suspend fun updateToken(token: String): DataState<Unit> {
return try {
val result = preferenceManager.updateToken(token)
DataState.Success(result)
} catch (e: Exception) {
DataState.Error(e)
}
}

override suspend fun updateTheme(theme: AppTheme): DataState<Unit> {
return try {
val result = preferenceManager.updateTheme(theme)
DataState.Success(result)
} catch (e: Exception) {
DataState.Error(e)
}
}

override suspend fun updateUser(user: UserData): DataState<Unit> {
return try {
val result = preferenceManager.updateUserInfo(user)
DataState.Success(result)
} catch (e: Exception) {
DataState.Error(e)
}
}

override suspend fun updateSettings(appSettings: AppSettings): DataState<Unit> {
return try {
val result = preferenceManager.updateSettingsInfo(appSettings)
DataState.Success(result)
} catch (e: Exception) {
DataState.Error(e)
}
}

override suspend fun updateProfileImage(image: String): DataState<Unit> {
return try {
val result = preferenceManager.updateProfileImage(image)
DataState.Success(result)
} catch (e: Exception) {
DataState.Error(e)
}
}

override suspend fun logOut() {
preferenceManager.clearInfo()
}
}
Loading

0 comments on commit 7520dd3

Please sign in to comment.