-
Notifications
You must be signed in to change notification settings - Fork 292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Integrate Recent Stops Data into External Surveys #1234
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package org.onebusaway.android.database | ||
|
||
import android.content.Context | ||
import androidx.room.Room | ||
|
||
/** | ||
* Provides a singleton instance of the Room database (`AppDatabase`). | ||
* Ensures that only one instance of the database is created and used throughout the application. | ||
*/ | ||
object DatabaseProvider { | ||
private var INSTANCE: AppDatabase? = null | ||
|
||
/** | ||
* Retrieves the singleton instance of the Room database. | ||
* If the instance does not exist, it creates and initializes it. | ||
* | ||
* @return The singleton `AppDatabase` instance. | ||
*/ | ||
fun getDatabase(context: Context): AppDatabase { | ||
return INSTANCE ?: synchronized(this) { | ||
val instance = Room.databaseBuilder( | ||
context.applicationContext, | ||
AppDatabase::class.java, | ||
"app_database" | ||
).build() | ||
INSTANCE = instance | ||
instance | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package org.onebusaway.android.database.recentStops | ||
|
||
import android.content.Context | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.launch | ||
import kotlinx.coroutines.runBlocking | ||
import kotlinx.coroutines.withContext | ||
import org.onebusaway.android.app.Application | ||
import org.onebusaway.android.database.DatabaseProvider | ||
import org.onebusaway.android.database.recentStops.entity.RegionEntity | ||
import org.onebusaway.android.database.recentStops.entity.StopEntity | ||
import org.onebusaway.android.io.elements.ObaStop | ||
|
||
/** | ||
* Manages recent stops data by interacting with the database. | ||
* Handles saving new stops, and retrieving recent stops. | ||
*/ | ||
object RecentStopsManager { | ||
|
||
// Maximum stops count to save | ||
private var MAX_STOP_COUNT = 5 | ||
|
||
/** | ||
* Inserts a region into the database if it does not already exist. | ||
* | ||
* @param regionId The ID of the region to insert. | ||
*/ | ||
private suspend fun insertRegion(context: Context, regionId: Int) { | ||
val db = DatabaseProvider.getDatabase(context) | ||
val regionDao = db.regionDao() | ||
|
||
withContext(Dispatchers.IO) { | ||
val existingRegion = regionDao.getRegionByID(regionId) | ||
if (existingRegion == null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what happens when properties of a region change? (e.g. the server URL changes or the support email address changes?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't save any of them I only save the region ID, currently, this table is customized for the survey feature only and can be extended in the future. |
||
regionDao.insertRegion(RegionEntity(regionId)) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Saves a new stop to the database. If the maximum number of stops is exceeded, deletes the oldest stop. | ||
* | ||
* @param stop The `ObaStop` object to save. | ||
*/ | ||
@JvmStatic | ||
fun saveStop(context: Context, stop: ObaStop) { | ||
val db = DatabaseProvider.getDatabase(context) | ||
val stopDao = db.stopDao() | ||
|
||
CoroutineScope(Dispatchers.IO).launch { | ||
val regionId = Application.get().currentRegion.id.toInt() | ||
val currentTime = System.currentTimeMillis() | ||
val stopCount = stopDao.getStopCount(regionId) | ||
|
||
if (stopCount >= MAX_STOP_COUNT) { | ||
// Delete the oldest stop and insert the new one | ||
stopDao.deleteOldestStop(regionId) | ||
} | ||
|
||
insertRegion(context, regionId) | ||
stopDao.insertStop(StopEntity(stop.id, stop.name, regionId, currentTime)) | ||
} | ||
} | ||
|
||
/** | ||
* Retrieves a list of recent stop IDs for the current region. | ||
* | ||
* @return A list of recent stop IDs or an empty list if none are found. | ||
*/ | ||
fun getRecentStops(context: Context): List<String> { | ||
return runBlocking { | ||
val db = DatabaseProvider.getDatabase(context) | ||
val stopDao = db.stopDao() | ||
|
||
withContext(Dispatchers.IO) { | ||
val regionId = Application.get().currentRegion.id.toInt() | ||
val stops = stopDao.getRecentStopsForRegion(regionId) | ||
stops.map { it.stop_id } | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.onebusaway.android.database.recentStops.dao | ||
|
||
import androidx.room.Dao | ||
import androidx.room.Insert | ||
import androidx.room.OnConflictStrategy | ||
import androidx.room.Query | ||
import org.onebusaway.android.database.recentStops.entity.RegionEntity | ||
|
||
/** | ||
* DAO interface for accessing and modifying the `regions` table in the database. | ||
*/ | ||
|
||
@Dao | ||
interface RegionDao { | ||
@Insert(onConflict = OnConflictStrategy.REPLACE) | ||
suspend fun insertRegion(region: RegionEntity): Long | ||
|
||
@Query("SELECT * FROM regions WHERE :regionId = regionId") | ||
suspend fun getRegionByID(regionId:Int):Int? | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package org.onebusaway.android.database.recentStops.dao | ||
|
||
import androidx.room.Dao | ||
import androidx.room.Insert | ||
import androidx.room.OnConflictStrategy | ||
import androidx.room.Query | ||
import org.onebusaway.android.database.recentStops.entity.StopEntity | ||
/** | ||
* DAO interface for accessing and managing the `stops` table in the database. | ||
*/ | ||
@Dao | ||
interface StopDao { | ||
@Insert(onConflict = OnConflictStrategy.REPLACE) | ||
suspend fun insertStop(stop: StopEntity): Long | ||
|
||
@Query("SELECT * FROM stops WHERE regionId = :regionId") | ||
suspend fun getRecentStopsForRegion(regionId: Int): List<StopEntity> | ||
|
||
@Query("SELECT COUNT(*) FROM stops WHERE regionId = :regionId") | ||
suspend fun getStopCount(regionId: Int): Int | ||
|
||
@Query("DELETE FROM stops WHERE stop_id = (SELECT stop_id FROM stops WHERE regionId = :regionId ORDER BY timestamp ASC LIMIT 1)") | ||
suspend fun deleteOldestStop(regionId: Int) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package org.onebusaway.android.database.recentStops.entity | ||
|
||
import androidx.room.Entity | ||
import androidx.room.PrimaryKey | ||
|
||
/** | ||
* Entity class representing a region in the `regions` table. | ||
* Contains a primary key `regionId` for identifying regions. | ||
*/ | ||
|
||
@Entity(tableName = "regions") | ||
data class RegionEntity( | ||
@PrimaryKey val regionId:Int | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package org.onebusaway.android.database.recentStops.entity | ||
|
||
import androidx.room.Entity | ||
import androidx.room.ForeignKey | ||
import androidx.room.PrimaryKey | ||
|
||
/** | ||
* Entity class representing a stop in the `stops` table. | ||
* Includes a primary key `stop_id`, foreign key `regionId`, stop name, and timestamp. | ||
*/ | ||
|
||
@Entity( | ||
tableName = "stops", | ||
foreignKeys = [ | ||
ForeignKey( | ||
entity = RegionEntity::class, | ||
parentColumns = ["regionId"], | ||
childColumns = ["regionId"], | ||
onDelete = ForeignKey.CASCADE | ||
) | ||
] | ||
) | ||
data class StopEntity( | ||
@PrimaryKey val stop_id: String, | ||
val name: String, | ||
val regionId: Int, | ||
val timestamp: Long | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,6 +59,7 @@ | |
|
||
import org.onebusaway.android.R; | ||
import org.onebusaway.android.app.Application; | ||
import org.onebusaway.android.database.recentStops.RecentStopsManager; | ||
import org.onebusaway.android.io.ObaAnalytics; | ||
import org.onebusaway.android.io.ObaApi; | ||
import org.onebusaway.android.io.elements.ObaArrivalInfo; | ||
|
@@ -231,6 +232,7 @@ public IntentBuilder(Context context, String stopId) { | |
public IntentBuilder(Context context, ObaStop stop, HashMap<String, ObaRoute> routes) { | ||
mIntent = new Intent(context, ArrivalsListFragment.class); | ||
mIntent.setData(Uri.withAppendedPath(ObaContract.Stops.CONTENT_URI, stop.getId())); | ||
RecentStopsManager.saveStop(context,stop); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. <3 |
||
setStopName(stop.getName()); | ||
setStopCode(stop.getStopCode()); | ||
setStopDirection(stop.getDirection()); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