Page Menu
Home
GitPull.it
Search
Configure Global Search
Log In
Files
F13368012
D232.1779760268.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Authored By
Unknown
Size
14 KB
Referenced Files
None
Subscribers
None
D232.1779760268.diff
View Options
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/ArrivalsFetcherContext.java b/app/src/main/java/it/reyboz/bustorino/backend/ArrivalsFetcherContext.java
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/backend/ArrivalsFetcherContext.java
@@ -0,0 +1,14 @@
+package it.reyboz.bustorino.backend;
+
+import android.content.Context;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public abstract class ArrivalsFetcherContext implements ArrivalsFetcher{
+
+ protected @Nullable Context appContext;
+
+ public void setContext(@NonNull Context appContext) {
+ this.appContext = appContext.getApplicationContext();
+ }
+}
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/GTTJSONFetcher.java b/app/src/main/java/it/reyboz/bustorino/backend/GTTJSONFetcher.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/GTTJSONFetcher.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/GTTJSONFetcher.java
@@ -1,6 +1,7 @@
/*
BusTO (backend components)
Copyright (C) 2016 Ludovico Pavesi
+ Copyright (C) 2026 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
@@ -22,6 +23,10 @@
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.android.volley.*;
+import com.android.volley.toolbox.HttpHeaderParser;
+import com.android.volley.toolbox.RequestFuture;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -29,21 +34,19 @@
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
-public class GTTJSONFetcher implements ArrivalsFetcher {
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+public class GTTJSONFetcher extends ArrivalsFetcherContext {
private final String DEBUG_TAG = "GTTJSONFetcher-BusTO";
@Override @NonNull
public Palina ReadArrivalTimesAll(String stopID, AtomicReference<Result> res) {
URL url;
Palina p = new Palina(stopID);
- String routename;
- String bacino;
- String content;
- JSONArray json;
- int howManyRoutes, howManyPassaggi, i, j, pos; // il misto inglese-italiano è un po' ridicolo ma tanto vale...
- JSONObject thisroute;
- JSONArray passaggi;
try {
url = new URL("https://www.gtt.to.it/cms/index.php?option=com_gtt&task=palina.getTransitiOld&palina=" + URLEncoder.encode(stopID, "utf-8") + "&bacino=U&realtime=true&get_param=value");
@@ -51,11 +54,9 @@
res.set(Result.PARSER_ERROR);
return p;
}
- HashMap<String, String> headers = new HashMap<>();
- //headers.put("Referer","https://www.gtt.to.it/cms/percorari/urbano?view=percorsi&bacino=U&linea=15&Regol=GE");
- headers.put("Host", "www.gtt.to.it");
- content = networkTools.queryURL(url, res, headers);
+
+ /*content = networkTools.queryURL(url, res, headers);
if(content == null) {
Log.w("GTTJSONFetcher", "NULL CONTENT");
return p;
@@ -71,66 +72,154 @@
return p;
}
- try {
- // returns [{"PassaggiRT":[],"Passaggi":[]}] for non existing stops!
- 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.NOT_FOUND);
- return p;
- }
-
- howManyRoutes = json.length();
- if(howManyRoutes == 0) {
- res.set(Result.EMPTY_RESULT_SET);
+ */
+ if (appContext == null) {
+ Log.w(DEBUG_TAG, "appContext is null");
+ res.set(Result.PARSER_ERROR);
return p;
}
- try {
- for(i = 0; i < howManyRoutes; i++) {
- thisroute = json.getJSONObject(i);
- routename = thisroute.getString("Linea");
- try {
- bacino = thisroute.getString("Bacino");
- } catch (JSONException ignored) { // if "Bacino" gets removed...
- bacino = "U";
- }
- final Route r = new Route(routename, thisroute.getString("Direzione"),
- "",
- FiveTNormalizer.decodeType(routename, bacino));
-
- passaggi = thisroute.getJSONArray("PassaggiRT");
- howManyPassaggi = passaggi.length();
- for(j = 0; j < howManyPassaggi; j++) {
- String mPassaggio = passaggi.getString(j);
- if (mPassaggio.contains("__")){
- mPassaggio = mPassaggio.replace("_", "");
- }
- r.addPassaggio(mPassaggio.concat("*"), Passaggio.Source.GTTJSON);
- }
-
+ boolean retry = true;
+ RequestQueue queue = NetworkVolleyManager.getInstance(appContext).getRequestQueue();
+ //use the volley class, max 5 tries
+ RequestFuture<Palina> future;
+ Request<Palina> request;
+ Response.ErrorListener responder = error -> {
+ //Log.w(DEBUG_TAG, "onErrorResponse: " + volleyError.getMessage());
+ if(error instanceof VolleyFetcherError){
+ Log.w(DEBUG_TAG, "Actual error: " + ((VolleyFetcherError) error).getReason());
+ }
+ };
+
+ for (int i = 0; i < 2; i++) {
+ future = RequestFuture.newFuture();
+ request = new GTTRequest(stopID, url.toString(), responder, future, res);
+
+ queue.add(request);
+
+ try {
+ p = future.get(10, SECONDS);
+ retry = false;
+ } catch (TimeoutException e) {
+ Log.d(DEBUG_TAG, "Request timed out: " + res.get());
+ retry = false;
+ res.set(Result.CONNECTION_ERROR);
+ } catch (InterruptedException | ExecutionException e) {
+ Log.w(DEBUG_TAG, "Error: " + e + " status: " + res.get());
+ res.set(Result.PARSER_ERROR);
+ }
- passaggi = thisroute.getJSONArray("PassaggiPR"); // now the non-real-time ones
- howManyPassaggi = passaggi.length();
- for(j = 0; j < howManyPassaggi; j++) {
- r.addPassaggio(passaggi.getString(j), Passaggio.Source.GTTJSON);
- }
- p.addRoute(r);
+ if(!retry){
+ break;
}
- } catch (JSONException e) {
- res.set(Result.PARSER_ERROR);
- e.printStackTrace();
- return p;
}
- p.sortRoutes();
- res.set(Result.OK);
return p;
}
+
@Override
public Passaggio.Source getSourceForFetcher() {
return Passaggio.Source.GTTJSON;
}
+
+ private final class GTTRequest extends Request<Palina> {
+ private final String stopID;
+ private final AtomicReference<Result> res;
+ private final Response.Listener<Palina> responder;
+
+ public GTTRequest(String stopID, String URL,
+ @Nullable Response.ErrorListener errorListener,
+ Response.Listener<Palina> resp,
+ AtomicReference<Result> resu) {
+ super(Method.GET, URL, errorListener);
+ this.stopID = stopID;
+ this.res = resu;
+ responder = resp;
+ }
+ @Override
+ protected Response<Palina> parseNetworkResponse(NetworkResponse networkResponse) {
+ if (networkResponse == null) {
+ return Response.error(new VolleyFetcherError(Result.PARSER_ERROR));
+ }
+
+ String data = new String(networkResponse.data);
+ JSONArray json;
+ try {
+ json = new JSONArray(data);
+ // returns [{"PassaggiRT":[],"Passaggi":[]}] for non existing stops!
+ 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.NOT_FOUND);
+ return Response.error(new VolleyFetcherError(Result.NOT_FOUND));
+ }
+
+ int howManyRoutes = json.length();
+ if(howManyRoutes == 0) {
+ res.set(Result.EMPTY_RESULT_SET);
+ return Response.error(new VolleyFetcherError(Result.EMPTY_RESULT_SET));
+ }
+
+ try {
+ JSONObject thisroute;
+ String routename, bacino;
+ JSONArray passaggi;
+ int howManyPassaggi;
+ Palina p = new Palina(stopID);
+ for(int i = 0; i < howManyRoutes; i++) {
+ thisroute = json.getJSONObject(i);
+ routename = thisroute.getString("Linea");
+ try {
+ bacino = thisroute.getString("Bacino");
+ } catch (JSONException ignored) { // if "Bacino" gets removed...
+ bacino = "U";
+ }
+ final Route r = new Route(routename, thisroute.getString("Direzione"),
+ "",
+ FiveTNormalizer.decodeType(routename, bacino));
+
+ passaggi = thisroute.getJSONArray("PassaggiRT");
+ howManyPassaggi = passaggi.length();
+ for(int j = 0; j < howManyPassaggi; j++) {
+ String mPassaggio = passaggi.getString(j);
+ if (mPassaggio.contains("__")){
+ mPassaggio = mPassaggio.replace("_", "");
+ }
+ r.addPassaggio(mPassaggio.concat("*"), Passaggio.Source.GTTJSON);
+ }
+
+
+ passaggi = thisroute.getJSONArray("PassaggiPR"); // now the non-real-time ones
+ howManyPassaggi = passaggi.length();
+ for(int j = 0; j < howManyPassaggi; j++) {
+ r.addPassaggio(passaggi.getString(j), Passaggio.Source.GTTJSON);
+ }
+ p.addRoute(r);
+ }
+ p.sortRoutes();
+ res.set(Result.OK);
+
+ return Response.success(p, HttpHeaderParser.parseCacheHeaders(networkResponse));
+ } catch (JSONException e) {
+ res.set(Result.PARSER_ERROR);
+ Log.d(DEBUG_TAG, "Failed to parse response into JSON: " + e.getMessage());
+ return Response.error(new VolleyFetcherError(Result.PARSER_ERROR));
+ }
+ }
+
+ @Override
+ public Map<String, String> getHeaders() {
+ HashMap<String, String> headers = new HashMap<>();
+ //headers.put("Referer","https://www.gtt.to.it/cms/percorari/urbano?view=percorsi&bacino=U&linea=15&Regol=GE");
+ headers.put("Host", "www.gtt.to.it");
+ return headers;
+ }
+
+ @Override
+ protected void deliverResponse(Palina palina) {
+ responder.onResponse(palina);
+ }
+ }
}
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/VolleyFetcherError.kt b/app/src/main/java/it/reyboz/bustorino/backend/VolleyFetcherError.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/backend/VolleyFetcherError.kt
@@ -0,0 +1,7 @@
+package it.reyboz.bustorino.backend
+
+import com.android.volley.VolleyError
+
+class VolleyFetcherError(
+ val reason: Fetcher.Result
+) : VolleyError()
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/VolleyFetcherErrorResponder.kt b/app/src/main/java/it/reyboz/bustorino/backend/VolleyFetcherErrorResponder.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/backend/VolleyFetcherErrorResponder.kt
@@ -0,0 +1,23 @@
+package it.reyboz.bustorino.backend
+
+import android.util.Log
+import com.android.volley.Response
+import com.android.volley.VolleyError
+
+interface VolleyFetcherErrorResponder: Response.ErrorListener {
+
+ fun onErrorResponse(error: VolleyFetcherError){
+
+ }
+
+ override fun onErrorResponse(p0: VolleyError?) {
+ p0.let {
+ if(p0 is VolleyFetcherError){
+ onErrorResponse(p0 as VolleyFetcherError)
+ }
+ else{
+ Log.e("VolleyFetcherError", "Error is not instance of VolleyFetcherError, ignoring")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt b/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
--- a/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
+++ b/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
@@ -39,11 +39,8 @@
open class MatoAPIFetcher(
private val minNumPassaggi: Int
-) : ArrivalsFetcher {
- var appContext: Context? = null
- set(value) {
- field = value!!.applicationContext
- }
+) : ArrivalsFetcherContext() {
+
constructor(): this(DEF_MIN_NUMPASSAGGI)
diff --git a/app/src/main/java/it/reyboz/bustorino/middleware/AsyncArrivalsSearcher.java b/app/src/main/java/it/reyboz/bustorino/middleware/AsyncArrivalsSearcher.java
--- a/app/src/main/java/it/reyboz/bustorino/middleware/AsyncArrivalsSearcher.java
+++ b/app/src/main/java/it/reyboz/bustorino/middleware/AsyncArrivalsSearcher.java
@@ -101,8 +101,8 @@
//get the data from the fetcher
ArrivalsFetcher f = r.getAndMoveForward();
AtomicReference<Fetcher.Result> resRef = new AtomicReference<>();
- if (f instanceof MatoAPIFetcher){
- ((MatoAPIFetcher)f).setAppContext(context);
+ if (f instanceof ArrivalsFetcherContext){
+ ((ArrivalsFetcherContext)f).setContext(context);
}
Log.d(TAG,"Using the ArrivalsFetcher: "+f.getClass());
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/ArrivalsViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/ArrivalsViewModel.kt
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/ArrivalsViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/ArrivalsViewModel.kt
@@ -125,8 +125,8 @@
sourcesLiveData.postValue(fetcher.sourceForFetcher)
- if (fetcher is MatoAPIFetcher) {
- fetcher.appContext = appContext
+ if (fetcher is ArrivalsFetcherContext) {
+ fetcher.setContext(appContext)
}
Log.d(DEBUG_TAG, "Using the ArrivalsFetcher: ${fetcher.javaClass}")
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, May 26, 03:51 (17 m, 28 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1918847
Default Alt Text
D232.1779760268.diff (14 KB)
Attached To
Mode
D232: Rewrite GTT arrivals parser to use Volley
Attached
Detach File
Event Timeline