Skip to content

Commit 2f9e3f5

Browse files
committed
Refactor Koin initialization and update UI components
This commit refactors the Koin initialization process for both Android and iOS platforms. It also includes several UI updates: - Uses `rememberSaveable` for `generateJson` state in `HomeScreen`. - Updates `Switch` and `ElevatedCard` colors to align with the MaterialTheme. - Modifies `GenerativeModelViewModel` to use `update` for `MutableStateFlow`. - Centralizes model names into a `Constant.kt` file. - Updates `.gitignore` to include more common ignore patterns.
1 parent d8c5bd8 commit 2f9e3f5

File tree

17 files changed

+167
-71
lines changed

17 files changed

+167
-71
lines changed

.gitignore

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,17 @@
33
**/build/
44
.kotlin
55
.gradle
6-
**/.idea/*
7-
6+
xcuserdata
7+
!src/**/build/
8+
local.properties
9+
.idea
10+
.DS_Store
11+
captures
12+
.externalNativeBuild
13+
.cxx
14+
*.xcodeproj/*
15+
!*.xcodeproj/project.pbxproj
16+
!*.xcodeproj/xcshareddata/
17+
!*.xcodeproj/project.xcworkspace/
18+
!*.xcworkspace/contents.xcworkspacedata
19+
**/xcshareddata/WorkspaceSettings.xcsettings

composeApp/src/androidMain/kotlin/dev/johnoreilly/vertexai/FirebaaseAILogicKMPApp.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@ package dev.johnoreilly.vertexai
22

33
import android.app.Application
44
import com.google.firebase.FirebaseApp
5-
import dev.johnoreilly.vertexai.di.initialiseKoin
5+
import dev.johnoreilly.vertexai.di.initKoin
6+
import org.koin.android.ext.koin.androidContext
7+
import org.koin.dsl.module
68

79

810
class FirebaaseAILogicKMPApp : Application() {
911
override fun onCreate() {
1012
super.onCreate()
1113
FirebaseApp.initializeApp(this)
1214

13-
initialiseKoin()
15+
initKoin(
16+
appDeclaration = {
17+
androidContext(this@FirebaaseAILogicKMPApp)
18+
},
19+
platformModule = module { single<GenerativeModel> { GenerativeModelAndroid() } }
20+
)
1421
}
1522
}

composeApp/src/androidMain/kotlin/dev/johnoreilly/vertexai/GenerativeModel.android.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import com.google.firebase.ai.type.Schema
66
import com.google.firebase.ai.type.generationConfig
77
import com.google.firebase.ai.ai
88
import com.google.firebase.ai.type.GenerativeBackend
9+
import dev.johnoreilly.vertexai.util.GEMINI_MODEL
10+
import dev.johnoreilly.vertexai.util.IMAGE_MODEL
911

1012

