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>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -144,6 +144,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>
 
@@ -173,5 +174,4 @@
     <string name="map">Map</string>
     <string name="stop_search_view_title">Search by stop</string>
 
-
 </resources>
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,8 @@
 
 import android.content.Context;
 import androidx.annotation.NonNull;
+
+import android.os.Build;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -28,6 +30,7 @@
 
     import java.util.List;
 
+import it.reyboz.bustorino.BuildConfig;
 import it.reyboz.bustorino.R;
 import it.reyboz.bustorino.backend.Palina;
 import it.reyboz.bustorino.backend.Passaggio;
@@ -64,6 +67,7 @@
     public PalinaAdapter(Context context, Palina p) {
         super(context, row_layout, p.queryAllRoutes());
         li = LayoutInflater.from(context);
+        sort(new RouteSorterByArrivalTime());
     }
 
     /**
@@ -101,6 +105,14 @@
         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);
@@ -138,6 +150,7 @@
         List<Passaggio> passaggi = route.passaggi;
         if(passaggi.size() == 0) {
             vh.rowRouteTimetable.setText(R.string.no_passages);
+
         } else {
             vh.rowRouteTimetable.setText(route.getPassaggiToString());
         }
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
@@ -15,6 +15,8 @@
 import java.util.Arrays;
 import java.util.List;
 
+import it.reyboz.bustorino.backend.mato.MatoAPIFetcher;
+
 public abstract class utils {
     private static final double EarthRadius = 6371e3;
     public static Double measuredistanceBetween(double lat1,double long1,double lat2,double long2){
@@ -132,6 +134,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/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: