Skip to content

Commit 0165ae6

Browse files
authored
Merge pull request #65 from yml-org/feature/CM-1326/images-generation
feature: add android sample to showcase image generations entry point
2 parents 1cc9cc6 + 5c56a6e commit 0165ae6

File tree

5 files changed

+171
-7
lines changed

5 files changed

+171
-7
lines changed

sample/android/src/main/java/co/yml/ychat/android/di/AppModule.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import co.yml.ychat.android.presentation.chatcompletions.viewmodel.ChatCompletio
66
import co.yml.ychat.android.presentation.completions.CompletionsViewModel
77
import co.yml.ychat.android.presentation.edits.EditsViewModel
88
import co.yml.ychat.android.presentation.home.viewmodel.HomeViewModel
9+
import co.yml.ychat.android.presentation.images.ImagesViewModel
910
import co.yml.ychat.android.presentation.models.viewmodel.ModelsViewModel
1011
import org.koin.androidx.viewmodel.dsl.viewModelOf
1112
import org.koin.dsl.module
@@ -17,4 +18,5 @@ val appModule = module {
1718
viewModelOf(::ModelsViewModel)
1819
viewModelOf(::CompletionsViewModel)
1920
viewModelOf(::EditsViewModel)
21+
viewModelOf(::ImagesViewModel)
2022
}
Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,99 @@
11
package co.yml.ychat.android.presentation.images
22

33
import androidx.compose.foundation.background
4-
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Box
55
import androidx.compose.foundation.layout.Column
6-
import androidx.compose.foundation.layout.fillMaxHeight
6+
import androidx.compose.foundation.layout.Spacer
7+
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.layout.fillMaxWidth
9+
import androidx.compose.foundation.layout.padding
10+
import androidx.compose.foundation.shape.RoundedCornerShape
11+
import androidx.compose.material.CircularProgressIndicator
712
import androidx.compose.runtime.Composable
13+
import androidx.compose.runtime.collectAsState
14+
import androidx.compose.ui.Alignment
815
import androidx.compose.ui.Modifier
16+
import androidx.compose.ui.draw.clip
17+
import androidx.compose.ui.layout.ContentScale
18+
import androidx.compose.ui.res.stringResource
919
import androidx.compose.ui.tooling.preview.Preview
20+
import co.yml.ychat.YChat
21+
import co.yml.ychat.android.BuildConfig
22+
import co.yml.ychat.android.R
23+
import co.yml.ychat.android.presentation.images.ImagesViewModel.State
24+
import co.yml.ychat.android.ui.components.button.ButtonContained
1025
import co.yml.ychat.android.ui.components.feedback.Feedback
1126
import co.yml.ychat.android.ui.components.feedback.model.FeedbackState
27+
import co.yml.ychat.android.ui.components.placeholder.ImagePlaceHolder
28+
import co.yml.ychat.android.ui.components.textfield.StandardTextField
29+
import co.yml.ychat.android.ui.theme.Dimens
30+
import co.yml.ychat.android.ui.theme.TypographyStyle
1231
import co.yml.ychat.android.ui.theme.YChatTheme
32+
import coil.compose.SubcomposeAsyncImage
33+
import org.koin.androidx.compose.getViewModel
1334

