App Android (Kotlin + Jetpack Compose) che legge un volantino pizzeria da foto, ne struttura le pizze con un LLM e ti lascia filtrare per ingrediente, offline.
Flusso: foto → OCR on-device (ML Kit) → Claude Haiku 4.5 (JSON) → SQLite (Room) → filtro locale.
- JDK 17
- Android SDK (via Android Studio o command-line tools)
- Una chiave API Anthropic
Copia local.properties.example in local.properties e inserisci la tua chiave:
ANTHROPIC_API_KEY=sk-ant-...
La chiave finisce in BuildConfig.ANTHROPIC_API_KEY (non nel codice sorgente).
I keystore (debug condiviso + release) e una copia di riferimento di local.properties
vivono nel repository privato My-Scan-Filter-Menu-secrets,
mai in questo repo pubblico.
- Clona
My-Scan-Filter-Menu-secretscome cartella sorella di questo progetto (../My-Scan-Filter-Menu-secrets, il default atteso daapp/build.gradle.kts). - Se lo cloni altrove, aggiungi in
local.properties(mai committato):secrets.dir=/percorso/assoluto/a/My-Scan-Filter-Menu-secrets - Sincronizza Gradle:
app/build.gradle.ktsleggekeystore.propertiesda quella cartella e configura lesigningConfigsdebugereleaseautomaticamente.
La build release ha isMinifyEnabled = true e isShrinkResources = true (R8): il codice
viene minificato/offuscato, con regole dedicate in app/proguard-rules.pro per non rompere
il parsing JSON di kotlinx.serialization.
Questo zip NON contiene il binario gradle/wrapper/gradle-wrapper.jar né gli script
gradlew/gradlew.bat (è un binario, non generabile da qui). Genera il wrapper una
volta sola, in uno dei due modi:
A) Android Studio (consigliato): File > Open sulla cartella. Al primo sync genera
wrapper e scarica tutto da solo.
B) Riga di comando (serve Gradle installato, es. brew install gradle o SDKMAN):
cd PizzaScanner
gradle wrapper --gradle-version 8.9
Dopo aver generato il wrapper:
./gradlew assembleDebug # produce app/build/outputs/apk/debug/app-debug.apk
./gradlew installDebug # installa su device/emulatore collegato
In VS Code installa le estensioni Kotlin e Gradle for Java per editing e lancio dei task;
la compilazione resta affidata a gradlew.
L'app chiede un solo permesso di sistema, Internet (per contattare Claude). Foto e fotocamera passano dai picker di sistema di Android (galleria e scatto foto), quindi non serve concedere permessi runtime aggiuntivi.
- Nella schermata principale tocca "Leggi menu".
- Scegli la sorgente:
- Sfoglia dalla galleria: seleziona una o più foto del menu già presenti sul telefono.
- Scatta foto al menu: apre la fotocamera; dopo ogni scatto puoi scegliere "Scatta un'altra" (per fotografare più pagine dello stesso menu) o "Procedi".
- Attendi mentre l'app mostra "Leggo il menu…" (OCR on-device + chiamata a Claude Haiku 4.5).
- Il locale letto compare nell'elenco della home, con nome, numero di piatti e telefoni (se presenti sul menu). Se il nome del locale non è leggibile, viene salvato come "fonte sconosciuta" (numerata se ne aggiungi più di uno).
- Nel campo "Cerca locale" in home, digita parte del nome del locale per filtrare l'elenco.
- Apri un locale (tap sulla card) per vedere i suoi piatti.
- Nel campo "Filtra ingrediente" del dettaglio:
funghi→ mostra solo i piatti che contengono "funghi".-funghi(prefisso-) → mostra solo i piatti che non contengono "funghi".
Tieni premuto (long-press) su un locale nella home per aprire il menu azioni:
- Aggiorna menu (rileggi): rifotografa/riseleziona le pagine del menu. Se il nome letto corrisponde al locale, i piatti vengono sostituiti; se risulta diverso, l'app non tocca il locale originale e crea invece un nuovo locale, avvisandoti.
- Elimina locale: cancella il locale e tutti i suoi piatti (richiede conferma).
Nel dettaglio di un locale puoi anche toccare il nome in alto per rinominarlo.
Dal menu ⋮ in alto a destra puoi scegliere tra "Caldo" (default), "Adatta al sistema" e "Scuro freddo". Dallo stesso menu, "Svuota tutto" cancella l'intero database (tutti i locali e i piatti, richiede conferma).
- "Nessun testo rilevato": l'OCR non ha trovato testo nella foto (immagine sfocata, troppo scura o non è un menu). Riprova con una foto più nitida e ben illuminata.
- "Nessun piatto letto": il testo è stato letto ma Claude non vi ha riconosciuto piatti. Verifica che la foto inquadri davvero l'elenco dei piatti.
- "Errore: ..." generico: problema di rete o risposta inattesa dall'API (es. chiave
API mancante/non valida in
local.properties, connessione assente). L'app ritenta automaticamente fino a 3 volte prima di mostrare l'errore. - Avviso "Menu molto lungo": se il menu ha moltissimi piatti, la risposta di Claude può essere troncata; alcuni piatti finali potrebbero mancare. Se serve, rileggi il menu suddividendolo in foto separate.
- Normalizzazione ingredienti: il prompt chiede già minuscolo/singolare, ma per filtri perfetti puoi aggiungere un dizionario di sinonimi lato app.
- Versioni: AGP/Kotlin/Compose sono a versioni stabili collaudate; Android Studio potrebbe proporti aggiornamenti.
- Costo: un volantino è qualche centinaio di token → frazioni di centesimo a scansione.