1113
class GenerativeModelAndroid : GenerativeModel {
@@ -20,15 +22,15 @@ class GenerativeModelAndroid : GenerativeModel {
2022

2123
override suspend fun generateTextContent(prompt: String): String? {
2224
val generativeModel = Firebase.ai(backend = GenerativeBackend.googleAI()).generativeModel(
23-
modelName = "gemini-2.5-flash"
25+
modelName = GEMINI_MODEL
2426
)
2527

2628
return generativeModel.generateContent(prompt).text
2729
}
2830

2931
override suspend fun generateJsonContent(prompt: String): String? {
3032
val generativeModel = Firebase.ai.generativeModel(
31-
modelName = "gemini-2.5-flash",
33+
modelName = GEMINI_MODEL,
3234
generationConfig = generationConfig {
3335
responseMimeType = "application/json"
3436
responseSchema = jsonSchema
@@ -41,7 +43,7 @@ class GenerativeModelAndroid : GenerativeModel {
4143
@OptIn(PublicPreviewAPI::class)
4244
override suspend fun generateImage(prompt: String): ByteArray? {
4345
val imageModel = Firebase.ai.imagenModel(
44-
modelName = "imagen-4.0-fast-generate-001"
46+
modelName = IMAGE_MODEL
4547
)
4648
val imageResponse = imageModel.generateImages(prompt)
4749
return if (imageResponse.images.isNotEmpty()) {

composeApp/src/androidMain/kotlin/dev/johnoreilly/vertexai/di/Koin.android.kt

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
package dev.johnoreilly.vertexai.di
22

33
import dev.johnoreilly.vertexai.ui.GenerativeModelViewModel
4-
import org.koin.core.module.dsl.factoryOf
4+
import org.koin.core.context.startKoin
5+
import org.koin.core.module.Module
6+
import org.koin.core.module.dsl.viewModelOf
7+
import org.koin.dsl.KoinAppDeclaration
58
import org.koin.dsl.module
69

710

811
val commonModule = module {
9-
factoryOf(::GenerativeModelViewModel)
12+
viewModelOf(::GenerativeModelViewModel)
1013
}
1114

15+
fun initKoin(appDeclaration: KoinAppDeclaration = {}, platformModule: Module = module {}) =
16+
startKoin {
17+
println("🔥 Initializing Koin")
18+
appDeclaration()
19+
modules(
20+
platformModule,
21+
commonModule,
22+
)
23+
println("🔥 Koin initialized")
24+
}

composeApp/src/commonMain/kotlin/dev/johnoreilly/vertexai/ui/GenerativeModelViewModel.kt

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
44
import androidx.lifecycle.viewModelScope
55
import dev.johnoreilly.vertexai.GenerativeModel
66
import kotlinx.coroutines.flow.MutableStateFlow
7+
import kotlinx.coroutines.flow.update
78
import kotlinx.coroutines.launch
89
import kotlinx.serialization.Serializable
910
import kotlinx.serialization.json.Json
@@ -32,35 +33,51 @@ class GenerativeModelViewModel(private val generativeModel: GenerativeModel) : V
3233
val uiState = MutableStateFlow<GenerativeModelUIState>(GenerativeModelUIState.Initial)
3334

3435
fun generateContent(prompt: String, generateJson: Boolean) {
35-
uiState.value = GenerativeModelUIState.Loading
36+
uiState.update {
37+
GenerativeModelUIState.Loading
38+
}
3639
viewModelScope.launch {
3740
try {
38-
uiState.value = if (generateJson) {
41+
if (generateJson) {
3942
val response = generativeModel.generateJsonContent(prompt)
4043
if (response != null) {
4144
val entities = Json.decodeFromString<List<Entity>>(response)
42-
GenerativeModelUIState.Success(entityContent = entities)
45+
uiState.update { GenerativeModelUIState.Success(entityContent = entities) }
4346
} else {
44-
GenerativeModelUIState.Error("Error generating content")
47+
uiState.update {
48+
GenerativeModelUIState.Error("Error generating content")
49+
}
4550
}
4651
} else {
4752
val response = generativeModel.generateTextContent(prompt)
48-
GenerativeModelUIState.Success(textContent = response)
53+
uiState.update { GenerativeModelUIState.Success(textContent = response) }
4954
}
5055
} catch (e: Exception) {
51-
GenerativeModelUIState.Error(e.message ?: "Error generating content")
56+
uiState.update {
57+
GenerativeModelUIState.Error(
58+
e.message ?: "Error generating content"
59+
)
60+
}
5261
}
5362
}
5463
}
5564

5665
fun generateImage(prompt: String) {
57-
uiState.value = GenerativeModelUIState.Loading
66+
uiState.update {
67+
GenerativeModelUIState.Loading
68+
}
5869
viewModelScope.launch {
59-
uiState.value = try {
70+
try {
6071
val imageData = generativeModel.generateImage(prompt)
61-
GenerativeModelUIState.Success(imageData = imageData)
72+
uiState.update {
73+
GenerativeModelUIState.Success(imageData = imageData)
74+
}
6275
} catch (e: Exception) {
63-
GenerativeModelUIState.Error(e.message ?: "Error generating content")
76+
uiState.update {
77+
GenerativeModelUIState.Error(
78+
e.message ?: "Error generating content"
79+
)
80+
}
6481
}
6582
}
6683
}

composeApp/src/commonMain/kotlin/dev/johnoreilly/vertexai/ui/HomeScreen.kt

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import androidx.compose.material3.ListItem
2323
import androidx.compose.material3.MaterialTheme
2424
import androidx.compose.material3.OutlinedTextField
2525
import androidx.compose.material3.Switch
26+
import androidx.compose.material3.SwitchDefaults
2627
import androidx.compose.material3.Text
2728
import androidx.compose.runtime.Composable
2829
import androidx.compose.runtime.LaunchedEffect
@@ -59,7 +60,7 @@ fun HomeScreen() {
5960
mutableStateOf(TextFieldValue())
6061
}
6162

62-
val generateJson = remember { mutableStateOf(false) }
63+
var generateJson by rememberSaveable { mutableStateOf(false) }
6364

6465
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
6566
val focusRequester = remember { FocusRequester() }
@@ -76,7 +77,7 @@ fun HomeScreen() {
7677
color = MaterialTheme.colorScheme.primary,
7778
modifier = Modifier.padding(bottom = 4.dp)
7879
)
79-
80+
8081
OutlinedTextField(
8182
value = prompt,
8283
onValueChange = { prompt = it },
@@ -87,7 +88,7 @@ fun HomeScreen() {
8788
.height(120.dp),
8889
shape = RoundedCornerShape(12.dp)
8990
)
90-
91+
9192
Spacer(modifier = Modifier.height(12.dp))
9293

9394
Row(
@@ -100,7 +101,16 @@ fun HomeScreen() {
100101
fontWeight = FontWeight.Medium
101102
)
102103
Spacer(modifier = Modifier.width(8.dp))
103-
Switch(checked = generateJson.value, onCheckedChange = { generateJson.value = it })
104+
Switch(checked = generateJson, onCheckedChange = { generateJson = it },
105+
colors = SwitchDefaults.colors(
106+
checkedThumbColor = MaterialTheme.colorScheme.primary,
107+
checkedTrackColor = MaterialTheme.colorScheme.primaryContainer,
108+
uncheckedThumbColor = MaterialTheme.colorScheme.primary,
109+
uncheckedTrackColor = MaterialTheme.colorScheme.primaryContainer,
110+
checkedBorderColor = MaterialTheme.colorScheme.primary,
111+
uncheckedBorderColor = MaterialTheme.colorScheme.primary
112+
)
113+
)
104114
}
105115

106116
Row(
@@ -110,7 +120,7 @@ fun HomeScreen() {
110120
onClick = {
111121
if (prompt.text.isNotBlank()) {
112122
keyboardController?.hide()
113-
viewModel.generateContent(prompt.text, generateJson = generateJson.value)
123+
viewModel.generateContent(prompt.text, generateJson = generateJson)
114124
}
115125
},
116126
modifier = Modifier.weight(1f),
@@ -120,7 +130,7 @@ fun HomeScreen() {
120130
}
121131

122132
Spacer(Modifier.width(16.dp))
123-
133+
124134
Button(
125135
onClick = {
126136
if (prompt.text.isNotBlank()) {
@@ -134,7 +144,7 @@ fun HomeScreen() {
134144
Text(stringResource(Res.string.generate_image))
135145
}
136146
}
137-
147+
138148
Spacer(modifier = Modifier.height(16.dp))
139149

140150
ResponseView(uiState, prompt.text)
@@ -161,7 +171,10 @@ fun ResponseView(uiState: GenerativeModelUIState, prompt: String) {
161171
ElevatedCard(
162172
modifier = Modifier.fillMaxWidth(),
163173
shape = RoundedCornerShape(12.dp),
164-
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 4.dp)
174+
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 4.dp),
175+
colors = CardDefaults.elevatedCardColors(
176+
containerColor = MaterialTheme.colorScheme.surfaceVariant
177+
)
165178
) {
166179
Column(modifier = Modifier.padding(16.dp)) {
167180
Text(
@@ -171,7 +184,7 @@ fun ResponseView(uiState: GenerativeModelUIState, prompt: String) {
171184
fontWeight = FontWeight.Bold,
172185
modifier = Modifier.padding(bottom = 8.dp)
173186
)
174-
187+
175188
if (uiState.entityContent != null) {
176189
LazyColumn {
177190
items(uiState.entityContent) { item ->
@@ -182,17 +195,17 @@ fun ResponseView(uiState: GenerativeModelUIState, prompt: String) {
182195
shape = RoundedCornerShape(8.dp)
183196
) {
184197
ListItem(
185-
headlineContent = {
198+
headlineContent = {
186199
Text(
187200
text = item.name,
188201
fontWeight = FontWeight.Medium
189-
)
202+
)
190203
},
191-
supportingContent = {
204+
supportingContent = {
192205
Text(
193206
text = item.country,
194207
style = MaterialTheme.typography.bodyMedium
195-
)
208+
)
196209
}
197210
)
198211
}

composeApp/src/commonMain/kotlin/dev/johnoreilly/vertexai/ui/theme/FirebaseTheme.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dev.johnoreilly.vertexai.ui.theme
22

3-
import androidx.compose.material3.ColorScheme
43
import androidx.compose.material3.darkColorScheme
54
import androidx.compose.material3.lightColorScheme
65
import androidx.compose.ui.graphics.Color
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package dev.johnoreilly.vertexai.util
2+
3+
const val GEMINI_MODEL = "gemini-2.5-flash"
4+
const val IMAGE_MODEL = "imagen-4.0-fast-generate-001"
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
package dev.johnoreilly.vertexai
22

33
import androidx.compose.ui.window.ComposeUIViewController
4+
import dev.johnoreilly.vertexai.di.initKoin
45
import dev.johnoreilly.vertexai.ui.App
6+
import org.koin.dsl.module
57

6-
fun MainViewController() = ComposeUIViewController() {
8+
fun MainViewController(
9+
generativeModel: GenerativeModel,
10+
) = ComposeUIViewController(
11+
configure = {
12+
initKoin(
13+
platformModule = module { single<GenerativeModel> { generativeModel } }
14+
)
15+
}
16+
) {
717
App()
818
}

0 commit comments

Comments
 (0)