An Android ChatGPT client with text and voice chat capabilities, built with Jetpack Compose.
git clone https://github.com/your-username/JumpChatGPT.git
cd JumpChatGPTThe app requires an OpenAI API key to use chat, online transcription, and TTS features.
Add your key to the local.properties file in the project root (this file is git-ignored and will never be committed):
# local.properties
OPENAI_API_KEY=sk-proj-your-key-hereNote: If the key is missing or empty, the app will show an error message at runtime instead of crashing. Offline features (VOSK transcription, audio import) will still work without a key.
Open the project in Android Studio and run on a device or emulator (API 26+).
- Android Studio Ladybug or newer
- JDK 17
- Android SDK 36 (compileSdk)
- Min SDK 26 (Android 8.0)
- Text chat with streaming responses via OpenAI Chat Completions API (gpt-4o-mini)
- Voice chat with real-time recording, transcription, and spoken AI responses
- Online transcription using OpenAI Whisper API
- Offline transcription using VOSK on-device model (downloaded automatically on first use, ~40 MB)
- Audio file import — transcribe MP3, WAV, M4A, AAC, OGG, FLAC files
- Conversation history with search, stored locally via Room
- Offline support — graceful degradation with connectivity feedback
- Kotlin, Jetpack Compose, Material 3
- Hilt (DI), Room (persistence), OkHttp (networking)
- OpenAI APIs (Chat Completions, Whisper, TTS)
- VOSK (on-device speech recognition)
- Android TextToSpeech (local TTS fallback)
The app follows a layered architecture with clear separation between UI, domain, and data. Dependencies flow inward: UI and data depend on the domain layer, which stays framework-agnostic.
| Layer | Role |
|---|---|
| UI | Jetpack Compose screens, ViewModels, navigation (AppRoute), and reusable components. ViewModels depend only on use cases (and infra such as NetworkMonitor, StringProvider), never on repositories or data types directly. |
| Domain | Models (Conversation, Message, ChatToken, etc.), repository interfaces, and use cases (one per business action). Use cases orchestrate repositories and other use cases; they contain no Android or network/DB specifics. |
| Data | Implementations of domain repository interfaces: Room (conversations, messages), OpenAiRepository (chat, Whisper, TTS). Entity-to-domain mapping lives in EntityMappers.kt. |
Typical flow: Screen → ViewModel (exposes StateFlow) → Use case → Repository(ies). Reactive data is exposed as Flow from repositories and collected in ViewModels.
- MVVM — ViewModels hold UI state in
StateFlow; Compose screens observe state and call ViewModel methods (e.g.sendMessage(),retryFromError()). - Repository — Each data source has an interface in
domain/repository/and an implementation indata/. One implementation can satisfy multiple interfaces (e.g.OpenAiRepositoryimplementsChatCompletionRepository,TranscriptionRepository,SpeechSynthesisRepository). - Use cases — Each significant action has an interface and an implementation that coordinates repositories and other use cases. Injected via Hilt; ViewModels depend on these interfaces only.
- DI (Dagger Hilt) — Modules:
AppModule(DB, HTTP, repositories, dispatchers),UseCaseModule(use case bindings),InfraModule(network, transcription). Qualifiers:@IoDispatcher,@MainDispatcher. ViewModels use@HiltViewModeland@Inject constructor.
app/src/main/java/com/arthurzettler/jump_chatgpt/
├── data/
│ ├── local/ # Room: AppDatabase, Entities, DAOs
│ ├── OpenAiRepository.kt # OpenAI API (chat, Whisper, TTS) — implements 3 repo interfaces
│ ├── ConversationRepositoryImpl.kt
│ ├── MessageRepositoryImpl.kt
│ └── EntityMappers.kt # Entity → domain model mapping
├── di/ # Hilt: AppModule, InfraModule, UseCaseModule, Qualifiers
├── domain/
│ ├── model/ # Conversation, Message, ChatToken, enums (MessageRole, MessageSource)
│ ├── repository/ # ChatCompletionRepository, TranscriptionRepository, SpeechSynthesisRepository,
│ │ # ConversationRepository, MessageRepository
│ └── usecase/ # SendTextMessage, StreamAiResponse, ProcessVoiceTurn, TranscribeAudioPcm,
│ # SpeakText (TTS), RetryFromError, CreateConversation, MessageQuery, etc.
├── network/ # NetworkMonitor (connectivity state)
├── transcription/ # OnDeviceTranscriptionManager (VOSK)
├── ui/ # ChatApp, ChatScreen, VoiceChatScreen, HistoryScreen, ViewModels, AppComponents, Theme
├── util/ # StringProvider, MarkdownUtils, AudioUriToFileHelper
├── MainActivity.kt
├── ChatGPTApplication.kt
└── Theme.kt
app/src/test/.../jump_chatgpt/ # Unit tests (JVM)
├── data/ # OpenAiRepositoryTest
├── domain/usecase/ # Use case tests (StreamAiResponse, SpeakText, TranscribeAudioPcm, etc.)
├── network/ # NetworkMonitorImplTest
├── ui/ # ChatViewModelTest, HistoryViewModelTest, VoiceViewModelTest
├── util/ # MainCoroutineRule, *UtilsTest, *HelperTest
└── ...
app/src/androidTest/.../jump_chatgpt/ # Instrumented tests (device/emulator)
├── ui/ChatScreenUiTest.kt # Compose UI tests (navigation, drawer, send)
└── HiltTestRunner.kt # Hilt test Application runner
This project is part of the Jump challenge.