Skip to content

Commit 3eb3c43

Browse files
Merge pull request #4 from Realtime-Coding/feature/ai-gemini
Feature/ai gemini
2 parents cb0e451 + b2cba1d commit 3eb3c43

File tree

25 files changed

+689
-127
lines changed

25 files changed

+689
-127
lines changed

composeApp/build.gradle.kts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
44
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
55
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
66
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
7+
import com.codingfeline.buildkonfig.compiler.FieldSpec
8+
import java.util.Properties
79

810
plugins {
911
alias(libs.plugins.kotlinMultiplatform)
1012
alias(libs.plugins.androidApplication)
1113
alias(libs.plugins.jetbrainsCompose)
1214
alias(libs.plugins.compose.compiler)
15+
alias(libs.plugins.buildkonfig)
1316
}
1417

1518
kotlin {
@@ -29,11 +32,12 @@ kotlin {
2932
}
3033
binaries.executable()
3134
}
32-
35+
3336
androidTarget {
34-
@OptIn(ExperimentalKotlinGradlePluginApi::class)
35-
compilerOptions {
36-
jvmTarget.set(JvmTarget.JVM_17)
37+
compilations.all {
38+
kotlinOptions {
39+
jvmTarget = "17"
40+
}
3741
}
3842
}
3943

@@ -67,7 +71,12 @@ kotlin {
6771
implementation(libs.coil.network.ktor)
6872
implementation(libs.voyager.navigator)
6973
implementation(libs.voyager.tab.navigator)
74+
implementation(libs.voyager.screenmodel)
7075
implementation(libs.lifecycle.viewmodel.compose)
76+
implementation(libs.markdown.renderer)
77+
api(libs.compose.window.size)
78+
api(libs.generativeai)
79+
implementation(libs.filekit.compose)
7180
}
7281
desktopMain.dependencies {
7382
implementation(compose.desktop.currentOs)
@@ -147,3 +156,25 @@ compose.desktop {
147156
}
148157
}
149158
}
159+
160+
buildkonfig {
161+
packageName = "com.travel.buddy"
162+
163+
val localPropsFile = rootProject.file("local.properties")
164+
val localProperties = Properties()
165+
if (localPropsFile.exists()) {
166+
runCatching {
167+
localProperties.load(localPropsFile.inputStream())
168+
}.getOrElse {
169+
it.printStackTrace()
170+
}
171+
}
172+
defaultConfigs {
173+
buildConfigField(
174+
FieldSpec.Type.STRING,
175+
"GEMINI_API_KEY",
176+
localProperties["gemini_api_key"]?.toString() ?: ""
177+
)
178+
}
179+
180+
}

composeApp/src/androidMain/kotlin/App.android.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import android.content.Intent
2+
import android.graphics.BitmapFactory
23
import android.net.Uri
4+
import androidx.compose.ui.graphics.ImageBitmap
5+
import androidx.compose.ui.graphics.asImageBitmap
36
import com.travel.buddy.MuseumApp
47

58
internal actual fun openUrl(url: String?) {
@@ -10,4 +13,8 @@ internal actual fun openUrl(url: String?) {
1013
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
1114
}
1215
MuseumApp.INSTANCE.startActivity(intent)
16+
}
17+
18+
actual fun ByteArray.toComposeImageBitmap(): ImageBitmap {
19+
return BitmapFactory.decodeByteArray(this, 0, size).asImageBitmap()
1320
}
1.51 KB
Loading

composeApp/src/commonMain/composeResources/values/string.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<resources>
33
<string name="home_tab">Home</string>
44
<string name="fav_tab">Favourite</string>
5-
<string name="profile_tab">Profile</string>
5+
<string name="profile_tab">Gemini</string>
66
<string name="cart_tab">Cart</string>
77
<string name="ratting">RATING</string>
88
<string name="type">TYPE</string>
@@ -18,4 +18,5 @@
1818
<string name="category">Category</string>
1919
<string name="all">All</string>
2020
<string name="num_of_people">Number Of People</string>
21+
<string name="gemini">Gemini ChatBot</string>
2122
</resources>

composeApp/src/commonMain/kotlin/App.kt

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import androidx.compose.material3.Scaffold
44
import androidx.compose.runtime.*
55
import androidx.compose.ui.Alignment
66
import androidx.compose.ui.Modifier
7-
import androidx.lifecycle.viewmodel.compose.viewModel
7+
import androidx.compose.ui.graphics.ImageBitmap
88
import cafe.adriel.voyager.navigator.LocalNavigator
99
import cafe.adriel.voyager.navigator.currentOrThrow
1010
import cafe.adriel.voyager.navigator.tab.CurrentTab
11-
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
1211
import cafe.adriel.voyager.navigator.tab.TabNavigator
1312
import coil3.ImageLoader
1413
import coil3.PlatformContext
@@ -18,27 +17,27 @@ import coil3.memory.MemoryCache
1817
import coil3.request.CachePolicy
1918
import coil3.request.crossfade
2019
import coil3.util.DebugLogger
20+
import di.HomeScreenModelProvider
2121
import okio.FileSystem
2222
import theme.TravelAppTheme
2323
import ui.component.BottomMenuBar
2424
import ui.component.tabs
2525
import ui.screen.CartTab
2626
import ui.screen.FavoriteTab
2727
import ui.screen.HomeTab
28-
import ui.screen.ProfileTab
29-
import ui.viewmodel.HomeViewModel
28+
import ui.screen.GeminiTab
3029
import util.AnimateVisibility
3130

