Open source · MIT · Android 8.0+

An agentic AI
layer for Android

It reads the live interface through Android's accessibility tree, works out which control you actually mean, and rings it — then waits for you to tap.

Hindi · Tamil · English spoken and spoken back On-device first — most requests never leave the phone
ScreenSaathi idle, waiting for a request ScreenSaathi listening, with a live microphone waveform A ring drawn around Uber's real Where to field The assistant repositioned above an open keyboard
ScreenSaathi

You

ScreenSaathi

grounded in ScreenSnapshot → node
Button · "Where to?" · clickable · confidence 100

Waiting quietly. Tap it, or circle anything on screen.

The problem

Technology got easier. Interfaces got harder.

A person can know exactly what they want to do and still be stuck, because today's interface expects them to already know where it hid the button. People shouldn't have to learn the interface before they can use it — the software should learn how to help them.

What people are asked to do

  • Recognise an unlabelled icon
  • Know that "⋮" hides the thing they need
  • Read 11px grey text on white
  • Guess which of four buttons is the real one
  • Do it again after the next app update

What they actually want to say

  • “Help me book a taxi.”
  • “Where do I find my ticket?”
  • “Help me pay this bill.”
  • “टैक्सी बुक करने में मेरी मदद करो”

The loop

Speak. Perceive. Decide.
Guide. Continue.

Not a chatbot with a screenshot. A loop that re-reads the screen after every step, so guidance survives the interface changing underneath it.

01

Speak

Say it in Hindi, Tamil or English — or circle something instead.

02

Perceive

Read the live accessibility tree: labels, roles, bounds, state.

03

Decide

Resolve which element is meant. Refuse if it's genuinely ambiguous.

04

Guide

Ring the real control and say what it is. The person taps it.

05

Continue

Screen changed → clear the ring, re-read, find the next step.

Screen understanding

It reads structure,
not just pixels.

Android already publishes what's on screen — every label, role, bound and state — to assistive technology. ScreenSaathi starts there. That's why it can say "the Book now button" instead of describing a picture of one.

The Uber home screen with ScreenSaathi's highlight around the Where to? field
// what the accessibility tree actually exposes node { class "android.widget.Button" text "Where to?" bounds [42,347][1038,505] clickable true enabled true editable false } // ranked match, not a substring guess resolved"Where to?" confidence 100 strategy ACCESSIBILITY_ONLY // nothing left the device

Told, not inferred

A pixels-only assistant has to infer that a rounded rectangle is a button. ScreenSaathi is told it is one — along with whether it's enabled, whether it takes text, and exactly where it sits.

Four wins, one decision

So the common case needs no model at all: private — the screen stays on the phone — free, instant, and checkable. You can see why it matched.

Pixels are an escalation

When the tree is weak or the region is genuinely just an image, PerceptionStrategy says so rather than inventing an answer.

Circle to understand

Circle anything.
Ask anything.
Get help doing it.

The difference isn't the circle — it's what happens after. ScreenSaathi keeps what you selected, so "okay, help me use it" still knows what it is.

A freehand circle being drawn around the Where to? field

You circle it

A freehand region, not a tap target. Nothing has been sent anywhere yet.

It resolves what that is

SelectionResolver ranks the region against the live tree — a clickable button labelled “Book now”, with a confidence score.

“Okay, help me use it.”

No re-selecting. “It” still refers to the circled button — the selection survives the turn.

It rings the real control

You tap. The screen changes. The old ring clears, the new screen is read, and it continues from there.

Traditional visual search

CircleAnswer

ScreenSaathi

Circle Understand Converse Decide Guide Observe Continue

Multilingual

Ask in your language. Get answered in it.

The spoken language is detected, and the reply comes back in the same one — including the assistant's own wording, not just a translated sentence.

YouHelp me book a taxi
ScreenSaathi Which app would you like to use? Guiding you

Verbatim from the app: these are the strings in Phrases.kt and PillLabels.kt.

Being precise: Hindi, Tamil and English are fully authored — the assistant's own words exist in each. The speech models recognise more Indian languages than that, but their interface wording still falls back to English, so those are marked planned rather than claimed. Adding one is a good first contribution →