1435
@Composable
15-
internal fun ImagesScreen() {
36+
internal fun ImagesScreen(viewModel: ImagesViewModel = getViewModel()) {
37+
val state = viewModel.state.collectAsState().value
1638
Column(
1739
modifier = Modifier
1840
.background(YChatTheme.colors.background)
19-
.fillMaxHeight(),
20-
verticalArrangement = Arrangement.Center,
41+
.padding(Dimens.MD)
42+
.fillMaxSize(),
2143
) {
22-
Feedback(feedbackState = FeedbackState.CONSTRUCTION)
44+
TypographyStyle.MediumBody.Text(text = stringResource(id = R.string.images_input_title))
45+
StandardTextField(
46+
value = viewModel.inputMessage.value,
47+
hint = stringResource(id = R.string.images_input_hint),
48+
modifier = Modifier
49+
.fillMaxWidth()
50+
.padding(top = Dimens.MD)
51+
,
52+
onTextChanged = { viewModel.onInputMessage(it) },
53+
enabled = state !is State.Loading,
54+
)
55+
ButtonContained(
56+
text = stringResource(id = R.string.images_action),
57+
modifier = Modifier
58+
.fillMaxWidth()
59+
.padding(top = Dimens.XM),
60+
enabled = viewModel.onEnableButton.value,
61+
onClick = { viewModel.requestEdits() }
62+
)
63+
Box(modifier = Modifier.fillMaxSize()) {
64+
when (state) {
65+
is State.Loading ->
66+
CircularProgressIndicator(
67+
modifier = Modifier.align(Alignment.Center)
68+
)
69+
is State.Error ->
70+
Feedback(
71+
modifier = Modifier.align(Alignment.Center),
72+
feedbackState = FeedbackState.ERROR
73+
)
74+
is State.Success ->
75+
SubcomposeAsyncImage(
76+
model = state.generatedImage,
77+
loading = { ImagePlaceHolder() },
78+
contentDescription = null,
79+
contentScale = ContentScale.FillBounds,
80+
modifier = Modifier
81+
.fillMaxSize()
82+
.padding(top = Dimens.XM)
83+
.clip(RoundedCornerShape(Dimens.XS))
84+
)
85+
else -> Spacer(modifier = Modifier)
86+
}
87+
}
2388
}
2489
}
2590

2691
@Preview
2792
@Composable
2893
private fun ImagesScreenPreview() {
2994
YChatTheme {
30-
ImagesScreen()
95+
val yChat = YChat.create(BuildConfig.API_KEY)
96+
val viewModel = ImagesViewModel(yChat)
97+
ImagesScreen(viewModel)
3198
}
3299
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package co.yml.ychat.android.presentation.images
2+
3+
import androidx.compose.runtime.mutableStateOf
4+
import androidx.lifecycle.ViewModel
5+
import androidx.lifecycle.viewModelScope
6+
import co.yml.ychat.YChat
7+
import kotlinx.coroutines.flow.MutableStateFlow
8+
import kotlinx.coroutines.flow.StateFlow
9+
import kotlinx.coroutines.flow.asStateFlow
10+
import kotlinx.coroutines.launch
11+
12+
internal class ImagesViewModel(private val yChat: YChat): ViewModel() {
13+
14+
val inputMessage = mutableStateOf("")
15+
16+
val onEnableButton = mutableStateOf(false)
17+
18+
private val _state = MutableStateFlow<State>(State.Idle)
19+
val state: StateFlow<State> = _state.asStateFlow()
20+
21+
private val imageGenerations by lazy { yChat.imageGenerations() }
22+
23+
fun onInputMessage(message: String) {
24+
this.inputMessage.value = message
25+
onEnableButton.value = inputMessage.value.isNotEmpty()
26+
}
27+
28+
fun requestEdits() = viewModelScope.launch {
29+
_state.value = State.Loading
30+
onEnableButton.value = false
31+
runCatching { imageGenerations.execute(inputMessage.value) }
32+
.also { onEnableButton.value = true }
33+
.onSuccess { _state.value = State.Success(it.first()) }
34+
.onFailure { _state.value = State.Error }
35+
}
36+
37+
sealed class State {
38+
object Idle: State()
39+
object Loading : State()
40+
data class Success(val generatedImage: String) : State()
41+
object Error : State()
42+
}
43+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package co.yml.ychat.android.ui.components.placeholder
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.fillMaxSize
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.foundation.layout.size
8+
import androidx.compose.runtime.Composable
9+
import androidx.compose.ui.Alignment
10+
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.tooling.preview.Preview
12+
import co.yml.ychat.android.ui.theme.Dimens
13+
import co.yml.ychat.android.ui.theme.Icons
14+
import co.yml.ychat.android.ui.theme.YChatTheme
15+
16+
@Composable
17+
fun ImagePlaceHolder() {
18+
Box(
19+
modifier = Modifier
20+
.background(YChatTheme.colors.primary4)
21+
.fillMaxSize(),
22+
contentAlignment = Alignment.Center,
23+
) {
24+
Icons.Image
25+
.Icon(
26+
modifier = Modifier.size(Dimens.XXXL),
27+
tint = YChatTheme.colors.primary3
28+
)
29+
}
30+
}
31+
32+
@Preview
33+
@Composable
34+
private fun ImagePlaceHolderPreview() {
35+
YChatTheme {
36+
Box(
37+
modifier = Modifier
38+
.background(YChatTheme.colors.background)
39+
.fillMaxSize()
40+
.padding(Dimens.XXXL)
41+
,
42+
contentAlignment = Alignment.Center,
43+
) {
44+
ImagePlaceHolder()
45+
}
46+
}
47+
}

sample/android/src/main/res/values/strings.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
<string name="edits_instruction_hint">Fix the grammar.</string>
2727
<string name="edits_action">Submit</string>
2828

29+
<!-- ImagesScreen -->
30+
<string name="images_input_title">Input the text description of the desired image</string>
31+
<string name="images_input_hint">A cute baby sea otter</string>
32+
<string name="images_action">Submit</string>
33+
2934
<!-- FeedbackState.ERROR -->
3035
<string name="feedback_state_error_title">Something went wrong</string>
3136
<string name="feedback_state_error_message">There was a problem with the request. Please try again in a few moments.</string>

0 commit comments

Comments
 (0)