Skip to content

Commit

Permalink
Merge pull request #4 from Realtime-Coding/feature/ai-gemini
Browse files Browse the repository at this point in the history
Feature/ai gemini
  • Loading branch information
v-shahzadahmad authored Oct 15, 2024
2 parents cb0e451 + b2cba1d commit 3eb3c43
Show file tree
Hide file tree
Showing 25 changed files with 689 additions and 127 deletions.
39 changes: 35 additions & 4 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
import com.codingfeline.buildkonfig.compiler.FieldSpec
import java.util.Properties

plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsCompose)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.buildkonfig)
}

kotlin {
Expand All @@ -29,11 +32,12 @@ kotlin {
}
binaries.executable()
}

androidTarget {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
compilations.all {
kotlinOptions {
jvmTarget = "17"
}
}
}

Expand Down Expand Up @@ -67,7 +71,12 @@ kotlin {
implementation(libs.coil.network.ktor)
implementation(libs.voyager.navigator)
implementation(libs.voyager.tab.navigator)
implementation(libs.voyager.screenmodel)
implementation(libs.lifecycle.viewmodel.compose)
implementation(libs.markdown.renderer)
api(libs.compose.window.size)
api(libs.generativeai)
implementation(libs.filekit.compose)
}
desktopMain.dependencies {
implementation(compose.desktop.currentOs)
Expand Down Expand Up @@ -147,3 +156,25 @@ compose.desktop {
}
}
}

buildkonfig {
packageName = "com.travel.buddy"

val localPropsFile = rootProject.file("local.properties")
val localProperties = Properties()
if (localPropsFile.exists()) {
runCatching {
localProperties.load(localPropsFile.inputStream())
}.getOrElse {
it.printStackTrace()
}
}
defaultConfigs {
buildConfigField(
FieldSpec.Type.STRING,
"GEMINI_API_KEY",
localProperties["gemini_api_key"]?.toString() ?: ""
)
}

}
7 changes: 7 additions & 0 deletions composeApp/src/androidMain/kotlin/App.android.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import android.content.Intent
import android.graphics.BitmapFactory
import android.net.Uri
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import com.travel.buddy.MuseumApp

