diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -11,6 +11,7 @@
Inserisci il nome della fermata
Verifica l\'accesso ad Internet!
Sembra che nessuna fermata abbia questo nome
+ Nessun passaggio trovato alla fermata
Errore di lettura del sito 5T/GTT (dannato sito!)
Fermata: %1$s
Linee: %1$s
diff --git a/res/values/strings.xml b/res/values/strings.xml
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -16,6 +16,7 @@
%s (unknown destination)
Verify your Internet connection!
Seems that no bus stop have this name
+ No arrivals found for this stop
Error parsing the 5T/GTT website (damn site!)
Name too short, type more characters and retry
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/Fetcher.java b/src/it/reyboz/bustorino/backend/Fetcher.java
--- a/src/it/reyboz/bustorino/backend/Fetcher.java
+++ b/src/it/reyboz/bustorino/backend/Fetcher.java
@@ -28,10 +28,11 @@
* for 404 special constant (see @FiveTAPIFetcher)
* PARSER_ERROR: the server replied something that can't be parsed, probably it's not the data we're looking for (e.g. "PHP: Fatal Error")
* EMPTY_RESULT_SET: the response is valid and indicates there are no stops\routes\"passaggi"\results for your query
+ * NOT_FOUND: response is valid, no parsing errors, but the desired stops/routes wasn't found
* QUERY_TOO_SHORT: input more characters and retry.
*/
enum Result {
OK, CLIENT_OFFLINE, SERVER_ERROR, SETUP_ERROR,PARSER_ERROR, EMPTY_RESULT_SET, QUERY_TOO_SHORT, SERVER_ERROR_404,
- CONNECTION_ERROR
+ CONNECTION_ERROR, NOT_FOUND
}
}
diff --git a/src/it/reyboz/bustorino/backend/FiveTAPIFetcher.java b/src/it/reyboz/bustorino/backend/FiveTAPIFetcher.java
--- a/src/it/reyboz/bustorino/backend/FiveTAPIFetcher.java
+++ b/src/it/reyboz/bustorino/backend/FiveTAPIFetcher.java
@@ -50,7 +50,7 @@
if(response==null) {
if(res.get()== Result.SERVER_ERROR_404) {
Log.w(DEBUG_NAME,"Got 404, either the server failed, or the stop was not found, or the address is wrong");
- res.set(Result.EMPTY_RESULT_SET);
+ //res.set(Result.S);
}
return p;
}
@@ -384,9 +384,8 @@
res.set(Result.PARSER_ERROR);
return null;
}
- String response = networkTools.queryURL(u,res,param);
- return response;
+ return networkTools.queryURL(u,res,param);
}
/**
diff --git a/src/it/reyboz/bustorino/backend/FiveTScraperFetcher.java b/src/it/reyboz/bustorino/backend/FiveTScraperFetcher.java
--- a/src/it/reyboz/bustorino/backend/FiveTScraperFetcher.java
+++ b/src/it/reyboz/bustorino/backend/FiveTScraperFetcher.java
@@ -83,7 +83,7 @@
String busStopID = grep("^(.+) ", span.html());
if (busStopID == null) {
//Log.e("BusStop", "Empty busStopID from " + span.html());
- res.set(Result.EMPTY_RESULT_SET);
+ res.set(Result.NOT_FOUND);
return p;
}
diff --git a/src/it/reyboz/bustorino/backend/GTTJSONFetcher.java b/src/it/reyboz/bustorino/backend/GTTJSONFetcher.java
--- a/src/it/reyboz/bustorino/backend/GTTJSONFetcher.java
+++ b/src/it/reyboz/bustorino/backend/GTTJSONFetcher.java
@@ -76,7 +76,7 @@
json.getJSONObject(0).getString("Linea"); // if we can get this, then there's something useful in the array.
} catch(JSONException e) {
Log.w(DEBUG_TAG, "No existing lines");
- res.set(Result.EMPTY_RESULT_SET);
+ res.set(Result.NOT_FOUND);
return p;
}
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;
@@ -18,7 +35,9 @@
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import it.reyboz.bustorino.backend.Fetcher;
import it.reyboz.bustorino.backend.Palina;
public class MapiArrivalRequest extends MapiVolleyRequest {
@@ -26,9 +45,11 @@
private final String stopName;
private final Date startingTime;
private final int timeRange, numberOfDepartures;
+ private final AtomicReference reqRes;
public MapiArrivalRequest(String stopName, Date startingTime, int timeRange,
int numberOfDepartures,
+ AtomicReference res,
Response.Listener listener,
@Nullable Response.ErrorListener errorListener) {
super(MatoAPIFetcher.QueryType.ARRIVALS, listener, errorListener);
@@ -36,6 +57,14 @@
this.startingTime = startingTime;
this.timeRange = timeRange;
this.numberOfDepartures = numberOfDepartures;
+ this.reqRes = res;
+ }
+ public MapiArrivalRequest(String stopName, Date startingTime, int timeRange,
+ int numberOfDepartures,
+ Response.Listener listener,
+ @Nullable Response.ErrorListener errorListener) {
+ this(stopName, startingTime, timeRange, numberOfDepartures,
+ new AtomicReference<>(), listener, errorListener);
}
@Nullable
@@ -52,7 +81,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);
@@ -65,8 +94,10 @@
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
- if(response.statusCode != 200)
- return Response.error(new VolleyError("Response Error Code "+response.statusCode));
+ if(response.statusCode != 200) {
+ reqRes.set(Fetcher.Result.SERVER_ERROR);
+ return Response.error(new VolleyError("Response Error Code " + response.statusCode));
+ }
final String stringResponse = new String(response.data);
Palina p = null;
@@ -75,35 +106,44 @@
JSONArray allStopsFound = data.getJSONArray("stops");
- boolean haveManyResults = allStopsFound.length() > 1;
+ boolean stopFound = false;
for (int i=0; i getParams() throws AuthFailureError {
- return new HashMap<>();
+ public StopNotFoundError(String message) {
+ super(message);
+ }
+
+ public StopNotFoundError() {
+ super();
+ }
}
}
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,49 +32,47 @@
-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)
+ val request = MapiArrivalRequest(stopID, now, numMinutes * 60, 10, res, future, future)
if (appContext == null || res == null) {
Log.e("BusTO:MatoAPIFetcher", "ERROR: Given null context or null result ref")
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) {
palina = palinaResult
- if (palina.totalNumberOfPassages > 0) {
- res.set(Fetcher.Result.OK)
- } else res.set(Fetcher.Result.EMPTY_RESULT_SET)
numPassaggi = palina.totalNumberOfPassages
- } else{
- res.set(Fetcher.Result.EMPTY_RESULT_SET)
}
} catch (e: InterruptedException) {
e.printStackTrace()
res.set(Fetcher.Result.PARSER_ERROR)
} catch (e: ExecutionException) {
e.printStackTrace()
+ if (res.get() == Fetcher.Result.OK)
res.set(Fetcher.Result.SERVER_ERROR)
} catch (e: TimeoutException) {
res.set(Fetcher.Result.CONNECTION_ERROR)
@@ -84,6 +99,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 +144,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 +159,7 @@
}
return ""
}
+ */
fun parseStopJSON(jsonStop: JSONObject): Palina{
val latitude = jsonStop.getDouble("lat")
val longitude = jsonStop.getDouble("lon")
@@ -118,6 +172,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 +180,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 +207,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 +243,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/fragments/ArrivalsFragment.java b/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
--- a/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
+++ b/src/it/reyboz/bustorino/fragments/ArrivalsFragment.java
@@ -271,8 +271,8 @@
}
return v;
}
- public Fetcher[] getCurrentFetchersAsArray(){
- Fetcher[] arr = new Fetcher[fetchers.size()];
+ public ArrivalsFetcher[] getCurrentFetchersAsArray(){
+ ArrivalsFetcher[] arr = new ArrivalsFetcher[fetchers.size()];
fetchers.toArray(arr);
return arr;
}
diff --git a/src/it/reyboz/bustorino/fragments/FragmentHelper.java b/src/it/reyboz/bustorino/fragments/FragmentHelper.java
--- a/src/it/reyboz/bustorino/fragments/FragmentHelper.java
+++ b/src/it/reyboz/bustorino/fragments/FragmentHelper.java
@@ -23,6 +23,8 @@
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
+
+import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
@@ -50,7 +52,8 @@
private final Context context;
public static final int NO_FRAME = -3;
private static final String DEBUG_TAG = "BusTO FragmHelper";
- private WeakReference lastTaskRef;
+ private WeakReference lastTaskRef;
+ private SearchRequestType lastTaskType;
private boolean shouldHaltAllActivities=false;
@@ -79,8 +82,8 @@
this.lastSuccessfullySearchedBusStop = stop;
}
- public void setLastTaskRef(WeakReference lastTaskRef) {
- this.lastTaskRef = lastTaskRef;
+ public void setLastTaskRef(AsyncTask task) {
+ this.lastTaskRef = new WeakReference<>(task);
}
/**
@@ -199,7 +202,7 @@
public void stopLastRequestIfNeeded(boolean interruptIfRunning){
if(lastTaskRef == null) return;
- AsyncDataDownload task = lastTaskRef.get();
+ AsyncTask task = lastTaskRef.get();
if(task!=null){
task.cancel(interruptIfRunning);
}
@@ -209,8 +212,9 @@
* Wrapper to show the errors/status that happened
* @param res result from Fetcher
*/
- public void showErrorMessage(Fetcher.Result res){
+ public void showErrorMessage(Fetcher.Result res, SearchRequestType type){
//TODO: implement a common set of errors for all fragments
+ Log.d(DEBUG_TAG, "Showing result for "+res);
switch (res){
case OK:
break;
@@ -231,6 +235,13 @@
showShortToast(R.string.query_too_short);
break;
case EMPTY_RESULT_SET:
+ if (type == SearchRequestType.STOPS)
+ showShortToast(R.string.no_bus_stop_have_this_name);
+ else if(type == SearchRequestType.ARRIVALS){
+ showShortToast(R.string.no_arrivals_stop);
+ }
+ break;
+ case NOT_FOUND:
showShortToast(R.string.no_bus_stop_have_this_name);
break;
}
diff --git a/src/it/reyboz/bustorino/fragments/MainScreenFragment.java b/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
--- a/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
+++ b/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
@@ -44,7 +44,8 @@
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.*;
import it.reyboz.bustorino.middleware.AppLocationManager;
-import it.reyboz.bustorino.middleware.AsyncDataDownload;
+import it.reyboz.bustorino.middleware.AsyncArrivalsSearcher;
+import it.reyboz.bustorino.middleware.AsyncStopsSearcher;
import it.reyboz.bustorino.util.LocationCriteria;
import it.reyboz.bustorino.util.Permissions;
@@ -106,10 +107,10 @@
} else{
String stopName = fragment.getStopID();
- new AsyncDataDownload(fragmentHelper, fragment.getCurrentFetchersAsArray(), getContext()).execute(stopName);
+ new AsyncArrivalsSearcher(fragmentHelper, fragment.getCurrentFetchersAsArray(), getContext()).execute(stopName);
}
} else //we create a new fragment, which is WRONG
- new AsyncDataDownload(fragmentHelper, arrivalsFetchers, getContext()).execute();
+ new AsyncArrivalsSearcher(fragmentHelper, arrivalsFetchers, getContext()).execute();
}
};
@@ -466,7 +467,7 @@
}
else {
fragmentHelper.stopLastRequestIfNeeded(true);
- new AsyncDataDownload(fragmentHelper, stopsFinderByNames, getContext()).execute(query);
+ new AsyncStopsSearcher(fragmentHelper, stopsFinderByNames).execute(query);
}
}
}
@@ -660,13 +661,13 @@
if (fragment != null && fragment.getStopID() != null && fragment.getStopID().equals(ID)){
// Run with previous fetchers
//fragment.getCurrentFetchers().toArray()
- new AsyncDataDownload(fragmentHelper,fragment.getCurrentFetchersAsArray(), getContext()).execute(ID);
+ new AsyncArrivalsSearcher(fragmentHelper,fragment.getCurrentFetchersAsArray(), getContext()).execute(ID);
} else{
- new AsyncDataDownload(fragmentHelper, arrivalsFetchers, getContext()).execute(ID);
+ new AsyncArrivalsSearcher(fragmentHelper, arrivalsFetchers, getContext()).execute(ID);
}
}
else {
- new AsyncDataDownload(fragmentHelper,arrivalsFetchers, getContext()).execute(ID);
+ new AsyncArrivalsSearcher(fragmentHelper,arrivalsFetchers, getContext()).execute(ID);
Log.d(DEBUG_TAG, "Started search for arrivals of stop " + ID);
}
}
diff --git a/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java b/src/it/reyboz/bustorino/middleware/AsyncArrivalsSearcher.java
rename from src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
rename to src/it/reyboz/bustorino/middleware/AsyncArrivalsSearcher.java
--- a/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
+++ b/src/it/reyboz/bustorino/middleware/AsyncArrivalsSearcher.java
@@ -49,27 +49,25 @@
/**
* This should be used to download data, but not to display it
*/
-public class AsyncDataDownload extends AsyncTask{
+public class AsyncArrivalsSearcher extends AsyncTask{
private static final String TAG = "BusTO-DataDownload";
private static final String DEBUG_TAG = TAG;
private boolean failedAll = false;
private final AtomicReference res;
- private final RequestType t;
private String query;
WeakReference helperRef;
private final ArrayList otherActivities = new ArrayList<>();
- private final Fetcher[] theFetchers;
+ private final ArrivalsFetcher[] theFetchers;
@SuppressLint("StaticFieldLeak")
private final Context context;
private final boolean replaceFragment;
- public AsyncDataDownload(FragmentHelper fh, @NonNull Fetcher[] fetchers, Context context) {
- RequestType type;
+ public AsyncArrivalsSearcher(FragmentHelper fh, @NonNull ArrivalsFetcher[] fetchers, Context context) {
helperRef = new WeakReference<>(fh);
- fh.setLastTaskRef(new WeakReference<>(this));
+ fh.setLastTaskRef(this);
res = new AtomicReference<>();
this.context = context.getApplicationContext();
this.replaceFragment = true;
@@ -78,22 +76,13 @@
if (theFetchers.length < 1){
throw new IllegalArgumentException("You have to put at least one Fetcher, idiot!");
}
- if (theFetchers[0] instanceof ArrivalsFetcher){
- type = RequestType.ARRIVALS;
- } else if (theFetchers[0] instanceof StopsFinderByName){
- type = RequestType.STOPS;
- } else{
- type = null;
- }
- t = type;
}
@Override
- protected Object doInBackground(String... params) {
- RecursionHelper r = new RecursionHelper<>(theFetchers);
- boolean success=false;
- Object result;
+ protected Palina doInBackground(String... params) {
+ RecursionHelper r = new RecursionHelper<>(theFetchers);
+ Palina result = null;
FragmentHelper fh = helperRef.get();
ArrayList results = new ArrayList<>(theFetchers.length);
//If the FragmentHelper is null, that means the activity doesn't exist anymore
@@ -107,87 +96,76 @@
return null;
}
//get the data from the fetcher
- switch (t){
- case ARRIVALS:
- ArrivalsFetcher f = (ArrivalsFetcher) r.getAndMoveForward();
- if (f instanceof MatoAPIFetcher){
- ((MatoAPIFetcher)f).setAppContext(context);
- }
- Log.d(TAG,"Using the ArrivalsFetcher: "+f.getClass());
-
- Stop lastSearchedBusStop = fh.getLastSuccessfullySearchedBusStop();
- Palina p;
- String stopID;
- if(params.length>0)
- stopID=params[0]; //(it's a Palina)
- else if(lastSearchedBusStop!=null)
- stopID = lastSearchedBusStop.ID; //(it's a Palina)
- else {
- publishProgress(Fetcher.Result.QUERY_TOO_SHORT);
- return null;
- }
- //Skip the FiveTAPIFetcher for the Metro Stops because it shows incomprehensible arrival times
- try {
- if (f instanceof FiveTAPIFetcher && Integer.parseInt(stopID) >= 8200)
- continue;
- } catch (NumberFormatException ex){
- Log.e(DEBUG_TAG, "The stop number is not a valid integer, expect failures");
- }
- p= f.ReadArrivalTimesAll(stopID,res);
-
- //if (res.get()!= Fetcher.Result.OK)
- Log.d(DEBUG_TAG, "Arrivals fetcher: "+f+"\n\tProgress: "+res.get());
-
-
- if(f instanceof FiveTAPIFetcher){
- AtomicReference gres = new AtomicReference<>();
- List branches = ((FiveTAPIFetcher) f).getDirectionsForStop(stopID,gres);
- Log.d(DEBUG_TAG, "FiveTArrivals fetcher: "+f+"\n\tDetails req: "+gres.get());
- if(gres.get() == Fetcher.Result.OK){
- p.addInfoFromRoutes(branches);
- Thread t = new Thread(new BranchInserter(branches, context));
- t.start();
- otherActivities.add(t);
-
- }
- //put updated values into Database
- }
+ ArrivalsFetcher f = r.getAndMoveForward();
+ AtomicReference resRef = new AtomicReference<>();
+ if (f instanceof MatoAPIFetcher){
+ ((MatoAPIFetcher)f).setAppContext(context);
+ }
+ Log.d(TAG,"Using the ArrivalsFetcher: "+f.getClass());
+
+ Stop lastSearchedBusStop = fh.getLastSuccessfullySearchedBusStop();
+ Palina p;
+ String stopID;
+ if(params.length>0)
+ stopID=params[0]; //(it's a Palina)
+ else if(lastSearchedBusStop!=null)
+ stopID = lastSearchedBusStop.ID; //(it's a Palina)
+ else {
+ publishProgress(Fetcher.Result.QUERY_TOO_SHORT);
+ return null;
+ }
+ //Skip the FiveTAPIFetcher for the Metro Stops because it shows incomprehensible arrival times
+ try {
+ if (f instanceof FiveTAPIFetcher && Integer.parseInt(stopID) >= 8200)
+ continue;
+ } catch (NumberFormatException ex){
+ Log.e(DEBUG_TAG, "The stop number is not a valid integer, expect failures");
+ }
+ p= f.ReadArrivalTimesAll(stopID,resRef);
+
+
+ //if (res.get()!= Fetcher.Result.OK)
+ Log.d(DEBUG_TAG, "Arrivals fetcher: "+f+"\n\tProgress: "+resRef.get());
+
+ if(f instanceof FiveTAPIFetcher){
+ AtomicReference gres = new AtomicReference<>();
+ List branches = ((FiveTAPIFetcher) f).getDirectionsForStop(stopID,gres);
+ Log.d(DEBUG_TAG, "FiveTArrivals fetcher: "+f+"\n\tDetails req: "+gres.get());
+ if(gres.get() == Fetcher.Result.OK){
+ p.addInfoFromRoutes(branches);
+ Thread t = new Thread(new BranchInserter(branches, context));
+ t.start();
+ otherActivities.add(t);
+ } else{
+ resRef.set(Fetcher.Result.NOT_FOUND);
+ }
+ //put updated values into Database
+ }
- if(lastSearchedBusStop != null && res.get()== Fetcher.Result.OK) {
- // check that we don't have the same stop
- if(lastSearchedBusStop.ID.equals(p.ID)) {
- // searched and it's the same
- String sn = lastSearchedBusStop.getStopDisplayName();
- if(sn != null) {
- // "merge" Stop over Palina and we're good to go
- p.mergeNameFrom(lastSearchedBusStop);
- }
- }
+ if(lastSearchedBusStop != null && resRef.get()== Fetcher.Result.OK) {
+ // check that we don't have the same stop
+ if(lastSearchedBusStop.ID.equals(p.ID)) {
+ // searched and it's the same
+ String sn = lastSearchedBusStop.getStopDisplayName();
+ if(sn != null) {
+ // "merge" Stop over Palina and we're good to go
+ p.mergeNameFrom(lastSearchedBusStop);
}
- p.mergeDuplicateRoutes(0);
- if (p.getTotalNumberOfPassages() == 0)
- res.set(Fetcher.Result.EMPTY_RESULT_SET);
- publishProgress(res.get());
- //p.sortRoutes();
- result = p;
-
- //TODO: find a way to avoid overloading the user with toasts
- break;
- case STOPS:
- StopsFinderByName finder = (StopsFinderByName) r.getAndMoveForward();
-
- List resultList= finder.FindByName(params[0], this.res); //it's a List
- Log.d(TAG,"Using the StopFinderByName: "+finder.getClass());
- query =params[0];
- result = resultList; //dummy result
- Log.d(DEBUG_TAG, "Result: "+res.get()+", "+resultList.size()+" stops");
- break;
- default:
- result = null;
+ }
+ }
+ p.mergeDuplicateRoutes(0);
+ if (resRef.get() == Fetcher.Result.OK && p.getTotalNumberOfPassages() == 0 ) {
+ resRef.set(Fetcher.Result.EMPTY_RESULT_SET);
+ Log.d(DEBUG_TAG, "Setting empty results");
+ }
+ publishProgress(resRef.get());
+ //TODO: find a way to avoid overloading the user with toasts
+ if (result == null){
+ result = p;
}
//find if it went well
- results.add(res.get());
- if(res.get()== Fetcher.Result.OK) {
+ results.add(resRef.get());
+ if(resRef.get()== Fetcher.Result.OK) {
//wait for other threads to finish
for(Thread t: otherActivities){
try {
@@ -196,7 +174,7 @@
//do nothing
}
}
- return result;
+ return p;
}
}
@@ -207,14 +185,10 @@
break;
}
}
- if(emptyResults){
- if(t==RequestType.STOPS)
- publishProgress(Fetcher.Result.EMPTY_RESULT_SET);
- }
//at this point, we are sure that the result has been negative
failedAll=true;
- return null;
+ return result;
}
@Override
@@ -223,7 +197,7 @@
if (fh!=null)
for (Fetcher.Result r : values){
//TODO: make Toast
- fh.showErrorMessage(r);
+ fh.showErrorMessage(r, SearchRequestType.ARRIVALS);
}
else {
Log.w(TAG,"We had to show some progress but activity was destroyed");
@@ -231,10 +205,10 @@
}
@Override
- protected void onPostExecute(Object o) {
+ protected void onPostExecute(Palina p) {
FragmentHelper fh = helperRef.get();
- if(failedAll || o == null || fh == null){
+ if(failedAll || p == null || fh == null){
//everything went bad
if(fh!=null) fh.toggleSpinner(false);
cancel(true);
@@ -244,37 +218,8 @@
if(isCancelled()) return;
- switch (t){
- case ARRIVALS:
- Palina palina = (Palina) o;
- fh.createOrUpdateStopFragment(palina, replaceFragment);
- break;
- case STOPS:
- //this should never be a problem
- if(!(o instanceof List>)){
- throw new IllegalStateException();
- }
- List> list = (List>) o;
- if (list.size() ==0) return;
- Object firstItem = list.get(0);
- if(!(firstItem instanceof Stop)) return;
- ArrayList stops = new ArrayList<>();
- for(Object x: list){
- if(x instanceof Stop) stops.add((Stop) x);
- //Log.d(DEBUG_TAG, "Parsing Stop: "+x);
- }
- if(list.size() != stops.size()){
- Log.w(DEBUG_TAG, "Wrong stop list size:\n incoming: "+
- list.size()+" out: "+stops.size());
- }
- //List stopList = (List) list;
- if(query!=null && !isCancelled()) {
- fh.createStopListFragment(stops,query, replaceFragment);
- } else Log.e(TAG,"QUERY NULL, COULD NOT CREATE FRAGMENT");
- break;
- case DBUPDATE:
- break;
- }
+
+ fh.createOrUpdateStopFragment( p, replaceFragment);
}
@Override
@@ -290,10 +235,6 @@
}
- public enum RequestType {
- ARRIVALS,STOPS,DBUPDATE
- }
-
public static class BranchInserter implements Runnable{
private final List routesToInsert;
diff --git a/src/it/reyboz/bustorino/middleware/AsyncStopsSearcher.java b/src/it/reyboz/bustorino/middleware/AsyncStopsSearcher.java
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/middleware/AsyncStopsSearcher.java
@@ -0,0 +1,131 @@
+/*
+ BusTO (middleware)
+ 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.middleware;
+
+import android.os.AsyncTask;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import it.reyboz.bustorino.backend.Fetcher;
+import it.reyboz.bustorino.backend.Stop;
+import it.reyboz.bustorino.backend.StopsFinderByName;
+import it.reyboz.bustorino.fragments.FragmentHelper;
+
+public class AsyncStopsSearcher extends AsyncTask> {
+
+ private static final String TAG = "BusTO-StopsSearcher";
+ private static final String DEBUG_TAG = TAG;
+ private final StopsFinderByName[] fetchers;
+ private final AtomicReference res;
+
+ private WeakReference helperWR;
+
+ private String theQuery;
+
+ public AsyncStopsSearcher(FragmentHelper fh, StopsFinderByName[] fetchers) {
+ this.fetchers = fetchers;
+ if (fetchers.length < 1){
+ throw new IllegalArgumentException("You have to put at least one Fetcher, idiot!");
+ }
+
+ this.res = new AtomicReference<>();
+ this.helperWR = new WeakReference<>(fh);
+ fh.setLastTaskRef(this);
+
+ }
+
+ @Override
+ protected List doInBackground(String... strings) {
+ RecursionHelper r = new RecursionHelper<>(fetchers);
+ if (helperWR.get()==null || strings.length == 0)
+ return null;
+ Log.d(DEBUG_TAG,"Running with query "+strings[0]);
+
+ ArrayList results = new ArrayList<>();
+ List resultsList;
+ while (r.valid()){
+ if (this.isCancelled()) return null;
+
+ final StopsFinderByName finder = r.getAndMoveForward();
+ theQuery = strings[0].trim();
+ resultsList = finder.FindByName(theQuery, res);
+ Log.d(DEBUG_TAG, "Result: "+res.get()+", "+resultsList.size()+" stops");
+
+ if (res.get()== Fetcher.Result.OK){
+ return resultsList;
+ }
+ results.add(res.get());
+ }
+ boolean emptyResults = true;
+ for (Fetcher.Result re: results){
+ if (!re.equals(Fetcher.Result.EMPTY_RESULT_SET)) {
+ emptyResults = false;
+ break;
+ }
+ }
+ if(emptyResults){
+ publishProgress(Fetcher.Result.EMPTY_RESULT_SET);
+ }
+ return new ArrayList<>();
+ }
+
+ @Override
+ protected void onProgressUpdate(Fetcher.Result... values) {
+ FragmentHelper fh = helperWR.get();
+ if (fh!=null)
+ for (Fetcher.Result r : values){
+ fh.showErrorMessage(r, SearchRequestType.STOPS);
+ }
+ else {
+ Log.w(TAG,"We had to show some progress but activity was destroyed");
+ }
+ }
+ @Override
+ protected void onCancelled() {
+ FragmentHelper fh = helperWR.get();
+ if (fh!=null) fh.toggleSpinner(false);
+ }
+
+ @Override
+ protected void onPreExecute() {
+ FragmentHelper fh = helperWR.get();
+ if (fh!=null) fh.toggleSpinner(true);
+ }
+
+ @Override
+ protected void onPostExecute(List stops) {
+ final FragmentHelper fh = helperWR.get();
+
+ if (stops==null || fh==null || theQuery==null) {
+ if (fh!=null) fh.toggleSpinner(false);
+ cancel(true);
+ return;
+ }
+ if(isCancelled()){
+ fh.toggleSpinner(false);
+ return;
+ }
+ fh.createStopListFragment(stops, theQuery, true);
+
+
+ }
+}
diff --git a/src/it/reyboz/bustorino/middleware/SearchRequestType.java b/src/it/reyboz/bustorino/middleware/SearchRequestType.java
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/middleware/SearchRequestType.java
@@ -0,0 +1,5 @@
+package it.reyboz.bustorino.middleware;
+
+public enum SearchRequestType {
+ ARRIVALS,STOPS,DBUPDATE
+}
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