Page MenuHomeGitPull.it

D71.1729588578.diff
No OneTemporary

Size
42 KB
Referenced Files
None
Subscribers
None

D71.1729588578.diff

diff --git a/build.gradle b/build.gradle
--- a/build.gradle
+++ b/build.gradle
@@ -51,7 +51,7 @@
defaultConfig {
applicationId "it.reyboz.bustorino"
- minSdkVersion 15
+ minSdkVersion 16
targetSdkVersion 29
versionCode 35
versionName "1.15.4"
@@ -103,12 +103,12 @@
implementation "androidx.work:work-runtime:$work_version"
implementation "com.google.android.material:material:1.4.0"
- implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1'
- implementation 'com.android.volley:volley:1.2.0'
+ implementation 'com.android.volley:volley:1.2.1'
implementation 'org.osmdroid:osmdroid-android:6.1.10'
// ACRA
diff --git a/res/layout/entry_bus_line_passage.xml b/res/layout/entry_bus_line_passage.xml
--- a/res/layout/entry_bus_line_passage.xml
+++ b/res/layout/entry_bus_line_passage.xml
@@ -14,7 +14,9 @@
android:background="@drawable/route_background_bus"
android:gravity="center"
android:textColor="@color/grey_100"
- android:textSize="21sp">
+ android:textSize="21sp"
+ android:layout_marginEnd="4dp"
+ android:layout_marginRight="4dp">
</TextView>
<!--the icon comes from setCompoundDrawables in PalinaAdapter -->
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
@@ -131,6 +131,8 @@
<string name="fivetapifetcher">App GTT</string>
<string name="gttjsonfetcher">Sito GTT</string>
<string name="fivetscraper">Sito 5T Torino</string>
+ <string name="source_mato">App Muoversi a Torino</string>
+
<string name="arrival_source_changing">Cambiamento sorgente orari…</string>
<string name="change_arrivals_source_message">Premi a lungo per cambiare la sorgente degli orari</string>
@@ -154,5 +156,13 @@
<string name="map">Mappa</string>
<string name="stop_search_view_title">Ricerca fermate</string>
<string name="app_version">Versione app</string>
+ <string name="arrival_times">Orari di arrivo</string>
+
+ <string name="pref_directions_capitalize">Mostra direzioni in maiuscolo</string>
+ <string-array name="directions_capitalize">
+ <item>Non cambiare</item>
+ <item>Tutto in maiuscolo</item>
+ <item>Solo la prima lettera maiuscola</item>
+ </string-array>
</resources>
diff --git a/res/values/pref_keys.xml b/res/values/pref_keys.xml
--- a/res/values/pref_keys.xml
+++ b/res/values/pref_keys.xml
@@ -4,4 +4,6 @@
<string name="pref_key_num_recents" translatable="false">pref_num_recents</string>
<string name="pref_key_radius_recents" translatable="false">pref_radius_recents</string>
<string name="pref_key_experimental" translatable="false">pref_exp_features</string>
+
+ <string name="pref_arrival_times_capit" translatable="false">pref_arrival_times_capitalize</string>
</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -51,6 +51,7 @@
\n <b>Long press on Arrivals source</b> to change the source of the arrival times
</string>
<string name="hint_button">GOT IT!</string>
+ <string name="arrival_times">Arrival times</string>
<string name="about_history">
<![CDATA[
<h1>Welcome!</h1>
@@ -144,6 +145,7 @@
<string name="fivetapifetcher">GTT App</string>
<string name="gttjsonfetcher">GTT Website</string>
<string name="fivetscraper">5T Torino website</string>
+ <string name="source_mato">Muoversi a Torino app</string>
<string name="arrival_source_changing">Changing arrival times source…</string>
<string name="change_arrivals_source_message">Long press to change the source of arrivals</string>
@@ -174,4 +176,17 @@
<string name="stop_search_view_title">Search by stop</string>
+ <!-- preferences -->
+ <string name="pref_directions_capitalize">Capitalize directions</string>
+ <string-array name="directions_capitalize">
+ <item >Do not change arrivals directions</item>
+ <item>Capitalize everything</item>
+ <item>Capitalize only first letter</item>
+ </string-array>
+ <array name="directions_capitalize_keys">
+ <item>KEEP</item>
+ <item>CAPITALIZE_ALL</item>
+ <item>CAPITALIZE_FIRST</item>
+
+ </array>
</resources>
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -49,4 +49,13 @@
android:key="version"
/>
-->
+ <androidx.preference.PreferenceCategory android:title="@string/arrival_times">
+
+ <androidx.preference.ListPreference
+ android:title="@string/pref_directions_capitalize"
+ android:entries="@array/directions_capitalize"
+ android:entryValues="@array/directions_capitalize_keys"
+ android:key="@string/pref_arrival_times_capit"
+ />
+ </androidx.preference.PreferenceCategory>
</androidx.preference.PreferenceScreen>
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
@@ -19,6 +19,10 @@
import android.content.Context;
import androidx.annotation.NonNull;
+import androidx.preference.PreferenceManager;
+
+import android.content.SharedPreferences;
+import android.os.Build;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
@@ -27,7 +31,9 @@
import android.widget.TextView;
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;
@@ -43,7 +49,7 @@
* @author Valerio Bozzolan
* @author Ludovico Pavesi
*/
-public class PalinaAdapter extends ArrayAdapter<Route> {
+public class PalinaAdapter extends ArrayAdapter<Route> implements SharedPreferences.OnSharedPreferenceChangeListener {
private LayoutInflater li;
private static int row_layout = R.layout.entry_bus_line_passage;
private static final int metroBg = R.drawable.route_background_metro;
@@ -52,6 +58,10 @@
private static final int busIcon = R.drawable.bus;
private static final int trainIcon = R.drawable.subway;
private static final int tramIcon = R.drawable.tram;
+
+ private final String KEY_CAPITALIZE;
+ private Capitalize capit = Capitalize.DO_NOTHING;
+
//private static final int cityIcon = R.drawable.city;
// hey look, a pattern!
@@ -60,10 +70,29 @@
TextView rowRouteDestination;
TextView rowRouteTimetable;
}
+ private static Capitalize getCapitalize(SharedPreferences shPr, String key){
+ String capitalize = shPr.getString(key, "");
+
+ switch (capitalize.trim()){
+ case "KEEP":
+ return Capitalize.DO_NOTHING;
+ case "CAPITALIZE_ALL":
+ return Capitalize.ALL;
+
+ case "CAPITALIZE_FIRST":
+ return Capitalize.FIRST;
+ }
+ return Capitalize.DO_NOTHING;
+ }
public PalinaAdapter(Context context, Palina p) {
super(context, row_layout, p.queryAllRoutes());
li = LayoutInflater.from(context);
+ sort(new RouteSorterByArrivalTime());
+ KEY_CAPITALIZE = context.getString(R.string.pref_arrival_times_capit);
+ SharedPreferences defSharPref = PreferenceManager.getDefaultSharedPreferences(context);
+ defSharPref.registerOnSharedPreferenceChangeListener(this);
+ this.capit = getCapitalize(defSharPref, KEY_CAPITALIZE);
}
/**
@@ -101,10 +130,30 @@
vh.rowStopIcon.setText(route.getNameForDisplay());
if(route.destinazione==null || route.destinazione.length() == 0) {
vh.rowRouteDestination.setVisibility(View.GONE);
+ // move around the route timetable
+ final ViewGroup.MarginLayoutParams pars = (ViewGroup.MarginLayoutParams) vh.rowRouteTimetable.getLayoutParams();
+ if (pars!=null){
+ pars.topMargin = 16;
+ if(Build.VERSION.SDK_INT >= 17)
+ pars.setMarginStart(20);
+ pars.leftMargin = 20;
+ }
} else {
// View Holder Pattern(R) renders each element from a previous one: if the other one had an invisible rowRouteDestination, we need to make it visible.
vh.rowRouteDestination.setVisibility(View.VISIBLE);
- vh.rowRouteDestination.setText(route.destinazione);
+ String dest = route.destinazione;
+ switch (capit){
+ case ALL:
+ dest = route.destinazione.toUpperCase(Locale.ROOT);
+ break;
+ case FIRST:
+ dest = utils.toTitleCase(route.destinazione, true);
+ break;
+ case DO_NOTHING:
+ default:
+
+ }
+ vh.rowRouteDestination.setText(dest);
}
switch (route.type) {
@@ -138,10 +187,25 @@
List<Passaggio> passaggi = route.passaggi;
if(passaggi.size() == 0) {
vh.rowRouteTimetable.setText(R.string.no_passages);
+
} else {
vh.rowRouteTimetable.setText(route.getPassaggiToString());
}
return convertView;
}
+
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if(key.equals(KEY_CAPITALIZE)){
+ capit = getCapitalize(sharedPreferences, KEY_CAPITALIZE);
+
+ notifyDataSetChanged();
+ }
+ }
+
+ enum Capitalize{
+ DO_NOTHING, ALL, FIRST
+ }
}
diff --git a/src/it/reyboz/bustorino/adapters/RouteSorterByArrivalTime.kt b/src/it/reyboz/bustorino/adapters/RouteSorterByArrivalTime.kt
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/adapters/RouteSorterByArrivalTime.kt
@@ -0,0 +1,29 @@
+package it.reyboz.bustorino.adapters
+
+import it.reyboz.bustorino.backend.Route
+
+class RouteSorterByArrivalTime : Comparator<Route> {
+
+ override fun compare(route1: Route?, route2: Route?): Int {
+ if (route1 == null){
+ if(route2 == null) return 0
+ else return 2;
+ } else if (route2 == null){
+ return -2;
+ }
+ val passaggi1 = route1.passaggi
+ val passaggi2 = route2.passaggi
+ // handle the case of midnight
+ if (passaggi1 == null || passaggi1.size == 0){
+ if (passaggi2 == null || passaggi2.size == 0) return 0
+ else return 2
+ } else if (passaggi2 == null || passaggi2.size == 0){
+ return -2
+ }
+ passaggi1.sort()
+ passaggi2.sort()
+
+ return passaggi1[0].compareTo(passaggi2[0])
+ }
+
+}
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/backend/ArrivalsFetcher.java b/src/it/reyboz/bustorino/backend/ArrivalsFetcher.java
--- a/src/it/reyboz/bustorino/backend/ArrivalsFetcher.java
+++ b/src/it/reyboz/bustorino/backend/ArrivalsFetcher.java
@@ -22,20 +22,10 @@
import java.util.concurrent.atomic.AtomicReference;
+/**
+ * Fetcher interface to describe ways to get information on arrival times
+ */
public interface ArrivalsFetcher extends Fetcher {
-// /**
-// * Reads arrival times from a (hopefully) real-time source, e.g. the GTT website.
-// * Don't call this in UI thread!
-// *
-// * @param stopID stop ID, in normalized form.
-// * @param routeID route ID, in normalized form.
-// * @param res result code (will be set by this method)
-// * @return arrival times
-// * @see it.reyboz.bustorino.backend.Fetcher.result
-// * @see FiveTNormalizer
-// */
-// Palina ReadArrivalTimesRoute(String stopID, String routeID, AtomicReference<Fetcher.result> res);
-
/**
* Reads arrival times from a (hopefully) real-time source, e.g. the GTT website.
* Don't call this in UI thread!
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
@@ -18,6 +18,7 @@
package it.reyboz.bustorino.backend;
import androidx.annotation.Nullable;
+
import android.util.Log;
import it.reyboz.bustorino.data.GTTInfoInject;
import org.json.JSONArray;
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
@@ -21,6 +21,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Calendar;
@@ -47,6 +48,15 @@
s.getRoutesThatStopHere(),s.getLatitude(),s.getLongitude());
}
+ public Palina(@NonNull String ID, @Nullable String name, @Nullable String userName,
+ @Nullable String location,
+ @Nullable Double lat, @Nullable Double lon) {
+ super(ID, name, userName, location, null, null, lat, lon);
+ }
+
+ public Palina(@Nullable String name, @NonNull String ID, @Nullable String location, @Nullable Route.Type type, @Nullable List<String> routesThatStopHere) {
+ super(name, ID, location, type, routesThatStopHere);
+ }
/**
* Adds a timetable entry to a route.
@@ -373,5 +383,26 @@
if (found) mergeDuplicateRoutes(startidx);
else mergeDuplicateRoutes(startidx+1);
}
+
+ public int getTotalNumberOfPassages(){
+
+ int tot = 0;
+ if(routes==null)
+ return tot;
+ for(Route r: routes){
+ tot += r.numPassaggi();
+ }
+ return tot;
+ }
+ public int getMinNumberOfPassages(){
+ if (routes == null) return 0;
+
+ int min = Integer.MAX_VALUE;
+ if( routes.size() == 0) min = 0;
+ else for (Route r : routes){
+ min = Math.min(min,r.numPassaggi());
+ }
+ return min;
+ }
//private void mergeRoute
}
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/backend/Passaggio.java b/src/it/reyboz/bustorino/backend/Passaggio.java
--- a/src/it/reyboz/bustorino/backend/Passaggio.java
+++ b/src/it/reyboz/bustorino/backend/Passaggio.java
@@ -19,8 +19,12 @@
package it.reyboz.bustorino.backend;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import android.util.Log;
+import java.util.Locale;
+
public final class Passaggio implements Comparable<Passaggio> {
private static final int UNKNOWN_TIME = -3;
@@ -28,6 +32,7 @@
private final String passaggioGTT;
public final int hh,mm;
+ private @Nullable Integer realtimeDifference;
public final boolean isInRealTime;
public final Source source;
@@ -88,6 +93,7 @@
this.hh = hour;
this.mm = min;
this.isInRealTime = realtime;
+
}
}
@@ -95,6 +101,7 @@
this.hh = hour;
this.mm = minutes;
this.isInRealTime = realtime;
+ if (!realtime) realtimeDifference = 0;
this.source = sorgente;
//Build the passaggio string
StringBuilder sb = new StringBuilder();
@@ -113,6 +120,24 @@
else return time;
}
}
+ public Passaggio(int numSeconds, boolean realtime, int timeDiff, Source source){
+ int minutes = numSeconds / 60;
+ int hours = minutes / 60;
+ //this.hh = hours;
+ this.mm = minutes - hours*60;
+ this.hh = hours % 24;
+ this.realtimeDifference = timeDiff/60;
+ this.isInRealTime = realtime;
+ this.source = source;
+ this.passaggioGTT = makePassaggioGTT(this.hh, this.mm, this.isInRealTime);
+ }
+
+ private static String makePassaggioGTT(int hour, int minutes, boolean realtime){
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format(Locale.ITALIAN,"%02d", hour)).append(":").append(String.format(Locale.ITALIAN,"%02d", minutes));
+ if(realtime) sb.append("*");
+ return sb.toString();
+ }
@Override
public int compareTo(@NonNull Passaggio other) {
@@ -133,16 +158,17 @@
// we should take into account if one is in real time and the other isn't, shouldn't we?
if (other.isInRealTime) {
- ++diff;
+ diff+=2;
}
if (this.isInRealTime) {
- --diff;
+ diff -=2;
}
- //TODO: separate Realtime and Non-Realtime, especially for the GTTJSONFetcher
return diff;
}
}
+
+
//
// @Override
// public String toString() {
@@ -154,6 +180,6 @@
// }
// }
public enum Source{
- FiveTAPI,GTTJSON,FiveTScraper, UNDETERMINED
+ FiveTAPI,GTTJSON,FiveTScraper,MatoAPI, UNDETERMINED
}
}
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/backend/Route.java b/src/it/reyboz/bustorino/backend/Route.java
--- a/src/it/reyboz/bustorino/backend/Route.java
+++ b/src/it/reyboz/bustorino/backend/Route.java
@@ -45,6 +45,7 @@
public int[] serviceDays ={};
//0=>feriale, 1=>festivo -2=>unknown
public FestiveInfo festivo = FestiveInfo.UNKNOWN;
+ private @Nullable String gtfsId;
public enum Type { // "long distance" sono gli extraurbani.
@@ -242,6 +243,12 @@
return sb.toString();
}
+ public int numPassaggi(){
+ if (passaggi==null)
+ return 0;
+ return passaggi.size();
+ }
+
@Override
public int compareTo(@NonNull Route other) {
@@ -267,6 +274,12 @@
if (res != 0) {
return res;
}
+ // compare gtfsID
+ if (this.gtfsId != null && other.gtfsId!=null){
+ res = this.gtfsId.compareTo(other.gtfsId);
+ if (res!=0) return 0;
+ }
+
}
// try comparing their destination
@@ -293,6 +306,15 @@
return 0;
}
+ @Nullable
+ public String getGtfsId() {
+ return gtfsId;
+ }
+
+ public void setGtfsId(@Nullable String gtfsId) {
+ this.gtfsId = gtfsId;
+ }
+
public boolean isBranchIdValid(){
return branchid!=BRANCHID_MISSING;
}
@@ -306,11 +328,14 @@
if(description!=null && r.description!=null)
if(!description.trim().equals(r.description.trim()))
return false;
+
if(destinazione!=null && r.destinazione!=null){
if(!this.destinazione.trim().equals(r.destinazione.trim()))
// they are not the same
return false;
}
+ if(gtfsId!=null && r.gtfsId!=null && !(gtfsId.trim().equals(r.gtfsId.trim())))
+ return false;
//check stops list
if(this.stopsList!=null && r.stopsList!=null){
int sizeDiff = this.stopsList.size()-r.stopsList.size();
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
@@ -46,6 +46,9 @@
private @Nullable String routesThatStopHereString = null;
private @Nullable String absurdGTTPlaceName = null;
+ //
+ public @Nullable String gtfsID = null;
+
/**
* Hey, look, method overloading!
*/
diff --git a/src/it/reyboz/bustorino/backend/mato/MapiArrivalRequest.java b/src/it/reyboz/bustorino/backend/mato/MapiArrivalRequest.java
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/backend/mato/MapiArrivalRequest.java
@@ -0,0 +1,109 @@
+package it.reyboz.bustorino.backend.mato;
+
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.volley.AuthFailureError;
+import com.android.volley.NetworkResponse;
+import com.android.volley.Response;
+import com.android.volley.VolleyError;
+import com.android.volley.toolbox.HttpHeaderParser;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import it.reyboz.bustorino.backend.Palina;
+
+public class MapiArrivalRequest extends MapiVolleyRequest<Palina> {
+
+ private final String stopName;
+ private final Date startingTime;
+ private final int timeRange, numberOfDepartures;
+
+ public MapiArrivalRequest(String stopName, Date startingTime, int timeRange,
+ int numberOfDepartures,
+ Response.Listener<Palina> listener,
+ @Nullable Response.ErrorListener errorListener) {
+ super(MatoAPIFetcher.QueryType.ARRIVALS, listener, errorListener);
+ this.stopName = stopName;
+ this.startingTime = startingTime;
+ this.timeRange = timeRange;
+ this.numberOfDepartures = numberOfDepartures;
+ }
+
+ @Nullable
+ @Override
+ public byte[] getBody() throws AuthFailureError {
+ JSONObject variables = new JSONObject();
+ JSONObject data = new JSONObject();
+ try {
+ data.put("operationName","AllStopsDirect");
+ variables.put("name", stopName);
+ variables.put("startTime", (long) startingTime.getTime()/1000);
+ variables.put("timeRange", timeRange);
+ variables.put("numberOfDepartures", numberOfDepartures);
+
+
+ data.put("variables", variables);
+ data.put("query", MatoAPIFetcher.QUERY_ARRIVALS);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ throw new AuthFailureError("Error with JSON enconding",e);
+ }
+ String requestBody = data.toString();
+ Log.d("MapiArrivalBusTO", "Request variables: "+ variables);
+ return requestBody.getBytes();
+ }
+
+
+ @Override
+ protected Response<Palina> parseNetworkResponse(NetworkResponse response) {
+ if(response.statusCode != 200)
+ return Response.error(new VolleyError("Response Error Code "+response.statusCode));
+ final String stringResponse = new String(response.data);
+ Palina p = null;
+
+ try {
+ JSONObject data = new JSONObject(stringResponse).getJSONObject("data");
+
+ JSONArray allStopsFound = data.getJSONArray("stops");
+
+ boolean haveManyResults = allStopsFound.length() > 1;
+ for (int i=0; i<allStopsFound.length(); i++){
+ final JSONObject currentObj = allStopsFound.getJSONObject(i);
+
+ p = MatoAPIFetcher.Companion.parseStopJSON(currentObj);
+ if (haveManyResults){
+ //check we got the right one
+ if (p.gtfsID == null){
+ continue;
+ } else if(p.gtfsID.contains("gtt:")){
+ //valid stop
+ break;
+ }
+ }
+
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ Log.e("BusTO:MapiRequest", "Error parsing JSON: "+stringResponse);
+ return Response.error(new VolleyError("Error parsing the response in JSON",
+ e));
+ }
+ return Response.success(p, HttpHeaderParser.parseCacheHeaders(response));
+ }
+
+
+ @Nullable
+ @Override
+ protected Map<String, String> 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
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/backend/mato/MapiVolleyRequest.java
@@ -0,0 +1,40 @@
+package it.reyboz.bustorino.backend.mato;
+
+import androidx.annotation.Nullable;
+
+import com.android.volley.AuthFailureError;
+import com.android.volley.Request;
+import com.android.volley.Response;
+
+import java.util.Map;
+
+public abstract class MapiVolleyRequest<T> extends Request<T> {
+ private static final String API_URL="https://mapi.5t.torino.it/routing/v1/routers/mat/index/graphql";
+
+ protected final Response.Listener<T> listener;
+ private final MatoAPIFetcher.QueryType type;
+ public MapiVolleyRequest(
+ MatoAPIFetcher.QueryType type,
+ Response.Listener<T> listener,
+ @Nullable Response.ErrorListener errorListener) {
+ super(Method.POST, API_URL, errorListener);
+ this.type = type;
+ this.listener = listener;
+
+ }
+
+
+ @Nullable
+ @Override
+ abstract protected Map<String, String> getParams() throws AuthFailureError;
+
+ @Override
+ protected void deliverResponse(T response) {
+ listener.onResponse(response);
+ }
+
+ @Override
+ public Map<String, String> 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
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
@@ -0,0 +1,242 @@
+package it.reyboz.bustorino.backend.mato
+
+import android.content.Context
+import android.util.Log
+import com.android.volley.toolbox.RequestFuture
+import it.reyboz.bustorino.backend.*
+import org.json.JSONObject
+import java.util.*
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicReference
+import java.util.concurrent.TimeoutException
+
+import java.util.concurrent.ExecutionException
+
+
+
+
+open class MatoAPIFetcher : ArrivalsFetcher {
+ var appContext: Context? = null
+ set(value) {
+ field = value!!.applicationContext
+ }
+
+ override fun ReadArrivalTimesAll(stopID: String?, res: AtomicReference<Fetcher.Result>?): Palina {
+ stopID!!
+ val future = RequestFuture.newFuture<Palina>()
+ val now = Calendar.getInstance().time;
+ var numMinutes = 30;
+ var palina = Palina(stopID)
+ var numPassaggi = 0
+ var trials = 0
+ while (numPassaggi < 2 && trials < 4) {
+
+ numMinutes += 15
+ val request = MapiArrivalRequest(stopID, now, numMinutes * 60, 10, 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)
+ 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()
+ res.set(Fetcher.Result.SERVER_ERROR)
+ } catch (e: TimeoutException) {
+ res.set(Fetcher.Result.CONNECTION_ERROR)
+ e.printStackTrace()
+ }
+ trials++
+
+ }
+
+ return palina
+ }
+
+ override fun getSourceForFetcher(): Passaggio.Source {
+ return Passaggio.Source.MatoAPI
+ }
+
+ companion object{
+ const val VOLLEY_TAG = "MatoAPIFetcher"
+
+ const val DEBUG_TAG = "BusTO:MatoAPIFetcher"
+
+ val REQ_PARAMETERS = mapOf(
+ "Content-Type" to "application/json; charset=utf-8",
+ "DNT" to "1",
+ "Host" to "mapi.5t.torino.it")
+
+ fun makeRequest(type: QueryType?, variables: JSONObject) : String{
+ type.let {
+ val requestData = JSONObject()
+ when (it){
+ QueryType.ARRIVALS ->{
+ requestData.put("operationName","AllStopsDirect")
+ requestData.put("variables", variables)
+ requestData.put("query", QUERY_ARRIVALS)
+ }
+ else -> {
+ //TODO all other cases
+ }
+ }
+
+
+ //todo make the request...
+ //https://pablobaxter.github.io/volley-docs/com/android/volley/toolbox/RequestFuture.html
+ //https://stackoverflow.com/questions/16904741/can-i-do-a-synchronous-request-with-volley
+
+ }
+ return ""
+ }
+ fun parseStopJSON(jsonStop: JSONObject): Palina{
+ val latitude = jsonStop.getDouble("lat")
+ val longitude = jsonStop.getDouble("lon")
+ val palina = Palina(
+ jsonStop.getString("code"),
+ jsonStop.getString("name"),
+ null, null, latitude, longitude
+ )
+ palina.gtfsID = jsonStop.getString("gtfsId")
+
+ val routesStoppingJSON = jsonStop.getJSONArray("routes")
+ val baseRoutes = mutableListOf<Route>()
+ for (i in 0 until routesStoppingJSON.length()){
+ val routeBaseInfo = routesStoppingJSON.getJSONObject(i)
+ val r = Route(routeBaseInfo.getString("shortName"), Route.Type.UNKNOWN,"")
+ r.gtfsId = routeBaseInfo.getString("gtfsId").trim()
+ 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
+ }
+ }
+ }
+ for (noArrivalRoute in baseRoutes){
+ palina.addRoute(noArrivalRoute)
+ }
+ //val gtfsRoutes = mutableListOf<>()
+
+
+ return palina
+ }
+ fun parseRouteStoptimesJSON(jsonPatternWithStops: JSONObject): Route{
+ val patternJSON = jsonPatternWithStops.getJSONObject("pattern")
+ val routeJSON = patternJSON.getJSONObject("route");
+
+ val passaggiJSON = jsonPatternWithStops.getJSONArray("stoptimes")
+ val gtfsId = routeJSON.getString("gtfsId").trim()
+ val passages = mutableListOf<Passaggio>()
+ for( i in 0 until passaggiJSON.length()){
+ val stoptime = passaggiJSON.getJSONObject(i)
+ val scheduledTime = stoptime.getInt("scheduledArrival")
+ val realtimeTime = stoptime.getInt("realtimeArrival")
+ val realtime = stoptime.getBoolean("realtime")
+ passages.add(
+ Passaggio(realtimeTime,realtime, realtimeTime-scheduledTime,
+ Passaggio.Source.MatoAPI)
+ )
+ }
+ var routeType = Route.Type.UNKNOWN
+ if (gtfsId[gtfsId.length-1] == 'E')
+ routeType = Route.Type.LONG_DISTANCE_BUS
+ else when( routeJSON.getString("mode").trim()){
+ "BUS" -> routeType = Route.Type.BUS
+ "TRAM" -> routeType = Route.Type.TRAM
+ }
+ val route = Route(
+ routeJSON.getString("shortName"),
+ patternJSON.getString("headsign"),
+ routeType,
+ passages,
+ )
+ route.gtfsId = gtfsId
+ 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
+ }
+ }
+ }
+ }
+ """
+ }
+
+
+ enum class QueryType {
+ ARRIVALS,
+ }
+
+}
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/backend/networkTools.java b/src/it/reyboz/bustorino/backend/networkTools.java
--- a/src/it/reyboz/bustorino/backend/networkTools.java
+++ b/src/it/reyboz/bustorino/backend/networkTools.java
@@ -247,6 +247,11 @@
}
+ /**
+ * Parses string into int, return 0 if it fails
+ * @param str the string to parse
+ * @return the value as int, 0 if it fails
+ */
static int failsafeParseInt(String str) {
try {
return Integer.parseInt(str);
diff --git a/src/it/reyboz/bustorino/backend/utils.java b/src/it/reyboz/bustorino/backend/utils.java
--- a/src/it/reyboz/bustorino/backend/utils.java
+++ b/src/it/reyboz/bustorino/backend/utils.java
@@ -14,6 +14,9 @@
import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
+
+import it.reyboz.bustorino.backend.mato.MatoAPIFetcher;
public abstract class utils {
private static final double EarthRadius = 6371e3;
@@ -105,14 +108,19 @@
return busStopID;
}
- public static String toTitleCase(String givenString) {
+ public static String toTitleCase(String givenString, boolean lowercaseRest) {
String[] arr = givenString.split(" ");
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
//Log.d("BusTO chars", "String parsing: "+givenString+" in array: "+ Arrays.toString(arr));
for (int i = 0; i < arr.length; i++) {
- if (arr[i].length() > 1)
- sb.append(Character.toUpperCase(arr[i].charAt(0)))
- .append(arr[i].substring(1)).append(" ");
+ if (arr[i].length() > 1) {
+ sb.append(Character.toUpperCase(arr[i].charAt(0)));
+ if (lowercaseRest)
+ sb.append(arr[i].substring(1).toLowerCase(Locale.ROOT));
+ else
+ sb.append(arr[i].substring(1));
+ sb.append(" ");
+ }
else sb.append(arr[i]);
}
return sb.toString().trim();
@@ -132,6 +140,10 @@
}
}
+ public static ArrivalsFetcher[] getDefaultArrivalsFetchers(){
+ return new ArrivalsFetcher[]{ new MatoAPIFetcher(),
+ new FiveTAPIFetcher(), new GTTJSONFetcher(), new FiveTScraperFetcher()};
+ }
/**
* Print the first i lines of the the trace of an exception
* https://stackoverflow.com/questions/21706722/fetch-only-first-n-lines-of-a-stack-trace
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
@@ -56,6 +56,7 @@
import it.reyboz.bustorino.backend.Passaggio;
import it.reyboz.bustorino.backend.Route;
import it.reyboz.bustorino.backend.Stop;
+import it.reyboz.bustorino.backend.utils;
import it.reyboz.bustorino.data.AppDataProvider;
import it.reyboz.bustorino.data.NextGenDB;
import it.reyboz.bustorino.data.UserDB;
@@ -69,7 +70,6 @@
private String DEBUG_TAG = DEBUG_TAG_ALL;
private final static int loaderFavId = 2;
private final static int loaderStopId = 1;
- private final static ArrivalsFetcher[] defaultFetchers = new ArrivalsFetcher[]{new FiveTAPIFetcher(), new GTTJSONFetcher(), new FiveTScraperFetcher()};
static final String STOP_TITLE = "messageExtra";
private @Nullable String stopID,stopName;
@@ -85,7 +85,7 @@
protected ImageButton addToFavorites;
protected TextView timesSourceTextView;
- private List<ArrivalsFetcher> fetchers = new ArrayList<>(Arrays.asList(defaultFetchers));
+ private List<ArrivalsFetcher> fetchers = new ArrayList<>(Arrays.asList(utils.getDefaultArrivalsFetchers()));
private boolean reloadOnResume = true;
@@ -324,14 +324,18 @@
case FiveTScraper:
source_txt = getString(R.string.fivetscraper);
break;
+ case MatoAPI:
+ source_txt = getString(R.string.source_mato);
+ break;
case UNDETERMINED:
//Don't show the view
- timesSourceTextView.setVisibility(View.GONE);
- return;
+ source_txt = "";
+ break;
default:
throw new IllegalStateException("Unexpected value: " + source);
}
int count = 0;
+ if (source!= Passaggio.Source.UNDETERMINED)
while (source != fetchers.get(0).getSourceForFetcher() && count < 100){
//we need to update the fetcher that is requested
rotateFetchers();
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
@@ -90,7 +90,7 @@
private static final int SEARCH_BY_ROUTE = 2; // TODO: implement this -- https://gitpull.it/T12
private int searchMode;
//private ImageButton addToFavorites;
- private final ArrivalsFetcher[] arrivalsFetchers = new ArrivalsFetcher[]{new FiveTAPIFetcher(), new GTTJSONFetcher(), new FiveTScraperFetcher()};
+ private final ArrivalsFetcher[] arrivalsFetchers = utils.getDefaultArrivalsFetchers();
//// HIDDEN BUT IMPORTANT ELEMENTS ////
FragmentManager fragMan;
Handler mainHandler;
@@ -334,6 +334,7 @@
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
+
Log.d(DEBUG_TAG, "OnAttach called, setupOnAttach: "+setupOnAttached);
mainHandler = new Handler();
if (context instanceof CommonFragmentListener) {
diff --git a/src/it/reyboz/bustorino/fragments/SettingsFragment.java b/src/it/reyboz/bustorino/fragments/SettingsFragment.java
--- a/src/it/reyboz/bustorino/fragments/SettingsFragment.java
+++ b/src/it/reyboz/bustorino/fragments/SettingsFragment.java
@@ -61,6 +61,8 @@
});
*/
+ //ListPreference preference = findPreference(R.string.arrival_times)
+
}
diff --git a/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java b/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
--- a/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
+++ b/src/it/reyboz/bustorino/middleware/AsyncDataDownload.java
@@ -17,6 +17,7 @@
*/
package it.reyboz.bustorino.middleware;
+import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -32,6 +33,7 @@
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.*;
+import it.reyboz.bustorino.backend.mato.MatoAPIFetcher;
import it.reyboz.bustorino.data.AppDataProvider;
import it.reyboz.bustorino.data.NextGenDB;
import it.reyboz.bustorino.fragments.FragmentHelper;
@@ -59,7 +61,8 @@
WeakReference<FragmentHelper> helperRef;
private final ArrayList<Thread> otherActivities = new ArrayList<>();
private final Fetcher[] theFetchers;
- private Context context;
+ @SuppressLint("StaticFieldLeak")
+ private final Context context;
private final boolean replaceFragment;
@@ -107,6 +110,9 @@
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();
@@ -128,7 +134,7 @@
Log.e(DEBUG_TAG, "The stop number is not a valid integer, expect failures");
}
p= f.ReadArrivalTimesAll(stopID,res);
- publishProgress(res.get());
+
//if (res.get()!= Fetcher.Result.OK)
Log.d(DEBUG_TAG, "Arrivals fetcher: "+f+"\n\tProgress: "+res.get());
@@ -159,10 +165,12 @@
}
}
p.mergeDuplicateRoutes(0);
- if(p.queryAllRoutes().size() == 0)
- //skip the rest and go to the next fetcher
- continue;
+ 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:

File Metadata

Mime Type
text/plain
Expires
Tue, Oct 22, 11:16 (4 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
639195
Default Alt Text
D71.1729588578.diff (42 KB)

Event Timeline