Skip to content

Database Schema (geotify.db)

Geotify uses Android Room for local persistence. The database file is geotify.db (Schema Version 3).


Entity Tables

1. locations (LocationEntity)

Stores points of interest (POIs) with alias names, GPS coordinates, and geofence parameters.

Column SQL Type Constraints & Defaults Description
id TEXT PRIMARY KEY Unique UUID string.
alias TEXT NOT NULL, COLLATE NOCASE, UNIQUE Unique location name (case-insensitive lookup).
latitude REAL NOT NULL Latitude coordinate (-90.0 to 90.0).
longitude REAL NOT NULL Longitude coordinate (-180.0 to 180.0).
radius_meters REAL NOT NULL, Default 150.0 Geofence trigger radius in meters (min 50m).
notification_responsiveness_ms INTEGER NOT NULL, Default 0 Geofence responsiveness delay hint in ms.

Indices: - index_locations_alias: Unique index on alias for fast case-insensitive lookups. - index_locations_latitude_longitude: Composite index on (latitude, longitude) for spatial queries.


2. reminders (ReminderEntity)

Stores geofenced reminders linked to a location entity.

Column SQL Type Constraints & Defaults Description
id TEXT PRIMARY KEY Unique UUID string.
location_id TEXT NOT NULL, FK -> locations(id) Foreign key linking to the location.
message TEXT NOT NULL Notification text displayed when triggered.
transition_type INTEGER NOT NULL 1 = Enter (Arrival), 2 = Exit (Departure).
is_active INTEGER NOT NULL, Default 1 Whether the reminder is active.
created_at INTEGER NOT NULL Creation timestamp (epoch ms).
is_in_range INTEGER NOT NULL, Default 0 Flag indicating whether device is inside fence.

Foreign Key & Cascading Deletion: - foreignKeys = [ForeignKey(entity = LocationEntity::class, parentColumns = ["id"], childColumns = ["location_id"], onDelete = CASCADE)] - Deleting a location automatically deletes all associated reminders in SQLite.


Key DAO Queries & Atomic Transactions

Definitions in LocationDao.kt and ReminderDao.kt.

Bounding-Box Spatial Query

To pre-filter candidate locations for sliding window recalculations, Room executes a bounding-box query handling International Date Line (180° meridian) wrap-around:

SELECT * FROM locations 
WHERE latitude BETWEEN :minLat AND :maxLat 
  AND (
    (:minLon <= :maxLon AND longitude BETWEEN :minLon AND :maxLon)
    OR 
    (:minLon > :maxLon AND (longitude >= :minLon OR longitude <= :maxLon))
  )

Atomic Range Status Update (@Transaction)

When spatial recalculations occur, updateInRangeStatus clears and updates is_in_range flags inside a single atomic SQLite transaction:

@Transaction
suspend fun updateInRangeStatus(locationIds: List<String>) {
    clearAllInRange()
    if (locationIds.isNotEmpty()) {
        setInRangeForLocations(locationIds)
    }
}

Thread Safety & Non-Blocking I/O

Repository operations (LocationRepository and ReminderRepository) enforce thread safety by injecting @IoDispatcher CoroutineDispatcher and executing DAO operations inside withContext(ioDispatcher). UI components observe changes asynchronously via Room Flow streams.