On this page
Users write in your app all the time: messages, reviews, posts, support tickets. A good writing assistant helps without taking over. It suggests improvements, explains what changed, and never sends anything the user didn't approve.
Features we'll build
- Fix grammar (proofread)
- Change tone: friendlier, more professional
- Make it shorter
- Suggestions shown as previews the user can accept, compare or dismiss
Architecture
The interface
enum class WritingAction { FIX_GRAMMAR, FRIENDLIER, MORE_PROFESSIONAL, SHORTER }
data class Suggestion(val text: String, val source: Source) {
enum class Source { ON_DEVICE, CLOUD }
}
interface WritingAssistant {
suspend fun supports(action: WritingAction): Boolean
suspend fun suggest(action: WritingAction, text: String): List<Suggestion>
}Cloud implementation with a constrained prompt
class CloudWritingAssistant(private val consent: () -> Boolean) : WritingAssistant {
private val model = Firebase.ai(backend = GenerativeBackend.googleAI()).generativeModel(
modelName = "gemini-3.5-flash-lite", // small, fast model is enough; check current names
generationConfig = generationConfig { temperature = 0.3f; maxOutputTokens = 400 },
systemInstruction = content {
text(
"You edit user text. Return ONLY the rewritten text, no explanations or quotes. " +
"Preserve meaning, names, numbers and the original language. Never add new facts."
)
},
)
override suspend fun supports(action: WritingAction) = consent()
override suspend fun suggest(action: WritingAction, text: String): List<Suggestion> {
val instruction = when (action) {
WritingAction.FIX_GRAMMAR -> "Fix grammar, spelling and punctuation only."
WritingAction.FRIENDLIER -> "Make the tone warmer and friendlier."
WritingAction.MORE_PROFESSIONAL -> "Make the tone polite and professional."
WritingAction.SHORTER -> "Make it about half as long."
}
// The user's text is clearly delimited as data, not instructions.
val prompt = "$instruction\n\n<text>\n${text.take(2_000)}\n</text>"
val out = model.generateContent(prompt).text?.trim().orEmpty()
return if (out.isBlank()) emptyList() else listOf(Suggestion(out, Suggestion.Source.CLOUD))
}
}The on-device implementation wraps ML Kit's Proofreading and Rewriting clients, as shown in ML Kit GenAI APIs. A router tries on-device first and falls back to cloud only if consent is given.
ViewModel
data class EditorState(
val text: String = "",
val busy: WritingAction? = null,
val suggestions: List<Suggestion> = emptyList(),
val error: String? = null,
)
class ComposeMessageViewModel(private val assistant: WritingAssistant) : ViewModel() {
private val _state = MutableStateFlow(EditorState())
val state = _state.asStateFlow()
fun onTextChange(t: String) = _state.update { it.copy(text = t, suggestions = emptyList()) }
fun request(action: WritingAction) = viewModelScope.launch {
val text = _state.value.text
if (text.isBlank() || !assistant.supports(action)) return@launch
_state.update { it.copy(busy = action, error = null) }
try {
_state.update { it.copy(suggestions = assistant.suggest(action, text), busy = null) }
} catch (e: Exception) {
if (e is CancellationException) throw e
_state.update { it.copy(busy = null, error = "Couldn't get a suggestion. Try again.") }
}
}
fun accept(s: Suggestion) = _state.update { it.copy(text = s.text, suggestions = emptyList()) }
fun dismiss() = _state.update { it.copy(suggestions = emptyList()) }
}Suggestion UX
@Composable
fun SuggestionCard(s: Suggestion, onAccept: () -> Unit, onDismiss: () -> Unit) {
OutlinedCard(Modifier.fillMaxWidth()) {
Column(Modifier.padding(12.dp)) {
Text(
if (s.source == Suggestion.Source.ON_DEVICE) "Suggestion ยท processed on your device"
else "Suggestion ยท AI-generated (cloud)",
style = MaterialTheme.typography.labelSmall,
)
Text(s.text, Modifier.padding(vertical = 8.dp))
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Button(onClick = onAccept) { Text("Use this") }
TextButton(onClick = onDismiss) { Text("Keep mine") }
}
}
}
}- Never replace text without consent. Show a preview and let the user accept.
- Keep "undo" available after accepting.
- Label the source and processing location.
- Disable actions while a request is in flight, to avoid duplicate calls and quota use.
Safety and privacy
- Prompt injection: user text can contain instructions ("ignore the aboveโฆ"). Delimit it, keep output constrained to text, and never let output trigger actions. See Prompt Injection.
- Privacy: cloud processing needs clear disclosure and consent, and an accurate Data safety form.
- Meaning changes: rewrites can alter facts. Ask the model to preserve names and numbers, and let users review.
Where it fits
Marketplace listings
"Make it more professional" for seller descriptions, reviewed before publishing.
Support tickets
"Fix grammar" helps users describe problems clearly.
Social posts
"Shorter" fits character limits without losing meaning.
Frequently asked questions
Should suggestions appear automatically as users type?
Usually not. On-demand actions respect user intent, avoid distraction, and stay within quotas and costs.
Which languages are supported?
On-device APIs support specific languages per API. Cloud models support more. Detect the language and route accordingly.
How do I measure quality?
Track acceptance rate per action and source, and review a sample of anonymised, consented outputs. See Evaluating AI Responses.
Conclusion
A trustworthy AI writing assistant suggests rather than replaces, runs on-device where possible with an opt-in cloud fallback, uses constrained prompts, and labels where processing happened. Users stay in control, and acceptance rates tell you what's working.
Next step: add another high-value feature with AI-Powered Text Summarization.
Official references
๐ฑ Apps built by Yunok Tech
Real Android apps on Google Play, free to download.
Walzi4K wallpapers for lock and home screenGet it โธ
NotiVaultNotification history: recover dismissed notificationsGet it โธ
Ready to build real Android applications?
Every AI feature still runs inside an Android app that has to be fast, reliable and well structured. Strengthen the Kotlin, Jetpack Compose and architecture skills that AI integrations depend on, then apply these AI concepts with confidence.
Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.
Explore the Android course and take your next step as an Android developer.