Destinations¶
The destinations feature handles discovery and selection of cycling points of interest. It follows Clean Architecture with di/, domain/, data/, and presentation/ layers.
How It Works¶
- On launch, the app gets the user's location
- The file resolver scans asset files and selects those whose center point is within 100 km of the user
- Selected files are parsed and inserted into the local database (each file is loaded only once)
- The app picks random destinations from the database within a target distance range
Key Use Cases¶
| Use Case | Purpose |
|---|---|
GetDestinationInfoUseCase |
Get random destinations or by specific ID |
GetUserLocationUseCase |
Get current user location |
InitializeDatabaseUseCase |
Seed database on first launch |
Database Consistency Check¶
The destination_files DataStore tracks which JSON asset files have been loaded into the Room
database. If the database is deleted and recreated (e.g., SQLCipher encryption recovery after
switching from debug to release build), the DataStore may still report files as "loaded" while
the database is actually empty.
DestinationsRepositoryImpl.loadDestinationsForLocation() detects this desync: if loaded files
are non-empty but the database has no destinations, it clears the DataStore and reloads from
scratch. The destination_files DataStore is also excluded from Android backup rules to prevent
the same issue on backup restore.
App Recovery Flow¶
When the app restarts with an active session, DestinationsViewModel:
- Checks for active session via
CyclingSessionUseCase.getActiveSessionDestination() - Fetches destination by ID via
GetDestinationInfoUseCase.getDestinations() - Restores UI state including slider position
Destination File Format¶
Destinations are stored as JSON files in the app assets and loaded into a Room database at runtime.
Each file is a JSON array of objects with 4 fields:
| Field | Type | Description |
|---|---|---|
id |
String | Unique identifier ({city}-{number}) |
title |
String | Display name, local name |
lat |
Double | Latitude |
long |
Double | Longitude |
[
{ "id": "tokyo-001", "title": "Imperial Palace Loop (皇居周回)", "lat": 35.6840, "long": 139.7528 },
{ "id": "tokyo-002", "title": "Yoyogi Park", "lat": 35.6728, "long": 139.6949 }
]
File Naming Convention¶
- city — lowercase city name (e.g.,
tokyo,hiroshima) - country — lowercase country name (e.g.,
japan) - centerLat / centerLon — geographic center of the file's coverage area
- tier — integer indicating coverage zone (see below)
Example: destinations_tokyo_japan_35.6812_139.7671_tier1.json
Tiers¶
Tiers organize destinations by distance from the city center. Lower tiers are loaded first.
| Tier | Coverage | ID range example |
|---|---|---|
| 1 | City center | tokyo-001 – 050 |
| 2 | Suburban areas | tokyo-051 – 100 |
| 3 | Wider region | tokyo-101 + |
Adding a New City¶
- Create a JSON file in
feature/destinations/src/main/assets/following the naming convention - Use a unique city prefix for IDs (e.g.,
osaka-001) - Set
centerLat/centerLonin the filename to the city center coordinates - Start with tier 1 (city center destinations) and add higher tiers as needed
- Build and run — the app will automatically discover and load the new file when a user is within 100 km