Android chat client for the Ollama Cloud API — streaming conversations, web search, image analysis, document processing, and LaTeX rendering.
Chat & Streaming
- Real-time SSE streaming with token-by-token rendering
- Conversations persisted in Room with Flow-based reactive queries
- Auto-compact: transparent conversation summarization when approaching context limits
- Auto-generated titles (5 words max, toggleable)
- Foreground service keeps streaming alive when the screen is off
AI Tools
web_searchandweb_fetch— the model decides when to search (up to 3 tool-calling rounds)- Research agent executes searches and auto-fetches result pages
- Visual tool-call cards with green checkmarks and nested fetch results
Vision & Attachments
- Image analysis via compatible models (kimi-k2, gemma4, qwen3.5...)
- Vision fallback: non-vision models delegate to a configurable vision model via
describe_image - Attachments: images (gallery/camera/photo capture), PDFs, text files
- Smart image resize (max 1024px, JPEG quality 85%)
Rich Rendering
- Markdown: tables, headings, links, code blocks via mikepenz renderer
- LaTeX math: display (
$$,\[) and inline ($,<latex>) via bundled KaTeX (0.16.21) - Mixed LaTeX+Markdown rendered in a WebView for pixel-perfect output
- Text selection on all message bubbles
Model Management
- Search, enable/disable, custom context size per model
- Research agent model and vision fallback model configurable
- Recent models (10 most used)
- 25+ pre-configured models with default context sizes and vision/thinking capabilities
Internationalization
- Default language: English
- Full French UI translation (
values-fr/) - Language selector: System / English / Français / Español (no UI) / Deutsch (no UI)
- Adaptive system prompt injects the selected language, but always follows the user's question
Theme & UX
- Dark / Light / System theme
- Thinking mode toggle (shows reasoning content from deepseek models)
- Chat search with real-time filtering
- Drawer-based navigation with persistent ViewModels
- API key: stored in EncryptedSharedPreferences (AES256-GCM, Android Keystore TEE/StrongBox)
- Cloud backup disabled (
allowBackup=false) to prevent key leakage - Prompt sanitization: 13 injection patterns blocked (
[END OF SYSTEM PROMPT],<|im_start|>, etc.) - URL validation (anti-SSRF): blocks localhost, private IPs (RFC 1918), link-local, local IPv6
- Conditional logging: debug/warning only in debug builds
[Add screenshots here]
- Android 9.0 (API 28) or higher
- An Ollama API key
Download the latest APK from the releases page.
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
export ANDROID_HOME="$HOME/Library/Android/sdk"
./gradlew assembleDebugThe APK is generated at app/build/outputs/apk/debug/app-debug.apk.
# Install
adb install -r app/build/outputs/apk/debug/app-debug.apk
# Launch
adb shell am start -n org.canalweb.ollamaapp/.MainActivity# Unit tests (35 tests)
./gradlew testDebugUnitTest
# Instrumentation tests (6 tests, requires emulator)
./gradlew connectedAndroidTestapp/src/main/java/org/canalweb/ollamaapp/
├── MainActivity.kt
├── navigation/
│ └── AppNavigation.kt # Compose NavHost (type-safe routes)
├── data/
│ ├── agent/
│ │ └── ResearchAgent.kt # web_search + web_fetch agent
│ ├── api/
│ │ ├── OllamaApiService.kt # Retrofit interface (chat, models)
│ │ ├── OllamaApiProvider.kt # OkHttp/Retrofit singleton
│ │ ├── OllamaToolApiProvider.kt # Tool API (web_search, web_fetch)
│ │ ├── RetryUtil.kt # Fibonacci backoff retry (max 9, 1-55s)
│ │ └── SseStreamParser.kt # Shared SSE parser
│ ├── database/ # Room v7 (chats, messages, models)
│ ├── storage/
│ │ └── ChatStorage.kt # Room facade
│ └── store/
│ └── SettingsDataStore.kt # EncryptedSharedPreferences + DataStore
├── ui/
│ ├── theme/ # Material3, dynamic colors
│ ├── screens/ # ChatList, Chat, ModelSelection, ModelManagement, Settings, Welcome
│ └── viewmodels/ # ChatList, Chat, ModelSelection, ModelManagement, Settings
└── utils/
├── AppLanguage.kt # Language resolution
├── FileUtils.kt # Base64, image resize, PDF/text extraction
├── HtmlRenderer.kt # CommonMark → HTML + KaTeX
├── Logger.kt # Conditional logging (DEBUG only)
├── TokenCounter.kt # JTokkit-based token counting
└── UrlValidator.kt # Anti-SSRF (blocks private IPs)
| Category | Technology |
|---|---|
| Language | Kotlin |
| UI | Jetpack Compose + Material3 |
| Database | Room (v7 with migrations) |
| Secure storage | EncryptedSharedPreferences (AES256-GCM) |
| HTTP | Retrofit + OkHttp |
| Markdown | multiplatform-markdown-renderer-m3 (mikepenz) |
| Markdown→HTML | CommonMark + commonmark-tables |
| LaTeX | KaTeX 0.16.21 (bundled JS/CSS assets) |
| Images | Coil Compose |
| pdfbox-android 2.0.27.0 | |
| Token counting | JTokkit |
| Build | Gradle 9.4.1 + KSP 2.2.10-2.0.2 |
| Min SDK | API 28 (Android 9.0) |
| Target SDK | API 36 |
The app auto-detects available models via the Ollama API. Context sizes and capabilities (vision, thinking) are pre-configured for 25+ models including deepseek-v4, kimi-k2, gemma4, qwen3, glm, minimax, ministral, nemotron, gemini, and gpt-oss.
This app was developed with significant assistance from DeepSeek-v4-pro, an AI coding assistant that contributed to architecture decisions, code generation, debugging, and optimization throughout the project.
MIT