internal actual fun openUrl(url: String?) {
Expand All @@ -10,4 +13,8 @@ internal actual fun openUrl(url: String?) {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
MuseumApp.INSTANCE.startActivity(intent)
}

actual fun ByteArray.toComposeImageBitmap(): ImageBitmap {
return BitmapFactory.decodeByteArray(this, 0, size).asImageBitmap()
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion composeApp/src/commonMain/composeResources/values/string.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<resources>
<string name="home_tab">Home</string>
<string name="fav_tab">Favourite</string>
<string name="profile_tab">Profile</string>
<string name="profile_tab">Gemini</string>
<string name="cart_tab">Cart</string>
<string name="ratting">RATING</string>
<string name="type">TYPE</string>
Expand All @@ -18,4 +18,5 @@
<string name="category">Category</string>
<string name="all">All</string>
<string name="num_of_people">Number Of People</string>
<string name="gemini">Gemini ChatBot</string>
</resources>
19 changes: 10 additions & 9 deletions composeApp/src/commonMain/kotlin/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import androidx.compose.material3.Scaffold
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.compose.ui.graphics.ImageBitmap
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import cafe.adriel.voyager.navigator.tab.CurrentTab
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
import cafe.adriel.voyager.navigator.tab.TabNavigator
import coil3.ImageLoader
import coil3.PlatformContext
Expand All @@ -18,27 +17,27 @@ import coil3.memory.MemoryCache
import coil3.request.CachePolicy
import coil3.request.crossfade
import coil3.util.DebugLogger
import di.HomeScreenModelProvider
import okio.FileSystem
import theme.TravelAppTheme
import ui.component.BottomMenuBar
import ui.component.tabs
import ui.screen.CartTab
import ui.screen.FavoriteTab
import ui.screen.HomeTab
import ui.screen.ProfileTab
import ui.viewmodel.HomeViewModel
import ui.screen.GeminiTab
import util.AnimateVisibility

@Composable
internal fun App(
viewModel: HomeViewModel = viewModel { HomeViewModel() }
) {
internal fun App() {
TravelAppTheme {

setSingletonImageLoaderFactory { context ->
getAsyncImageLoader(context)
}

val viewModel = HomeScreenModelProvider.homeScreenModel

val bottomNavBarVisibility by viewModel.bottomNavBarVisible.collectAsState()

TabNavigator(HomeTab) {
Expand All @@ -57,7 +56,7 @@ internal fun App(
HomeTab -> LocalNavigator.currentOrThrow.push(HomeTab)
FavoriteTab -> LocalNavigator.currentOrThrow.push(FavoriteTab)
CartTab -> LocalNavigator.currentOrThrow.push(CartTab)
ProfileTab -> LocalNavigator.currentOrThrow.push(ProfileTab)
GeminiTab -> LocalNavigator.currentOrThrow.push(GeminiTab)
}
}
}
Expand All @@ -81,4 +80,6 @@ fun newDiskCache(): DiskCache {
return DiskCache.Builder().directory(FileSystem.SYSTEM_TEMPORARY_DIRECTORY / "image_cache")
.maxSizeBytes(1024L * 1024 * 1024) // 512MB
.build()
}
}

expect fun ByteArray.toComposeImageBitmap(): ImageBitmap
47 changes: 47 additions & 0 deletions composeApp/src/commonMain/kotlin/data/GeminiApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package data

import com.travel.buddy.BuildKonfig
import dev.shreyaspatil.ai.client.generativeai.GenerativeModel
import dev.shreyaspatil.ai.client.generativeai.type.GenerateContentResponse
import dev.shreyaspatil.ai.client.generativeai.type.PlatformImage
import dev.shreyaspatil.ai.client.generativeai.type.content
import kotlinx.coroutines.flow.Flow
import kotlin.io.encoding.ExperimentalEncodingApi

class GeminiApi {
companion object {
const val PROMPT_GENERATE_UI = "Act as an Android app developer. " +
"For the image provided, use Jetpack Compose to build the screen so that " +
"the Compose Preview is as close to this image as possible. Also make sure " +
"to include imports and use Material3. Only give code part without any extra " +
"text or description neither at start or end, your response should contain " +
"only code without any explanation."
}


private val apiKey = BuildKonfig.GEMINI_API_KEY


val generativeVisionModel = GenerativeModel(
modelName = "gemini-1.5-flash",
apiKey = apiKey
)

val generativeModel = GenerativeModel(
modelName = "gemini-pro",
apiKey = apiKey
)

fun generateContent(prompt: String): Flow<GenerateContentResponse> {
return generativeModel.generateContentStream(prompt)
}

@OptIn(ExperimentalEncodingApi::class)
fun generateContent(prompt: String, imageData: ByteArray): Flow<GenerateContentResponse> {
val content = content {
image(PlatformImage(imageData))
text(prompt)
}
return generativeVisionModel.generateContentStream(content)
}
}
8 changes: 8 additions & 0 deletions composeApp/src/commonMain/kotlin/di/Koin.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package di

import ui.viewmodel.HomeScreenModel

//import org.koin.core.context.startKoin
//import org.koin.dsl.module
//
Expand All @@ -14,3 +16,9 @@ package di
// )
// }
//}

object HomeScreenModelProvider {
val homeScreenModel: HomeScreenModel by lazy {
HomeScreenModel()
}
}
3 changes: 3 additions & 0 deletions composeApp/src/commonMain/kotlin/model/ChatMessage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package model

data class ChatMessage(val text: String, val isSender: Boolean)
5 changes: 4 additions & 1 deletion composeApp/src/commonMain/kotlin/theme/Color.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ val ReviewBodyBg = Color(0xFFF6F8FA)
val Yellow = Color(0xFFF8E545)
val Red = Color(0xFFFF2828)
val CategoryChipBg = Color(0xFF4D5652)
val BorderColor = Color(0xFFAFAFAF)
val BorderColor = Color(0xFFDDDDDD) // Light grey
val LinkColor = Color(0xFF0000FF) // Blue
val CodeBackground = Color(0xFFf9f9f9)

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
Expand Down Expand Up @@ -40,7 +39,7 @@ import theme.SecondTextColor
import ui.screen.CartTab
import ui.screen.FavoriteTab
import ui.screen.HomeTab
import ui.screen.ProfileTab
import ui.screen.GeminiTab

interface Tabx: Tab {
fun defaultTitle(): StringResource
Expand All @@ -51,7 +50,7 @@ val tabs = arrayListOf<Tabx>().apply {
add(HomeTab)
add(FavoriteTab)
add(CartTab)
add(ProfileTab)
add(GeminiTab)
}

@Composable
Expand Down
82 changes: 82 additions & 0 deletions composeApp/src/commonMain/kotlin/ui/component/Shimmer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package ui.component

import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import theme.PrimaryColor

val ShimmerColorShades = listOf(
Color.LightGray.copy(0.9f),
PrimaryColor.copy(0.2f),
Color.LightGray.copy(0.9f)
)
@Composable
fun ShimmerAnimation(
) {
/*
Create InfiniteTransition
which holds child animation like [Transition]
animations start running as soon as they enter
the composition and do not stop unless they are removed
*/
val transition = rememberInfiniteTransition()
val translateAnim by transition.animateFloat(
/*
Specify animation positions,
initial Values 0F means it
starts from 0 position
*/
initialValue = 0f,
targetValue = 1000f,
animationSpec = infiniteRepeatable(


// Tween Animates between values over specified [durationMillis]
tween(durationMillis = 1200, easing = FastOutSlowInEasing),
RepeatMode.Reverse
)
)

/*
Create a gradient using the list of colors
Use Linear Gradient for animating in any direction according to requirement
start=specifies the position to start with in cartesian like system Offset(10f,10f) means x(10,0) , y(0,10)
end = Animate the end position to give the shimmer effect using the transition created above
*/
val brush = Brush.linearGradient(
colors = ShimmerColorShades,
start = Offset(10f, 10f),
end = Offset(translateAnim, translateAnim)
)

ShimmerItem(brush = brush)
}

@Composable
fun ShimmerItem(
brush: Brush
) {
Spacer(
modifier = Modifier
.padding(top = 8.dp)
.fillMaxWidth()
.size(20.dp)
.background(brush = brush, shape = RoundedCornerShape(8.dp))
)
}
Loading

0 comments on commit 3eb3c43

Please sign in to comment.