{"slug": "frames-of-truth-an-android-app-that-reads-the-story-behind-your-photos", "title": "Frames of Truth: An Android App That Reads the Story Behind Your Photos", "summary": "A developer built Frames of Truth, an open-source Android app that reads GPS coordinates from a photo's EXIF metadata and uses open-weight AI models to generate the story of the location where the photo was taken. The app runs Python 3.8 on-device via Chaquopy with a local Flask server and WebView UI, calling Groq (Qwen, GPT-OSS) and OpenRouter (Llama 3.3) models alongside OpenStreetMap, Wikipedia and Wikidata lookups, and is released under the MIT license with an APK on GitHub.", "body_md": "*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass](https://dev.to/challenges/hacktoberfest-week1-2026-10-05)*\n\nI've always been fascinated by the invisible layers of a place. You walk past a building every day and never wonder who lived there, what happened in that street, or why that monument exists. You take a photo, and all of that context stays invisible.\n\n**Frames of Truth** is my attempt to fix that.\n\nIt's an Android app that takes a photo, reads its GPS coordinates from the EXIF metadata, and uses **open-source AI models** to tell you the story of the place where the photo was taken. Not the story of what's in the photo — the story of *where you were standing* when you took it.\n\nEvery smartphone photo carries hidden metadata: when it was taken, with what device, and — if geolocation was enabled — where. That last piece of information is a GPS coordinate. It tells you *where the camera was*, not necessarily what the camera was pointing at.\n\nThat distinction matters. If you point your phone at a distant hill and shoot from your backyard, the GPS gives your backyard. The photo has your perspective, not the hill's location.\n\nMost apps ignore this. They geotag the photo with a generic address and move on. **Frames of Truth** doesn't. It treats the GPS coordinate as the start of an investigation.\n\nHere's what happens when you load a photo:\n\nIf you want more, there's a second button that generates a deeper **investigative report** with six sections: Location, Surroundings, Wikipedia & Wikidata, Recent news (via GDELT), Photo metadata, and an Investigative hypothesis.\n\nThe UI is in 7 languages: Italian, English, French, German, Spanish, Portuguese, Dutch.\n\nFor anyone who walks past places every day and never wonders what's behind them. For travelers who want context without opening ten tabs. For people who love going outside and want a reason to explore their own neighborhood.\n\nThe app is the opposite of doomscrolling. You *have* to go outside to use it. You have to walk somewhere, point your camera at something, and come back with a real photo of a real place. The screen is the shortest part of the experience. The place is the point.\n\n**APK Download**: [Latest release on GitHub](https://github.com/vincenzodesanctis/frames-of-truth/releases)\n\nInstall it on any Android device (7.0+). On first launch, open the ⚙️ menu and enter your own API keys (Groq, Geoapify — both have free tiers).\n\n`.txt` import feature\nThe full source code is available on GitHub under the MIT license:\n\n**Repository**: [github.com/vincenzodesanctis/frames-of-truth](https://github.com/vincenzodesanctis/frames-of-truth)\n\n| Layer | Technology | \n|---|---|\n| Mobile | Android (Kotlin) with **Chaquopy** for Python integration | \n| Backend | Python (Flask) as a local HTTP server | \n| Frontend | WebView with HTML/CSS/JS | \n| AI Models | **Open-weight** via Groq (Qwen 3.8, GPT-OSS) and OpenRouter (Llama 3.3) | \n| Geocoding | OpenStreetMap (Photon, Nominatim, Overpass, Wikipedia, Wikidata) | \n| EXIF/HEIC | **MetadataExtractor** (Drew Noakes) | \n\nThe entire AI pipeline is built on open-weight models:\n\nThe choice of open-weight models wasn't an afterthought — it was the design constraint. I wanted to be able to swap providers without rewriting the app, and to avoid being locked into any single vendor.\n\nI used [Chaquopy](https://chaquo.com/chaquopy/) to run Python 3.8 directly on the phone. Flask serves a local HTTP server on `127.0.0.1:5000`, and a WebView loads the UI from there. It's a bit unusual, but it means I can reuse all my Python logic — geocoding, POI discovery, Wikipedia lookups, LLM calls — inside an Android app without rewriting anything in Kotlin.\n\n**HEIC support**: Modern Android phones and iPhones save photos as `.heic` files by default. Python's PIL library doesn't read HEIC natively. My solution: detect HEIC files on the Kotlin side (reading the magic bytes `ftyp`), extract the EXIF metadata **before** converting, then convert the image to JPEG for the vision model. This way I preserve GPS coordinates even though the file format changes.\n\n**GPS redaction on Android 10+**: Starting with Android 10, the OS redacts EXIF GPS data from any file read through a content URI — unless the app requests the `ACCESS_MEDIA_LOCATION` permission. Without it, every photo comes back with `lat: null`. With it, the coordinates come through intact. This one took me days to figure out, and required using `MediaStore.setRequireOriginal()` on the URI.\n\n**User-configurable API keys**: Instead of hardcoding my keys, I built an in-app settings menu with a modal that lets users enter their own keys or import them from a `.txt` file. The keys are saved in `SharedPreferences` and pushed to the Python layer via a JavaScript Interface bridge. They never leave the device.\n\nThe challenge mentions \"an open-weight model that works with no signal.\" Frames of Truth currently requires an internet connection for AI inference — but the architecture is designed to be **portable to on-device models**.\n\nHere's how:\n\nThe reason I built it this way is that open-weight models are moving fast. Today they need a GPU in the cloud. Tomorrow they'll run on your phone. Frames of Truth is ready for either world.\n\nBuilding this app on open-weight models wasn't just a cost-saving decision. It changed how I think about software.\n\n**No vendor lock-in.** If Groq changes its pricing tomorrow or shuts down its free tier, I can swap to OpenRouter, Together AI, or a self-hosted model. The app code doesn't change. Only one API call changes.\n\n**Portability.** Because the AI pipeline is provider-agnostic, the app can eventually run a small local model on the phone itself. Closed APIs make this impossible — you can't run GPT-4 on a phone, but you *can* run Llama 3.2 or Gemma 2.\n\n**Transparency.** Anyone can read the pipeline end-to-end. They can see exactly which model processes the photo, what prompt is sent, and how the response is structured. There's no black box.\n\n**Community-driven improvement.** The whole project relies on open data and open infrastructure: OpenStreetMap for geocoding, Wikipedia and Wikidata for context, GDELT for news. Every one of these is maintained by a global community of volunteers.\n\n**Cost control for indie developers.** A solo developer can't afford to pay per-token for a closed API on a hobby project. Free tiers on Groq and OpenRouter make this kind of experiment possible.\n\nIf I had built this on a closed vision API, I would have been locked into:\n\nWith open-weight models, I keep control. That's the whole point of open innovation.\n\nI did not use DevRelay for this project, so there's no agent session to embed. The whole project was built iteratively by hand, testing each layer on a real Android device and debugging via Logcat.\n\nBuilding this app taught me that open-source AI isn't just a cost-saving alternative to closed models. It's a different way of building software.\n\nWhen I can see the model, swap the provider, and read the pipeline end-to-end, I feel like I'm actually in control of the product. I don't wake up one day to find that a service I depend on has changed its terms, or started charging 10x more, or shut down its free tier.\n\nFor an indie project like this, that kind of control is everything.\n\nThe best part of building Frames of Truth was that it made me want to go outside and test it. Every time I took a photo, I learned something new about a place I thought I already knew.\n\nThat's what \"Touch Grass\" should mean: not abandoning technology, but using it to reconnect with the world. Open-source AI, done right, is a bridge — not a wall.\n\nIf you try the app, let me know what your neighborhood looks like through its eyes. 🌍\n\n*Built with ❤️ for the Hacktoberfest Open-Source AI Challenge 2026.*", "url": "https://wpnews.pro/news/frames-of-truth-an-android-app-that-reads-the-story-behind-your-photos", "canonical_source": "https://dev.to/vincenzo_desanctis_94502/frames-of-truth-an-android-app-that-reads-the-story-behind-your-photos-1bdl", "published_at": "2026-10-11 20:58:13+00:00", "updated_at": "2026-10-11 21:03:40.241343+00:00", "lang": "en", "topics": ["ai-products", "ai-tools", "large-language-models", "generative-ai", "developer-tools"], "entities": ["Frames of Truth", "Groq", "OpenRouter", "Chaquopy", "OpenStreetMap", "Wikipedia", "Wikidata", "GDELT"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/frames-of-truth-an-android-app-that-reads-the-story-behind-your-photos", "markdown": "https://wpnews.pro/news/frames-of-truth-an-android-app-that-reads-the-story-behind-your-photos.md", "text": "https://wpnews.pro/news/frames-of-truth-an-android-app-that-reads-the-story-behind-your-photos.txt", "jsonld": "https://wpnews.pro/news/frames-of-truth-an-android-app-that-reads-the-story-behind-your-photos.jsonld"}}