Safety

The model proposes.
Policy decides.

An assistant that can read your screen needs limits a model cannot argue its way past. Actions are classified before anything is drawn, by deterministic code — not by asking the model whether it thinks it should proceed.

MODEL PROPOSES
ActionPolicy.classify()
SAFE
CONFIRM
BLOCKED

Pick a row — the policy runs on the element's own label, not on what you asked for.

Action policy outcomes. Selecting a row shows which policy level it maps to.
ActionPolicyWhat happens
Reading something aloudSafeProceeds
Ringing a controlSafeProceeds — you do the tapping
Submitting or sendingConfirmStates what it is before you commit
Payments and transfersConfirmNamed explicitly, never a generic “continue?”
Granting app permissionsBlockedRefused outright
Factory reset, device adminBlockedRefused outright
Recovery phrases, private keysBlockedRefused outright

Blocked, not merely confirmed

A confirmation prompt for something that should never happen just trains people to approve prompts. Talking someone through granting permissions is a social-engineering pattern, so it isn't a dialog — it's a refusal.

You are the irreversible step

ScreenSaathi does not tap, type or swipe. The accessibility service deliberately omits gesture permissions. It points; a human decides.

Structural, not advisory

The ruling is enforced again at execution by SafetyGuard, so a later code path cannot route around the decision.

Architecture

Provider-agnostic
by construction.

No vendor name appears in any AI interface in the codebase. Swapping or adding a model provider is one class, not a rewrite, and the app is fully functional with none configured. Every name below is a real file in src/ScreenSaathi.

Perceive

ScreenReaderService

The AccessibilityService itself — window-change events and the live node tree, deliberately without gesture-dispatch permission.

ScreenSnapshot

One flattened, immutable read of the screen. Everything downstream reasons about this, never the live tree.

Resolve

ScreenSelection

The circled region, normalised into screen coordinates the rest of the system can reason about.

SelectionResolver

Ranked element match with a confidence score — not a substring guess, and it declines when nothing clears the bar.

Decide

PerceptionStrategy

Chooses ACCESSIBILITY_ONLY, HYBRID or VISION_ONLY. Pixels are an escalation with a stated reason, never the default.

ModelRouter

Picks a provider behind a vendor-neutral interface. With none configured it falls back to the deterministic StepEngine.

Guard

ActionPolicy

Classifies into SAFE_READ, SAFE_GUIDE, USER_CONFIRMATION_REQUIRED, HIGH_RISK_ACTION or BLOCKED_ACTION — before anything is drawn.

SafetyGuard

Enforces the ruling at the point of execution, so a policy decision cannot be bypassed by a later code path.

Act

SessionController

Owns the conversation: language, task state, the circled selection across turns, and the decision to re-read after every step.

OverlayService · HighlightView

Draws the pill and the highlight over other apps, lifts clear of the keyboard, and clears the ring when the screen changes.

Vision

VisionProvider

Built and tested end-to-end, but no vision provider is currently integrated. Circle a photograph today and ScreenSaathi says it would need visual understanding rather than guessing.

CircleContext

What a provider would receive: the pixels plus the resolved element, its confidence, nearby text, the screen, the conversation and the active task.

Open source

Built in the open.

MIT licensed, with the architecture decisions written down — including the ones that look wrong until you know which measured bug they fixed. Contributions welcome across Android, AI providers, vision, languages, accessibility, evaluation, UX and docs.

NITISH-R-G/ScreenSaathi
LicenseMIT
LanguageKotlin
Minimum Android8.0 · API 26
CIbuild · test · lint · secret scan
Docsarchitecture · decisions · privacy
Latest releaseAPK + SHA256
Issue Pull request Review Merge Release every change reaches the APK this way

Try it

Tell your phone what you need.

A physical Android phone is required — accessibility services, overlay windows and the microphone don't behave meaningfully on an emulator. Running in about two minutes.

01
Install

Download and open the APK. Allow install from unknown sources.

02
Grant

Display over other apps → microphone → accessibility service.

03
Start

Tap Start assistant. A small pill appears.

04
Ask

Open any app, tap the pill, and say what you want to do.