Two bugs combined to cause this: (1) Android blocks plain-HTTP traffic by default since API 28, so every call to our HTTP-only local backend was throwing and being silently swallowed; (2) GuideViewModel's loadNearby() returned early on that exception without ever setting isLoading = false, so the spinner never cleared and no error showed. Fix: debug-only manifest override (src/debug/AndroidManifest.xml) enables cleartext traffic for local dev against the Docker backend; GuideViewModel now always resolves isLoading and surfaces a errorMessage + retry() the UI can act on instead of hanging forever. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|---|---|---|
| android | ||
| backend | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| README.md | ||
guideCity
Android walking city-guide app. Detects the user's location, finds nearby landmarks, and narrates historical/architectural information about them by voice while showing a swipeable card stack. Starting city: Moscow.
Architecture
┌────────────────────┐
│ Android app │ Kotlin + Jetpack Compose
│ (android/) │ 2GIS map, on-device TTS,
└─────────┬──────────┘ location, Room favorites
│ HTTPS / REST (JSON)
┌─────────▼──────────┐
│ FastAPI backend │ Python, SQLAlchemy, Alembic
│ (backend/) │ Dockerized
└─────────┬──────────┘
│ SQL
┌─────────▼──────────┐
│ PostgreSQL+PostGIS │ cities / places / place_content
└────────────────────┘
backend/— FastAPI service exposing city/place/nearby-search endpoints, backed by PostgreSQL+PostGIS. Seebackend/for details.android/— Kotlin/Compose app skeleton (MVVM, Hilt, Retrofit, Room, DataStore, 2GIS MapKit, on-device TextToSpeech).
Backend quickstart
cp .env.example .env
docker compose up -d db
docker compose exec api alembic upgrade head # (once api image is built: docker compose up -d --build api adminer first)
docker compose exec api python -m seed.seed_loader --file seed/moscow_gorky_park.yaml
docker compose exec api python -m seed.seed_loader --file seed/moscow_red_square.yaml
docker compose exec api python -m seed.seed_loader --file seed/moscow_city.yaml
curl http://localhost:8000/api/v1/health
curl "http://localhost:8000/api/v1/nearby?lat=55.7525&lon=37.6231"
Swagger UI: http://localhost:8000/docs Adminer (DB inspection): http://localhost:8080
The api service binds 0.0.0.0:8000 (see docker-compose.yml), so it's
also reachable from other devices on the same LAN at
http://<this-machine's-LAN-IP>:8000/ — no extra config needed, just make
sure nothing (firewall, VPN) blocks port 8000 on that interface.
Android quickstart
cp android/local.properties.example android/local.properties
# fill in sdk.dir and DGIS_API_KEY (test key: b4df01a8-61db-4cb9-8286-7e069495987d)
cd android && ./gradlew :app:assembleDebug
Point the app's API base URL (API_BASE_URL in local.properties) at
http://10.0.2.2:8000/ for the emulator, or your machine's LAN IP
(e.g. http://192.168.8.173:8000/) for a physical device on the same
Wi-Fi/LAN.
Notes / current scope
This is iteration 1: Moscow only (Gorky Park, Red Square area, Moscow-City), no user accounts (favorites are local/Room-only), no speed/heading-aware auto content-length selection yet (reserved API params exist, unused).
The 2GIS API key above is used directly via local.properties/BuildConfig
for development convenience — don't ship it as-is in a public repo.