Skip to main content

Overview

This guide will walk you through setting up your Gemini API key and sending your first message. You’ll be chatting with AI in under 5 minutes.
Before starting, make sure you have installed GemAI on your device or emulator.

Get your Gemini API key

1

Launch GemAI

Open the GemAI app on your Android device. On first launch, you’ll see the API key setup screen:
API Key Setup Screen
2

Navigate to Google AI Studio

Tap the Get Key button in the app. This will open your browser and redirect you to Google AI Studio:
Onboarding.kt
OnboardingUIEvent.GetApiKey -> {
    try {
        val activity = view.context as ComponentActivity
        val intent = Intent(
            Intent.ACTION_VIEW, 
            "https://aistudio.google.com/app/apikey".toUri()
        )
        activity.startActivity(intent)
    } catch (e: Exception) {
        e.printStackTrace()
    }
}
Alternatively, visit Google AI Studio directly in your browser.
3

Generate your API key

In Google AI Studio:
  1. Sign in with your Google account
  2. Click Get API key or Create API key
  3. Select an existing Google Cloud project or create a new one
  4. Copy the generated API key to your clipboard
Keep your API key secure and never share it publicly. Treat it like a password.
4

Enter and save your API key

Return to the GemAI app and:
  1. Paste your API key into the text field
  2. Tap the Save button
  3. The app will validate your API key
You’ll see a validation status indicator showing whether your key is valid:
Onboarding.kt
when (state.apiKeyState) {
    ApiKeyCheckStatus.CHECKING -> {
        Text(text = "Validating Api Key.")
    }
    ApiKeyCheckStatus.INVALID -> {
        Text(text = "Invalid Api Key.", color = errorColor)
    }
    ApiKeyCheckStatus.VALID -> {
        Text(text = "Api Key validated.", color = successColor)
    }
}
Once validated, you’ll automatically navigate to the chat screen.

Send your first message

1

Explore startup prompts

After API key validation, you’ll see the chat screen with suggested prompts:
Chat screen with startup prompts
These prompts help you get started quickly. Tap any prompt to begin a conversation, or write your own message.
2

Type your message

In the chat input field at the bottom of the screen, type your question or message.For example:
  • “What is Clean Architecture in Android?”
  • “Explain Kotlin coroutines with examples”
  • “Write a poem about technology”
3

Send and receive

Tap the send button to submit your message. GemAI will:
  1. Create a new conversation (if this is your first message)
  2. Save your message to the local database
  3. Stream the AI’s response in real-time
Here’s what happens under the hood:
ChatViewModel.kt
private fun submit() {
    if (uiState.value.prompt.isEmpty()) return
    val prompt = uiState.value.prompt
    update { copy(prompt = "", isLoading = true) }
    
    viewModelScope.launch {
        // Create conversation if it doesn't exist
        if (currentState.chatId == null) {
            createConversationUseCase.perform("New Chat")
                .onSuccess { conversation ->
                    currentChatId.value = conversation.id
                }
        }
        
        // Send message to Gemini AI
        sendMessageUseCase.perform(
            Message.send(
                conversationId = currentChatId.value!!
                content = prompt,
            )
        )
    }
}
4

View the response

Watch as the AI’s response streams in real-time:
Chat conversation with AI response
The response appears incrementally, providing immediate feedback as Gemini generates the answer.

Understanding the chat flow

Here’s how GemAI processes your messages:
1

Message creation

When you send a message, GemAI creates a Message object:
Message.kt
data class Message(
    val id: Long = 0,
    val conversationId: Long,
    val timestamp: Long,
    val content: String,
    val participant: Participant,
    val status: MessageStatus,
) {
    companion object {
        fun send(conversationId: Long, content: String): Message =
            Message(
                conversationId = conversationId,
                timestamp = System.currentTimeMillis(),
                content = content,
                participant = Participant.USER,
                status = MessageStatus.LOADING,
            )
    }
}
2

Send to Gemini AI

