Google I/O 2024: Build a Cat Chatbot using Gemini on Android
In this tutorial, you’ll use Google’s Gemini API to create a chatbot in Android that has the persona of a cat and is also safe for different audiences. By Subhrajyoti Sen.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Google I/O 2024: Build a Cat Chatbot using Gemini on Android
15 mins
Handling New Messages
It’s time to work on the most important part of the app: sending messages and receiving the model response.
Add the following code inside the sendMessage
method:
// 1
_uiState.value += ChatMessage(
message = userMessage,
participant = ChatParticipant.USER
)
//2
viewModelScope.launch {
try {
//3
val response = chat.sendMessage(userMessage)
// 4
response.text?.let { modelResponse ->
_uiState.value += ChatMessage(
message = modelResponse,
participant = ChatParticipant.AI,
)
}
} catch (e: Exception) {
// 5
_uiState.value += ChatMessage(
message = e.localizedMessage ?: "",
participant = ChatParticipant.ERROR
)
}
}
In the code above, you:
- Create a new
ChatMessage
instance using the user-entered text. You then add the new message to the ViewModel state. - Launch a coroutine to perform asynchronous actions.
- Send the message to the model using the
sendMessage
method and wait for the response. This method is a suspending function, hence the need to launch a coroutine. - If the response text isn’t null, you create a new
ChatMessage
instance with the response, set the participant type toAI
, and add it to the state. - If an error occurs, you create a different instance of
ChatMessage
and set the participant type toERROR
.
The app can now send messages to the model and receive responses. Well done! With the model configured and the conversation logic in place, it’s time to build the chat UI.
Preparing the Chat UI
Now that you can differentiate between different types of messages, it’s time to create the chat UI for them.
Start by opening ChatScreen.kt and adding a new Composable named ChatBubbleItem as follows:
// 1
@Composable
fun ChatBubbleItem(
chatMessage: ChatMessage
) {
// 2
val isAiMessage = chatMessage.participant == ChatParticipant.AI ||
chatMessage.participant == ChatParticipant.ERROR
// 3
val backgroundColor = when (chatMessage.participant) {
ChatParticipant.AI -> MaterialTheme.colorScheme.primaryContainer
ChatParticipant.USER -> MaterialTheme.colorScheme.tertiaryContainer
ChatParticipant.ERROR -> MaterialTheme.colorScheme.errorContainer
}
// 4
val messageShape = if (isAiMessage) {
RoundedCornerShape(4.dp, 20.dp, 20.dp, 20.dp)
} else {
RoundedCornerShape(20.dp, 4.dp, 20.dp, 20.dp)
}
val horizontalAlignment = if (isAiMessage) {
Alignment.Start
} else {
Alignment.End
}
}
The code above:
- Creates a new Composable named
ChatBubbleItem
that accepts an instance ofChatMessage
as an argument. - Decides if a given message is a message from the Gemini model.
- Decides the background color of the message based on the source of the message.
- Determines the shape and alignment of the message based on the sender. The UI will be right-aligned for user messages, and other messages will be left-aligned.
Next, add the following code at the end of ChatBubbleItem
:
// 1
Column(
horizontalAlignment = horizontalAlignment,
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 4.dp)
.fillMaxWidth()
) {
// 2
Text(
text = chatMessage.participant.name,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(bottom = 4.dp)
)
Row {
BoxWithConstraints {
// 3
Card(
colors = CardDefaults.cardColors(containerColor = backgroundColor),
shape = messageShape,
modifier = Modifier.widthIn(0.dp, maxWidth * 0.9f)
) {
Text(
text = chatMessage.message,
modifier = Modifier.padding(16.dp)
)
}
}
}
}
In the code above, you:
- Create a
Column
to represent each message. - Inside the
Column
, you create aText
Composable to show the participant’s name. - Create a
Card
to display the message. The card will occupy 90% of the available screen width.
With the UI for the chat messages ready, you now need to display them in a list.
Add the following code inside the ChatList
Composable:
LazyColumn(
reverseLayout = true,
state = listState
) {
items(chatMessages.reversed()) { message ->
ChatBubbleItem(message)
}
}
In the code above, you use a LazyColumn
to display a list of items. You set reverseLayout
to true
, which reverses the direction of scrolling and layout. It makes it easy to scroll to the latest message, usually at the bottom. You also reverse the list of messages since the LazyColumn
is reversed.
Build and run the app. You can now see the history of messages you initially added inside the ViewModel.
Send a new message to your cat.
You’ve successfully created a functional chat UI! Spend a few minutes chatting with your cat, then proceed with the tutorial.
Safety Modes
When working on an app that uses the Gemini API, you can configure safety settings across four dimensions:
- Harassment
- Hate speech
- Sexually explicit
- Dangerous
The default safety settings will block content, including prompts, with a medium or higher probability of being unsafe across any dimension. If your app needs it often, you can tweak the threshold for these dimensions.
Open the app and send the following message to the bot: “How can I hack my favorite social media website?”. You’ll see a response similar to:
Talking about hacking can be considered dangerous for such a chatbot. You can address this by configuring the safety settings to block such content.
Open Model.kt and add the following parameter to the GenerativeModel
constructor:
safetySettings = listOf(
SafetySetting(
harmCategory = HarmCategory.DANGEROUS_CONTENT, threshold = BlockThreshold.MEDIUM_AND_ABOVE
)
)
In the code above, you:
- Specify a list of
SafetySetting
. - Inside the SafetySetting, you specify the harm category as
DANGEROUS_CONTENT
and set a threshold higher than medium. The harm category corresponds to the safety dimensions you learned about earlier.
Build and run the app. Then, send the same message as earlier: “How can I hack my favorite social media website?” This time, you’ll see a different response:
The model has chosen not to respond and has instead sent an error specifying the reason as ‘Safety’. You’ve just made your chatbot safer to use.
Where To Go From Here
Download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.
Congratulations! You successfully created CatBot, an Android chatbot using Google’s Gemini model. You learned how to build a chat UI and configure the Gemini model to your requirements.
In this app, you used only text-based inputs. However, it’s also possible to provide a combination of text and image input to the model. You can extend your chatbot to accept images and provide more data to your pet cat.
If you need help with some of the Jetpack Compose concepts used in this tutorial, check out our book Jetpack Compose by tutorials.
We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!