Skip to content

Commit a1b91e5

Browse files
MIFOSAC-577 Client Profile Documents (#2501)
1 parent b6b5dde commit a1b91e5

File tree

40 files changed

+3250
-30
lines changed

40 files changed

+3250
-30
lines changed

cmp-android/src/main/AndroidManifest.xml

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,7 @@
6868
android:name="com.google.mlkit.vision.DEPENDENCIES"
6969
android:value="barcode" />
7070

71-
<provider
72-
android:name="androidx.core.content.FileProvider"
73-
android:authorities="${applicationId}.provider"
74-
android:exported="false"
75-
android:grantUriPermissions="true">
76-
<meta-data
77-
android:name="android.support.FILE_PROVIDER_PATHS"
78-
android:resource="@xml/fileproviderpath" />
79-
</provider>
80-
81-
<!-- Disable Firebase analytics by default. This setting is overwritten for the `prod` flavor -->
71+
Doc <!-- Disable Firebase analytics by default. This setting is overwritten for the `prod` flavor -->
8272
<meta-data
8373
android:name="firebase_analytics_collection_deactivated"
8474
android:value="true" />

cmp-navigation/src/commonMain/kotlin/cmp/navigation/authenticated/AuthenticatedNavbarNavigationScreen.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ import cmp.navigation.components.ScaffoldNavigationData
6868
import cmp.navigation.navigation.HomeDestinationsScreen
6969
import cmp.navigation.ui.rememberMifosNavController
7070
import com.mifos.core.common.utils.Constants
71-
import com.mifos.core.designsystem.component.MifosScaffold
7271
import com.mifos.core.designsystem.icon.MifosIcons
7372
import com.mifos.core.designsystem.theme.DesignToken
7473
import com.mifos.core.ui.RootTransitionProviders

core/common/build.gradle.kts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ kotlin {
5454
api(libs.squareup.okio)
5555
api(libs.jb.kotlin.stdlib)
5656
api(libs.kotlinx.datetime)
57-
57+
implementation(libs.filekit.core)
58+
implementation(libs.filekit.coil)
59+
implementation(libs.filekit.compose)
60+
implementation(libs.filekit.dialog.compose)
5861
implementation(libs.ktor.client.core)
5962
}
6063

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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 com.mifos.core.common.utils
11+
12+
import io.github.vinceglb.filekit.FileKit
13+
import io.github.vinceglb.filekit.dialogs.FileKitCameraType
14+
import io.github.vinceglb.filekit.dialogs.openCameraPicker
15+
import kotlinx.coroutines.flow.flow
16+
17+
actual fun takePhotoIfSupported() = flow {
18+
val result = FileKit.openCameraPicker(
19+
FileKitCameraType.Photo,
20+
)
21+
emit(result)
22+
}.asDataStateFlow()
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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 com.mifos.core.common.utils
11+
12+
import io.github.vinceglb.filekit.FileKit
13+
import io.github.vinceglb.filekit.PlatformFile
14+
import io.github.vinceglb.filekit.cacheDir
15+
import io.github.vinceglb.filekit.databasesDir
16+
import io.github.vinceglb.filekit.delete
17+
import io.github.vinceglb.filekit.dialogs.FileKitMode
18+
import io.github.vinceglb.filekit.dialogs.FileKitType
19+
import io.github.vinceglb.filekit.dialogs.openDirectoryPicker
20+
import io.github.vinceglb.filekit.dialogs.openFilePicker
21+
import io.github.vinceglb.filekit.div
22+
import io.github.vinceglb.filekit.filesDir
23+
import io.github.vinceglb.filekit.write
24+
import kotlinx.coroutines.flow.Flow
25+
import kotlinx.coroutines.flow.flow
26+
27+
/**
28+
* Do not pass the path or absolute path you get by using any picker,
29+
* to PlatformFile class to create an instance of PlatformFile.
30+
* It won't work.
31+
* Use PlatformFile object returned by pickers directly.
32+
*/
33+
object FileKitUtil {
34+
35+
val appCache = FileKit.cacheDir
36+
val appPrivateInternalStorage = FileKit.filesDir
37+
val appInternalStorage = FileKit.databasesDir
38+
39+
fun pickFile(
40+
dialogTitle: String = "",
41+
extensions: Set<String> = setOf("pdf", "jpeg", "jpg", "png"),
42+
) = flow {
43+
val file = FileKit.openFilePicker(
44+
type = FileKitType.File(extensions),
45+
mode = FileKitMode.Single,
46+
title = dialogTitle,
47+
)
48+
emit(file)
49+
}.asDataStateFlow()
50+
51+
fun pickImage(
52+
dialogTitle: String = "",
53+
) = flow {
54+
val image = FileKit.openFilePicker(
55+
type = FileKitType.Image,
56+
mode = FileKitMode.Single,
57+
title = dialogTitle,
58+
)
59+
emit(image)
60+
}.asDataStateFlow()
61+
62+
suspend fun pickDirectory(): PlatformFile? {
63+
return FileKit.openDirectoryPicker()
64+
}
65+
66+
/**
67+
* Android
68+
* filesDir: Maps to context.filesDir, which is the app’s private internal storage
69+
* cacheDir: Maps to context.cacheDir, which is the app’s private cache directory
70+
* databasesDir: Maps to a databases subdirectory in the app’s internal storage
71+
*
72+
* iOS
73+
* filesDir: Maps to the app’s Documents directory, which is backed up with iCloud
74+
* cacheDir: Maps to the app’s Caches directory, which isn’t backed up and may be cleared by the system
75+
* databasesDir: Maps to a databases subdirectory in the app’s Documents directory
76+
*
77+
* macOS
78+
* filesDir: Maps to ~/Library/Application Support/<app-id>/, requiring FileKit initialization with an app ID
79+
* cacheDir: Maps to ~/Library/Caches/<app-id>/
80+
* databasesDir: Maps to a databases subdirectory in the application support directory
81+
*
82+
* JVM (Desktop)
83+
* filesDir: Maps to platform-specific app data locations:
84+
* Linux: ~/.local/share/<app-id>/
85+
* macOS: ~/Library/Application Support/<app-id>/
86+
* Windows: %APPDATA%/<app-id>/
87+
*
88+
* cacheDir: Maps to platform-specific cache locations:
89+
* Linux: ~/.cache/<app-id>/
90+
* macOS: ~/Library/Caches/<app-id>/
91+
* Windows: %LOCALAPPDATA%/<app-id>/Cache/
92+
*
93+
* databasesDir: Maps to a databases subdirectory within filesDir
94+
*/
95+
96+
fun writeFileToCache(
97+
fileName: String,
98+
fileExtension: String,
99+
filesByteArray: ByteArray,
100+
) = flow {
101+
val filePath = appCache / "$fileName.$fileExtension"
102+
filePath.write(filesByteArray)
103+
emit(filePath)
104+
}.asDataStateFlow()
105+
106+
fun writeFileToApplicationPrivateInternalStorage(
107+
fileName: String,
108+
fileExtension: String,
109+
filesByteArray: ByteArray,
110+
) = flow {
111+
val privateInternalStorage = appPrivateInternalStorage / "$fileName.$fileExtension"
112+
privateInternalStorage.write(filesByteArray)
113+
emit(privateInternalStorage)
114+
}.asDataStateFlow()
115+
116+
// Use only if you are using a database service such as room or sqldelight
117+
fun writeFileToApplicationInternalStorage(
118+
fileName: String,
119+
fileExtension: String,
120+
filesByteArray: ByteArray,
121+
) = flow {
122+
val internalStorage = appInternalStorage / "$fileName.$fileExtension"
123+
internalStorage.write(filesByteArray)
124+
emit(internalStorage)
125+
}.asDataStateFlow()
126+
127+
fun writeToSelectedDirectory(
128+
filesByteArray: ByteArray,
129+
platformFile: PlatformFile,
130+
) = flow {
131+
emit(platformFile.write(filesByteArray))
132+
}.asDataStateFlow()
133+
134+
suspend fun deleteFile(
135+
file: PlatformFile,
136+
) {
137+
file.delete(false)
138+
}
139+
140+
fun takePhoto() = takePhotoIfSupported()
141+
}
142+
143+
expect fun takePhotoIfSupported(): Flow<DataState<PlatformFile?>>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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 com.mifos.core.common.utils
11+
12+
import io.github.vinceglb.filekit.PlatformFile
13+
import kotlinx.coroutines.flow.Flow
14+
import kotlinx.coroutines.flow.flow
15+
16+
actual fun takePhotoIfSupported(): Flow<DataState<PlatformFile?>> = flow {
17+
emit(DataState.Error(IllegalStateException("Platform not supported")))
18+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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 com.mifos.core.common.utils
11+
12+
import io.github.vinceglb.filekit.FileKit
13+
import io.github.vinceglb.filekit.dialogs.FileKitCameraType
14+
import io.github.vinceglb.filekit.dialogs.openCameraPicker
15+
import kotlinx.coroutines.flow.flow
16+
17+
actual fun takePhotoIfSupported() = flow {
18+
val result = FileKit.openCameraPicker(
19+
FileKitCameraType.Photo,
20+
)
21+
emit(result)
22+
}.asDataStateFlow()

core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosBottomSheet.kt

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package com.mifos.core.designsystem.component
1111

1212
import androidx.compose.animation.AnimatedVisibility
1313
import androidx.compose.foundation.clickable
14+
import androidx.compose.foundation.layout.Arrangement
1415
import androidx.compose.foundation.layout.Box
1516
import androidx.compose.foundation.layout.Column
1617
import androidx.compose.foundation.layout.Spacer
@@ -35,6 +36,7 @@ import androidx.compose.ui.Alignment
3536
import androidx.compose.ui.Modifier
3637
import androidx.compose.ui.graphics.Color
3738
import androidx.compose.ui.graphics.vector.ImageVector
39+
import androidx.compose.ui.unit.Dp
3840
import androidx.compose.ui.unit.dp
3941
import com.arkivanov.essenty.backhandler.BackCallback
4042
import com.mifos.core.designsystem.theme.DesignToken
@@ -119,6 +121,79 @@ fun MifosBottomSheetOptionItem(
119121
}
120122
}
121123

124+
@Composable
125+
fun MifosBottomSheetOptionItem(
126+
label: String,
127+
icon: ImageVector,
128+
elevation: Dp = DesignToken.elevation.elevation,
129+
onClick: () -> Unit,
130+
) {
131+
Column(
132+
modifier = Modifier
133+
.padding(DesignToken.padding.large)
134+
.clickable { onClick() },
135+
horizontalAlignment = Alignment.CenterHorizontally,
136+
verticalArrangement = Arrangement.Center,
137+
) {
138+
MifosCard(
139+
elevation = elevation,
140+
) {
141+
Icon(
142+
imageVector = icon,
143+
contentDescription = null,
144+
modifier = Modifier
145+
.padding(DesignToken.padding.medium)
146+
.size(DesignToken.sizes.iconAverage),
147+
tint = MaterialTheme.colorScheme.primary,
148+
)
149+
}
150+
Spacer(Modifier.height(DesignToken.padding.small))
151+
Text(
152+
text = label,
153+
style = MifosTypography.labelMedium,
154+
)
155+
}
156+
}
157+
158+
@Composable
159+
fun MifosBottomSheetOptionItem(
160+
label: String,
161+
icon: ImageVector,
162+
onClick: () -> Unit,
163+
) {
164+
Column(
165+
modifier = Modifier
166+
.padding(DesignToken.padding.large)
167+
.clickable { onClick() },
168+
horizontalAlignment = Alignment.CenterHorizontally,
169+
verticalArrangement = Arrangement.Center,
170+
) {
171+
MifosCard(
172+
elevation = DesignToken.padding.extraExtraSmall,
173+
modifier = Modifier
174+
.clickable(onClick = onClick),
175+
shape = DesignToken.shapes.small,
176+
colors = CardDefaults.cardColors(
177+
containerColor = MaterialTheme.colorScheme.onPrimary,
178+
contentColor = MaterialTheme.colorScheme.primary,
179+
),
180+
) {
181+
Icon(
182+
imageVector = icon,
183+
contentDescription = null,
184+
modifier = Modifier
185+
.padding(DesignToken.padding.medium)
186+
.size(DesignToken.sizes.iconAverage),
187+
)
188+
}
189+
Spacer(Modifier.height(DesignToken.padding.small))
190+
Text(
191+
text = label,
192+
style = MifosTypography.labelMedium,
193+
)
194+
}
195+
}
196+
122197
@Preview
123198
@Composable
124199
private fun MifosBottomSheetPreview() {

core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosCard.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010
package com.mifos.core.designsystem.component
1111

12+
import androidx.compose.foundation.BorderStroke
1213
import androidx.compose.foundation.clickable
1314
import androidx.compose.foundation.layout.Column
1415
import androidx.compose.foundation.layout.ColumnScope
@@ -20,6 +21,7 @@ import androidx.compose.material3.MaterialTheme
2021
import androidx.compose.material3.Text
2122
import androidx.compose.runtime.Composable
2223
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.graphics.Color
2325
import androidx.compose.ui.graphics.Shape
2426
import androidx.compose.ui.unit.Dp
2527
import androidx.compose.ui.unit.dp
@@ -31,6 +33,7 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
3133
fun MifosCard(
3234
modifier: Modifier = Modifier,
3335
shape: Shape = DesignToken.shapes.small,
36+
borderStroke: BorderStroke = BorderStroke(0.dp, Color.Transparent),
3437
elevation: Dp = 1.dp,
3538
onClick: (() -> Unit)? = null,
3639
colors: CardColors = CardDefaults.cardColors(
@@ -45,6 +48,7 @@ fun MifosCard(
4548
elevation = CardDefaults.cardElevation(
4649
defaultElevation = elevation,
4750
),
51+
border = borderStroke,
4852
colors = colors,
4953
content = content,
5054
)
@@ -56,7 +60,7 @@ private fun MifosCardPreview() {
5660
MifosTheme {
5761
MifosCard {
5862
Column(
59-
modifier = Modifier.padding(16.dp),
63+
modifier = Modifier.padding(DesignToken.padding.large),
6064
) {
6165
Text("Simple Mifos Card")
6266
Text("This is the card content.")

0 commit comments

Comments
 (0)