diff --git a/src/it/reyboz/bustorino/adapters/AdapterListener.java b/src/it/reyboz/bustorino/adapters/AdapterListener.java --- a/src/it/reyboz/bustorino/adapters/AdapterListener.java +++ b/src/it/reyboz/bustorino/adapters/AdapterListener.java @@ -1,3 +1,20 @@ +/* + BusTO - Adapter 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.adapters; import it.reyboz.bustorino.backend.Stop; diff --git a/src/it/reyboz/bustorino/adapters/PalinaAdapter.java b/src/it/reyboz/bustorino/adapters/PalinaAdapter.java --- a/src/it/reyboz/bustorino/adapters/PalinaAdapter.java +++ b/src/it/reyboz/bustorino/adapters/PalinaAdapter.java @@ -30,15 +30,15 @@ import android.widget.ArrayAdapter; import android.widget.TextView; - import java.util.List; +import java.util.List; import java.util.Locale; -import it.reyboz.bustorino.BuildConfig; import it.reyboz.bustorino.R; import it.reyboz.bustorino.backend.Palina; import it.reyboz.bustorino.backend.Passaggio; import it.reyboz.bustorino.backend.Route; import it.reyboz.bustorino.backend.utils; +import it.reyboz.bustorino.util.RouteSorterByArrivalTime; /** * This once was a ListView Adapter for BusLine[]. @@ -60,7 +60,7 @@ private static final int tramIcon = R.drawable.tram; private final String KEY_CAPITALIZE; - private Capitalize capit = Capitalize.DO_NOTHING; + private Capitalize capit; //private static final int cityIcon = R.drawable.city; @@ -89,6 +89,25 @@ super(context, row_layout, p.queryAllRoutes()); li = LayoutInflater.from(context); sort(new RouteSorterByArrivalTime()); + /* + sort(new Comparator() { + + @Override + public int compare(Route route, Route t1) { + LinesNameSorter sorter = new LinesNameSorter(); + if(route.getNameForDisplay()!= null){ + if(t1.getNameForDisplay()!=null){ + return sorter.compare(route.getNameForDisplay(), t1.getNameForDisplay()); + } + else return -1; + } else if(t1.getNameForDisplay()!=null){ + return +1; + } + else return 0; + } + }); + + */ KEY_CAPITALIZE = context.getString(R.string.pref_arrival_times_capit); SharedPreferences defSharPref = PreferenceManager.getDefaultSharedPreferences(context); defSharPref.registerOnSharedPreferenceChangeListener(this); diff --git a/src/it/reyboz/bustorino/adapters/StopRecyclerAdapter.java b/src/it/reyboz/bustorino/adapters/StopRecyclerAdapter.java --- a/src/it/reyboz/bustorino/adapters/StopRecyclerAdapter.java +++ b/src/it/reyboz/bustorino/adapters/StopRecyclerAdapter.java @@ -1,3 +1,20 @@ +/* + BusTO - Adapter 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.adapters; import android.util.Log; diff --git a/src/it/reyboz/bustorino/backend/Palina.java b/src/it/reyboz/bustorino/backend/Palina.java --- a/src/it/reyboz/bustorino/backend/Palina.java +++ b/src/it/reyboz/bustorino/backend/Palina.java @@ -26,8 +26,11 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; +import java.util.Comparator; import java.util.List; +import it.reyboz.bustorino.util.LinesNameSorter; + /** * Timetable for multiple routes.
*
@@ -91,83 +94,42 @@ * @return array index for this route */ public int addRoute(String routeID, String destinazione, Route.Type type) { - this.routes.add(new Route(routeID, destinazione, type, new ArrayList<>(6))); - routesModified = true; - return this.routes.size() - 1; // last inserted element and pray that direct access to ArrayList elements really is direct + return addRoute(new Route(routeID, destinazione, type, new ArrayList<>(6))); } public int addRoute(Route r){ this.routes.add(r); routesModified = true; - return this.routes.size()-1; + buildRoutesString(); + return this.routes.size()-1; // last inserted element and pray that direct access to ArrayList elements really is direct } + public void setRoutes(List routeList){ routes = new ArrayList<>(routeList); } + @Nullable + @Override + protected String buildRoutesString() { + // no routes => no string + if(routes == null || routes.size() == 0) { + return ""; + } + final StringBuilder sb = new StringBuilder(); + final LinesNameSorter nameSorter = new LinesNameSorter(); + Collections.sort(routes, (o1, o2) -> nameSorter.compare(o1.getName().trim(), o2.getName().trim())); + int i, lenMinusOne = routes.size() - 1; + + for (i = 0; i < lenMinusOne; i++) { + sb.append(routes.get(i).getName().trim()).append(", "); + } + // last one: + sb.append(routes.get(i).getName()); + + setRoutesThatStopHereString(sb.toString()); + return routesThatStopHereToString(); + } -// /** -// * Clears a route timetable (or creates an empty route) and returns its index -// * -// * @param routeID name -// * @param destinazione end of line\terminus -// * @return array index for this route -// */ -// public int updateRoute(String routeID, String destinazione) { -// int s = this.routes.size(); -// RouteInternal r; -// -// for(int i = 0; i < s; i++) { -// r = routes.get(i); -// if(r.name.compareTo(routeID) == 0 && r.destinazione.compareTo(destinazione) == 0) { -// // capire se รจ possibile che ci siano stessa linea e stessa destinazione su 2 righe diverse del sito e qui una sovrascrive l'altra (probabilmente no) -// r.updateFlag(); -// r.deletePassaggio(); -// return i; -// } -// } -// -// return this.addRoute(routeID, destinazione); -// } -// -// /** -// * Deletes routes marked as "not updated" (= disappeared from the GTT website\API\whatever). -// * Sets all remaining routes to "not updated" because that's how this contraption works. -// */ -// public void finishUpdatingRoutes() { -// RouteInternal r; -// -// for(Iterator itr = this.routes.iterator(); itr.hasNext(); ) { -// r = itr.next(); -// if(r.unupdateFlag()) { -// itr.remove(); -// } -// } -// } -// /** -// * Gets the current timetable for a route. Returns null if the route doesn't exist. -// * This is slower than queryRouteByIndex. -// * -// * @return timetable (passaggi) -// */ -// public List queryRoute(String routeID) { -// for(Route r : this.routes) { -// if(routeID.equals(r.name)) { -// return r.getPassaggi(); -// } -// } -// -// return null; -// } -// -// /** -// * Gets the current timetable for this route, from its index in the array. -// * -// * @return timetable (passaggi) -// */ -// public List queryRouteByIndex(int index) { -// return this.routes.get(index).getPassaggi(); -// } protected void checkPassaggi(){ Passaggio.Source mSource = null; for (Route r: routes){ @@ -222,6 +184,7 @@ public int addInfoFromRoutes(List additionalRoutes){ if(routes == null || routes.size()==0) { this.routes = new ArrayList<>(additionalRoutes); + buildRoutesString(); return routes.size(); } int count=0; @@ -280,6 +243,7 @@ //MERGE INFO if(r.mergeRouteWithAnother(selected)) count++; } + if (count> 0) buildRoutesString(); return count; } diff --git a/src/it/reyboz/bustorino/backend/Stop.java b/src/it/reyboz/bustorino/backend/Stop.java --- a/src/it/reyboz/bustorino/backend/Stop.java +++ b/src/it/reyboz/bustorino/backend/Stop.java @@ -115,12 +115,16 @@ this.routesThatStopHere = routesThatStopHere; } + protected void setRoutesThatStopHereString(String routesStopping){ + this.routesThatStopHereString = routesStopping; + } + @Nullable protected List getRoutesThatStopHere(){ return routesThatStopHere; } - private @Nullable String buildRoutesString() { + protected @Nullable String buildRoutesString() { // no routes => no string if(this.routesThatStopHere == null || this.routesThatStopHere.size() == 0) { return null; diff --git a/src/it/reyboz/bustorino/backend/gtfs/GtfsDataParser.java b/src/it/reyboz/bustorino/backend/gtfs/GtfsDataParser.java --- a/src/it/reyboz/bustorino/backend/gtfs/GtfsDataParser.java +++ b/src/it/reyboz/bustorino/backend/gtfs/GtfsDataParser.java @@ -1,3 +1,20 @@ +/* + BusTO - Backend 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.backend.gtfs; import android.content.Context; @@ -162,7 +179,7 @@ stream.close(); } - public static void readCSVWithColumns(BufferedReader reader, String tableName, Context con) throws IOException { + public static void readCSVWithColumns(BufferedReader reader, String tableName, Context con) { //String[] elements; List lineElements; @@ -255,7 +272,7 @@ public static List readCsvLine(String line) throws IllegalArgumentException { - List list = new ArrayList(); + List list = new ArrayList<>(); line += ","; for (int x = 0; x < line.length(); x++) diff --git a/src/it/reyboz/bustorino/backend/mato/MapiArrivalRequest.java b/src/it/reyboz/bustorino/backend/mato/MapiArrivalRequest.java --- a/src/it/reyboz/bustorino/backend/mato/MapiArrivalRequest.java +++ b/src/it/reyboz/bustorino/backend/mato/MapiArrivalRequest.java @@ -1,3 +1,20 @@ +/* + BusTO - Backend 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.backend.mato; import android.util.Log; @@ -52,7 +69,7 @@ data.put("variables", variables); - data.put("query", MatoAPIFetcher.QUERY_ARRIVALS); + data.put("query", MatoQueries.QUERY_ARRIVALS); } catch (JSONException e) { e.printStackTrace(); throw new AuthFailureError("Error with JSON enconding",e); @@ -99,11 +116,4 @@ } return Response.success(p, HttpHeaderParser.parseCacheHeaders(response)); } - - - @Nullable - @Override - protected Map getParams() throws AuthFailureError { - return new HashMap<>(); - } } diff --git a/src/it/reyboz/bustorino/backend/mato/MapiVolleyRequest.java b/src/it/reyboz/bustorino/backend/mato/MapiVolleyRequest.java --- a/src/it/reyboz/bustorino/backend/mato/MapiVolleyRequest.java +++ b/src/it/reyboz/bustorino/backend/mato/MapiVolleyRequest.java @@ -24,10 +24,6 @@ } - @Nullable - @Override - abstract protected Map getParams() throws AuthFailureError; - @Override protected void deliverResponse(T response) { listener.onResponse(response); @@ -37,4 +33,5 @@ public Map getHeaders() throws AuthFailureError { return MatoAPIFetcher.Companion.getREQ_PARAMETERS(); } + } diff --git a/src/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt b/src/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt --- a/src/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt +++ b/src/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt @@ -1,3 +1,20 @@ +/* + BusTO - Backend 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.backend.mato import android.content.Context @@ -15,21 +32,23 @@ -open class MatoAPIFetcher : ArrivalsFetcher { +open class MatoAPIFetcher(val minNumPassaggi: Int) : ArrivalsFetcher { var appContext: Context? = null set(value) { field = value!!.applicationContext } + constructor(): this(3) + override fun ReadArrivalTimesAll(stopID: String?, res: AtomicReference?): Palina { stopID!! val future = RequestFuture.newFuture() - val now = Calendar.getInstance().time; - var numMinutes = 30; + val now = Calendar.getInstance().time + var numMinutes = 30 var palina = Palina(stopID) var numPassaggi = 0 var trials = 0 - while (numPassaggi < 2 && trials < 4) { + while (numPassaggi < minNumPassaggi && trials < 4) { numMinutes += 15 val request = MapiArrivalRequest(stopID, now, numMinutes * 60, 10, future, future) @@ -38,10 +57,9 @@ return Palina(stopID) } val requestQueue = NetworkVolleyManager.getInstance(appContext).requestQueue - request.setTag(VOLLEY_TAG) + request.setTag(getVolleyReqTag(QueryType.ARRIVALS)) requestQueue.add(request) - try { val palinaResult = future.get(5, TimeUnit.SECONDS) if (palinaResult!=null) { @@ -84,6 +102,44 @@ "DNT" to "1", "Host" to "mapi.5t.torino.it") + fun getVolleyReqTag(type: QueryType): String{ + return when (type){ + QueryType.ALL_STOPS -> VOLLEY_TAG +"_AllStops" + QueryType.ARRIVALS -> VOLLEY_TAG+"_Arrivals" + } + } + + /** + * Get stops from the MatoAPI, set [res] accordingly + */ + fun getAllStopsGTT(context: Context, res: AtomicReference?): List{ + val requestQueue = NetworkVolleyManager.getInstance(context).requestQueue + val future = RequestFuture.newFuture>() + + val request = VolleyAllStopsRequest(future, future) + request.tag = getVolleyReqTag(QueryType.ALL_STOPS) + + requestQueue.add(request) + + var palinaList:List = mutableListOf() + + try { + palinaList = future.get(30, TimeUnit.SECONDS) + + res?.set(Fetcher.Result.OK) + }catch (e: InterruptedException) { + e.printStackTrace() + res?.set(Fetcher.Result.PARSER_ERROR) + } catch (e: ExecutionException) { + e.printStackTrace() + res?.set(Fetcher.Result.SERVER_ERROR) + } catch (e: TimeoutException) { + res?.set(Fetcher.Result.CONNECTION_ERROR) + e.printStackTrace() + } + return palinaList + } + /* fun makeRequest(type: QueryType?, variables: JSONObject) : String{ type.let { val requestData = JSONObject() @@ -91,7 +147,7 @@ QueryType.ARRIVALS ->{ requestData.put("operationName","AllStopsDirect") requestData.put("variables", variables) - requestData.put("query", QUERY_ARRIVALS) + requestData.put("query", MatoQueries.QUERY_ARRIVALS) } else -> { //TODO all other cases @@ -106,6 +162,7 @@ } return "" } + */ fun parseStopJSON(jsonStop: JSONObject): Palina{ val latitude = jsonStop.getDouble("lat") val longitude = jsonStop.getDouble("lon") @@ -118,6 +175,7 @@ val routesStoppingJSON = jsonStop.getJSONArray("routes") val baseRoutes = mutableListOf() + // get all the possible routes for (i in 0 until routesStoppingJSON.length()){ val routeBaseInfo = routesStoppingJSON.getJSONObject(i) val r = Route(routeBaseInfo.getString("shortName"), Route.Type.UNKNOWN,"") @@ -125,20 +183,26 @@ baseRoutes.add(r) } - - val routesStopTimes = jsonStop.getJSONArray("stoptimesForPatterns") - - for (i in 0 until routesStopTimes.length()){ - val patternJSON = routesStopTimes.getJSONObject(i) - val mRoute = parseRouteStoptimesJSON(patternJSON) - - //val directionId = patternJSON.getJSONObject("pattern").getInt("directionId") - //TODO: use directionId - palina.addRoute(mRoute) - for (r in baseRoutes) { - if (palina.gtfsID != null && r.gtfsId.equals(palina.gtfsID)) { - baseRoutes.remove(r) - break + if (jsonStop.has("desc")){ + palina.location = jsonStop.getString("desc") + } + //there is also "zoneId" which is the zone of the stop (0-> city, etc) + + if(jsonStop.has("stoptimesForPatterns")) { + val routesStopTimes = jsonStop.getJSONArray("stoptimesForPatterns") + + for (i in 0 until routesStopTimes.length()) { + val patternJSON = routesStopTimes.getJSONObject(i) + val mRoute = parseRouteStoptimesJSON(patternJSON) + + //val directionId = patternJSON.getJSONObject("pattern").getInt("directionId") + //TODO: use directionId + palina.addRoute(mRoute) + for (r in baseRoutes) { + if (palina.gtfsID != null && r.gtfsId.equals(palina.gtfsID)) { + baseRoutes.remove(r) + break + } } } } @@ -146,13 +210,11 @@ palina.addRoute(noArrivalRoute) } //val gtfsRoutes = mutableListOf<>() - - return palina } fun parseRouteStoptimesJSON(jsonPatternWithStops: JSONObject): Route{ val patternJSON = jsonPatternWithStops.getJSONObject("pattern") - val routeJSON = patternJSON.getJSONObject("route"); + val routeJSON = patternJSON.getJSONObject("route") val passaggiJSON = jsonPatternWithStops.getJSONArray("stoptimes") val gtfsId = routeJSON.getString("gtfsId").trim() @@ -184,59 +246,19 @@ return route } - const val QUERY_ARRIVALS="""query AllStopsDirect( - ${'$'}name: String - ${'$'}startTime: Long - ${'$'}timeRange: Int - ${'$'}numberOfDepartures: Int - ) { - stops(name: ${'$'}name) { - __typename - lat - lon - gtfsId - code - name - desc - wheelchairBoarding - routes { - __typename - gtfsId - shortName - } - stoptimesForPatterns( - startTime: ${'$'}startTime - timeRange: ${'$'}timeRange - numberOfDepartures: ${'$'}numberOfDepartures - ) { - __typename - pattern { - __typename - headsign - directionId - route { - __typename - gtfsId - shortName - mode - } - } - stoptimes { - __typename - scheduledArrival - realtimeArrival - realtime - realtimeState - } - } - } - } - """ - } + fun makeRequestParameters(requestName:String, variables: JSONObject, query: String): JSONObject{ + val data = JSONObject() + data.put("operationName", requestName) + data.put("variables", variables) + data.put("query", query) + return data + } + + } enum class QueryType { - ARRIVALS, + ARRIVALS, ALL_STOPS } } \ No newline at end of file diff --git a/src/it/reyboz/bustorino/backend/mato/MatoQueries.kt b/src/it/reyboz/bustorino/backend/mato/MatoQueries.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/backend/mato/MatoQueries.kt @@ -0,0 +1,90 @@ +/* + BusTO - Backend 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.backend.mato + +class MatoQueries { + + companion object{ + const val QUERY_ARRIVALS="""query AllStopsDirect( + ${'$'}name: String + ${'$'}startTime: Long + ${'$'}timeRange: Int + ${'$'}numberOfDepartures: Int + ) { + stops(name: ${'$'}name) { + __typename + lat + lon + gtfsId + code + name + desc + wheelchairBoarding + routes { + __typename + gtfsId + shortName + } + stoptimesForPatterns( + startTime: ${'$'}startTime + timeRange: ${'$'}timeRange + numberOfDepartures: ${'$'}numberOfDepartures + ) { + __typename + pattern { + __typename + headsign + directionId + route { + __typename + gtfsId + shortName + mode + } + } + stoptimes { + __typename + scheduledArrival + realtimeArrival + realtime + realtimeState + } + } + } + } + """ + + const val ALL_STOPS_BY_FEEDS=""" + query AllStops(${'$'}feeds: [String!]){ + stops(feeds: ${'$'}feeds) { + + lat + lon + gtfsId + code + name + desc + routes { + gtfsId + shortName + } + } + } + """ + } +} \ No newline at end of file diff --git a/src/it/reyboz/bustorino/backend/mato/VolleyAllStopsRequest.kt b/src/it/reyboz/bustorino/backend/mato/VolleyAllStopsRequest.kt new file mode 100644 --- /dev/null +++ b/src/it/reyboz/bustorino/backend/mato/VolleyAllStopsRequest.kt @@ -0,0 +1,79 @@ +/* + BusTO - Backend 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.backend.mato + +import android.util.Log +import com.android.volley.NetworkResponse +import com.android.volley.Response +import com.android.volley.VolleyError +import com.android.volley.toolbox.HttpHeaderParser +import it.reyboz.bustorino.backend.Palina +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject + +class VolleyAllStopsRequest( + listener: Response.Listener>, + errorListener: Response.ErrorListener, +) : MapiVolleyRequest>( + MatoAPIFetcher.QueryType.ALL_STOPS,listener, errorListener) { + private val FEEDS = JSONArray() + init { + + FEEDS.put("gtt") + } + override fun getBody(): ByteArray { + val variables = JSONObject() + variables.put("feeds", FEEDS) + + val data = MatoAPIFetcher.makeRequestParameters("AllStops", variables, MatoQueries.ALL_STOPS_BY_FEEDS) + + return data.toString().toByteArray() + } + + override fun parseNetworkResponse(response: NetworkResponse?): Response> { + if (response==null) + return Response.error(VolleyError("Null response")) + else if(response.statusCode != 200) + return Response.error(VolleyError("Response not ready, status "+response.statusCode)) + + val stringResponse = String(response.data) + val palinas = ArrayList() + + try { + val allData = JSONObject(stringResponse).getJSONObject("data") + val allStops = allData.getJSONArray("stops") + + for (i in 0 until allStops.length()){ + val jsonStop = allStops.getJSONObject(i) + palinas.add(MatoAPIFetcher.parseStopJSON(jsonStop)) + } + + } catch (e: JSONException){ + Log.e("VolleyBusTO","Cannot parse response as JSON") + e.printStackTrace() + return Response.error(VolleyError("Error parsing JSON")) + + } + return Response.success(palinas, HttpHeaderParser.parseCacheHeaders(response)) + } + companion object{ + val FEEDS_STR = arrayOf("gtt") + + } +} \ No newline at end of file diff --git a/src/it/reyboz/bustorino/data/DBUpdateWorker.java b/src/it/reyboz/bustorino/data/DBUpdateWorker.java --- a/src/it/reyboz/bustorino/data/DBUpdateWorker.java +++ b/src/it/reyboz/bustorino/data/DBUpdateWorker.java @@ -53,6 +53,8 @@ public static final String DEBUG_TAG = "Busto-UpdateWorker"; + private static final long UPDATE_MIN_DELAY= 3*7*24*3600; //3 weeks + public DBUpdateWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); @@ -72,9 +74,14 @@ final boolean isUpdateCompulsory = getInputData().getBoolean(FORCED_UPDATE,false); + final long lastDBUpdateTime = shPr.getLong(DatabaseUpdate.DB_LAST_UPDATE_KEY, 0); + long currentTime = System.currentTimeMillis()/1000; + final int notificationID = showNotification(); Log.d(DEBUG_TAG, "Have previous version: "+current_DB_version +" and new version "+new_DB_version); Log.d(DEBUG_TAG, "Update compulsory: "+isUpdateCompulsory); + /* + SKIP CHECK (Reason: The Old API might fail at any moment) if (new_DB_version < 0){ //there has been an error final Data out = new Data.Builder().putInt(ERROR_REASON_KEY, ERROR_FETCHING_VERSION) @@ -82,9 +89,11 @@ cancelNotification(notificationID); return ListenableWorker.Result.failure(out); } + */ //we got a good version - if (current_DB_version >= new_DB_version && !isUpdateCompulsory) { + if (!(current_DB_version < new_DB_version || currentTime > lastDBUpdateTime + UPDATE_MIN_DELAY ) + && !isUpdateCompulsory) { //don't need to update cancelNotification(notificationID); return ListenableWorker.Result.success(new Data.Builder(). @@ -115,6 +124,8 @@ //update the version in the shared preference final SharedPreferences.Editor editor = shPr.edit(); editor.putInt(DatabaseUpdate.DB_VERSION_KEY, new_DB_version); + currentTime = System.currentTimeMillis()/1000; + editor.putLong(DatabaseUpdate.DB_LAST_UPDATE_KEY, currentTime); editor.apply(); cancelNotification(notificationID); diff --git a/src/it/reyboz/bustorino/data/DatabaseUpdate.java b/src/it/reyboz/bustorino/data/DatabaseUpdate.java --- a/src/it/reyboz/bustorino/data/DatabaseUpdate.java +++ b/src/it/reyboz/bustorino/data/DatabaseUpdate.java @@ -27,12 +27,18 @@ import it.reyboz.bustorino.R; import it.reyboz.bustorino.backend.Fetcher; import it.reyboz.bustorino.backend.FiveTAPIFetcher; +import it.reyboz.bustorino.backend.Palina; import it.reyboz.bustorino.backend.Route; import it.reyboz.bustorino.backend.Stop; +import it.reyboz.bustorino.backend.mato.MatoAPIFetcher; + import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -41,13 +47,15 @@ public class DatabaseUpdate { - public static final String DEBUG_TAG = "BusTO-DBUpdate"; - public static final int VERSION_UNAVAILABLE = -2; - public static final int JSON_PARSING_ERROR = -4; + public static final String DEBUG_TAG = "BusTO-DBUpdate"; + public static final int VERSION_UNAVAILABLE = -2; + public static final int JSON_PARSING_ERROR = -4; + + public static final String DB_VERSION_KEY = "NextGenDB.GTTVersion"; + public static final String DB_LAST_UPDATE_KEY = "NextGenDB.LastDBUpdate"; - public static final String DB_VERSION_KEY = "NextGenDB.GTTVersion"; - enum Result { + enum Result { DONE, ERROR_STOPS_DOWNLOAD, ERROR_LINES_DOWNLOAD } @@ -80,6 +88,7 @@ public static Result performDBUpdate(Context con, AtomicReference gres) { final FiveTAPIFetcher f = new FiveTAPIFetcher(); + /* final ArrayList stops = f.getAllStopsFromGTT(gres); //final ArrayList cpOp = new ArrayList<>(); @@ -88,9 +97,18 @@ return DatabaseUpdate.Result.ERROR_STOPS_DOWNLOAD; } - // return false; //If the commit to the SharedPreferences didn't succeed, simply stop updating the database + + */ final NextGenDB dbHelp = new NextGenDB(con.getApplicationContext()); final SQLiteDatabase db = dbHelp.getWritableDatabase(); + + final List palinasMatoAPI = MatoAPIFetcher.Companion.getAllStopsGTT(con, gres); + if (gres.get() != Fetcher.Result.OK) { + Log.w(DEBUG_TAG, "Something went wrong downloading"); + return DatabaseUpdate.Result.ERROR_STOPS_DOWNLOAD; + + } + //TODO: Get the type of stop from the lines //Empty the needed tables db.beginTransaction(); //db.execSQL("DELETE FROM "+StopsTable.TABLE_NAME); @@ -99,20 +117,20 @@ //put new data long startTime = System.currentTimeMillis(); - Log.d(DEBUG_TAG, "Inserting " + stops.size() + " stops"); - for (final Stop s : stops) { + Log.d(DEBUG_TAG, "Inserting " + palinasMatoAPI.size() + " stops"); + for (final Palina p : palinasMatoAPI) { final ContentValues cv = new ContentValues(); - cv.put(NextGenDB.Contract.StopsTable.COL_ID, s.ID); - cv.put(NextGenDB.Contract.StopsTable.COL_NAME, s.getStopDefaultName()); - if (s.location != null) - cv.put(NextGenDB.Contract.StopsTable.COL_LOCATION, s.location); - cv.put(NextGenDB.Contract.StopsTable.COL_LAT, s.getLatitude()); - cv.put(NextGenDB.Contract.StopsTable.COL_LONG, s.getLongitude()); - if (s.getAbsurdGTTPlaceName() != null) cv.put(NextGenDB.Contract.StopsTable.COL_PLACE, s.getAbsurdGTTPlaceName()); - cv.put(NextGenDB.Contract.StopsTable.COL_LINES_STOPPING, s.routesThatStopHereToString()); - if (s.type != null) cv.put(NextGenDB.Contract.StopsTable.COL_TYPE, s.type.getCode()); - + cv.put(NextGenDB.Contract.StopsTable.COL_ID, p.ID); + cv.put(NextGenDB.Contract.StopsTable.COL_NAME, p.getStopDefaultName()); + if (p.location != null) + cv.put(NextGenDB.Contract.StopsTable.COL_LOCATION, p.location); + cv.put(NextGenDB.Contract.StopsTable.COL_LAT, p.getLatitude()); + cv.put(NextGenDB.Contract.StopsTable.COL_LONG, p.getLongitude()); + if (p.getAbsurdGTTPlaceName() != null) cv.put(NextGenDB.Contract.StopsTable.COL_PLACE, p.getAbsurdGTTPlaceName()); + cv.put(NextGenDB.Contract.StopsTable.COL_LINES_STOPPING, p.routesThatStopHereToString()); + if (p.type != null) cv.put(NextGenDB.Contract.StopsTable.COL_TYPE, p.type.getCode()); + if (p.gtfsID != null) cv.put(NextGenDB.Contract.StopsTable.COL_GTFS_ID, p.gtfsID); //Log.d(DEBUG_TAG,cv.toString()); //cpOp.add(ContentProviderOperation.newInsert(uritobeused).withValues(cv).build()); //valuesArr[i] = cv; @@ -184,16 +202,23 @@ public static void requestDBUpdateWithWork(Context con, boolean forced){ final SharedPreferences theShPr = PreferencesHolder.getMainSharedPreferences(con); final WorkManager workManager = WorkManager.getInstance(con); - PeriodicWorkRequest wr = new PeriodicWorkRequest.Builder(DBUpdateWorker.class, 1, TimeUnit.DAYS) + PeriodicWorkRequest wr = new PeriodicWorkRequest.Builder(DBUpdateWorker.class, 7, TimeUnit.DAYS) .setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.MINUTES) .setConstraints(new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED) .build()) .build(); final int version = theShPr.getInt(DatabaseUpdate.DB_VERSION_KEY, -10); - if (version >= 0 && !forced) + final long lastDBUpdateTime = theShPr.getLong(DatabaseUpdate.DB_LAST_UPDATE_KEY, -10); + if ((version >= 0 || lastDBUpdateTime >=0) && !forced) workManager.enqueueUniquePeriodicWork(DBUpdateWorker.DEBUG_TAG, ExistingPeriodicWorkPolicy.KEEP, wr); else workManager.enqueueUniquePeriodicWork(DBUpdateWorker.DEBUG_TAG, ExistingPeriodicWorkPolicy.REPLACE, wr); } + /* + public static boolean isDBUpdating(){ + return false; + TODO + } + */ } diff --git a/src/it/reyboz/bustorino/data/NextGenDB.java b/src/it/reyboz/bustorino/data/NextGenDB.java --- a/src/it/reyboz/bustorino/data/NextGenDB.java +++ b/src/it/reyboz/bustorino/data/NextGenDB.java @@ -36,7 +36,7 @@ public class NextGenDB extends SQLiteOpenHelper{ public static final String DATABASE_NAME = "bustodatabase.db"; - public static final int DATABASE_VERSION = 2; + public static final int DATABASE_VERSION = 3; public static final String DEBUG_TAG = "NextGenDB-BusTO"; //NO Singleton instance //private static volatile NextGenDB instance = null; @@ -67,6 +67,7 @@ private static final String SQL_CREATE_STOPS_TABLE="CREATE TABLE "+Contract.StopsTable.TABLE_NAME+" ("+ Contract.StopsTable.COL_ID+" TEXT PRIMARY KEY, "+ Contract.StopsTable.COL_TYPE+" INTEGER, "+Contract.StopsTable.COL_LAT+" REAL NOT NULL, "+ Contract.StopsTable.COL_LONG+" REAL NOT NULL, "+ Contract.StopsTable.COL_NAME+" TEXT NOT NULL, "+ + StopsTable.COL_GTFS_ID+" TEXT, "+ Contract.StopsTable.COL_LOCATION+" TEXT, "+Contract.StopsTable.COL_PLACE+" TEXT, "+ Contract.StopsTable.COL_LINES_STOPPING +" TEXT )"; @@ -77,7 +78,7 @@ Contract.StopsTable.COL_LINES_STOPPING +" TEXT )"; public static final String[] QUERY_COLUMN_stops_all = { - StopsTable.COL_ID, StopsTable.COL_NAME, StopsTable.COL_LOCATION, + StopsTable.COL_ID, StopsTable.COL_NAME, StopsTable.COL_GTFS_ID, StopsTable.COL_LOCATION, StopsTable.COL_TYPE, StopsTable.COL_LAT, StopsTable.COL_LONG, StopsTable.COL_LINES_STOPPING}; public static final String QUERY_WHERE_LAT_AND_LNG_IN_RANGE = StopsTable.COL_LAT + " >= ? AND " + @@ -125,6 +126,14 @@ DatabaseUpdate.requestDBUpdateWithWork(appContext, true); } + if(oldVersion < 3 && newVersion == 3){ + Log.d("BusTO-Database", "Running upgrades for version 3"); + //add the new column + db.execSQL("ALTER TABLE "+StopsTable.TABLE_NAME+ + " ADD COLUMN "+StopsTable.COL_GTFS_ID+" TEXT "); + + // DatabaseUpdate.requestDBUpdateWithWork(appContext, true); + } } @Override @@ -209,7 +218,9 @@ while(result.moveToNext()) { final String stopID = result.getString(colID).trim(); - final Route.Type type = Route.getTypeFromSymbol(result.getString(colType)); + final Route.Type type; + if(result.getString(colType) == null) type = Route.Type.BUS; + else type = Route.getTypeFromSymbol(result.getString(colType)); String lines = result.getString(colLines).trim(); String locationSometimesEmpty = result.getString(colLocation); @@ -252,6 +263,10 @@ return success; } + int updateLinesStoppingInStop(List stops){ + return 0; + } + public static List splitLinesString(String linesStr){ return Arrays.asList(linesStr.split("\\s*,\\s*")); } @@ -330,19 +345,21 @@ public static final String COL_ID = "stopid"; //integer public static final String COL_TYPE = "stop_type"; public static final String COL_NAME = "stop_name"; + public static final String COL_GTFS_ID = "gtfs_id"; public static final String COL_LAT = "stop_latitude"; public static final String COL_LONG = "stop_longitude"; public static final String COL_LOCATION = "stop_location"; public static final String COL_PLACE = "stop_placeName"; public static final String COL_LINES_STOPPING = "stop_lines"; + @Override public String getTableName() { return TABLE_NAME; } @Override public String[] getFields() { - return new String[]{COL_ID,COL_TYPE,COL_NAME,COL_LAT,COL_LONG,COL_LOCATION,COL_PLACE,COL_LINES_STOPPING}; + return new String[]{COL_ID,COL_TYPE,COL_NAME,COL_GTFS_ID,COL_LAT,COL_LONG,COL_LOCATION,COL_PLACE,COL_LINES_STOPPING}; } } } diff --git a/src/it/reyboz/bustorino/adapters/RouteSorterByArrivalTime.kt b/src/it/reyboz/bustorino/util/RouteSorterByArrivalTime.kt rename from src/it/reyboz/bustorino/adapters/RouteSorterByArrivalTime.kt rename to src/it/reyboz/bustorino/util/RouteSorterByArrivalTime.kt --- a/src/it/reyboz/bustorino/adapters/RouteSorterByArrivalTime.kt +++ b/src/it/reyboz/bustorino/util/RouteSorterByArrivalTime.kt @@ -1,4 +1,4 @@ -package it.reyboz.bustorino.adapters +package it.reyboz.bustorino.util import it.reyboz.bustorino.backend.Route @@ -7,9 +7,9 @@ override fun compare(route1: Route?, route2: Route?): Int { if (route1 == null){ if(route2 == null) return 0 - else return 2; + else return 2 } else if (route2 == null){ - return -2; + return -2 } val passaggi1 = route1.passaggi val passaggi2 = route2.passaggi