Integrate Gemini Nano (AICore) into the app. Use when implementing or modifying QuizGeneratorRepository, PromptBuilder, TextChunker, availability checks, FakeQuizGeneratorRepository, or JSON quiz output parsing. Covers on-device model availability guards, token-window limits, and Kotlinx Serialization.
QuizGeneratorRepository AICore backendPromptBuilder or TextChunkerFakeQuizGeneratorRepository for emulator/CITextChunker must cap each chunk accordingly.QuizGeneratorRepository interface — never call AICore directly from a ViewModel.Always guard AICore calls before use:
val availability = generativeModel.checkAvailability()
if (availability != AvailabilityStatus.AVAILABLE) {
return Result.Failure(ModelUnavailableException(availability))
}
data/TextChunker.kt; implementation is TextChunkerImpl, injected via Hilt.MAX_CHUNK_TOKENS (a const val in the companion object).seed: Long parameter for deterministic selection in tests.String.interface TextChunker {
fun randomExcerpt(text: String, seed: Long = Random.nextLong()): String
companion object {
const val MAX_CHUNK_TOKENS = 3_000 // leaves room for prompt overhead
}
}
class TextChunkerImpl @Inject constructor() : TextChunker {
override fun randomExcerpt(text: String, seed: Long): String { … }
}
domain/PromptBuilder.kt; implementation is PromptBuilderImpl, injected via Hilt.String.interface PromptBuilder {
fun build(excerpt: String): String
}
class PromptBuilderImpl @Inject constructor() : PromptBuilder {
override fun build(excerpt: String): String =
"""
Generate a 5-question multiple-choice quiz based on the following text.
Return ONLY a JSON array with this structure (no markdown, no explanation):
[{"question":"…","options":["A)…","B)…","C)…","D)…"],"answer":"A)…"}]
Text:
$excerpt
""".trimIndent()
}
Use Kotlinx Serialization. Define:
@Serializable
data class QuizQuestion(
val question: String,
val options: List<String>,
val answer: String,
)
Parse with:
val questions = Json { ignoreUnknownKeys = true }
.decodeFromString<List<QuizQuestion>>(rawJson)
Wrap in try/catch and surface as Result.Failure(ParseException(…)) on error.
Provide this for emulator/CI where AICore is unavailable:
class FakeQuizGeneratorRepository(
private val response: Result<List<QuizQuestion>> = Result.Success(FAKE_QUESTIONS),
) : QuizGeneratorRepository {
override suspend fun generateQuiz(text: String): Result<List<QuizQuestion>> = response
}
Keep FAKE_QUESTIONS as a const/top-level property in the same file.
// build.gradle.kts (app)
implementation("com.google.ai.edge.aicore:aicore:<version>")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:<version>")