3231
@Composable
33-
internal fun App(
34-
viewModel: HomeViewModel = viewModel { HomeViewModel() }
35-
) {
32+
internal fun App() {
3633
TravelAppTheme {
3734

3835
setSingletonImageLoaderFactory { context ->
3936
getAsyncImageLoader(context)
4037
}
4138

39+
val viewModel = HomeScreenModelProvider.homeScreenModel
40+
4241
val bottomNavBarVisibility by viewModel.bottomNavBarVisible.collectAsState()
4342

4443
TabNavigator(HomeTab) {
@@ -57,7 +56,7 @@ internal fun App(
5756
HomeTab -> LocalNavigator.currentOrThrow.push(HomeTab)
5857
FavoriteTab -> LocalNavigator.currentOrThrow.push(FavoriteTab)
5958
CartTab -> LocalNavigator.currentOrThrow.push(CartTab)
60-
ProfileTab -> LocalNavigator.currentOrThrow.push(ProfileTab)
59+
GeminiTab -> LocalNavigator.currentOrThrow.push(GeminiTab)
6160
}
6261
}
6362
}
@@ -81,4 +80,6 @@ fun newDiskCache(): DiskCache {
8180
return DiskCache.Builder().directory(FileSystem.SYSTEM_TEMPORARY_DIRECTORY / "image_cache")
8281
.maxSizeBytes(1024L * 1024 * 1024) // 512MB
8382
.build()
84-
}
83+
}
84+
85+
expect fun ByteArray.toComposeImageBitmap(): ImageBitmap
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package data
2+
3+
import com.travel.buddy.BuildKonfig
4+
import dev.shreyaspatil.ai.client.generativeai.GenerativeModel
5+
import dev.shreyaspatil.ai.client.generativeai.type.GenerateContentResponse
6+
import dev.shreyaspatil.ai.client.generativeai.type.PlatformImage
7+
import dev.shreyaspatil.ai.client.generativeai.type.content
8+
import kotlinx.coroutines.flow.Flow
9+
import kotlin.io.encoding.ExperimentalEncodingApi
10+
11+
class GeminiApi {
12+
companion object {
13+
const val PROMPT_GENERATE_UI = "Act as an Android app developer. " +
14+
"For the image provided, use Jetpack Compose to build the screen so that " +
15+
"the Compose Preview is as close to this image as possible. Also make sure " +
16+
"to include imports and use Material3. Only give code part without any extra " +
17+
"text or description neither at start or end, your response should contain " +
18+
"only code without any explanation."
19+
}
20+
21+
22+
private val apiKey = BuildKonfig.GEMINI_API_KEY
23+
24+
25+
val generativeVisionModel = GenerativeModel(
26+
modelName = "gemini-1.5-flash",
27+
apiKey = apiKey
28+
)
29+
30+
val generativeModel = GenerativeModel(
31+
modelName = "gemini-pro",
32+
apiKey = apiKey
33+
)
34+
35+
fun generateContent(prompt: String): Flow<GenerateContentResponse> {
36+
return generativeModel.generateContentStream(prompt)
37+
}
38+
39+
@OptIn(ExperimentalEncodingApi::class)
40+
fun generateContent(prompt: String, imageData: ByteArray): Flow<GenerateContentResponse> {
41+
val content = content {
42+
image(PlatformImage(imageData))
43+
text(prompt)
44+
}
45+
return generativeVisionModel.generateContentStream(content)
46+
}
47+
}

composeApp/src/commonMain/kotlin/di/Koin.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package di
22

3+
import ui.viewmodel.HomeScreenModel
4+
35
//import org.koin.core.context.startKoin
46
//import org.koin.dsl.module
57
//
@@ -14,3 +16,9 @@ package di
1416
// )
1517
// }
1618
//}
19+
20+
object HomeScreenModelProvider {
21+
val homeScreenModel: HomeScreenModel by lazy {
22+
HomeScreenModel()
23+
}
24+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package model
2+
3+
data class ChatMessage(val text: String, val isSender: Boolean)

composeApp/src/commonMain/kotlin/theme/Color.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@ val ReviewBodyBg = Color(0xFFF6F8FA)
1818
val Yellow = Color(0xFFF8E545)
1919
val Red = Color(0xFFFF2828)
2020
val CategoryChipBg = Color(0xFF4D5652)
21-
val BorderColor = Color(0xFFAFAFAF)
21+
val BorderColor = Color(0xFFDDDDDD) // Light grey
22+
val LinkColor = Color(0xFF0000FF) // Blue
23+
val CodeBackground = Color(0xFFf9f9f9)
24+

composeApp/src/commonMain/kotlin/ui/component/BottomNavigationBar.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding
1010
import androidx.compose.foundation.layout.size
1111
import androidx.compose.foundation.layout.width
1212
import androidx.compose.foundation.layout.wrapContentSize
13-
import androidx.compose.foundation.lazy.LazyItemScope
1413
import androidx.compose.foundation.lazy.LazyRow
1514
import androidx.compose.foundation.lazy.items
1615
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -40,7 +39,7 @@ import theme.SecondTextColor
4039
import ui.screen.CartTab
4140
import ui.screen.FavoriteTab
4241
import ui.screen.HomeTab
43-
import ui.screen.ProfileTab
42+
import ui.screen.GeminiTab
4443

4544
interface Tabx: Tab {
4645
fun defaultTitle(): StringResource
@@ -51,7 +50,7 @@ val tabs = arrayListOf<Tabx>().apply {
5150
add(HomeTab)
5251
add(FavoriteTab)
5352
add(CartTab)
54-
add(ProfileTab)
53+
add(GeminiTab)
5554
}
5655

5756
@Composable

0 commit comments

Comments
 (0)