diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java --- a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java +++ b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java @@ -238,7 +238,8 @@ startedFromIntent = tryedFromIntent; //period database check - DBUpdateCheckWorker.Companion.schedulePeriodicCheck(this,false); + //DBUpdateCheckWorker.Companion.schedulePeriodicCheck(this,false); + DBUpdateCheckWorker.Companion.runDBUpdateCheckWorker(this); //Watch for database update DBUpdateWorker.getWorkInfoLiveData(this) diff --git a/app/src/main/java/it/reyboz/bustorino/backend/utils.java b/app/src/main/java/it/reyboz/bustorino/backend/utils.java --- a/app/src/main/java/it/reyboz/bustorino/backend/utils.java +++ b/app/src/main/java/it/reyboz/bustorino/backend/utils.java @@ -35,6 +35,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.text.SimpleDateFormat; +import java.time.OffsetDateTime; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -411,4 +412,8 @@ return new BigDecimal(value).setScale(decimalPlace, RoundingMode.HALF_UP).stripTrailingZeros().doubleValue(); } + + public static long parseFullDateToEpochMilliseconds(String date){ + return OffsetDateTime.parse(date).toInstant().toEpochMilli(); + } } diff --git a/app/src/main/java/it/reyboz/bustorino/data/DBUpdateCheckWorker.kt b/app/src/main/java/it/reyboz/bustorino/data/DBUpdateCheckWorker.kt --- a/app/src/main/java/it/reyboz/bustorino/data/DBUpdateCheckWorker.kt +++ b/app/src/main/java/it/reyboz/bustorino/data/DBUpdateCheckWorker.kt @@ -20,9 +20,24 @@ import android.content.Context import android.content.SharedPreferences import android.util.Log +import androidx.core.app.NotificationCompat import androidx.work.* +import com.android.volley.Response +import com.android.volley.VolleyError +import com.android.volley.toolbox.HttpHeaderParser +import com.android.volley.toolbox.JsonRequest +import com.android.volley.toolbox.RequestFuture +import it.reyboz.bustorino.backend.NetworkVolleyManager +import it.reyboz.bustorino.backend.utils import it.reyboz.bustorino.data.gtfs.GtfsDatabase +import it.reyboz.bustorino.data.gtfs.GtfsStop +import it.reyboz.bustorino.data.gtfs.GtfsTableJsonInserter +import org.json.JSONObject import java.util.concurrent.TimeUnit +import androidx.core.content.edit +import androidx.work.WorkManager.Companion.getInstance +import it.reyboz.bustorino.R +import it.reyboz.bustorino.backend.Notifications /** * Lightweight periodic worker that checks local state and enqueues [DBUpdateWorker] @@ -39,14 +54,15 @@ val lastDBUpdateTime = sharedPrefs.getLong(PreferencesHolder.DB_LAST_UPDATE_KEY, 0L) val currentTime = System.currentTimeMillis() / 1000 - val neverUpdated = currentDBVersion < 0 || lastDBUpdateTime <= 0 + + val neverUpdated = lastDBUpdateTime <= 0 val timeElapsed = currentTime > lastDBUpdateTime + UPDATE_MIN_DELAY val isSpecialCase = checkIfNeedSpecialUpgradeDB(con) - + //Last launch the DB update from MaTO if (neverUpdated || timeElapsed || isSpecialCase) { - Log.d(DEBUG_TAG, "Scheduling DBUpdateWorker") + Log.d(DEBUG_TAG, "Scheduling DBUpdateWorker, never updated: $neverUpdated, timeElapsed : $timeElapsed") DBUpdateWorker.requestDBUpdateUniqueWork(con, forced = true) if(isSpecialCase){ val gtfsDBVer = getGtfsDBVersion(con) @@ -58,15 +74,90 @@ Log.d(DEBUG_TAG, "No update needed") } + //// FIRST, RUN CHECK FOR GTFS DATA + val lastGTFSDatabaseUpdateTime = sharedPrefs.getLong(PreferencesHolder.DB_GTFS_LAST_UPDATE_STOPS, 0L) + val newGtfsUpdateTime = checkGtfsVersion("stops") + + if(newGtfsUpdateTime > lastGTFSDatabaseUpdateTime) { + Log.d(DEBUG_TAG, "Updating GTFS data, tables are more recent") + val downloader = GtfsTableJsonInserter(applicationContext, "stops", GtfsStop.Companion) + + val done = downloader.downloadAndInsertStops() + if(done){ + sharedPrefs.edit { putLong(PreferencesHolder.DB_GTFS_LAST_UPDATE_STOPS, newGtfsUpdateTime) } + } + Log.d(DEBUG_TAG, "Completed download, insert worked: $done") + } + return Result.success() } + override suspend fun getForegroundInfo(): ForegroundInfo { + val context = applicationContext + Notifications.createDBNotificationChannelIfNeeded(context) + + val builder = NotificationCompat.Builder( + context, + Notifications.DB_UPDATE_CHANNELS_ID + ) + .setContentTitle(context.getString(it.reyboz.bustorino.R.string.database_update_msg_notif)) + .setProgress(0, 0, true) + .setPriority(NotificationCompat.PRIORITY_LOW) + builder.setSmallIcon(R.drawable.refresh_line) + + return ForegroundInfo(NOTIFICATION_ID, builder.build()) + } + + + fun checkGtfsVersion(table: String): Long { + val volleyManager = NetworkVolleyManager.getInstance(applicationContext) + val errorListener = Response.ErrorListener { error -> + Log.w(DEBUG_TAG, "Error fetching gtfs version",error) + } + var version: Long = 0 + val future = RequestFuture.newFuture() + //response -> + //} + val req = object : JsonRequest(Method.GET, URL_JSON_GTFS_VERSIONS, null, future, errorListener) { + override fun parseNetworkResponse(p0: com.android.volley.NetworkResponse): Response { + var response:Long = 0 + try{ + val data = JSONObject(String(p0.data)) + val selobj = data.getJSONObject("$table.txt") + val lastDate = selobj.getString("last_modified") + + response = utils.parseFullDateToEpochMilliseconds(lastDate) + } catch (e: Exception){ + //Log.w(DEBUG_TAG, "Error parsing network response",e) + return Response.error(VolleyError(e)) + } + + return Response.success(response, HttpHeaderParser.parseCacheHeaders(p0)) + } + } + volleyManager.requestQueue.add(req) + + try { + version = future.get(120, TimeUnit.SECONDS) + }catch (e: Exception){ + e.printStackTrace() + } + finally { + future.cancel(true) + } + return version + } + companion object { const val DEBUG_TAG = "BusTO-DBUpdateScheduler" const val WORK_NAME = "DBUpdateChecker" + const val URL_JSON_GTFS_VERSIONS = "https://github.com/fabmazz/gtfs_gtt_check/releases/latest/download/versions.json" + + private const val NOTIFICATION_ID = 328918201 + private const val UPDATE_MIN_DELAY = (3 * 24 * 3600L) // fun schedulePeriodicCheck(context: Context, restart: Boolean = false) { @@ -87,6 +178,20 @@ .enqueueUniquePeriodicWork(WORK_NAME, policy, workRequest) } + fun runDBUpdateCheckWorker(context: Context) { + val workManager = getInstance(context) + + val wr = OneTimeWorkRequest.Builder(DBUpdateCheckWorker::class.java) + .setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.SECONDS) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .setConstraints( + Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED) + .build() + ) + .build() + + workManager.enqueueUniqueWork(WORK_NAME, ExistingWorkPolicy.REPLACE, wr) + } @JvmStatic private fun getGtfsDBVersion(context: Context): Int { val gtfsDB = GtfsDatabase.Companion.getGtfsDatabase(context) @@ -103,10 +208,7 @@ //applicationContext.getMainSharedPreferences() val old_version = PreferencesHolder.getGtfsDBVersion(theShPr) - Log.d( - DEBUG_TAG, - "GTFS Database: old version is $old_version, new version is $db_version" - ) + if (old_version < db_version) { //decide update conditions in the future if (old_version < 2 && db_version >= 2) { @@ -115,6 +217,7 @@ } //PreferencesHolder.setGtfsDBVersion(theShPr, db_version) } + Log.d(DEBUG_TAG,"Special update needed: $dataUpdateNeeded, old version: $old_version, new version: $db_version") return dataUpdateNeeded } diff --git a/app/src/main/java/it/reyboz/bustorino/data/DBUpdateWorker.kt b/app/src/main/java/it/reyboz/bustorino/data/DBUpdateWorker.kt --- a/app/src/main/java/it/reyboz/bustorino/data/DBUpdateWorker.kt +++ b/app/src/main/java/it/reyboz/bustorino/data/DBUpdateWorker.kt @@ -30,6 +30,7 @@ import it.reyboz.bustorino.backend.Notifications import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicReference +import androidx.core.content.edit /** * Worker class that runs the DB update, without checking if it is needed or not @@ -89,11 +90,11 @@ } Log.d(DEBUG_TAG, "Update finished successfully!") //update the version in the shared preference - val editor = sharedPrefs.edit() - editor.putInt(PreferencesHolder.DB_GTT_VERSION_KEY, newDBVersion) - val currentTime = System.currentTimeMillis() / 1000 - editor.putLong(PreferencesHolder.DB_LAST_UPDATE_KEY, currentTime) - editor.apply() + sharedPrefs.edit { + putInt(PreferencesHolder.DB_GTT_VERSION_KEY, newDBVersion) + val currentTime = System.currentTimeMillis() / 1000 + putLong(PreferencesHolder.DB_LAST_UPDATE_KEY, currentTime) + } //cancelNotification(NOTIFICATION_ID) return Result.success(Data.Builder().putInt(SUCCESS_REASON_KEY, SUCCESS_UPDATE_DONE).build()) diff --git a/app/src/main/java/it/reyboz/bustorino/data/FavoritesLiveData.java b/app/src/main/java/it/reyboz/bustorino/data/FavoritesLiveData.java deleted file mode 100644 --- a/app/src/main/java/it/reyboz/bustorino/data/FavoritesLiveData.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - BusTO - Data components - Copyright (C) 2021 Fabio Mazza - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ -package it.reyboz.bustorino.data; - - -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.database.Cursor; -import android.database.DatabaseErrorHandler; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import androidx.lifecycle.LiveData; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import it.reyboz.bustorino.BuildConfig; -import it.reyboz.bustorino.backend.Stop; - -public class FavoritesLiveData extends LiveData> implements CustomAsyncQueryHandler.AsyncQueryListener { - private static final String TAG = "BusTO-FavoritesLiveData"; - private final boolean notifyChangesDescendants; - - - @NonNull - private final Context mContext; - - @NonNull - private final FavoritesLiveData.ForceLoadContentObserver mObserver; - private final CustomAsyncQueryHandler queryHandler; - - - private final Uri FAVORITES_URI = AppDataProvider.getUriBuilderToComplete().appendPath( - AppDataProvider.FAVORITES).build(); - - - private final int FAV_TOKEN = 23, STOPS_TOKEN_BASE=220; - - - @Nullable - private List stopsFromFavorites, stopsDone; - - private boolean isQueryRunning = false; - private int stopNeededCount = 0; - - public FavoritesLiveData(@NonNull Context context, boolean notifyDescendantsChanges) { - super(); - mContext = context.getApplicationContext(); - mObserver = new FavoritesLiveData.ForceLoadContentObserver(); - notifyChangesDescendants = notifyDescendantsChanges; - queryHandler = new CustomAsyncQueryHandler(mContext.getContentResolver(),this); - - } - - public void loadData() { - loadData(false); - } - private static Uri.Builder getStopsBuilder(){ - return AppDataProvider.getUriBuilderToComplete().appendPath("stop"); - - } - - private void loadData(boolean forceQuery) { - Log.d(TAG, "loadData() force: "+forceQuery); - - if (!forceQuery){ - if (getValue()!= null){ - //Data already loaded - Log.d(TAG, "Data already loaded"); - return; - } - } - if (isQueryRunning){ - //we are waiting for data, we will get an update soon - Log.d(TAG, "Query is running, abort"); - return; - } - - isQueryRunning = true; - startQuery(); - - } - - private void startQuery(){ - Log.d(TAG, "startQuery for token "+FAV_TOKEN); - queryHandler.startQuery(FAV_TOKEN,null, FAVORITES_URI, UserDB.FAVORITES_COLUMNS_ARRAY, null, null, null); - - } - - public void stopQuery(){ - queryHandler.cancelOperation(FAV_TOKEN); - isQueryRunning = false; - } - - public void forceReload(){ - loadData(true); - } - - @Override - protected void onActive() { - //Log.d(TAG, "onActive()"); - loadData(true); - } - - /** - * Clear the data for the cursor - */ - public void onClear(){ - - ContentResolver resolver = mContext.getContentResolver(); - resolver.unregisterContentObserver(mObserver); - - } - - - @Override - protected void setValue(List stops) { - //Log.d("BusTO-FavoritesLiveData","Setting the new values for the stops, have "+ - // stops.size()+" stops"); - - ContentResolver resolver = mContext.getContentResolver(); - resolver.registerContentObserver(FAVORITES_URI, notifyChangesDescendants,mObserver); - - super.setValue(stops); - } - - @Override - public void onQueryComplete(int token, Object cookie, Cursor cursor) { - if (cursor == null){ - Log.e(TAG, "Null cursor for token "+token); - if(token == FAV_TOKEN){ - //restart query - Log.d(TAG, "Restarting query"); - queryHandler.cancelOperation(FAV_TOKEN); - - isQueryRunning = false; - loadData(true); - } - return; - } - if (token == FAV_TOKEN) { - stopsFromFavorites = UserDB.getFavoritesFromCursor(cursor, UserDB.FAVORITES_COLUMNS_ARRAY); - cursor.close(); - //reset counters - stopNeededCount = stopsFromFavorites.size(); - stopsDone = new ArrayList<>(); - if(stopsFromFavorites.isEmpty()){ - //we don't need to call the other query - setValue(stopsDone); - isQueryRunning = false; - } else - for (int i = 0; i < stopsFromFavorites.size(); i++) { - Stop s = stopsFromFavorites.get(i); - queryHandler.startQuery(STOPS_TOKEN_BASE + i, null, - getStopsBuilder().appendPath(s.ID).build(), - NextGenDB.QUERY_COLUMN_stops_all, null, null, null); - } - - - - } else if(token >= STOPS_TOKEN_BASE){ - final int index = token - STOPS_TOKEN_BASE; - assert stopsFromFavorites != null; - Stop stopUpdate = stopsFromFavorites.get(index); - Stop finalStop; - - List result = NextGenDB.getStopsFromCursorAllFields(cursor); - cursor.close(); - if (result.isEmpty()){ - // stop is not in the DB - finalStop = stopUpdate; - } else { - finalStop = result.get(0); - if (BuildConfig.DEBUG && !(finalStop.ID.equals(stopUpdate.ID))) { - throw new AssertionError("Assertion failed"); - } - finalStop.setStopUserName(stopUpdate.getStopUserName()); - } - if (stopsDone!=null) - stopsDone.add(finalStop); - - stopNeededCount--; - if (stopNeededCount == 0) { - // we have finished the queries - isQueryRunning = false; - Collections.sort(stopsDone); - - setValue(stopsDone); - } - - } - } - - - /** - * Content Observer that forces reload of cursor when data changes - * On different thread (new Handler) - */ - public final class ForceLoadContentObserver - extends ContentObserver { - - public ForceLoadContentObserver() { - super(new Handler(Looper.myLooper())); - } - - @Override - public boolean deliverSelfNotifications() { - return true; - } - - @Override - public void onChange(boolean selfChange) { - Log.d(TAG, "ForceLoadContentObserver.onChange()"); - loadData(true); - } - - } - - -} - diff --git a/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java b/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java --- a/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java +++ b/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java @@ -42,6 +42,7 @@ public static final String PREF_INTRO_ACTIVITY_RUN ="pref_intro_activity_run"; public static final String DB_GTT_VERSION_KEY = "NextGenDB.GTTVersion"; public static final String DB_LAST_UPDATE_KEY = "NextGenDB.LastDBUpdate"; + public static final String DB_GTFS_LAST_UPDATE_STOPS = "GtfsDatabase.LastUpdate.Stops"; public static final String PREF_FAVORITE_LINES = "pref_favorite_lines"; // match the one in preferences.xml diff --git a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt --- a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt +++ b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt @@ -37,7 +37,10 @@ fun getAllStopsIDs() : List @Query("SELECT * FROM "+GtfsStop.DB_TABLE+" WHERE "+GtfsStop.COL_STOP_CODE+" LIKE :queryID") - fun getStopByStopID(queryID: String): LiveData> + fun getStopByStopCode(queryID: String): LiveData + + //@Query("SELECT * FROM ${GtfsStop.DB_TABLE} WHERE ${GtfsStop.COL_STOP_ID} LIKE :stopID") + //fun getStopByGtfsID(stopID: Int) : LiveData @Query("SELECT * FROM "+GtfsShape.DB_TABLE+ " WHERE "+GtfsShape.COL_SHAPE_ID+" LIKE :shapeID"+ diff --git a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsStop.kt b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsStop.kt --- a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsStop.kt +++ b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsStop.kt @@ -17,9 +17,14 @@ */ package it.reyboz.bustorino.data.gtfs +import android.content.Context import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey +import de.siegmar.fastcsv.reader.CsvRecord +import de.siegmar.fastcsv.reader.NamedCsvRecord +import it.reyboz.bustorino.data.GtfsRepository +import it.reyboz.bustorino.data.gtfs.GtfsStop @Entity(tableName = GtfsStop.DB_TABLE) data class GtfsStop( @@ -27,7 +32,7 @@ @ColumnInfo(name= COL_STOP_ID) val internalID: Int, @ColumnInfo(name= COL_STOP_CODE) - val gttStopID: String, + val stopCode: String, @ColumnInfo(name= COL_STOP_NAME) val stopName: String, @ColumnInfo(name= COL_GTT_PLACE) @@ -52,7 +57,8 @@ //valuesByColumn["zone_id"]?.toIntOrNull()!!, Converters.wheelchairFromString(valuesByColumn[COL_WHEELCHAIR]) ) - companion object{ + + companion object : RecordInserter { const val DB_TABLE="stops_gtfs" const val COL_STOP_CODE="stop_code" const val COL_STOP_ID = "stop_id" @@ -71,6 +77,25 @@ //"zone_id", COL_WHEELCHAIR ) + + override fun fromRecord(record: NamedCsvRecord): GtfsStop { + return GtfsStop(record.getField(COL_STOP_ID).toInt(), + record.getField(COL_STOP_CODE), + record.getField(COL_STOP_NAME), + record.getField(COL_GTT_PLACE), + record.getField(COL_LATITUDE).toDouble(), + record.getField(COL_LONGITUDE).toDouble(), + Converters.wheelchairFromString(record.getField(COL_WHEELCHAIR)) + ) + } + + override fun insertInDatabase( + elements: List, + context: Context + ): Boolean { + GtfsRepository(context).gtfsDao.insertStops(elements) + return true + } } override fun getColumns(): Array { diff --git a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsTable.java b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsTable.java --- a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsTable.java +++ b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsTable.java @@ -20,5 +20,4 @@ public interface GtfsTable { String[] getColumns(); - } diff --git a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsTableJsonInserter.kt b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsTableJsonInserter.kt new file mode 100644 --- /dev/null +++ b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsTableJsonInserter.kt @@ -0,0 +1,83 @@ +package it.reyboz.bustorino.data.gtfs + +import android.content.Context +import android.util.Log +import com.android.volley.NetworkResponse +import com.android.volley.Request.Method +import com.android.volley.Response +import com.android.volley.VolleyError +import com.android.volley.toolbox.HttpHeaderParser +import com.android.volley.toolbox.JsonRequest +import com.android.volley.toolbox.RequestFuture +import de.siegmar.fastcsv.reader.CsvReader +import it.reyboz.bustorino.backend.NetworkVolleyManager +import it.reyboz.bustorino.backend.Result +import it.reyboz.bustorino.backend.utils +import it.reyboz.bustorino.data.DBUpdateCheckWorker.Companion.DEBUG_TAG +import it.reyboz.bustorino.data.DBUpdateCheckWorker.Companion.URL_JSON_GTFS_VERSIONS +import it.reyboz.bustorino.data.GtfsRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.json.JSONObject +import java.io.ByteArrayInputStream +import java.util.concurrent.TimeUnit +import java.util.zip.GZIPInputStream + +class GtfsTableJsonInserter( + context: Context, + //callback: Result.Callback + val tableName: String, + val inserter: RecordInserter +) { + private val context = context.applicationContext + + fun downloadAndInsertStops(): Boolean { + val volleyManager = NetworkVolleyManager.getInstance(context) + val url = "$BASE_URL/$tableName.txt.gz" + val errorListener = Response.ErrorListener { error -> + Log.w(DEBUG_TAG, "Error fetching table stops, url $url ",error) + } + val future = RequestFuture.newFuture>() + //response -> + //} + val req = object : JsonRequest>(Method.GET,url,null, future, errorListener) { + override fun parseNetworkResponse(p0: NetworkResponse): Response> { + var stops: List + try{ + val input = GZIPInputStream(ByteArrayInputStream(p0.data)) + + val reader = CsvReader.builder().ofNamedCsvRecord(input) + + stops = reader.map { record -> + inserter.fromRecord(record) + } + + } catch (e: Exception){ + //Log.w(DEBUG_TAG, "Error parsing network response",e) + return Response.error(VolleyError(e)) + } + + return Response.success(stops, HttpHeaderParser.parseCacheHeaders(p0)) + } + } + volleyManager.requestQueue.add(req) + + try { + val stops = future.get(120, TimeUnit.SECONDS) + + inserter.insertInDatabase(stops, context) + }catch (e: Exception){ + e.printStackTrace() + return false + } + finally { + future.cancel(true) + } + return true + } + + companion object { + const val BASE_URL = "https://github.com/fabmazz/gtfs_gtt_check/releases/latest/download" + } +} \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/data/gtfs/RecordInserter.kt b/app/src/main/java/it/reyboz/bustorino/data/gtfs/RecordInserter.kt new file mode 100644 --- /dev/null +++ b/app/src/main/java/it/reyboz/bustorino/data/gtfs/RecordInserter.kt @@ -0,0 +1,11 @@ +package it.reyboz.bustorino.data.gtfs + +import android.content.Context +import de.siegmar.fastcsv.reader.NamedCsvRecord + +interface RecordInserter { + + fun fromRecord(record: NamedCsvRecord): T + + fun insertInDatabase(elements: List, context: Context): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt --- a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt @@ -516,7 +516,7 @@ lastUpdatedPalina = p //set the gtfsID for the alerts if(isAdded){ - //alertsViewModel.setStopFilter(p) + alertsViewModel.setStopFilter(p) } else{ Log.w(DEBUG_TAG, "Cannot filter alerts for palina $p, the fragment is not added") } diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/ServiceAlertsViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/ServiceAlertsViewModel.kt --- a/app/src/main/java/it/reyboz/bustorino/viewmodels/ServiceAlertsViewModel.kt +++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/ServiceAlertsViewModel.kt @@ -31,11 +31,9 @@ import androidx.work.WorkInfo import androidx.work.WorkManager import com.google.transit.realtime.GtfsRealtime.Alert -import it.reyboz.bustorino.backend.NetworkVolleyManager import it.reyboz.bustorino.backend.Stop import it.reyboz.bustorino.data.GtfsAlertDBDownloadWorker import it.reyboz.bustorino.data.GtfsRepository -import it.reyboz.bustorino.data.gtfs.GtfsDatabase import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -44,9 +42,7 @@ class ServiceAlertsViewModel(app: Application) : AndroidViewModel(app) { private val gtfsRepo = GtfsRepository(app) - private val volleyManager = NetworkVolleyManager.getInstance(app) - private val alertsDao = GtfsDatabase.getGtfsDatabase(app).alertsDao() private val workManager = WorkManager.getInstance(app) @@ -68,8 +64,12 @@ gtfsRepo.getAlertsByRouteID(it).map{ l -> l.filter { al->al.isActive(unixTimestamp) }} } - val alertsByStopLiveData = stopGtfsIdToFilter.switchMap { - if(it.gtfsID!=null) gtfsRepo.alertsDao.getAlertsForStopGtfsId(it.gtfsID!!) else MutableLiveData() + //intermediate pass: request stop from GTFS database + private val gtfsStopLiveData = stopGtfsIdToFilter.switchMap { + gtfsRepo.gtfsDao.getStopByStopCode(it.ID) + } + val alertsByStopLiveData = gtfsStopLiveData.switchMap { + gtfsRepo.alertsDao.getAlertsForStopGtfsId("gtt:${it.internalID}") } val allAlertsLiveData = gtfsRepo.alertsDao.getAllAlertsLiveData() @@ -100,13 +100,11 @@ */ /// WE DO NOT KNOW HOW TO GET THE GTFS STOP ID, the one given by MaTO is the same as the stop CODE /// but we need the ID from the stops.txt table of GTT GTFS data - /// DISABLING THIS FUNCTION - /*fun setStopFilter(stop: Stop) { + fun setStopFilter(stop: Stop) { Log.d(DEBUG_TAG, "Setting stop to filter: ${stop.ID} - ${stop.stopDisplayName}, gtfsID: ${stop.gtfsID}") - stopGtfsIdToFilter.value = stop + if(stopGtfsIdToFilter.value?.ID != stop.ID) + stopGtfsIdToFilter.value = stop } - - */ fun setGtfsLineFilter(routeId: String) { routeToFilter.value = routeId } diff --git a/app/src/main/res/layout/fragment_arrivals.xml b/app/src/main/res/layout/fragment_arrivals.xml --- a/app/src/main/res/layout/fragment_arrivals.xml +++ b/app/src/main/res/layout/fragment_arrivals.xml @@ -161,7 +161,7 @@ android:textSize="19sp" android:visibility="gone" /> - - + Italiano Inglese Avvisi per la linea %1$s: + Avvisi per la fermata %1$s: + Controllo degli avvisi disponibili in corso Servizio GPS non trovato sul dispositivo! Download dati avvisi in tempo reale @@ -264,4 +266,6 @@ Segui il sistema Imposta tema scuro o chiaro + + Avvisi di servizio diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -415,5 +415,6 @@ English Checking new alerts now Press back again to close the app + Service alerts