What Was Built
A focused polish pass fixing a persistent Gemini response bug, improving resilience for all LLM providers, and fixing a UI regression at maximum system font size.
The Gemini JSON Bug (Root Cause Found)
For several sessions, Gemini was displaying {"answer":"Based on your notes... as the literal answer text instead of the parsed value. Multiple attempts to fix Gson deserialization didn't resolve it.
The actual root cause was maxOutputTokens: 512. Gemini's structured JSON response was getting cut off before the closing }, producing malformed JSON. JSONObject threw, lastIndexOf('}') returned -1, and the raw string fell through to the answer field.
Fixes applied:
-
Raised token limits โ All providers now use 4096 output tokens. Gemini omits
maxOutputTokensentirely for search calls, using the model's default (8192). -
Replaced Gson with
JSONObjectโ Android's built-inJSONObject(json).optString("answer", "")has no generic type-erasure issues and handles malformed input gracefully. Removed theLlmJsonResponsedata class entirely. -
Truncation fallback โ Even if the JSON is malformed (no closing
}), a string walk extracts whatever answer text was generated before the cutoff:val answerKey = cleaned.indexOf("\"answer\"") val colonIdx = cleaned.indexOf(':', answerKey + 8) val quoteOpen = cleaned.indexOf('"', colonIdx + 1) val quoteClose = cleaned.indexOf('"', quoteOpen + 1) val answer = if (quoteClose == -1) cleaned.substring(quoteOpen + 1).trim() else cleaned.substring(quoteOpen + 1, quoteClose).trim()
Token Limit Warnings
Added a yellow warning box to both Settings โ LLM and the onboarding tutorial (Ask AI page):
"AI responses are limited to 4,096 tokens. Very long responses may be cut off mid-sentence."
The right place for this information is at the moment the user is configuring the feature โ not in a README.
Calendar Heatmap: Large Font Fix
At maximum system font size, the month labels in the Note Activity heatmap (labelMedium โ sp-based) grew wider than the 13dp cell boxes, making each column wider and overflowing the card.
Fix: Wrap month labels in CompositionLocalProvider with fontScale = 1f:
val d = LocalDensity.current
CompositionLocalProvider(
LocalDensity provides Density(density = d.density, fontScale = 1f)
) {
Text(monthName, style = MaterialTheme.typography.labelMedium, ...)
}The labels are decorative (heatmap axis ticks), so locking them to font scale 1.0 is appropriate. The rest of the screen still respects the user's accessibility setting.
Files Changed
| File | Change |
|---|---|
LlmRepository.kt | Replaced Gson parsing with JSONObject, removed LlmJsonResponse, raised maxTokens to 4096 (Gemini: unlimited), added truncation fallback |
SettingsLlmScreen.kt | Added yellow token-limit warning box |
OnboardingScreen.kt | Added same warning on Ask AI tutorial page |
CalendarScreen.kt | Wrapped month labels in fontScale = 1f provider |