Skip to content

Commit db835a0

Browse files
committed
feat: Android sample integration
1 parent 52fcf68 commit db835a0

File tree

7 files changed

+123
-11
lines changed

7 files changed

+123
-11
lines changed

buildSrc/src/main/kotlin/Dependencies.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ object Versions {
1111
const val COMPOSE_ACTIVITY = "1.6.1"
1212
const val COMPOSE_NAVIGATION = "2.5.3"
1313
const val COMPOSE_LIVEDATA = "1.3.3"
14+
const val COIL = "2.2.2"
1415
const val KTOR = "2.2.2"
1516
const val KOIN = "3.2.0"
1617
const val MATERIAL_DESIGN = "1.6.1"
@@ -52,6 +53,7 @@ object Dependencies {
5253
const val COMPOSE_ACTIVITY = "androidx.activity:activity-compose:${Versions.COMPOSE_ACTIVITY}"
5354
const val COMPOSE_NAVIGATION = "androidx.navigation:navigation-compose:${Versions.COMPOSE_NAVIGATION}"
5455
const val COMPOSE_LIVEDATA = "androidx.compose.runtime:runtime-livedata:${Versions.COMPOSE_LIVEDATA}"
56+
const val COIL = "io.coil-kt:coil-compose:${Versions.COIL}"
5557
}
5658

5759
object Test {

sample/android/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ dependencies {
5353
implementation(Dependencies.UI.COMPOSE_ACTIVITY)
5454
implementation(Dependencies.UI.COMPOSE_NAVIGATION)
5555
implementation(Dependencies.UI.COMPOSE_LIVEDATA)
56+
implementation(Dependencies.UI.COIL)
5657
implementation(Dependencies.DI.KOIN_CORE)
5758
implementation(Dependencies.DI.KOIN_ANDROID)
5859
implementation(Dependencies.DI.KOIN_COMPOSE)

sample/android/src/main/java/co/yml/ychat/android/MainViewModel.kt

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package co.yml.ychat.android
22

3-
import android.util.Log
43
import androidx.compose.runtime.mutableStateListOf
54
import androidx.compose.runtime.mutableStateOf
65
import androidx.lifecycle.MutableLiveData
@@ -37,7 +36,7 @@ class MainViewModel(private val chatGpt: YChat) : ViewModel() {
3736
private var typingItem = mutableStateOf(MessageItem(message = typingTxt.value, isOut = false))
3837

3938
private fun setLoading(isLoading: Boolean) {
40-
_isLoading.value = isLoading
39+
_isLoading.postValue(isLoading)
4140
}
4241

4342
fun onSendMessage(message: String, typingStr: String) {
@@ -48,15 +47,17 @@ class MainViewModel(private val chatGpt: YChat) : ViewModel() {
4847
}
4948
}
5049

51-
fun onImageRequest(prompt: String) {
50+
fun onImageRequest(prompt: String, typingStr: String) {
51+
updateTypingMessage(typingStr)
5252
viewModelScope.launch {
53-
imageGenerations.execute(prompt, object: Callback<List<String>> {
53+
showTypingAnimation(prompt)
54+
imageGenerations.execute(prompt, object : Callback<List<String>> {
5455
override fun onSuccess(result: List<String>) {
55-
Log.d("callback", "onSuccess: $result")
56+
showImages(result)
5657
}
5758

5859
override fun onError(throwable: Throwable) {
59-
Log.d("callback", "onError: $throwable")
60+
writeResponse("Error")
6061
}
6162
})
6263
}
@@ -75,6 +76,14 @@ class MainViewModel(private val chatGpt: YChat) : ViewModel() {
7576
setLoading(false)
7677
}
7778

79+
private fun showImages(result: List<String>) {
80+
items.remove(items[items.lastIndex])
81+
result.forEach {
82+
items.add(MessageItem(message = IMAGE, isOut = false, url = it))
83+
}
84+
setLoading(false)
85+
}
86+
7887
private suspend fun requestCompletion(message: String): String {
7988
return try {
8089
chatCompletions.execute(message).last().content
@@ -98,5 +107,6 @@ class MainViewModel(private val chatGpt: YChat) : ViewModel() {
98107
companion object {
99108
private const val ERROR = "Error"
100109
private const val MAX_TOKENS = 1024
110+
private const val IMAGE = "image"
101111
}
102112
}

sample/android/src/main/java/co/yml/ychat/android/MessageItem.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ package co.yml.ychat.android
33
data class MessageItem(
44
val message: String,
55
val isOut: Boolean,
6+
val url: String? = null
67
)

sample/android/src/main/java/co/yml/ychat/android/ui/ChatLayout.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,15 @@ fun ChatLayout(
7777
.padding(spaceMedium),
7878
) {
7979
items(messages) { message ->
80-
MessageItemLayout(
81-
messageText = message.message, isOut = message.isOut
82-
)
80+
message.url?.let {
81+
ImageItemLayout(
82+
messageText = message.url, isOut = message.isOut
83+
)
84+
} ?: run {
85+
MessageItemLayout(
86+
messageText = message.message, isOut = message.isOut
87+
)
88+
}
8389
}
8490
coroutineScope.launch {
8591
listState.animateScrollToItem(messages.size)
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package co.yml.ychat.android.ui
2+
3+
import android.content.res.Configuration.UI_MODE_NIGHT_YES
4+
import androidx.compose.foundation.Image
5+
import androidx.compose.foundation.background
6+
import androidx.compose.foundation.layout.Box
7+
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.Row
9+
import androidx.compose.foundation.layout.Spacer
10+
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.height
12+
import androidx.compose.foundation.layout.padding
13+
import androidx.compose.foundation.layout.width
14+
import androidx.compose.foundation.shape.CircleShape
15+
import androidx.compose.foundation.shape.RoundedCornerShape
16+
import androidx.compose.runtime.Composable
17+
import androidx.compose.ui.Alignment
18+
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.draw.clip
20+
import androidx.compose.ui.res.colorResource
21+
import androidx.compose.ui.res.painterResource
22+
import androidx.compose.ui.tooling.preview.Preview
23+
import androidx.compose.ui.unit.dp
24+
import co.yml.ychat.android.R
25+
import co.yml.ychat.android.ui.Dimensions.default
26+
import co.yml.ychat.android.ui.Dimensions.robotMessageIconSize
27+
import co.yml.ychat.android.ui.Dimensions.robotMessagePaddingSize
28+
import co.yml.ychat.android.ui.Dimensions.spaceExtraSmall
29+
import co.yml.ychat.android.ui.Dimensions.spaceMedium
30+
import co.yml.ychat.android.ui.Dimensions.spaceSmall
31+
import coil.compose.AsyncImage
32+
33+
@Composable
34+
fun ImageItemLayout(
35+
messageText: String,
36+
isOut: Boolean
37+
) {
38+
Column(
39+
modifier = Modifier.fillMaxWidth(),
40+
horizontalAlignment = if (isOut) Alignment.End else Alignment.Start
41+
) {
42+
Row(
43+
modifier = Modifier.padding(top = spaceMedium),
44+
verticalAlignment = Alignment.Bottom
45+
) {
46+
if (isOut.not()) {
47+
Image(
48+
painterResource(R.drawable.ic_robot),
49+
contentDescription = "",
50+
modifier = Modifier
51+
.width(robotMessageIconSize)
52+
.height(robotMessageIconSize)
53+
.clip(shape = CircleShape)
54+
.background(colorResource(id = R.color.softGreen))
55+
.padding(robotMessagePaddingSize),
56+
)
57+
Spacer(modifier = Modifier.padding(spaceExtraSmall))
58+
}
59+
Box(
60+
modifier = Modifier
61+
.clip(
62+
shape = RoundedCornerShape(
63+
topStart = spaceMedium,
64+
topEnd = spaceMedium,
65+
bottomEnd = if (isOut) default else spaceMedium,
66+
bottomStart = if (isOut) spaceMedium else default
67+
)
68+
)
69+
.background(if (isOut) colorResource(id = R.color.softBlue) else colorResource(id = R.color.opaqueWhite))
70+
.padding(spaceSmall)
71+
) {
72+
AsyncImage(
73+
modifier = Modifier.clip(RoundedCornerShape(8.dp)),
74+
model = messageText,
75+
contentDescription = messageText,
76+
placeholder = painterResource(R.drawable.ic_robot),
77+
)
78+
}
79+
}
80+
}
81+
}
82+
83+
@Preview(uiMode = UI_MODE_NIGHT_YES)
84+
@Composable
85+
fun PreviewImageItemLayout() {
86+
MessageItemLayout(messageText = "Message", isOut = false)
87+
}

sample/android/src/main/java/co/yml/ychat/android/ui/SendMessageLayout.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ fun SendMessageLayout() {
5252
val scope = rememberCoroutineScope()
5353
val viewModel = koinViewModel<MainViewModel>()
5454
val isLoading: Boolean by viewModel.isLoading.observeAsState(initial = false)
55+
56+
5557
Row(
5658
modifier = Modifier
5759
.background(color = MaterialTheme.colors.background)
@@ -91,8 +93,11 @@ fun SendMessageLayout() {
9193
.background(if (textFieldState.isNotEmpty() && isLoading.not()) colorResource(id = R.color.softBlue) else colorResource(id = R.color.opaqueWhite)),
9294
onClick = {
9395
scope.launch {
94-
//viewModel.onSendMessage(textFieldState, typingString)
95-
viewModel.onImageRequest(textFieldState)
96+
if (textFieldState.startsWith("/image ")) {
97+
viewModel.onImageRequest(textFieldState, typingString)
98+
} else {
99+
viewModel.onSendMessage(textFieldState, typingString)
100+
}
96101
textFieldState = ""
97102
}
98103
},

0 commit comments

Comments
 (0)