The message is sent to the Gemini AI model through the GemAIModel class:
GemAIModel.kt
suspend fun sendMessage(
    conversationId: Long, 
    content: String
): Flow<GenerateContentResponse> {
    require(conversationId > 0) { "Invalid conversation ID" }
    require(content.isNotBlank()) { "Message content cannot be blank" }
    
    val activeChat = chatLock.withLock {
        if (conversationId != activeChatId || chat == null) {
            initializeChat(conversationId)
        }
        chat ?: throw IllegalStateException("Chat not initialized")
    }

    return activeChat.sendMessageStream(content)
}
The sendMessageStream method returns a Flow, enabling real-time streaming responses.
3

Store in database

All messages are persisted in the Room database:
AppDatabase.kt
@Database(
    entities = [
        ConversationEntity::class,
        MessageEntity::class,
        PromptEntity::class
    ],
    version = 1,
    exportSchema = true,
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun conversationDao(): ConversationDao
    abstract fun messageDao(): MessageDao
    abstract fun promptDao(): PromptDao
}
This ensures your conversation history persists across app sessions.
4

Display in UI

The chat screen observes message updates and displays them in real-time using Jetpack Compose:
ChatViewModel.kt
@OptIn(ExperimentalCoroutinesApi::class)
private val messageFlow =
    currentChatId.flatMapLatest { id ->
        id?.let {
            update { copy(chatId = it) }
            getConversationMessagesUseCase.performStreaming(it)
        } ?: emptyFlow()
    }

init {
    viewModelScope.launch {
        messageFlow
            .catch { update { copy(isLoading = false) } }
            .collectLatest { update { copy(chat = it) } }
    }
}

Key features to explore

Multiple conversations

Create multiple chat threads and switch between them using the side navigation

Conversation history

All your conversations are saved locally and accessible anytime

Dark mode

Toggle between light and dark themes for comfortable viewing

Markdown rendering

AI responses support markdown formatting for code blocks and rich text

How data is stored

GemAI uses a clean architecture approach with three layers:

Data layer

The API key is stored securely using DataStore with Protocol Buffers:
// API keys are encrypted and stored in DataStore
dataStore: DataStore<UserConfig>

Domain layer

Business logic is encapsulated in use cases:
SendMessageUseCase.kt
class SendMessageUseCase @Inject constructor(
    private val chatRepository: ChatRepository,
    @Dispatcher(GemAIDispatchers.IO) private val dispatcher: CoroutineDispatcher,
) : BaseUseCase<Message, Result<Unit, RequestError>> {
    override suspend fun perform(params: Message): Result<Unit, RequestError> =
        withContext(dispatcher) { 
            chatRepository.sendMessage(params) 
        }
}

Presentation layer

ViewModels manage UI state and coordinate use cases:
ChatViewModel.kt
@HiltViewModel
class ChatViewModel @Inject constructor(
    private val createConversationUseCase: CreateConversationUseCase,
    private val sendMessageUseCase: SendMessageUseCase,
    private val getConversationMessagesUseCase: GetConversationMessagesUseCase,
    private val getConversationsUseCase: GetConversationsUseCase,
    // ... other use cases
) : BaseViewModel<ChatUIState, ChatUIEvent, ChatUIAction>()

Troubleshooting

If your API key is rejected:
  1. Verify you copied the entire key without extra spaces
  2. Check that the key is from Google AI Studio (not Google Cloud Console)
  3. Ensure your Google Cloud project has the Generative AI API enabled
  4. Try generating a new API key
If messages aren’t sending:
  1. Check your internet connection
  2. Verify your API key is still valid
  3. Check the Android Logcat for error messages
  4. Ensure the app has network permissions (already configured in manifest)
If your chats disappear:
  1. Check app storage permissions
  2. Verify the app isn’t being force-stopped by battery optimization
  3. Check if device storage is full
  4. Database is stored at: /data/data/com.sarath.gem/databases/gemai.db
If responses are slow:
  1. Check your internet connection speed
  2. Large messages may take longer to process
  3. Google’s API may experience high traffic during peak hours
  4. Consider using a different Gemini model (if customization is added)

Next steps

Now that you’re up and running, explore more advanced features:

Architecture overview

Understand how GemAI is structured with Clean Architecture

Database schema

Learn about the Room database structure and relationships

System prompts

Customize the AI’s behavior with system prompts

Extending GemAI

Learn how to add new features to GemAI
Remember to star the project on GitHub if you find GemAI useful!

Build docs developers (and LLMs) love