diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityIntro.kt b/app/src/main/java/it/reyboz/bustorino/ActivityIntro.kt
index 237d070..3320c94 100644
--- a/app/src/main/java/it/reyboz/bustorino/ActivityIntro.kt
+++ b/app/src/main/java/it/reyboz/bustorino/ActivityIntro.kt
@@ -1,107 +1,135 @@
 package it.reyboz.bustorino
 
 import android.content.Intent
 import android.os.Bundle
 import android.util.Log
+import android.util.TypedValue
 import android.view.View
 import android.widget.ImageButton
 import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.res.ResourcesCompat
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentActivity
 import androidx.viewpager2.adapter.FragmentStateAdapter
 import androidx.viewpager2.widget.ViewPager2
 import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
 import com.google.android.material.tabs.TabLayout
 import com.google.android.material.tabs.TabLayoutMediator
 import it.reyboz.bustorino.data.PreferencesHolder
 import it.reyboz.bustorino.fragments.IntroFragment
 
 class ActivityIntro : AppCompatActivity(), IntroFragment.IntroListener {
 
     private lateinit var viewPager : ViewPager2
     private lateinit var btnForward: ImageButton
     private lateinit var btnBackward: ImageButton
+    private lateinit var closeBottomButton: ImageButton
 
     private var restartMain = true
 
 
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_intro)
 
         viewPager = findViewById(R.id.viewPager)
         btnBackward = findViewById(R.id.btnPrevious)
         btnForward = findViewById(R.id.btnNext)
+        closeBottomButton = findViewById(R.id.btnCompactClose)
 
         val extras = intent.extras
         if(extras!=null){
             restartMain = extras.getBoolean(RESTART_MAIN)
         }
 
 
         val adapter = IntroPagerAdapter(this)
         viewPager.adapter = adapter
 
         val tabLayout = findViewById<TabLayout>(R.id.tab_layout)
         val tabLayoutMediator = TabLayoutMediator(tabLayout, viewPager) { tab, pos ->
             Log.d(DEBUG_TAG, "tabview on position $pos")
 
         }
         tabLayoutMediator.attach()
 
 
         btnForward.setOnClickListener {
             viewPager.setCurrentItem(viewPager.currentItem+1,true)
         }
         btnBackward.setOnClickListener {
             viewPager.setCurrentItem(viewPager.currentItem-1, true)
         }
+        /*closeBottomButton.setOnClickListener {
+            closeIntroduction()
+        }
+
+         */
 
         viewPager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
 
             override fun onPageSelected(position: Int) {
                 if(position == 0){
                     btnBackward.visibility = View.INVISIBLE
                 } else{
                     btnBackward.visibility = View.VISIBLE
                 }
                 if(position == NUM_ITEMS-1){
                     btnForward.visibility = View.INVISIBLE
-                } else{
-                    btnForward.visibility = View.VISIBLE
+                    closeBottomButton.visibility = View.VISIBLE
+                }else if(position == NUM_ITEMS-2){
+                    if(closeBottomButton.visibility == View.VISIBLE) {
+                        closeBottomButton.visibility = View.INVISIBLE
+                        btnForward.visibility = View.VISIBLE
+                    }
+                    //btnForward.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.arrow_forward_white, null))
+                    //btnForward.setBackgroundColor(ResourcesCompat.getColor(resources,R.attr.colorAccent, theme))
+                    /*val
+                    GET THE COLOR VALUE OF THE THEMER
+                    colo = TypedValue()
+                    theme.resolveAttribute(R.attr.colorAccent,colo, true)
+                    btnForward.backgroundTintList  //(colo.data)
+
+                     */
                 }
             }
 
+
         })
+
+        closeBottomButton.setOnClickListener {
+            closeIntroduction()
+        }
     }
 
 
 
     /**
      * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
      * sequence.
      */
     private inner class IntroPagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
         override fun getItemCount(): Int = NUM_ITEMS
 
         override fun createFragment(position: Int): Fragment = IntroFragment.newInstance(position)
     }
 
     companion object{
         const private val DEBUG_TAG = "BusTO-IntroActivity"
         const val RESTART_MAIN = "restartMainActivity"
 
         const val NUM_ITEMS = 7
     }
 
     override fun closeIntroduction() {
         if(restartMain) startActivity(Intent(this, ActivityPrincipal::class.java))
         val pref = PreferencesHolder.getMainSharedPreferences(this)
         val editor = pref.edit()
         editor.putBoolean(PreferencesHolder.PREF_INTRO_ACTIVITY_RUN, true)
         //use commit so we don't "lose" info
         editor.commit()
         finish()
     }
 
 }
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/RouteAdapter.kt b/app/src/main/java/it/reyboz/bustorino/adapters/RouteAdapter.kt
index 8577422..0a7a4f4 100644
--- a/app/src/main/java/it/reyboz/bustorino/adapters/RouteAdapter.kt
+++ b/app/src/main/java/it/reyboz/bustorino/adapters/RouteAdapter.kt
@@ -1,59 +1,59 @@
 package it.reyboz.bustorino.adapters
 
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.TextView
 import androidx.cardview.widget.CardView
 import androidx.recyclerview.widget.RecyclerView
 import it.reyboz.bustorino.R
 import it.reyboz.bustorino.data.gtfs.GtfsRoute
 import java.lang.ref.WeakReference
 
 class RouteAdapter(val routes: List<GtfsRoute>,
-                    click: onItemClick,
-                    private val layoutId: Int = R.layout.line_title_header) :
+                   click: ItemClicker,
+                   private val layoutId: Int = R.layout.line_title_header) :
     RecyclerView.Adapter<RouteAdapter.ViewHolder>()
 {
-        val clickreference: WeakReference<onItemClick>
+        val clickreference: WeakReference<ItemClicker>
         init {
             clickreference = WeakReference(click)
         }
 
     class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
         val descrptionTextView: TextView
         val nameTextView : TextView
         val innerCardView : CardView?
         init {
             // Define click listener for the ViewHolder's View
             nameTextView = view.findViewById(R.id.lineShortNameTextView)
             descrptionTextView = view.findViewById(R.id.lineDirectionTextView)
             innerCardView = view.findViewById(R.id.innerCardView)
         }
     }
 
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
         val view = LayoutInflater.from(parent.context)
             .inflate(layoutId, parent, false)
 
         return ViewHolder(view)
     }
 
     override fun getItemCount() = routes.size
 
     override fun onBindViewHolder(holder: ViewHolder, position: Int) {
         // Get element from your dataset at this position and replace the
         // contents of the view with that element
         val route = routes[position]
         holder.nameTextView.text = route.shortName
         holder.descrptionTextView.text = route.longName
 
         holder.itemView.setOnClickListener{
             clickreference.get()?.onRouteItemClicked(route)
         }
     }
 
-    fun interface onItemClick{
+    fun interface ItemClicker{
         fun onRouteItemClicked(gtfsRoute: GtfsRoute)
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt b/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt
index c780291..bce4356 100644
--- a/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt
+++ b/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt
@@ -1,48 +1,62 @@
 package it.reyboz.bustorino.adapters
 
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.TextView
 import androidx.recyclerview.widget.RecyclerView
 import it.reyboz.bustorino.R
 import it.reyboz.bustorino.backend.Palina
+import java.lang.ref.WeakReference
 
-class RouteOnlyLineAdapter (val routeNames: List<String>) :
+class RouteOnlyLineAdapter (val routeNames: List<String>,
+                            onItemClick: OnClick?) :
     RecyclerView.Adapter<RouteOnlyLineAdapter.ViewHolder>() {
 
+
+    private val clickreference: WeakReference<OnClick>?
+    init {
+        clickreference = if(onItemClick!=null) WeakReference(onItemClick) else null
+    }
+
     /**
      * Provide a reference to the type of views that you are using
      * (custom ViewHolder)
      */
     class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
         val textView: TextView
 
         init {
             // Define click listener for the ViewHolder's View
             textView = view.findViewById(R.id.routeBallID)
         }
     }
-    constructor(palina: Palina, showOnlyEmpty: Boolean): this(palina.routesNamesWithNoPassages)
+    constructor(palina: Palina, showOnlyEmpty: Boolean): this(palina.routesNamesWithNoPassages, null)
 
     // Create new views (invoked by the layout manager)
     override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
         // Create a new view, which defines the UI of the list item
         val view = LayoutInflater.from(viewGroup.context)
             .inflate(R.layout.round_line_header, viewGroup, false)
 
         return ViewHolder(view)
     }
 
     // Replace the contents of a view (invoked by the layout manager)
     override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
 
         // Get element from your dataset at this position and replace the
         // contents of the view with that element
         viewHolder.textView.text = routeNames[position]
+        viewHolder.itemView.setOnClickListener{
+            clickreference?.get()?.onItemClick(position, routeNames[position])
+        }
     }
 
     // Return the size of your dataset (invoked by the layout manager)
     override fun getItemCount() = routeNames.size
 
+    fun interface OnClick{
+        fun onItemClick(index: Int, name: String)
+    }
 }
diff --git a/app/src/main/java/it/reyboz/bustorino/data/GtfsRepository.kt b/app/src/main/java/it/reyboz/bustorino/data/GtfsRepository.kt
index bba97c0..e41b57b 100644
--- a/app/src/main/java/it/reyboz/bustorino/data/GtfsRepository.kt
+++ b/app/src/main/java/it/reyboz/bustorino/data/GtfsRepository.kt
@@ -1,38 +1,42 @@
 package it.reyboz.bustorino.data
 
 import android.content.Context
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import it.reyboz.bustorino.data.gtfs.*
 
 class GtfsRepository(
         val gtfsDao: GtfsDBDao
 ) {
 
     constructor(context: Context) : this(GtfsDatabase.getGtfsDatabase(context).gtfsDao())
     fun getLinesLiveDataForFeed(feed: String): LiveData<List<GtfsRoute>>{
         //return withContext(Dispatchers.IO){
             return gtfsDao.getRoutesForFeed(feed)
         //}
     }
     fun getPatternsForRouteID(routeID: String): LiveData<List<MatoPattern>>{
         return if(routeID.isNotEmpty())
             gtfsDao.getPatternsLiveDataByRouteID(routeID)
         else
             MutableLiveData(listOf())
     }
 
     /**
      * Get the patterns with the stops lists (gtfsIDs only)
      */
     fun getPatternsWithStopsForRouteID(routeID: String): LiveData<List<MatoPatternWithStops>>{
         return if(routeID.isNotEmpty())
             gtfsDao.getPatternsWithStopsByRouteID(routeID)
         else
             MutableLiveData(listOf())
     }
 
     fun getAllRoutes(): LiveData<List<GtfsRoute>>{
         return  gtfsDao.getAllRoutes()
     }
+
+    fun getRouteFromGtfsId(gtfsId: String): LiveData<GtfsRoute>{
+        return gtfsDao.getRouteByGtfsID(gtfsId)
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java b/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java
index 477ec9b..3581adc 100644
--- a/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java
+++ b/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java
@@ -1,62 +1,91 @@
 /*
 	BusTO - Data 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 <http://www.gnu.org/licenses/>.
  */
 package it.reyboz.bustorino.data;
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.util.Log;
 import it.reyboz.bustorino.R;
 
 import static android.content.Context.MODE_PRIVATE;
 
 import androidx.preference.PreferenceManager;
 
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * Static class for commonly used SharedPreference operations
  */
 public abstract class PreferencesHolder {
 
     public static final String PREF_GTFS_DB_VERSION = "gtfs_db_version";
     public static final String PREF_INTRO_ACTIVITY_RUN ="pref_intro_activity_run";
 
+    public static final String PREF_FAVORITE_LINES = "pref_favorite_lines";
+
     public static SharedPreferences getMainSharedPreferences(Context context){
         return context.getSharedPreferences(context.getString(R.string.mainSharedPreferences), MODE_PRIVATE);
     }
 
     public static SharedPreferences getAppPreferences(Context con){
         return PreferenceManager.getDefaultSharedPreferences(con);
     }
 
     public static int getGtfsDBVersion(SharedPreferences pref){
         return pref.getInt(PREF_GTFS_DB_VERSION,-1);
     }
     public static void setGtfsDBVersion(SharedPreferences pref,int version){
         SharedPreferences.Editor ed = pref.edit();
         ed.putInt(PREF_GTFS_DB_VERSION,version);
         ed.apply();
     }
 
     /**
      * Check if the introduction activity has been run at least one
      * @param con the context needed
      * @return true if it has been run
      */
     public static boolean hasIntroFinishedOneShot(Context con){
         final SharedPreferences pref = getMainSharedPreferences(con);
         return pref.getBoolean(PREF_INTRO_ACTIVITY_RUN, false);
     }
+
+    public static boolean addOrRemoveLineToFavorites(Context con, String gtfsLineId, boolean addToFavorites){
+        final SharedPreferences pref = getMainSharedPreferences(con);
+        final HashSet<String> favorites = new HashSet<>(pref.getStringSet(PREF_FAVORITE_LINES, new HashSet<>()));
+        boolean modified = true;
+        if(addToFavorites)
+            favorites.add(gtfsLineId);
+        else if(favorites.contains(gtfsLineId))
+            favorites.remove(gtfsLineId);
+        else
+            modified = false; // we are not changing anything
+        if(modified) {
+            final SharedPreferences.Editor editor = pref.edit();
+            editor.putStringSet(PREF_FAVORITE_LINES, favorites);
+            editor.apply();
+        }
+        return modified;
+    }
+
+    public static HashSet<String> getFavoritesLinesGtfsIDs(Context con){
+        final SharedPreferences pref = getMainSharedPreferences(con);
+        return new HashSet<>(pref.getStringSet(PREF_FAVORITE_LINES, new HashSet<>()));
+    }
 }
diff --git a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt
index b1c02c6..f36f360 100644
--- a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt
+++ b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt
@@ -1,156 +1,156 @@
 /*
 	BusTO - Data 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 <http://www.gnu.org/licenses/>.
  */
 package it.reyboz.bustorino.data.gtfs
 
 import androidx.lifecycle.LiveData
 import androidx.room.*
 
 @Dao
 interface GtfsDBDao {
     // get queries
     @Query("SELECT * FROM "+GtfsRoute.DB_TABLE)
     fun getAllRoutes() : LiveData<List<GtfsRoute>>
 
-    @Query("SELECT * FROM ${GtfsRoute.DB_TABLE} WHERE ${GtfsRoute.COL_ROUTE_ID} IN (:routeGtfsIds)")
-    fun getRoutesByIDs(routeGtfsIds: List<String>): LiveData<List<GtfsRoute>>
+    @Query("SELECT * FROM ${GtfsRoute.DB_TABLE} WHERE ${GtfsRoute.COL_ROUTE_ID} LIKE :gtfsId")
+    fun getRouteByGtfsID(gtfsId: String) : LiveData<GtfsRoute>
 
 
     @Query("SELECT "+GtfsTrip.COL_TRIP_ID+" FROM "+GtfsTrip.DB_TABLE)
     fun getAllTripsIDs() : List<String>
 
     @Query("SELECT "+GtfsStop.COL_STOP_ID+" FROM "+GtfsStop.DB_TABLE)
     fun getAllStopsIDs() : List<Int>
 
     @Query("SELECT * FROM "+GtfsStop.DB_TABLE+" WHERE "+GtfsStop.COL_STOP_CODE+" LIKE :queryID")
     fun getStopByStopID(queryID: String): LiveData<List<GtfsStop>>
 
     @Query("SELECT * FROM "+GtfsShape.DB_TABLE+
             " WHERE "+GtfsShape.COL_SHAPE_ID+" LIKE :shapeID"+
             " ORDER BY "+GtfsShape.COL_POINT_SEQ+ " ASC"
     )
     fun getShapeByID(shapeID: String) : LiveData<List<GtfsShape>>
 
     @Query("SELECT * FROM ${GtfsRoute.DB_TABLE} WHERE ${GtfsRoute.COL_AGENCY_ID} LIKE :agencyID")
     fun getRoutesByAgency(agencyID:String) : LiveData<List<GtfsRoute>>
 
     @Query("SELECT ${MatoPattern.COL_CODE} FROM ${MatoPattern.TABLE_NAME} ")
     fun getPatternsCodes(): List<String>
     @Query("SELECT * FROM ${MatoPattern.TABLE_NAME} WHERE ${MatoPattern.COL_ROUTE_ID} LIKE :routeID")
     fun getPatternsLiveDataByRouteID(routeID: String): LiveData<List<MatoPattern>>
 
     @Query("SELECT * FROM "+MatoPattern.TABLE_NAME+" WHERE ${MatoPattern.COL_ROUTE_ID} LIKE :routeGtfsId")
     fun getPatternsForRouteID(routeGtfsId: String) : List<MatoPattern>
 
     @Query("SELECT * FROM ${PatternStop.TABLE_NAME} WHERE ${PatternStop.COL_PATTERN_ID} LIKE :patternGtfsID")
     fun getStopsByPatternID(patternGtfsID: String): LiveData<List<PatternStop>>
 
     @Transaction
     @Query("SELECT * FROM ${MatoPattern.TABLE_NAME} WHERE ${MatoPattern.COL_ROUTE_ID} LIKE :routeID")
     fun getPatternsWithStopsByRouteID(routeID: String): LiveData<List<MatoPatternWithStops>>
 
     @Transaction
     @Query("SELECT * FROM ${MatoPattern.TABLE_NAME} WHERE ${MatoPattern.COL_CODE} IN (:patternGtfsIDs)")
     fun getPatternsWithStopsFromIDs(patternGtfsIDs: List<String>) : LiveData<List<MatoPatternWithStops>>
 
     @Query("SELECT ${MatoPattern.COL_CODE} FROM ${MatoPattern.TABLE_NAME} WHERE ${MatoPattern.COL_CODE} IN (:codes)")
     fun getPatternsCodesInTheDB(codes: List<String>): List<String>
 
     @Transaction
     @Query("SELECT * FROM ${GtfsTrip.DB_TABLE} WHERE ${GtfsTrip.COL_TRIP_ID} IN (:tripsIds)")
     fun getTripsFromIDs(tripsIds: List<String>) : List<GtfsTrip>
 
     @Transaction
     @Query("SELECT * FROM ${GtfsTrip.DB_TABLE} WHERE ${GtfsTrip.COL_TRIP_ID} IN (:trips)")
     fun getTripPatternStops(trips: List<String>): LiveData<List<TripAndPatternWithStops>>
 
 
 
     fun getRoutesForFeed(feed:String): LiveData<List<GtfsRoute>>{
         val agencyID = "${feed}:%"
         return getRoutesByAgency(agencyID)
     }
 
 
     @Transaction
     fun clearAndInsertRoutes(routes: List<GtfsRoute>){
         deleteAllRoutes()
         insertRoutes(routes)
     }
     @Transaction
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertRoutes(routes: List<GtfsRoute>)
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertStops(stops: List<GtfsStop>)
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertCalendarServices(services: List<GtfsService>)
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertShapes(shapes: List<GtfsShape>)
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertDates(dates: List<GtfsServiceDate>)
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertServices(services: List<GtfsService>)
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertTrips(trips: List<GtfsTrip>)
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertStopTimes(stopTimes: List<GtfsStopTime>)
 
     @Query("DELETE FROM "+GtfsRoute.DB_TABLE)
     fun deleteAllRoutes()
     @Query("DELETE FROM "+GtfsStop.DB_TABLE)
     fun deleteAllStops()
     @Query("DELETE FROM "+GtfsTrip.DB_TABLE)
     fun deleteAllTrips()
 
     @Query("DELETE FROM ${MatoPattern.TABLE_NAME} WHERE ${MatoPattern.COL_CODE} IN (:codes)")
     fun deletePatternsWithCodes(codes: List<String>): Int
     @Update(onConflict = OnConflictStrategy.REPLACE)
     fun updateShapes(shapes: List<GtfsShape>) : Int
 
     @Transaction
     fun updateAllStops(stops: List<GtfsStop>){
         deleteAllStops()
         insertStops(stops)
     }
     @Query("DELETE FROM "+GtfsStopTime.DB_TABLE)
     fun deleteAllStopTimes()
     @Query("DELETE FROM "+GtfsService.DB_TABLE)
     fun deleteAllServices()
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertFeeds(feeds: List<GtfsFeed>)
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertAgencies(agencies: List<GtfsAgency>)
 
     @Transaction
     fun insertAgenciesWithFeeds(feeds: List<GtfsFeed>, agencies: List<GtfsAgency>){
         insertFeeds(feeds)
         insertAgencies(agencies)
     }
     //patterns
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertPatterns(patterns: List<MatoPattern>)
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     fun insertPatternStops(patternStops: List<PatternStop>)
 }
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java
index 74333ce..fca2a6c 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java
@@ -1,693 +1,693 @@
 /*
 	BusTO  - Fragments components
     Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
  */
 package it.reyboz.bustorino.fragments;
 
 
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
 
 import android.widget.*;
 import androidx.annotation.Nullable;
 import androidx.annotation.NonNull;
 import androidx.core.widget.NestedScrollView;
 import androidx.loader.app.LoaderManager;
 import androidx.loader.content.CursorLoader;
 import androidx.loader.content.Loader;
 
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 import androidx.recyclerview.widget.DividerItemDecoration;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import it.reyboz.bustorino.R;
 import it.reyboz.bustorino.adapters.AdapterClickListener;
 import it.reyboz.bustorino.adapters.PalinaAdapter;
 import it.reyboz.bustorino.adapters.RouteOnlyLineAdapter;
 import it.reyboz.bustorino.backend.ArrivalsFetcher;
 import it.reyboz.bustorino.backend.DBStatusManager;
 import it.reyboz.bustorino.backend.Fetcher;
 import it.reyboz.bustorino.backend.FiveTNormalizer;
 import it.reyboz.bustorino.backend.Palina;
 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;
 import it.reyboz.bustorino.middleware.AsyncStopFavoriteAction;
 import it.reyboz.bustorino.util.LinesNameSorter;
 import it.reyboz.bustorino.util.ViewUtils;
 
 import static it.reyboz.bustorino.fragments.ScreenBaseFragment.setOption;
 
 public class ArrivalsFragment extends ResultBaseFragment implements LoaderManager.LoaderCallbacks<Cursor> {
 
     private static final String OPTION_SHOW_LEGEND = "show_legend";
     private final static String KEY_STOP_ID = "stopid";
     private final static String KEY_STOP_NAME = "stopname";
     private final static String DEBUG_TAG_ALL = "BUSTOArrivalsFragment";
     private String DEBUG_TAG = DEBUG_TAG_ALL;
     private final static int loaderFavId = 2;
     private final static int loaderStopId = 1;
     static final String STOP_TITLE = "messageExtra";
     private final static String SOURCES_TEXT="sources_textview_message";
 
     private @Nullable String stopID,stopName;
     private DBStatusManager prefs;
     private DBStatusManager.OnDBUpdateStatusChangeListener listener;
     private boolean justCreated = false;
     private Palina lastUpdatedPalina = null;
     private boolean needUpdateOnAttach = false;
     private boolean fetchersChangeRequestPending = false;
     private boolean stopIsInFavorites = false;
 
     //Views
     protected ImageButton addToFavorites;
     protected TextView timesSourceTextView;
     protected TextView messageTextView;
     protected RecyclerView arrivalsRecyclerView;
     private PalinaAdapter mListAdapter = null;
 
     private TextView howDoesItWorkTextView;
     private Button hideHintButton;
 
 
     //private NestedScrollView theScrollView;
     protected RecyclerView noArrivalsRecyclerView;
     private RouteOnlyLineAdapter noArrivalsAdapter;
     private TextView noArrivalsTitleView;
     private GridLayoutManager layoutManager;
 
     //private View canaryEndView;
     private List<ArrivalsFetcher> fetchers = null; //new ArrayList<>(Arrays.asList(utils.getDefaultArrivalsFetchers()));
 
     private boolean reloadOnResume = true;
 
     private final AdapterClickListener<Route> mRouteClickListener = route -> {
             String routeName;
 
             routeName = FiveTNormalizer.routeInternalToDisplay(route.getNameForDisplay());
             if (routeName == null) {
                 routeName = route.getNameForDisplay();
             }
             if(getContext()==null)
                 Log.e(DEBUG_TAG, "Touched on a route but Context is null");
             else if (route.destinazione == null || route.destinazione.length() == 0) {
                 Toast.makeText(getContext(),
                         getString(R.string.route_towards_unknown, routeName), Toast.LENGTH_SHORT).show();
             } else {
                 Toast.makeText(getContext(),
                         getString(R.string.route_towards_destination, routeName, route.destinazione), Toast.LENGTH_SHORT).show();
             }
 
     };
 
     public static ArrivalsFragment newInstance(String stopID){
         return newInstance(stopID, null);
     }
 
     public static ArrivalsFragment newInstance(@NonNull String stopID, @Nullable String stopName){
         ArrivalsFragment fragment = new ArrivalsFragment();
         Bundle args = new Bundle();
         args.putString(KEY_STOP_ID,stopID);
         //parameter for ResultListFragmentrequestArrivalsForStopID
         //args.putSerializable(LIST_TYPE,FragmentKind.ARRIVALS);
         if (stopName != null){
             args.putString(KEY_STOP_NAME,stopName);
         }
         fragment.setArguments(args);
         return fragment;
     }
 
     public static String getFragmentTag(Palina p) {
         return "palina_"+p.ID;
     }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         stopID = getArguments().getString(KEY_STOP_ID);
         DEBUG_TAG = DEBUG_TAG_ALL+" "+stopID;
 
         //this might really be null
         stopName = getArguments().getString(KEY_STOP_NAME);
         final ArrivalsFragment arrivalsFragment = this;
         listener = new DBStatusManager.OnDBUpdateStatusChangeListener() {
             @Override
             public void onDBStatusChanged(boolean updating) {
                 if(!updating){
                     getLoaderManager().restartLoader(loaderFavId,getArguments(),arrivalsFragment);
                 } else {
                     final LoaderManager lm = getLoaderManager();
                     lm.destroyLoader(loaderFavId);
                     lm.destroyLoader(loaderStopId);
                 }
             }
 
             @Override
             public boolean defaultStatusValue() {
                 return true;
             }
         };
         prefs = new DBStatusManager(getContext().getApplicationContext(),listener);
         justCreated = true;
 
     }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
         View root = inflater.inflate(R.layout.fragment_arrivals, container, false);
         messageTextView = root.findViewById(R.id.messageTextView);
         addToFavorites = root.findViewById(R.id.addToFavorites);
         // "How does it work part"
         howDoesItWorkTextView = root.findViewById(R.id.howDoesItWorkTextView);
         hideHintButton = root.findViewById(R.id.hideHintButton);
         hideHintButton.setOnClickListener(this::onHideHint);
 
         //theScrollView = root.findViewById(R.id.arrivalsScrollView);
         // recyclerview holding the arrival times
         arrivalsRecyclerView = root.findViewById(R.id.arrivalsRecyclerView);
         final LinearLayoutManager manager = new LinearLayoutManager(getContext());
         arrivalsRecyclerView.setLayoutManager(manager);
         final DividerItemDecoration mDividerItemDecoration = new DividerItemDecoration(arrivalsRecyclerView.getContext(),
                 manager.getOrientation());
         arrivalsRecyclerView.addItemDecoration(mDividerItemDecoration);
         timesSourceTextView = root.findViewById(R.id.timesSourceTextView);
         timesSourceTextView.setOnLongClickListener(view -> {
             if(!fetchersChangeRequestPending){
                 rotateFetchers();
                 //Show we are changing provider
                 timesSourceTextView.setText(R.string.arrival_source_changing);
 
                 mListener.requestArrivalsForStopID(stopID);
                 fetchersChangeRequestPending = true;
                 return true;
             }
             return false;
         });
         timesSourceTextView.setOnClickListener(view -> {
             Toast.makeText(getContext(), R.string.change_arrivals_source_message, Toast.LENGTH_SHORT)
                     .show();
         });
         //Button
         addToFavorites.setClickable(true);
         addToFavorites.setOnClickListener(v -> {
             // add/remove the stop in the favorites
             toggleLastStopToFavorites();
         });
 
         String displayName = getArguments().getString(STOP_TITLE);
         if(displayName!=null)
             setTextViewMessage(String.format(
                 getString(R.string.passages), displayName));
 
 
         String probablemessage = getArguments().getString(MESSAGE_TEXT_VIEW);
         if (probablemessage != null) {
             //Log.d("BusTO fragment " + this.getTag(), "We have a possible message here in the savedInstaceState: " + probablemessage);
             messageTextView.setText(probablemessage);
             messageTextView.setVisibility(View.VISIBLE);
         }
         //no arrivals stuff
         noArrivalsRecyclerView = root.findViewById(R.id.noArrivalsRecyclerView);
         layoutManager = new GridLayoutManager(getContext(),60);
         layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
             @Override
             public int getSpanSize(int position) {
                 return 12;
             }
         });
         noArrivalsRecyclerView.setLayoutManager(layoutManager);
         noArrivalsTitleView = root.findViewById(R.id.noArrivalsMessageTextView);
 
         //canaryEndView = root.findViewById(R.id.canaryEndView);
 
         /*String sourcesTextViewData = getArguments().getString(SOURCES_TEXT);
         if (sourcesTextViewData!=null){
             timesSourceTextView.setText(sourcesTextViewData);
         }*/
         //need to do this when we recreate the fragment but we haven't updated the arrival times
         if (lastUpdatedPalina!=null)
             showArrivalsSources(lastUpdatedPalina);
         return root;
     }
 
     @Override
     public void onResume() {
         super.onResume();
         LoaderManager loaderManager  = getLoaderManager();
         Log.d(DEBUG_TAG, "OnResume, justCreated "+justCreated+", lastUpdatedPalina is: "+lastUpdatedPalina);
         /*if(needUpdateOnAttach){
             updateFragmentData(null);
             needUpdateOnAttach=false;
         }*/
         /*if(lastUpdatedPalina!=null){
             updateFragmentData(null);
             showArrivalsSources(lastUpdatedPalina);
         }*/
         mListener.readyGUIfor(FragmentKind.ARRIVALS);
 
         if (mListAdapter!=null)
             resetListAdapter(mListAdapter);
         if(noArrivalsAdapter!=null){
             noArrivalsRecyclerView.setAdapter(noArrivalsAdapter);
 
         }
 
         if(stopID!=null){
             if(!justCreated){
                 fetchers = utils.getDefaultArrivalsFetchers(getContext());
                 adjustFetchersToSource();
 
                 if (reloadOnResume)
                     mListener.requestArrivalsForStopID(stopID);
             }
             else justCreated = false;
             //start the loader
             if(prefs.isDBUpdating(true)){
                 prefs.registerListener();
             } else {
                 Log.d(DEBUG_TAG, "Restarting loader for stop");
                 loaderManager.restartLoader(loaderFavId, getArguments(), this);
             }
             updateMessage();
         }
 
         if (ScreenBaseFragment.getOption(requireContext(),OPTION_SHOW_LEGEND, true)) {
             showHints();
         }
 
 
     }
 
 
     @Override
     public void onStart() {
         super.onStart();
         if (needUpdateOnAttach){
             updateFragmentData(null);
             needUpdateOnAttach = false;
         }
     }
 
     @Override
     public void onPause() {
         if(listener!=null)
             prefs.unregisterListener();
         super.onPause();
         LoaderManager loaderManager  = getLoaderManager();
         Log.d(DEBUG_TAG, "onPause, have running loaders: "+loaderManager.hasRunningLoaders());
         loaderManager.destroyLoader(loaderFavId);
 
     }
 
     @Override
     public void onAttach(@NonNull Context context) {
         super.onAttach(context);
 
         //get fetchers
         fetchers = utils.getDefaultArrivalsFetchers(context);
     }
 
     @Nullable
     public String getStopID() {
         return stopID;
     }
 
     public boolean reloadsOnResume() {
         return reloadOnResume;
     }
 
     public void setReloadOnResume(boolean reloadOnResume) {
         this.reloadOnResume = reloadOnResume;
     }
 
     // HINT "HOW TO USE"
     private void showHints() {
         howDoesItWorkTextView.setVisibility(View.VISIBLE);
         hideHintButton.setVisibility(View.VISIBLE);
         //actionHelpMenuItem.setVisible(false);
     }
 
     private void hideHints() {
         howDoesItWorkTextView.setVisibility(View.GONE);
         hideHintButton.setVisibility(View.GONE);
         //actionHelpMenuItem.setVisible(true);
     }
 
     public void onHideHint(View v) {
         hideHints();
         setOption(requireContext(),OPTION_SHOW_LEGEND, false);
     }
     /**
      * Give the fetchers
      * @return the list of the fetchers
      */
     public ArrayList<Fetcher> getCurrentFetchers(){
         return new ArrayList<>(this.fetchers);
     }
     public ArrivalsFetcher[] getCurrentFetchersAsArray(){
         ArrivalsFetcher[] arr = new ArrivalsFetcher[fetchers.size()];
         fetchers.toArray(arr);
         return arr;
     }
 
     private void rotateFetchers(){
         Log.d(DEBUG_TAG, "Rotating fetchers, before: "+fetchers);
         Collections.rotate(fetchers, -1);
         Log.d(DEBUG_TAG, "Rotating fetchers, afterwards: "+fetchers);
 
     }
 
 
     /**
      * Update the UI with the new data
      * @param p the full Palina
      */
     public void updateFragmentData(@Nullable Palina p){
         if (p!=null)
             lastUpdatedPalina = p;
 
         if (!isAdded()){
             //defer update at next show
             if (p==null)
                 Log.w(DEBUG_TAG, "Asked to update the data, but we're not attached and the data is null");
             else needUpdateOnAttach = true;
         } else {
 
             final PalinaAdapter adapter = new PalinaAdapter(getContext(), lastUpdatedPalina, mRouteClickListener, true);
             showArrivalsSources(lastUpdatedPalina);
             resetListAdapter(adapter);
 
             final ArrayList<String> routesWithNoPassages = lastUpdatedPalina.getRoutesNamesWithNoPassages();
             Collections.sort(routesWithNoPassages, new LinesNameSorter());
-            noArrivalsAdapter = new RouteOnlyLineAdapter(routesWithNoPassages);
+            noArrivalsAdapter = new RouteOnlyLineAdapter(routesWithNoPassages, null);
             if(noArrivalsRecyclerView!=null){
                 noArrivalsRecyclerView.setAdapter(noArrivalsAdapter);
                 //hide the views if there are no empty routes
                 if(routesWithNoPassages.isEmpty()){
                     noArrivalsRecyclerView.setVisibility(View.GONE);
                     noArrivalsTitleView.setVisibility(View.GONE);
                 } else {
                     noArrivalsRecyclerView.setVisibility(View.VISIBLE);
                     noArrivalsTitleView.setVisibility(View.VISIBLE);
                 }
             }
 
             //canaryEndView.setVisibility(View.VISIBLE);
             //check if canaryEndView is visible
             //boolean isCanaryVisibile = ViewUtils.Companion.isViewPartiallyVisibleInScroll(canaryEndView, theScrollView);
             //Log.d(DEBUG_TAG, "Canary view fully visibile: "+isCanaryVisibile);
 
         }
     }
 
 
 
     /**
      * Set the message of the arrival times source
      * @param p Palina with the arrival times
      */
     protected void showArrivalsSources(Palina p){
         final Passaggio.Source source = p.getPassaggiSourceIfAny();
         if (source == null){
             Log.e(DEBUG_TAG, "NULL SOURCE");
             return;
         }
         String source_txt;
         switch (source){
             case GTTJSON:
                 source_txt = getString(R.string.gttjsonfetcher);
                 break;
             case FiveTAPI:
                 source_txt = getString(R.string.fivetapifetcher);
                 break;
             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
                 source_txt = getString(R.string.undetermined_source);
                 break;
             default:
                 throw new IllegalStateException("Unexpected value: " + source);
         }
         //
         final boolean updatedFetchers = adjustFetchersToSource(source);
         if(!updatedFetchers)
             Log.w(DEBUG_TAG, "Tried to update the source fetcher but it didn't work");
         final String base_message = getString(R.string.times_source_fmt, source_txt);
         timesSourceTextView.setText(base_message);
         timesSourceTextView.setVisibility(View.VISIBLE);
 
         if (p.getTotalNumberOfPassages() > 0) {
             timesSourceTextView.setVisibility(View.VISIBLE);
         } else {
             timesSourceTextView.setVisibility(View.INVISIBLE);
         }
         fetchersChangeRequestPending = false;
     }
 
     protected boolean adjustFetchersToSource(Passaggio.Source source){
         if (source == null) return false;
         int count = 0;
         if (source!= Passaggio.Source.UNDETERMINED)
             while (source != fetchers.get(0).getSourceForFetcher() && count < 200){
                 //we need to update the fetcher that is requested
                 rotateFetchers();
                 count++;
             }
         return count < 200;
 
     }
     protected boolean adjustFetchersToSource(){
         if (lastUpdatedPalina == null) return false;
         final Passaggio.Source source = lastUpdatedPalina.getPassaggiSourceIfAny();
         return adjustFetchersToSource(source);
     }
 
     /**
      * Update the message in the fragment
      *
      * It may eventually change the "Add to Favorite" icon
      */
     private void updateMessage(){
         String message = null;
         if (stopName != null && stopID != null && stopName.length() > 0) {
             message = (stopID.concat(" - ").concat(stopName));
         } else if(stopID!=null) {
             message = stopID;
         } else {
             Log.e("ArrivalsFragm"+getTag(),"NO ID FOR THIS FRAGMENT - something went horribly wrong");
         }
         if(message!=null) {
             setTextViewMessage(getString(R.string.passages,message));
         }
 
         // whatever is the case, update the star icon
         //updateStarIconFromLastBusStop();
 
     }
 
     @NonNull
     @Override
     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
         if(args.getString(KEY_STOP_ID)==null) return null;
         final String stopID = args.getString(KEY_STOP_ID);
         final Uri.Builder builder = AppDataProvider.getUriBuilderToComplete();
         CursorLoader cl;
         switch (id){
             case loaderFavId:
                 builder.appendPath("favorites").appendPath(stopID);
                 cl = new CursorLoader(getContext(),builder.build(),UserDB.getFavoritesColumnNamesAsArray,null,null,null);
 
                 break;
             case loaderStopId:
                 builder.appendPath("stop").appendPath(stopID);
                 cl = new CursorLoader(getContext(),builder.build(),new String[]{NextGenDB.Contract.StopsTable.COL_NAME},
                         null,null,null);
                 break;
             default:
                 return null;
         }
         cl.setUpdateThrottle(500);
         return cl;
     }
 
     @Override
     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
 
         switch (loader.getId()){
             case loaderFavId:
                 final int colUserName = data.getColumnIndex(UserDB.getFavoritesColumnNamesAsArray[1]);
                 if(data.getCount()>0){
                     // IT'S IN FAVORITES
                     data.moveToFirst();
                     final String probableName = data.getString(colUserName);
                     stopIsInFavorites = true;
                     stopName = probableName;
                     //update the message in the textview
                     updateMessage();
 
                 } else {
                     stopIsInFavorites =false;
                 }
                 updateStarIcon();
 
                 if(stopName == null){
                     //stop is not inside the favorites and wasn't provided
                     Log.d("ArrivalsFragment"+getTag(),"Stop wasn't in the favorites and has no name, looking in the DB");
                     getLoaderManager().restartLoader(loaderStopId,getArguments(),this);
                 }
                 break;
             case loaderStopId:
                 if(data.getCount()>0){
                     data.moveToFirst();
                     int index = data.getColumnIndex(
                             NextGenDB.Contract.StopsTable.COL_NAME
                     );
                     if (index == -1){
                         Log.e(DEBUG_TAG, "Index is -1, column not present. App may explode now...");
                     }
                     stopName = data.getString(index);
                     updateMessage();
                 } else {
                     Log.w("ArrivalsFragment"+getTag(),"Stop is not inside the database... CLOISTER BELL");
                 }
         }
 
     }
 
     @Override
     public void onLoaderReset(Loader<Cursor> loader) {
         //NOTHING TO DO
     }
     protected void resetListAdapter(PalinaAdapter adapter) {
         mListAdapter = adapter;
         if (arrivalsRecyclerView != null) {
             arrivalsRecyclerView.setAdapter(adapter);
             arrivalsRecyclerView.setVisibility(View.VISIBLE);
         }
     }
 
     /**
      * Set the message textView
      * @param message the whole message to write in the textView
      */
     public void setTextViewMessage(String message) {
         messageTextView.setText(message);
         messageTextView.setVisibility(View.VISIBLE);
     }
 
     public void toggleLastStopToFavorites() {
 
         Stop stop = lastUpdatedPalina;
         if (stop != null) {
 
             // toggle the status in background
             new AsyncStopFavoriteAction(getContext().getApplicationContext(), AsyncStopFavoriteAction.Action.TOGGLE,
                     v->updateStarIconFromLastBusStop(v)).execute(stop);
         } else {
             // this case have no sense, but just immediately update the favorite icon
             updateStarIconFromLastBusStop(true);
         }
     }
     /**
      * Update the star "Add to favorite" icon
      */
     public void updateStarIconFromLastBusStop(Boolean toggleDone) {
         if (stopIsInFavorites)
             stopIsInFavorites = !toggleDone;
         else stopIsInFavorites = toggleDone;
 
         updateStarIcon();
 
         // check if there is a last Stop
         /*
         if (stopID == null) {
             addToFavorites.setVisibility(View.INVISIBLE);
         } else {
             // filled or outline?
             if (isStopInFavorites(stopID)) {
                 addToFavorites.setImageResource(R.drawable.ic_star_filled);
             } else {
                 addToFavorites.setImageResource(R.drawable.ic_star_outline);
             }
 
             addToFavorites.setVisibility(View.VISIBLE);
         }
          */
     }
 
     /**
      * Update the star icon according to `stopIsInFavorites`
      */
     public void updateStarIcon() {
 
         // no favorites no party!
 
         // check if there is a last Stop
 
         if (stopID == null) {
             addToFavorites.setVisibility(View.INVISIBLE);
         } else {
             // filled or outline?
             if (stopIsInFavorites) {
                 addToFavorites.setImageResource(R.drawable.ic_star_filled);
             } else {
                 addToFavorites.setImageResource(R.drawable.ic_star_outline);
             }
 
             addToFavorites.setVisibility(View.VISIBLE);
         }
 
 
     }
 
     @Override
     public void onDestroyView() {
         arrivalsRecyclerView = null;
         if(getArguments()!=null) {
             getArguments().putString(SOURCES_TEXT, timesSourceTextView.getText().toString());
             getArguments().putString(MESSAGE_TEXT_VIEW, messageTextView.getText().toString());
         }
         super.onDestroyView();
     }
 
     public boolean isFragmentForTheSameStop(Palina p) {
         if (getTag() != null)
             return getTag().equals(getFragmentTag(p));
         else return false;
     }
 }
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/IntroFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/IntroFragment.kt
index 5006d75..7cd18a3 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/IntroFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/IntroFragment.kt
@@ -1,151 +1,154 @@
 package it.reyboz.bustorino.fragments
 
 import android.content.Context
 import android.graphics.BitmapFactory
 import android.graphics.text.LineBreaker
 import android.os.Build
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.Button
+import android.widget.ImageButton
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.fragment.app.Fragment
 import it.reyboz.bustorino.R
 import it.reyboz.bustorino.backend.utils
 import java.lang.IllegalStateException
 
 
 // TODO: Rename parameter arguments, choose names that match
 // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
 private const val SCREEN_INDEX = "screenindex"
 
 /**
  * A simple [Fragment] subclass.
  * Use the [IntroFragment.newInstance] factory method to
  * create an instance of this fragment.
  */
 class IntroFragment : Fragment() {
     // TODO: Rename and change types of parameters
     private var screenIndex = 1
     private lateinit var imageHolder: ImageView
     private lateinit var textView: TextView
 
 
+
     private lateinit var listener: IntroListener
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         arguments?.let {
             screenIndex = it.getInt(SCREEN_INDEX)
         }
     }
 
     override fun onAttach(context: Context) {
         super.onAttach(context)
         if(context !is IntroListener){
             throw IllegalStateException("Context must implement IntroListener")
         }
         listener = context
     }
 
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View? {
         // Inflate the layout for this fragment
         val root=  inflater.inflate(R.layout.fragment_intro, container, false)
         imageHolder = root.findViewById(R.id.image_tutorial)
         textView = root.findViewById(R.id.tutorialTextView)
 
 
         when(screenIndex){
             0 -> {
                 setImageBitmap(imageHolder, R.drawable.tuto_busto, 300f)
                 textView.text = utils.convertHtml(getString(R.string.tutorial_first))
             }
 
             1->{
                 setImageBitmap(imageHolder, R.drawable.tuto_search)
                 setTextHtmlDescription(R.string.tutorial_search)
             }
             2 ->{
                 setImageBitmap(imageHolder, R.drawable.tuto_arrivals)
                 textView.text = utils.convertHtml(getString(R.string.tutorial_arrivals))
             }
             3 ->{
                 setImageBitmap(imageHolder, R.drawable.tuto_stops)
                 textView.text = utils.convertHtml(getString(R.string.tutorial_stops))
             }
             4 ->{
                 setImageBitmap(imageHolder, R.drawable.tuto_map)
                 textView.text = utils.convertHtml(getString(R.string.tutorial_map))
             }
             5 ->{
                 setImageBitmap(imageHolder, R.drawable.tuto_line_det)
                 textView.text = utils.convertHtml(getString(R.string.tutorial_line))
             }
             6-> {
                 setImageBitmap(imageHolder,R.drawable.tuto_menu)
                 setTextHtmlDescription(R.string.tutorial_menu)
+                //this is the cheapest trick ever lol
                 val closeButton = root.findViewById<Button>(R.id.closeAllButton)
                 closeButton.visibility = View.VISIBLE
                 closeButton.setOnClickListener {
                     listener.closeIntroduction()
                 }
             }
         }
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             textView.breakStrategy = LineBreaker.BREAK_STRATEGY_HIGH_QUALITY
         }
 
         return root
     }
 
     private fun setTextHtmlDescription(resId: Int){
         textView.text = utils.convertHtml(getString(resId))
     }
 
 
     private fun setImageBitmap(imageView: ImageView, resId: Int, maxDpToScale:Float = DP_LIM_IMAGE){
         val bitmap = BitmapFactory.decodeResource(resources,resId)
         /*val limPix = utils.convertDipToPixels(resources, maxDpToScale)
         if (bitmap.width > limPix) {
             val rescFac = limPix/bitmap.width
             val rescaledBitmap = Bitmap.createScaledBitmap(bitmap,(limPix).toInt(), (bitmap.height*rescFac).toInt(),false)
             imageView.setImageBitmap(rescaledBitmap)
         }
         else
         */
         imageView.setImageBitmap(bitmap)
 
     }
 
     companion object {
 
         const val DP_LIM_IMAGE = 1000f
         /**
          * Use this factory method to create a new instance of
          * this fragment using the provided parameters.
          *
          * @param index the screen index
          * @return A new instance of fragment IntroFragment.
          */
         @JvmStatic
         fun newInstance(index: Int) =
             IntroFragment().apply {
                 arguments = Bundle().apply {
                     putInt(SCREEN_INDEX, index)
                 }
             }
         @JvmStatic
         fun makeArguments(index: Int) = Bundle().apply {
             putInt(SCREEN_INDEX, index) }
 
     }
 
     interface IntroListener{
         fun closeIntroduction()
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt
index d9fdc4c..2311be3 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt
@@ -1,758 +1,803 @@
 /*
 	BusTO  - Fragments components
     Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
  */
 package it.reyboz.bustorino.fragments
 
 import android.animation.ObjectAnimator
 import android.annotation.SuppressLint
 import android.content.Context
+import android.content.SharedPreferences
 import android.graphics.Paint
 import android.os.Bundle
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.*
 import androidx.appcompat.content.res.AppCompatResources
 import androidx.core.content.ContextCompat
 import androidx.core.content.res.ResourcesCompat
 import androidx.fragment.app.viewModels
 import androidx.lifecycle.lifecycleScope
 import androidx.preference.PreferenceManager
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import it.reyboz.bustorino.R
 import it.reyboz.bustorino.adapters.NameCapitalize
 import it.reyboz.bustorino.adapters.StopAdapterListener
 import it.reyboz.bustorino.adapters.StopRecyclerAdapter
 import it.reyboz.bustorino.backend.Stop
 import it.reyboz.bustorino.backend.gtfs.GtfsUtils
 import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate
 import it.reyboz.bustorino.backend.gtfs.PolylineParser
 import it.reyboz.bustorino.backend.utils
 import it.reyboz.bustorino.data.MatoTripsDownloadWorker
+import it.reyboz.bustorino.data.PreferencesHolder
 import it.reyboz.bustorino.data.gtfs.MatoPattern
 import it.reyboz.bustorino.data.gtfs.MatoPatternWithStops
 import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops
 import it.reyboz.bustorino.map.*
 import it.reyboz.bustorino.map.CustomInfoWindow.TouchResponder
 import it.reyboz.bustorino.viewmodels.LinesViewModel
 import it.reyboz.bustorino.viewmodels.LivePositionsViewModel
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import org.osmdroid.config.Configuration
 import org.osmdroid.tileprovider.tilesource.TileSourceFactory
 import org.osmdroid.util.BoundingBox
 import org.osmdroid.util.GeoPoint
 import org.osmdroid.views.MapView
 import org.osmdroid.views.overlay.FolderOverlay
 import org.osmdroid.views.overlay.Marker
 import org.osmdroid.views.overlay.Polyline
 import org.osmdroid.views.overlay.advancedpolyline.MonochromaticPaintList
 
 
 class LinesDetailFragment() : ScreenBaseFragment() {
 
-    private lateinit var lineID: String
+    private var lineID = ""
     private lateinit var patternsSpinner: Spinner
     private var patternsAdapter: ArrayAdapter<String>? = null
 
     //private var patternsSpinnerState: Parcelable? = null
 
     private lateinit var currentPatterns: List<MatoPatternWithStops>
 
     private lateinit var map: MapView
     private var viewingPattern: MatoPatternWithStops? = null
 
     private val viewModel: LinesViewModel by viewModels()
     private val mapViewModel: MapViewModel by viewModels()
     private var firstInit = true
     private var pausedFragment = false
     private lateinit var switchButton: ImageButton
+
+    private var favoritesButton: ImageButton? = null
+    private var isLineInFavorite = false
+    private val lineSharedPrefMonitor = SharedPreferences.OnSharedPreferenceChangeListener { pref, keychanged ->
+        if(keychanged!=PreferencesHolder.PREF_FAVORITE_LINES || lineID.isEmpty()) return@OnSharedPreferenceChangeListener
+        val newFavorites = pref.getStringSet(PreferencesHolder.PREF_FAVORITE_LINES, HashSet())
+        newFavorites?.let {
+            isLineInFavorite = it.contains(lineID)
+            //if the button has been intialized, change the icon accordingly
+            favoritesButton?.let { button->
+                if(isLineInFavorite) {
+                    button.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_star_filled, null))
+                    Toast.makeText(context,R.string.favorites_line_add,Toast.LENGTH_SHORT).show()
+                } else {
+                    button.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_star_outline, null))
+                    Toast.makeText(context,R.string.favorites_line_remove,Toast.LENGTH_SHORT).show()
+                }
+
+
+            }
+        }
+    }
+
     private lateinit var stopsRecyclerView: RecyclerView
+    private lateinit var descripTextView: TextView
     //adapter for recyclerView
     private val stopAdapterListener= object : StopAdapterListener {
         override fun onTappedStop(stop: Stop?) {
 
             if(viewModel.shouldShowMessage) {
                 Toast.makeText(context, R.string.long_press_stop_4_options, Toast.LENGTH_SHORT).show()
                 viewModel.shouldShowMessage=false
             }
             stop?.let {
-                fragmentListener?.requestArrivalsForStopID(it.ID)
+                fragmentListener.requestArrivalsForStopID(it.ID)
             }
             if(stop == null){
                 Log.e(DEBUG_TAG,"Passed wrong stop")
             }
             if(fragmentListener == null){
                 Log.e(DEBUG_TAG, "Fragment listener is null")
             }
         }
 
         override fun onLongPressOnStop(stop: Stop?): Boolean {
             TODO("Not yet implemented")
         }
 
     }
 
 
     private var polyline: Polyline? = null
     //private var stopPosList = ArrayList<GeoPoint>()
 
     private lateinit var stopsOverlay: FolderOverlay
     private lateinit var locationOverlay: LocationOverlay
     //fragment actions
     private lateinit var fragmentListener: CommonFragmentListener
 
     private val stopTouchResponder = TouchResponder { stopID, stopName ->
         Log.d(DEBUG_TAG, "Asked to show arrivals for stop ID: $stopID")
         fragmentListener.requestArrivalsForStopID(stopID)
     }
     private var showOnTopOfLine = true
     private var recyclerInitDone = false
 
     private var useMQTTPositions = true
 
     //position of live markers
     private val busPositionMarkersByTrip = HashMap<String,Marker>()
     private var busPositionsOverlay = FolderOverlay()
 
     private val tripMarkersAnimators = HashMap<String, ObjectAnimator>()
 
     private val liveBusViewModel: LivePositionsViewModel by viewModels()
     @SuppressLint("SetTextI18n")
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View? {
         val rootView = inflater.inflate(R.layout.fragment_lines_detail, container, false)
         lineID = requireArguments().getString(LINEID_KEY, "")
         switchButton = rootView.findViewById(R.id.switchImageButton)
+        favoritesButton = rootView.findViewById(R.id.favoritesButton)
         stopsRecyclerView = rootView.findViewById(R.id.patternStopsRecyclerView)
+        descripTextView = rootView.findViewById(R.id.lineDescripTextView)
+        descripTextView.visibility = View.INVISIBLE
 
         val titleTextView = rootView.findViewById<TextView>(R.id.titleTextView)
-
         titleTextView.text = getString(R.string.line)+" "+GtfsUtils.getLineNameFromGtfsID(lineID)
 
+        favoritesButton?.isClickable = true
+        favoritesButton?.setOnClickListener {
+            if(lineID.isNotEmpty())
+                PreferencesHolder.addOrRemoveLineToFavorites(requireContext(),lineID,!isLineInFavorite)
+        }
+        val preferences = PreferencesHolder.getMainSharedPreferences(requireContext())
+        val favorites = preferences.getStringSet(PreferencesHolder.PREF_FAVORITE_LINES, HashSet())
+        if(favorites!=null && favorites.contains(lineID)){
+            favoritesButton?.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_star_filled, null))
+            isLineInFavorite = true
+        }
+        preferences.registerOnSharedPreferenceChangeListener(lineSharedPrefMonitor)
+
         patternsSpinner = rootView.findViewById(R.id.patternsSpinner)
         patternsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, ArrayList<String>())
         patternsSpinner.adapter = patternsAdapter
 
         initializeMap(rootView)
 
         initializeRecyclerView()
 
         switchButton.setOnClickListener{
             if(map.visibility == View.VISIBLE){
                 map.visibility = View.GONE
                 stopsRecyclerView.visibility = View.VISIBLE
 
                 viewModel.setMapShowing(false)
                 liveBusViewModel.stopMatoUpdates()
                 switchButton.setImageDrawable(AppCompatResources.getDrawable(requireContext(), R.drawable.ic_map_white_30))
             } else{
                 stopsRecyclerView.visibility = View.GONE
                 map.visibility = View.VISIBLE
                 viewModel.setMapShowing(true)
                 if(useMQTTPositions)
                     liveBusViewModel.requestMatoPosUpdates(lineID)
                 else
                     liveBusViewModel.requestGTFSUpdates()
                 switchButton.setImageDrawable(AppCompatResources.getDrawable(requireContext(), R.drawable.ic_list_30))
             }
         }
         viewModel.setRouteIDQuery(lineID)
 
         val keySourcePositions = getString(R.string.pref_positions_source)
         useMQTTPositions = PreferenceManager.getDefaultSharedPreferences(requireContext())
             .getString(keySourcePositions, "mqtt").contentEquals("mqtt")
 
         viewModel.patternsWithStopsByRouteLiveData.observe(viewLifecycleOwner){
             patterns -> savePatternsToShow(patterns)
         }
         /*
             We have the pattern and the stops here, time to display them
          */
         viewModel.stopsForPatternLiveData.observe(viewLifecycleOwner) { stops ->
             if(map.visibility ==View.VISIBLE)
                 showPatternWithStopsOnMap(stops)
             else{
                 if(stopsRecyclerView.visibility==View.VISIBLE)
                     showStopsAsList(stops)
             }
         }
+        viewModel.gtfsRoute.observe(viewLifecycleOwner){route->
+             descripTextView.text = route.longName
+            descripTextView.visibility = View.VISIBLE
+        }
         if(pausedFragment && viewModel.selectedPatternLiveData.value!=null){
             val patt = viewModel.selectedPatternLiveData.value!!
             Log.d(DEBUG_TAG, "Recreating views on resume, setting pattern: ${patt.pattern.code}")
             showPattern(patt)
             pausedFragment = false
         }
 
         Log.d(DEBUG_TAG,"Data ${viewModel.stopsForPatternLiveData.value}")
 
         //listeners
         patternsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
             override fun onItemSelected(p0: AdapterView<*>?, p1: View?, position: Int, p3: Long) {
                 val patternWithStops = currentPatterns.get(position)
                 //viewModel.setPatternToDisplay(patternWithStops)
                 setPatternAndReqStops(patternWithStops)
 
                 Log.d(DEBUG_TAG, "item Selected, cleaning bus markers")
                 if(map?.visibility == View.VISIBLE) {
                     busPositionsOverlay.closeAllInfoWindows()
                     busPositionsOverlay.items.clear()
                     busPositionMarkersByTrip.clear()
 
                     stopAnimations()
                     tripMarkersAnimators.clear()
                     liveBusViewModel.retriggerPositionUpdate()
                 }
             }
 
             override fun onNothingSelected(p0: AdapterView<*>?) {
             }
         }
 
 
         //live bus positions
         liveBusViewModel.updatesWithTripAndPatterns.observe(viewLifecycleOwner){
             if(map.visibility == View.GONE || viewingPattern ==null){
                 //DO NOTHING
                 return@observe
             }
             val filtdLineID = GtfsUtils.stripGtfsPrefix(lineID)
             //filter buses with direction, show those only with the same direction
             val outmap = HashMap<String, Pair<LivePositionUpdate, TripAndPatternWithStops?>>()
             val currentPattern = viewingPattern!!.pattern
             val numUpds = it.entries.size
             Log.d(DEBUG_TAG, "Got $numUpds updates, current pattern is: ${currentPattern.name}, directionID: ${currentPattern.directionId}")
             val patternsDirections = HashMap<String,Int>()
             for((tripId, pair) in it.entries){
                 //remove trips with wrong line ideas
                 if(pair.first.routeID!=filtdLineID)
                     continue
 
                 if(pair.second!=null && pair.second?.pattern !=null){
                     val dir = pair.second?.pattern?.directionId
                     if(dir !=null && dir == currentPattern.directionId){
                         outmap[tripId] = pair
                     }
                     patternsDirections.set(tripId,if (dir!=null) dir else -10)
                 } else{
                     outmap[tripId] = pair
                     //Log.d(DEBUG_TAG, "No pattern for tripID: $tripId")
                     patternsDirections[tripId] = -10
                 }
             }
             Log.d(DEBUG_TAG, " Filtered updates are ${outmap.keys.size}") // Original updates directs: $patternsDirections\n
             updateBusPositionsInMap(outmap)
             //if not using MQTT positions
             if(!useMQTTPositions){
                 liveBusViewModel.requestDelayedGTFSUpdates(2000)
             }
         }
 
         //download missing tripIDs
         liveBusViewModel.tripsGtfsIDsToQuery.observe(viewLifecycleOwner){
             //gtfsPosViewModel.downloadTripsFromMato(dat);
             MatoTripsDownloadWorker.downloadTripsFromMato(
                 it, requireContext().applicationContext,
                 "BusTO-MatoTripDownload"
             )
         }
 
 
         return rootView
     }
 
     private fun initializeMap(rootView : View){
         val ctx = requireContext().applicationContext
         Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx))
 
         map = rootView.findViewById(R.id.lineMap)
         map.let {
             it.setTileSource(TileSourceFactory.MAPNIK)
 
             locationOverlay = LocationOverlay.createLocationOverlay(true, it, requireContext(), object : LocationOverlay.OverlayCallbacks{
                 override fun onDisableFollowMyLocation() {
                     Log.d(DEBUG_TAG, "Follow location disabled")
                 }
 
                 override fun onEnableFollowMyLocation() {
                     Log.d(DEBUG_TAG, "Follow location enabled")
                 }
 
             })
             locationOverlay.disableFollowLocation()
 
             stopsOverlay = FolderOverlay()
             busPositionsOverlay =  FolderOverlay()
 
 
             //map.setTilesScaledToDpi(true);
             //map.setTilesScaledToDpi(true);
             it.setFlingEnabled(true)
             it.setUseDataConnection(true)
 
             // add ability to zoom with 2 fingers
             it.setMultiTouchControls(true)
-            it.minZoomLevel = 11.0
+            it.minZoomLevel = 12.0
 
             //map controller setup
             val mapController = it.controller
             var zoom = 12.0
             var centerMap = GeoPoint(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON)
             if(mapViewModel.currentLat.value!=MapViewModel.INVALID) {
                 Log.d(DEBUG_TAG, "mapViewModel posi: ${mapViewModel.currentLat.value}, ${mapViewModel.currentLong.value}"+
                         " zoom ${mapViewModel.currentZoom.value}")
                 zoom = mapViewModel.currentZoom.value!!
                 centerMap = GeoPoint(mapViewModel.currentLat.value!!, mapViewModel.currentLong.value!!)
                 /*viewLifecycleOwner.lifecycleScope.launch {
                     delay(100)
                     Log.d(DEBUG_TAG, "zooming back to point")
                     controller.animateTo(GeoPoint(mapViewModel.currentLat.value!!, mapViewModel.currentLong.value!!),
                         mapViewModel.currentZoom.value!!,null,null)
                     //controller.setCenter(GeoPoint(mapViewModel.currentLat.value!!, mapViewModel.currentLong.value!!))
                     //controller.setZoom(mapViewModel.currentZoom.value!!)
 
                  */
 
                 }
             mapController.setZoom(zoom)
             mapController.setCenter(centerMap)
             Log.d(DEBUG_TAG, "Initializing map, first init $firstInit")
             //map.invalidate()
 
             it.overlayManager.add(stopsOverlay)
             it.overlayManager.add(locationOverlay)
             it.overlayManager.add(busPositionsOverlay)
 
             zoomToCurrentPattern()
             firstInit = false
 
         }
 
 
     }
 
     override fun onAttach(context: Context) {
         super.onAttach(context)
         if(context is CommonFragmentListener){
             fragmentListener = context
         } else throw RuntimeException("$context must implement CommonFragmentListener")
 
     }
 
 
     private fun stopAnimations(){
         for(anim in tripMarkersAnimators.values){
             anim.cancel()
         }
     }
 
     private fun savePatternsToShow(patterns: List<MatoPatternWithStops>){
         currentPatterns = patterns.sortedBy { p-> p.pattern.code }
 
         patternsAdapter?.let {
             it.clear()
             it.addAll(currentPatterns.map { p->"${p.pattern.directionId} - ${p.pattern.headsign}" })
             it.notifyDataSetChanged()
         }
         viewingPattern?.let {
             showPattern(it)
         }
 
     }
 
     /**
      * Called when the position of the spinner is updated
      */
     private fun setPatternAndReqStops(patternWithStops: MatoPatternWithStops){
         Log.d(DEBUG_TAG, "Requesting stops for pattern ${patternWithStops.pattern.code}")
         viewModel.selectedPatternLiveData.value = patternWithStops
         viewModel.currentPatternStops.value =  patternWithStops.stopsIndices.sortedBy { i-> i.order }
         viewingPattern = patternWithStops
 
         viewModel.requestStopsForPatternWithStops(patternWithStops)
     }
     private fun showPattern(patternWs: MatoPatternWithStops){
         Log.d(DEBUG_TAG, "Finding pattern to show: ${patternWs.pattern.code}")
         var pos = -2
         val code = patternWs.pattern.code.trim()
         for(k in currentPatterns.indices){
             if(currentPatterns[k].pattern.code.trim() == code){
                 pos = k
                 break
             }
         }
         Log.d(DEBUG_TAG, "Found pattern $code in position: $pos")
         if(pos>=0)
             patternsSpinner.setSelection(pos)
         //set pattern
         setPatternAndReqStops(patternWs)
     }
 
     private fun zoomToCurrentPattern(){
         var pointsList: List<GeoPoint>
         if(viewingPattern==null) {
             Log.e(DEBUG_TAG, "asked to zoom to pattern but current viewing pattern is null")
             if(polyline!=null)
             pointsList = polyline!!.actualPoints
             else {
                 Log.d(DEBUG_TAG, "The polyline is null")
                 return
             }
         }else{
             val pattern = viewingPattern!!.pattern
 
             pointsList = PolylineParser.decodePolyline(pattern.patternGeometryPoly, pattern.patternGeometryLength)
         }
 
         var maxLat = -4000.0
         var minLat = -4000.0
         var minLong = -4000.0
         var maxLong = -4000.0
         for (p in pointsList){
             // get max latitude
             if(maxLat == -4000.0)
                 maxLat = p.latitude
             else if (maxLat < p.latitude) maxLat = p.latitude
             // find min latitude
             if (minLat == -4000.0)
                 minLat = p.latitude
             else if (minLat > p.latitude) minLat = p.latitude
             if(maxLong == -4000.0 || maxLong < p.longitude )
                 maxLong = p.longitude
             if (minLong == -4000.0 || minLong > p.longitude)
                 minLong = p.longitude
         }
 
         val del = 0.008
         //map.controller.c
         Log.d(DEBUG_TAG, "Setting limits of bounding box of line: $minLat -> $maxLat, $minLong -> $maxLong")
         map.zoomToBoundingBox(BoundingBox(maxLat+del, maxLong+del, minLat-del, minLong-del), false)
     }
 
     private fun showPatternWithStopsOnMap(stops: List<Stop>){
         Log.d(DEBUG_TAG, "Got the stops: ${stops.map { s->s.gtfsID }}}")
         if(viewingPattern==null || map == null) return
 
         val pattern = viewingPattern!!.pattern
 
         val pointsList = PolylineParser.decodePolyline(pattern.patternGeometryPoly, pattern.patternGeometryLength)
 
         var maxLat = -4000.0
         var minLat = -4000.0
         var minLong = -4000.0
         var maxLong = -4000.0
         for (p in pointsList){
             // get max latitude
             if(maxLat == -4000.0)
                 maxLat = p.latitude
             else if (maxLat < p.latitude) maxLat = p.latitude
             // find min latitude
             if (minLat == -4000.0)
                 minLat = p.latitude
             else if (minLat > p.latitude) minLat = p.latitude
             if(maxLong == -4000.0 || maxLong < p.longitude )
                 maxLong = p.longitude
             if (minLong == -4000.0 || minLong > p.longitude)
                 minLong = p.longitude
         }
         //val polyLine=Polyline(map)
         //polyLine.setPoints(pointsList)
         //save points
         if(map.overlayManager.contains(polyline)){
             map.overlayManager.remove(polyline)
         }
         polyline = Polyline(map, false)
         polyline!!.setPoints(pointsList)
         //polyline.color = ContextCompat.getColor(context!!,R.color.brown_vd)
         polyline!!.infoWindow = null
         val paint = Paint()
         paint.color = ContextCompat.getColor(requireContext(),R.color.line_drawn_poly)
         paint.isAntiAlias = true
         paint.strokeWidth = 16f
         paint.style = Paint.Style.FILL_AND_STROKE
         paint.strokeJoin = Paint.Join.ROUND
         paint.strokeCap = Paint.Cap.ROUND
         polyline!!.outlinePaintLists.add(MonochromaticPaintList(paint))
 
         map.overlayManager.add(0,polyline!!)
 
         stopsOverlay.closeAllInfoWindows()
         stopsOverlay.items.clear()
         val stopIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ball)
 
         for(s in stops){
             val gp = if (showOnTopOfLine)
                 findOptimalPosition(s,pointsList)
             else GeoPoint(s.latitude!!,s.longitude!!)
 
             val marker = MarkerUtils.makeMarker(
                 gp, s.ID, s.stopDefaultName,
                 s.routesThatStopHereToString(),
                 map,stopTouchResponder, stopIcon,
                 R.layout.linedetail_stop_infowindow,
                 R.color.line_drawn_poly
             )
             marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
             stopsOverlay.add(marker)
         }
         //POINTS LIST IS NOT IN ORDER ANY MORE
         //if(!map.overlayManager.contains(stopsOverlay)){
         //    map.overlayManager.add(stopsOverlay)
         //}
         polyline!!.setOnClickListener(Polyline.OnClickListener { polyline, mapView, eventPos ->
             Log.d(DEBUG_TAG, "clicked")
             true
         })
 
         //map.controller.zoomToB//#animateTo(pointsList[0])
         val del = 0.008
         map.zoomToBoundingBox(BoundingBox(maxLat+del, maxLong+del, minLat-del, minLong-del), true)
         //map.invalidate()
     }
 
     private fun initializeRecyclerView(){
         val llManager = LinearLayoutManager(context)
         llManager.orientation = LinearLayoutManager.VERTICAL
 
         stopsRecyclerView.layoutManager = llManager
     }
     private fun showStopsAsList(stops: List<Stop>){
 
         Log.d(DEBUG_TAG, "Setting stops from: "+viewModel.currentPatternStops.value)
         val orderBy = viewModel.currentPatternStops.value!!.withIndex().associate{it.value.stopGtfsId to it.index}
         val stopsSorted = stops.sortedBy { s -> orderBy[s.gtfsID] }
         val numStops = stopsSorted.size
         Log.d(DEBUG_TAG, "RecyclerView adapter is: ${stopsRecyclerView.adapter}")
 
         val setNewAdapter = true
         if(setNewAdapter){
             stopsRecyclerView.adapter = StopRecyclerAdapter(
                 stopsSorted, stopAdapterListener, StopRecyclerAdapter.Use.LINES,
                 NameCapitalize.FIRST
             )
 
         }
 
 
 
     }
 
 
     /**
      * Remove bus marker from overlay associated with tripID
      */
     private fun removeBusMarker(tripID: String){
         if(!busPositionMarkersByTrip.containsKey(tripID)){
             Log.e(DEBUG_TAG, "Asked to remove veh with tripID $tripID but it's supposedly not shown")
             return
         }
         val marker = busPositionMarkersByTrip[tripID]
         busPositionsOverlay.remove(marker)
         busPositionMarkersByTrip.remove(tripID)
 
         val animator = tripMarkersAnimators[tripID]
         animator?.let{
             it.cancel()
             tripMarkersAnimators.remove(tripID)
         }
 
     }
 
     private fun showPatternWithStop(patternId: String){
         //var index = 0
         Log.d(DEBUG_TAG, "Showing pattern with code $patternId ")
         for (i in currentPatterns.indices){
             val pattStop = currentPatterns[i]
             if(pattStop.pattern.code == patternId){
                 Log.d(DEBUG_TAG, "Pattern found in position $i")
                 //setPatternAndReqStops(pattStop)
                 patternsSpinner.setSelection(i)
                 break
             }
         }
     }
 
     /**
      * draw the position of the buses in the map. Copied from MapFragment
      */
     private fun updateBusPositionsInMap(tripsPatterns: java.util.HashMap<String, Pair<LivePositionUpdate, TripAndPatternWithStops?>>
                                         ) {
         //Log.d(MapFragment.DEBUG_TAG, "Updating positions of the buses")
         //if(busPositionsOverlay == null) busPositionsOverlay = new FolderOverlay();
         val noPatternsTrips = ArrayList<String>()
         for (tripID in tripsPatterns.keys) {
             val (update, tripWithPatternStops) = tripsPatterns[tripID] ?: continue
 
             var marker: Marker? = null
             //check if Marker is already created
             if (busPositionMarkersByTrip.containsKey(tripID)) {
 
                 //check if the trip direction ID is the same, if not remove
                 if(tripWithPatternStops?.pattern != null &&
                     tripWithPatternStops.pattern.directionId != viewingPattern?.pattern?.directionId){
                             removeBusMarker(tripID)
 
                 } else {
                     //need to change the position of the marker
                     marker = busPositionMarkersByTrip.get(tripID)!!
                     BusPositionUtils.updateBusPositionMarker(map, marker, update, tripMarkersAnimators, false)
                     // Set the pattern to add the info
                     if (marker.infoWindow != null && marker.infoWindow is BusInfoWindow) {
                         val window = marker.infoWindow as BusInfoWindow
                         if (window.pattern == null && tripWithPatternStops != null) {
                             //Log.d(DEBUG_TAG, "Update pattern for trip: "+tripID);
                             window.setPatternAndDraw(tripWithPatternStops.pattern)
                         }
                     }
                 }
             } else {
                 //marker is not there, need to make it
                 //if (mapView == null) Log.e(MapFragment.DEBUG_TAG, "Creating marker with null map, things will explode")
                 marker = Marker(map)
 
                 //String route = GtfsUtils.getLineNameFromGtfsID(update.getRouteID());
                 val mdraw = ResourcesCompat.getDrawable(getResources(), R.drawable.map_bus_position_icon, null)!!
                 //mdraw.setBounds(0,0,28,28);
 
                 marker.icon = mdraw
                 var markerPattern: MatoPattern? = null
                 if (tripWithPatternStops != null) {
                     if (tripWithPatternStops.pattern != null)
                         markerPattern = tripWithPatternStops.pattern
                 }
                 marker.infoWindow = BusInfoWindow(map, update, markerPattern, true) {
                     // set pattern to show
                     if(it!=null)
                         showPatternWithStop(it.code)
                 }
                 //marker.infoWindow as BusInfoWindow
                 marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
                 BusPositionUtils.updateBusPositionMarker(map,marker, update, tripMarkersAnimators,true)
                 // the overlay is null when it's not attached yet?
                 // cannot recreate it because it becomes null very soon
                 // if(busPositionsOverlay == null) busPositionsOverlay = new FolderOverlay();
                 //save the marker
                 if (busPositionsOverlay != null) {
                     busPositionsOverlay.add(marker)
                     busPositionMarkersByTrip.put(tripID, marker)
                 }
             }
         }
         if (noPatternsTrips.size > 0) {
             Log.i(DEBUG_TAG, "These trips have no matching pattern: $noPatternsTrips")
         }
     }
 
     override fun onResume() {
         super.onResume()
         Log.d(DEBUG_TAG, "Resetting paused from onResume")
         pausedFragment = false
 
         val keySourcePositions = getString(R.string.pref_positions_source)
         useMQTTPositions = PreferenceManager.getDefaultSharedPreferences(requireContext())
             .getString(keySourcePositions, "mqtt").contentEquals("mqtt")
 
         //separate paths
         if(useMQTTPositions)
             liveBusViewModel.requestMatoPosUpdates(GtfsUtils.getLineNameFromGtfsID(lineID))
         else
             liveBusViewModel.requestGTFSUpdates()
 
 
         if(mapViewModel.currentLat.value!=MapViewModel.INVALID) {
             Log.d(DEBUG_TAG, "mapViewModel posi: ${mapViewModel.currentLat.value}, ${mapViewModel.currentLong.value}"+
                     " zoom ${mapViewModel.currentZoom.value}")
             val controller = map.controller
             viewLifecycleOwner.lifecycleScope.launch {
                 delay(100)
                 Log.d(DEBUG_TAG, "zooming back to point")
                 controller.animateTo(GeoPoint(mapViewModel.currentLat.value!!, mapViewModel.currentLong.value!!),
                     mapViewModel.currentZoom.value!!,null,null)
                 //controller.setCenter(GeoPoint(mapViewModel.currentLat.value!!, mapViewModel.currentLong.value!!))
                 //controller.setZoom(mapViewModel.currentZoom.value!!)
             }
 
             //controller.setZoom()
         }
         //initialize GUI here
         fragmentListener.readyGUIfor(FragmentKind.LINES)
 
     }
 
     override fun onPause() {
         super.onPause()
         liveBusViewModel.stopMatoUpdates()
         pausedFragment = true
         //save map
         val center = map.mapCenter
         mapViewModel.currentLat.value = center.latitude
         mapViewModel.currentLong.value = center.longitude
         mapViewModel.currentZoom.value = map.zoomLevel.toDouble()
     }
 
     override fun getBaseViewForSnackBar(): View? {
         return null
     }
 
     companion object {
         private const val LINEID_KEY="lineID"
         fun newInstance() = LinesDetailFragment()
         const val DEBUG_TAG="LinesDetailFragment"
 
         fun makeArgs(lineID: String): Bundle{
             val b = Bundle()
             b.putString(LINEID_KEY, lineID)
             return b
         }
         @JvmStatic
         private fun findOptimalPosition(stop: Stop, pointsList: MutableList<GeoPoint>): GeoPoint{
             if(stop.latitude==null || stop.longitude ==null|| pointsList.isEmpty())
                 throw IllegalArgumentException()
             val sLat = stop.latitude!!
             val sLong = stop.longitude!!
             if(pointsList.size < 2)
                 return  pointsList[0]
             pointsList.sortBy { utils.measuredistanceBetween(sLat, sLong, it.latitude, it.longitude) }
 
             val p1 = pointsList[0]
             val p2 = pointsList[1]
             if (p1.longitude == p2.longitude){
                 //Log.e(DEBUG_TAG, "Same longitude")
                 return GeoPoint(sLat, p1.longitude)
             } else if (p1.latitude == p2.latitude){
                 //Log.d(DEBUG_TAG, "Same latitude")
                 return GeoPoint(p2.latitude,sLong)
             }
 
             val m = (p1.latitude - p2.latitude) / (p1.longitude - p2.longitude)
             val minv = (p1.longitude-p2.longitude)/(p1.latitude - p2.latitude)
             val cR = p1.latitude - p1.longitude * m
 
             val longNew = (minv * sLong + sLat -cR ) / (m+minv)
             val latNew = (m*longNew + cR)
             //Log.d(DEBUG_TAG,"Stop ${stop.ID} old pos: ($sLat, $sLong), new pos ($latNew,$longNew)")
             return GeoPoint(latNew,longNew)
         }
 
         private const val DEFAULT_CENTER_LAT = 45.12
         private const val DEFAULT_CENTER_LON = 7.6858
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt
index 609b22b..7ab74d9 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt
@@ -1,281 +1,337 @@
 package it.reyboz.bustorino.fragments
 
 import android.content.Context
 import android.os.Bundle
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.view.animation.Animation
 import android.view.animation.LinearInterpolator
 import android.view.animation.RotateAnimation
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.fragment.app.viewModels
 import androidx.recyclerview.widget.RecyclerView
 import it.reyboz.bustorino.R
 import it.reyboz.bustorino.adapters.RouteAdapter
+import it.reyboz.bustorino.adapters.RouteOnlyLineAdapter
 import it.reyboz.bustorino.backend.utils
+import it.reyboz.bustorino.data.PreferencesHolder
 import it.reyboz.bustorino.data.gtfs.GtfsRoute
 import it.reyboz.bustorino.middleware.AutoFitGridLayoutManager
 import it.reyboz.bustorino.util.LinesNameSorter
 import it.reyboz.bustorino.util.ViewUtils
 import it.reyboz.bustorino.viewmodels.LinesGridShowingViewModel
 
 
 class LinesGridShowingFragment : ScreenBaseFragment() {
 
 
 
     private val viewModel: LinesGridShowingViewModel by viewModels()
     //private lateinit var gridLayoutManager: AutoFitGridLayoutManager
-
+    private lateinit var favoritesRecyclerView: RecyclerView
     private lateinit var urbanRecyclerView: RecyclerView
     private lateinit var extraurbanRecyclerView: RecyclerView
     private lateinit var touristRecyclerView: RecyclerView
 
+    private lateinit var favoritesTitle: TextView
     private lateinit var urbanLinesTitle: TextView
     private lateinit var extrurbanLinesTitle: TextView
     private lateinit var touristLinesTitle: TextView
 
 
     private var routesByAgency = HashMap<String, ArrayList<GtfsRoute>>()
         /*hashMapOf(
         AG_URBAN to ArrayList<GtfsRoute>(),
         AG_EXTRAURB to ArrayList(),
         AG_TOUR to ArrayList()
     )*/
 
     private lateinit var fragmentListener: CommonFragmentListener
 
     private val linesNameSorter = LinesNameSorter()
     private val linesComparator = Comparator<GtfsRoute> { a,b ->
         return@Comparator linesNameSorter.compare(a.shortName, b.shortName)
     }
 
-    private val routeClickListener = RouteAdapter.onItemClick {
+    private val routeClickListener = RouteAdapter.ItemClicker {
         fragmentListener.showLineOnMap(it.gtfsId)
     }
     private val arrows = HashMap<String, ImageView>()
     private val durations = HashMap<String, Long>()
     private var openRecyclerView = "AG_URBAN"
 
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View? {
         val rootView =  inflater.inflate(R.layout.fragment_lines_grid, container, false)
 
+        favoritesRecyclerView = rootView.findViewById(R.id.favoritesRecyclerView)
         urbanRecyclerView = rootView.findViewById(R.id.urbanLinesRecyclerView)
         extraurbanRecyclerView = rootView.findViewById(R.id.extraurbanLinesRecyclerView)
         touristRecyclerView = rootView.findViewById(R.id.touristLinesRecyclerView)
 
+        favoritesTitle = rootView.findViewById(R.id.favoritesTitleView)
         urbanLinesTitle = rootView.findViewById(R.id.urbanLinesTitleView)
         extrurbanLinesTitle = rootView.findViewById(R.id.extraurbanLinesTitleView)
         touristLinesTitle = rootView.findViewById(R.id.touristLinesTitleView)
 
         arrows[AG_URBAN] = rootView.findViewById(R.id.arrowUrb)
         arrows[AG_TOUR] = rootView.findViewById(R.id.arrowTourist)
         arrows[AG_EXTRAURB] = rootView.findViewById(R.id.arrowExtraurban)
+        arrows[AG_FAV] = rootView.findViewById(R.id.arrowFavorites)
         //show urban expanded by default
 
         val recViews = listOf(urbanRecyclerView, extraurbanRecyclerView,  touristRecyclerView)
         for (recyView in recViews) {
             val gridLayoutManager = AutoFitGridLayoutManager(
                 requireContext().applicationContext,
                 (utils.convertDipToPixels(context, COLUMN_WIDTH_DP.toFloat())).toInt()
             )
             recyView.layoutManager = gridLayoutManager
         }
+        //init favorites recyclerview
+        val gridLayoutManager = AutoFitGridLayoutManager(
+            requireContext().applicationContext,
+            (utils.convertDipToPixels(context, 70f)).toInt()
+        )
+        favoritesRecyclerView.layoutManager = gridLayoutManager
+
 
         viewModel.routesLiveData.observe(viewLifecycleOwner){
             //routesList = ArrayList(it)
             //routesList.sortWith(linesComparator)
             routesByAgency.clear()
 
             for(route in it){
                 val agency = route.agencyID
                 if(!routesByAgency.containsKey(agency)){
                     routesByAgency[agency] = ArrayList()
                 }
                 routesByAgency[agency]?.add(route)
 
             }
 
 
             //val adapter = RouteOnlyLineAdapter(routesByAgency.map { route-> route.shortName })
             //zip agencies and recyclerviews
             Companion.AGENCIES.zip(recViews) { ag, recView  ->
                 routesByAgency[ag]?.let { routeList ->
                     routeList.sortWith(linesComparator)
                     //val adapter = RouteOnlyLineAdapter(it.map { rt -> rt.shortName })
                     val adapter = RouteAdapter(routeList,routeClickListener)
                     recView.adapter = adapter
                     durations[ag] = if(routeList.size < 20) ViewUtils.DEF_DURATION else 1000
                 }
             }
 
         }
+        viewModel.favoritesLines.observe(viewLifecycleOwner){ routes->
+            val routesNames = routes.map { it.shortName }
+            //create new item click listener every time
+            val adapter = RouteOnlyLineAdapter(routesNames){  pos, _ ->
+                val r = routes[pos]
+                fragmentListener.showLineOnMap(r.gtfsId)
+            }
+            favoritesRecyclerView.adapter = adapter
+        }
 
         //onClicks
         urbanLinesTitle.setOnClickListener {
             openLinesAndCloseOthersIfNeeded(AG_URBAN)
         }
         extrurbanLinesTitle.setOnClickListener {
             openLinesAndCloseOthersIfNeeded(AG_EXTRAURB)
         }
         touristLinesTitle.setOnClickListener {
             openLinesAndCloseOthersIfNeeded(AG_TOUR)
         }
+        favoritesTitle.setOnClickListener {
+            closeOpenFavorites()
+        }
+        arrows[AG_FAV]?.setOnClickListener {
+            closeOpenFavorites()
+        }
         //arrows onClicks
-        for(k in arrows.keys){
+        for(k in Companion.AGENCIES){
             //k is either AG_TOUR, AG_EXTRAURBAN, AG_URBAN
             arrows[k]?.setOnClickListener { openLinesAndCloseOthersIfNeeded(k) }
         }
 
+
         return rootView
     }
+    private fun closeOpenFavorites(){
+        if(favoritesRecyclerView.visibility == View.VISIBLE){
+            //close it
+            favoritesRecyclerView.visibility = View.GONE
+            setOpen(arrows[AG_FAV]!!, false)
+            viewModel.favoritesExpanded.value = false
+        } else{
+            favoritesRecyclerView.visibility = View.VISIBLE
+            setOpen(arrows[AG_FAV]!!, true)
+            viewModel.favoritesExpanded.value = true
+        }
+    }
 
     private fun openLinesAndCloseOthersIfNeeded(agency: String){
         if(openRecyclerView!="" && openRecyclerView!= agency) {
             switchRecyclerViewStatus(openRecyclerView)
 
         }
         switchRecyclerViewStatus(agency)
     }
 
     private fun switchRecyclerViewStatus(agency: String){
         val recyclerView = when(agency){
             AG_TOUR -> touristRecyclerView
             AG_EXTRAURB -> extraurbanRecyclerView
             AG_URBAN -> urbanRecyclerView
             else -> throw IllegalArgumentException("$DEBUG_TAG: Agency Invalid")
         }
         val expandedLiveData = when(agency){
             AG_TOUR -> viewModel.isTouristExpanded
             AG_URBAN -> viewModel.isUrbanExpanded
             AG_EXTRAURB -> viewModel.isExtraUrbanExpanded
             else -> throw IllegalArgumentException("$DEBUG_TAG: Agency Invalid")
         }
         val duration = durations[agency]
         val arrow = arrows[agency]
         val durArrow = if(duration == null || duration==ViewUtils.DEF_DURATION) 500 else duration
         if(duration!=null&&arrow!=null)
             when (recyclerView.visibility){
                 View.GONE -> {
                     Log.d(DEBUG_TAG, "Open recyclerview $agency")
                     //val a =ViewUtils.expand(recyclerView, duration, 0)
                     recyclerView.visibility = View.VISIBLE
                     expandedLiveData.value = true
                     Log.d(DEBUG_TAG, "Arrow for $agency has rotation: ${arrow.rotation}")
 
                     setOpen(arrow, true)
                     //arrow.startAnimation(rotateArrow(true,durArrow))
                     openRecyclerView = agency
 
                 }
                 View.VISIBLE -> {
                     Log.d(DEBUG_TAG, "Close recyclerview $agency")
                     //ViewUtils.collapse(recyclerView, duration)
                     recyclerView.visibility = View.GONE
                     expandedLiveData.value = false
                     //arrow.rotation = 90f
                     Log.d(DEBUG_TAG, "Arrow for $agency has rotation ${arrow.rotation} pre-rotate")
                     setOpen(arrow, false)
                     //arrow.startAnimation(rotateArrow(false,durArrow))
                     openRecyclerView = ""
                 }
                 View.INVISIBLE -> {
                     TODO()
                 }
             }
     }
 
     override fun onAttach(context: Context) {
         super.onAttach(context)
         if(context is CommonFragmentListener){
             fragmentListener = context
         } else throw RuntimeException("$context must implement CommonFragmentListener")
 
     }
 
     override fun getBaseViewForSnackBar(): View? {
         return null
     }
 
     override fun onResume() {
         super.onResume()
+        val pref = PreferencesHolder.getMainSharedPreferences(requireContext())
+        val res = pref.getStringSet(PreferencesHolder.PREF_FAVORITE_LINES, HashSet())
+        res?.let { viewModel.setFavoritesLinesIDs(HashSet(it))}
+        //restore state
+        viewModel.favoritesExpanded.value?.let {
+            if(!it){
+                //close it
+                favoritesRecyclerView.visibility = View.GONE
+                setOpen(arrows[AG_FAV]!!, false)
+            } else{
+                favoritesRecyclerView.visibility = View.VISIBLE
+                setOpen(arrows[AG_FAV]!!, true)
+            }
+        }
         viewModel.isUrbanExpanded.value?.let {
             if(it) {
                 urbanRecyclerView.visibility = View.VISIBLE
                 arrows[AG_URBAN]?.rotation= 90f
                 openRecyclerView = AG_URBAN
                 Log.d(DEBUG_TAG, "RecyclerView gtt:U is expanded")
             }
             else {
                 urbanRecyclerView.visibility = View.GONE
                 arrows[AG_URBAN]?.rotation= 0f
             }
         }
         viewModel.isTouristExpanded.value?.let {
             val recview = touristRecyclerView
             if(it) {
                 recview.visibility = View.VISIBLE
                 arrows[AG_TOUR]?.rotation=90f
                 openRecyclerView = AG_TOUR
             } else {
                 recview.visibility = View.GONE
                 arrows[AG_TOUR]?.rotation= 0f
             }
         }
         viewModel.isExtraUrbanExpanded.value?.let {
             val recview = extraurbanRecyclerView
             if(it) {
                 openRecyclerView = AG_EXTRAURB
                 recview.visibility = View.VISIBLE
                 arrows[AG_EXTRAURB]?.rotation=90f
             } else {
                 recview.visibility = View.GONE
                 arrows[AG_EXTRAURB]?.rotation=0f
             }
         }
         fragmentListener.readyGUIfor(FragmentKind.LINES)
 
     }
 
 
     companion object {
         private const val COLUMN_WIDTH_DP=200
+        private const val AG_FAV = "fav"
         private const val AG_URBAN = "gtt:U"
         private const val AG_EXTRAURB ="gtt:E"
         private const val AG_TOUR ="gtt:T"
         private const val DEBUG_TAG ="BusTO-LinesGridFragment"
 
         const val FRAGMENT_TAG = "LinesGridShowingFragment"
 
         private val AGENCIES = listOf(AG_URBAN, AG_EXTRAURB, AG_TOUR)
         fun newInstance() = LinesGridShowingFragment()
 
         @JvmStatic
         fun setOpen(imageView: ImageView, value: Boolean){
             if(value)
                 imageView.rotation = 90f
             else
                 imageView.rotation  = 0f
         }
         @JvmStatic
         fun rotateArrow(toOpen: Boolean, duration: Long):  RotateAnimation{
             val start = if (toOpen) 0f else 90f
             val stop = if(toOpen) 90f else 0f
             Log.d(DEBUG_TAG, "Rotate arrow from $start to $stop")
             val rotate = RotateAnimation(start, stop, Animation.RELATIVE_TO_SELF,
                 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
             rotate.duration = duration
             rotate.interpolator = LinearInterpolator()
             //rotate.fillAfter = true
             rotate.fillBefore = false
             return rotate
         }
     }
 
 }
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt
index b5b5bbd..23a7d24 100644
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt
@@ -1,27 +1,51 @@
 package it.reyboz.bustorino.viewmodels
 
 import android.app.Application
 import androidx.lifecycle.AndroidViewModel
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
+import androidx.lifecycle.map
 import it.reyboz.bustorino.data.GtfsRepository
-import it.reyboz.bustorino.data.NextGenDB
-import it.reyboz.bustorino.data.OldDataRepository
 import it.reyboz.bustorino.data.gtfs.GtfsDatabase
+import it.reyboz.bustorino.data.gtfs.GtfsRoute
+import it.reyboz.bustorino.util.LinesNameSorter
 
 class LinesGridShowingViewModel(application: Application) : AndroidViewModel(application) {
 
     private val gtfsRepo: GtfsRepository
 
     init {
         val gtfsDao = GtfsDatabase.getGtfsDatabase(application).gtfsDao()
         gtfsRepo = GtfsRepository(gtfsDao)
 
     }
 
     val routesLiveData = gtfsRepo.getAllRoutes()
 
     val isUrbanExpanded = MutableLiveData(true)
     val isExtraUrbanExpanded = MutableLiveData(false)
     val isTouristExpanded = MutableLiveData(false)
+    val favoritesExpanded = MutableLiveData(true)
+
+    val favoritesLinesIDs = MutableLiveData<HashSet<String>>()
+
+    private val linesNameSorter = LinesNameSorter()
+    private val linesComparator = Comparator<GtfsRoute> { a,b ->
+        return@Comparator linesNameSorter.compare(a.shortName, b.shortName)
+    }
+
+    fun setFavoritesLinesIDs(linesIds: HashSet<String>){
+        favoritesLinesIDs.value = linesIds
+    }
+
+    val favoritesLines = favoritesLinesIDs.map {lineIds ->
+        val linesList = ArrayList<GtfsRoute>()
+        if (lineIds.size == 0 || routesLiveData.value==null) return@map linesList
+        for(line in routesLiveData.value!!){
+            if(lineIds.contains(line.gtfsId))
+                linesList.add(line)
+        }
+        linesList.sortWith(linesComparator)
+        return@map linesList
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesViewModel.kt
index 38949b1..96d16d9 100644
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesViewModel.kt
@@ -1,108 +1,111 @@
 package it.reyboz.bustorino.viewmodels
 
 import android.app.Application
 import android.util.Log
 import androidx.lifecycle.*
 import it.reyboz.bustorino.backend.Result
 import it.reyboz.bustorino.backend.Stop
 import it.reyboz.bustorino.data.GtfsRepository
 import it.reyboz.bustorino.data.NextGenDB
 import it.reyboz.bustorino.data.OldDataRepository
 import it.reyboz.bustorino.data.gtfs.GtfsDatabase
 import it.reyboz.bustorino.data.gtfs.GtfsRoute
 import it.reyboz.bustorino.data.gtfs.MatoPatternWithStops
 import it.reyboz.bustorino.data.gtfs.PatternStop
 import java.util.concurrent.Executors
 
 class LinesViewModel(application: Application) : AndroidViewModel(application) {
 
     private val gtfsRepo: GtfsRepository
     private val oldRepo: OldDataRepository
     //val patternsByRouteLiveData: LiveData<List<MatoPattern>>
 
     private val routeIDToSearch = MutableLiveData<String>()
     private var lastShownPatternStops = ArrayList<String>()
 
     val currentPatternStops = MutableLiveData<List<PatternStop>>()
     val selectedPatternLiveData = MutableLiveData<MatoPatternWithStops>()
 
 
     val stopsForPatternLiveData = MutableLiveData<List<Stop>>()
     private val executor = Executors.newFixedThreadPool(2)
 
     val mapShowing = MutableLiveData(true)
     fun setMapShowing(yes: Boolean){
         mapShowing.value = yes
         //retrigger redraw
         stopsForPatternLiveData.postValue(stopsForPatternLiveData.value)
     }
     init {
         val gtfsDao = GtfsDatabase.getGtfsDatabase(application).gtfsDao()
         gtfsRepo = GtfsRepository(gtfsDao)
 
         oldRepo = OldDataRepository(executor, NextGenDB.getInstance(application))
 
     }
 
 
     val routesGTTLiveData: LiveData<List<GtfsRoute>> by lazy{
         gtfsRepo.getLinesLiveDataForFeed("gtt")
     }
     val patternsWithStopsByRouteLiveData = routeIDToSearch.switchMap {
         gtfsRepo.getPatternsWithStopsForRouteID(it)
 
     }
+    val gtfsRoute = routeIDToSearch.switchMap {
+        gtfsRepo.getRouteFromGtfsId(it)
+    }
 
 
     fun setRouteIDQuery(routeID: String){
         routeIDToSearch.value = routeID
     }
 
     fun getRouteIDQueried(): String?{
         return routeIDToSearch.value
     }
     var shouldShowMessage = true
 
     fun setPatternToDisplay(patternStops: MatoPatternWithStops){
 
         selectedPatternLiveData.value = patternStops
     }
     /**
      * Find the
      */
     private fun requestStopsForGTFSIDs(gtfsIDs: List<String>){
         if (gtfsIDs.equals(lastShownPatternStops)){
             //nothing to do
             return
         }
         oldRepo.requestStopsWithGtfsIDs(gtfsIDs) {
             if (it.isSuccess) {
                 stopsForPatternLiveData.postValue(it.result)
             } else {
                 Log.e("BusTO-LinesVM", "Got error on callback with stops for gtfsID")
                 it.exception?.printStackTrace()
             }
         }
         lastShownPatternStops.clear()
         for(id in gtfsIDs)
             lastShownPatternStops.add(id)
     }
 
     fun requestStopsForPatternWithStops(patternStops: MatoPatternWithStops){
         val gtfsIDs = ArrayList<String>()
         for(pat in patternStops.stopsIndices){
             gtfsIDs.add(pat.stopGtfsId)
         }
         requestStopsForGTFSIDs(gtfsIDs)
     }
 
 
     /*fun getLinesGTT(): MutableLiveData<List<GtfsRoute>> {
         val routesData = MutableLiveData<List<GtfsRoute>>()
             viewModelScope.launch {
                  val routes=gtfsRepo.getLinesForFeed("gtt")
                 routesData.postValue(routes)
             }
         return routesData
     }*/
 }
\ No newline at end of file
diff --git a/app/src/main/res/drawable-it-xhdpi/tuto_menu.webp b/app/src/main/res/drawable-it-xhdpi/tuto_menu.webp
index b59f82a..ed396e3 100644
Binary files a/app/src/main/res/drawable-it-xhdpi/tuto_menu.webp and b/app/src/main/res/drawable-it-xhdpi/tuto_menu.webp differ
diff --git a/app/src/main/res/drawable-xhdpi/tuto_menu.webp b/app/src/main/res/drawable-xhdpi/tuto_menu.webp
index 2354e37..8d0dc2f 100644
Binary files a/app/src/main/res/drawable-xhdpi/tuto_menu.webp and b/app/src/main/res/drawable-xhdpi/tuto_menu.webp differ
diff --git a/app/src/main/res/drawable/close_white_large.xml b/app/src/main/res/drawable/close_white_large.xml
new file mode 100644
index 0000000..f51cf41
--- /dev/null
+++ b/app/src/main/res/drawable/close_white_large.xml
@@ -0,0 +1,5 @@
+<vector android:height="32dp"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_star_filled.xml b/app/src/main/res/drawable/ic_star_filled.xml
index c41f592..2a9554e 100644
--- a/app/src/main/res/drawable/ic_star_filled.xml
+++ b/app/src/main/res/drawable/ic_star_filled.xml
@@ -1,9 +1,9 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
+    android:width="28dp"
+    android:height="28dp"
     android:viewportWidth="24"
     android:viewportHeight="24">
   <path
       android:fillColor="@color/orange_500"
       android:pathData="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"/>
 </vector>
diff --git a/app/src/main/res/drawable/ic_star_outline.xml b/app/src/main/res/drawable/ic_star_outline.xml
index bdb5efe..dc062f0 100644
--- a/app/src/main/res/drawable/ic_star_outline.xml
+++ b/app/src/main/res/drawable/ic_star_outline.xml
@@ -1,9 +1,9 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
+    android:width="28dp"
+    android:height="28dp"
     android:viewportWidth="24"
     android:viewportHeight="24">
   <path
       android:fillColor="@color/orange_500"
       android:pathData="M12,15.39L8.24,17.66L9.23,13.38L5.91,10.5L10.29,10.13L12,6.09L13.71,10.13L18.09,10.5L14.77,13.38L15.76,17.66M22,9.24L14.81,8.63L12,2L9.19,8.63L2,9.24L7.45,13.97L5.82,21L12,17.27L18.18,21L16.54,13.97L22,9.24Z"/>
 </vector>
diff --git a/app/src/main/res/layout/activity_intro.xml b/app/src/main/res/layout/activity_intro.xml
index 586f221..bf02e69 100644
--- a/app/src/main/res/layout/activity_intro.xml
+++ b/app/src/main/res/layout/activity_intro.xml
@@ -1,47 +1,58 @@
 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:tools="http://schemas.android.com/tools"
         xmlns:app="http://schemas.android.com/apk/res-auto"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         tools:context=".ActivityIntro">
 
     <ImageButton
             android:src="@drawable/arrow_forward_white"
             android:rotation="180"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content" android:id="@+id/btnPrevious"
             android:backgroundTint="?colorAccent"
             android:textColor="@color/white"
             android:contentDescription="@string/previous"
             app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"/>
     <ImageButton
             android:src="@drawable/arrow_forward_white"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content" android:id="@+id/btnNext"
             android:backgroundTint="?colorAccent"
             android:textColor="@color/white"
             app:circularflow_radiusInDP="5dp"
             android:contentDescription="@string/next"
 
             app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent"/>
+
+    <ImageButton
+            android:src="@drawable/close_white_large"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:id="@+id/btnCompactClose"
+            android:backgroundTint="?colorAccent"
+            android:textColor="@color/white"
+            app:circularflow_radiusInDP="5dp"
+            android:contentDescription="@string/next"
+            android:visibility="gone"
+            app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent"/>
     <androidx.viewpager2.widget.ViewPager2
             android:layout_width="0dp"
             android:layout_height="0dp"
             android:id="@+id/viewPager"
             app:layout_constraintBottom_toTopOf="@+id/btnPrevious" android:layout_marginBottom="8dp"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/>
     <com.google.android.material.tabs.TabLayout
             android:id="@+id/tab_layout"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             app:tabBackground="@drawable/tab_selector"
             app:tabGravity="center"
             app:tabIndicatorHeight="0dp"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintLeft_toRightOf="@id/btnPrevious"
             app:layout_constraintRight_toLeftOf="@id/btnNext"
     />
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_intro.xml b/app/src/main/res/layout/fragment_intro.xml
index d0d4e8b..f82a1f2 100644
--- a/app/src/main/res/layout/fragment_intro.xml
+++ b/app/src/main/res/layout/fragment_intro.xml
@@ -1,43 +1,45 @@
 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              tools:context=".fragments.IntroFragment">
 
 
     <ImageView
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:id="@+id/image_tutorial"
             app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintBottom_toTopOf="@+id/tutorialTextView"
         app:layout_constraintVertical_chainStyle="packed"/>
     <TextView
             android:text="Blabla"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content" android:id="@+id/tutorialTextView"
             app:layout_constraintTop_toBottomOf="@+id/image_tutorial"
-            android:layout_marginTop="60dp"
+            android:layout_marginTop="30dp"
             android:maxWidth="280dp"
             android:textSize="18sp"
             android:textAlignment="center"
             android:textColor="@color/grey_900"
             android:fontFamily="@font/pitagon_medium"
             app:layout_constraintBottom_toTopOf="@id/closeAllButton" app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"/>
     <Button
             android:text="@string/close_tutorial"
             android:layout_width="wrap_content"
             android:visibility="gone"
             android:layout_height="wrap_content" android:id="@+id/closeAllButton"
             app:layout_constraintTop_toBottomOf="@id/tutorialTextView"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="@id/tutorialTextView"
             android:backgroundTint="?colorPrimaryDark"
+            android:fontFamily="@font/pitagon_semibold"
             android:textColor="@color/white"
-            app:layout_constraintEnd_toEndOf="@id/tutorialTextView" android:layout_marginTop="12dp"/>
+            app:layout_constraintEnd_toEndOf="@id/tutorialTextView"
+            android:layout_marginTop="18dp"/>
 
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_lines_detail.xml b/app/src/main/res/layout/fragment_lines_detail.xml
index 19894a4..a1b91db 100644
--- a/app/src/main/res/layout/fragment_lines_detail.xml
+++ b/app/src/main/res/layout/fragment_lines_detail.xml
@@ -1,124 +1,155 @@
 <?xml version="1.0" encoding="utf-8"?>
 <FrameLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:app="http://schemas.android.com/apk/res-auto"
         xmlns:tools="http://schemas.android.com/tools"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         tools:context=".fragments.LinesDetailFragment">
     <androidx.constraintlayout.widget.ConstraintLayout
             android:layout_width="match_parent"
             android:layout_height="match_parent">
         <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
                   android:textAppearance="@style/TextAppearance.AppCompat.Headline"
                   android:text="Line 10"
                   android:id="@+id/titleTextView"
                   android:textAlignment="center"
                   android:textSize="28sp"
 
                   app:layout_constraintTop_toTopOf="parent"
                   android:layout_marginTop="8dp" android:gravity="center_horizontal|center_vertical"
                   app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
                   android:layout_marginStart="8dp" android:layout_marginEnd="8dp"/>
+        <ImageButton
+                android:src="@drawable/ic_list_30"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" android:id="@+id/switchImageButton"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintRight_toRightOf="parent"
+                android:layout_margin="6dp"
+                android:backgroundTint="@color/blue_620"
+        />
+        <androidx.cardview.widget.CardView
+                android:layout_width="wrap_content" android:layout_height="30dp"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="@id/switchImageButton"
+                app:layout_constraintBottom_toBottomOf="@id/switchImageButton"
+                android:id="@+id/starCardView"
+                android:layout_marginStart="10dp"
+                android:minHeight="20sp"
+                android:elevation="10dp"
+                android:padding="5dp">
+            <ImageButton
+                    android:id="@+id/favoritesButton"
+                    android:layout_width="45dp"
+                    android:layout_height="match_parent"
+                    android:layout_gravity="end"
+                    android:background="@android:color/transparent"
+                    android:foreground="?attr/selectableItemBackground"
+                    app:srcCompat="@drawable/ic_star_outline"
+                    tools:ignore="OnClick"/>
+        </androidx.cardview.widget.CardView>
+        <TextView
+                android:text="DCCII"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" android:id="@+id/lineDescripTextView"
+                app:layout_constraintTop_toBottomOf="@id/titleTextView"
+                app:layout_constraintLeft_toLeftOf="@id/titleTextView"
+                app:layout_constraintRight_toRightOf="@id/titleTextView"
+                android:textColor="@color/grey_700"
+                android:textSize="16sp"
+                android:maxWidth="300sp"
+                android:layout_marginTop="8dp"/>
         <Spinner
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:id="@+id/patternsSpinner"
-                app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/routeDescrTextView"
-                android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/titleTextView"
+                app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/headingToTextView"
+                android:layout_marginTop="4dp" app:layout_constraintTop_toBottomOf="@+id/lineDescripTextView"
                 android:layout_marginStart="4dp"/>
         <TextView
                 android:text="@string/direction_duep"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:id="@+id/routeDescrTextView"
+                android:id="@+id/headingToTextView"
                 app:layout_constraintStart_toStartOf="parent"
                 android:textAppearance="@style/TextAppearance.AppCompat.Body1"
                 android:textColor="?android:attr/textColorPrimary"
                 android:gravity="center_vertical"
                 android:textSize="18sp"
                 android:layout_marginLeft="10dp"
 
                 app:layout_constraintTop_toTopOf="@+id/patternsSpinner"
                 app:layout_constraintBottom_toBottomOf="@+id/patternsSpinner"
         />
         <org.osmdroid.views.MapView android:id="@+id/lineMap"
                                     android:layout_width="fill_parent"
                                     android:layout_height="0dp"
                                     android:layout_marginTop="10dp"
                                     app:layout_constraintTop_toBottomOf="@id/patternsSpinner"
                                     app:layout_constraintStart_toStartOf="parent"
                                     app:layout_constraintEnd_toEndOf="parent"
                                     app:layout_constraintBottom_toBottomOf="parent"/>
     <!--<ImageButton
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:id="@+id/icon_center_map"
             android:src="@drawable/ic_center_map"
 
             android:layout_marginTop="10dp"
             android:layout_marginRight="10dp"
             android:layout_marginEnd="10dp"
             android:background="#00ffffff"
             android:contentDescription="@string/bt_center_map_description"
             app:layout_constraintTop_toTopOf="@id/lineMap"
             app:layout_constraintEnd_toEndOf="@id/lineMap"
             android:cropToPadding="true" />
 
     <ImageButton
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:id="@+id/icon_follow"
             android:src="@drawable/ic_follow_me"
             android:background="#00ffffff"
             android:contentDescription="@string/bt_follow_me_description"
             android:cropToPadding="true"
             app:layout_constraintEnd_toEndOf="@id/lineMap"
             app:layout_constraintTop_toBottomOf="@id/icon_center_map"
             android:layout_marginTop="10dp"
             android:layout_marginRight="10dp"
             android:layout_marginEnd="10dp"
     />-->
-        <ImageButton
-                android:src="@drawable/ic_list_30"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content" android:id="@+id/switchImageButton"
-                app:layout_constraintTop_toTopOf="parent"
-                app:layout_constraintRight_toRightOf="parent"
-                app:layout_constraintBottom_toTopOf="@id/patternsSpinner"
-                android:layout_margin="6dp"
-                android:backgroundTint="@color/blue_500"
-        />
+
 
         <View
                 android:id="@+id/divider"
                 android:layout_width="match_parent"
                 android:layout_height="1dp"
                 android:background="?android:attr/listDivider"
                 app:layout_constraintTop_toBottomOf="@id/patternsSpinner"
 
                 android:layout_marginTop="8dp"/>
         <androidx.recyclerview.widget.RecyclerView
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:id="@+id/patternStopsRecyclerView"
                 app:layout_constraintTop_toBottomOf="@+id/divider"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintVertical_bias="0.0"
                 android:layout_marginBottom="8dp"
                 android:layout_marginTop="0dp"
                 android:layout_margin="4dp"
                 app:layout_constraintHorizontal_bias="0.0"
                 app:fastScrollEnabled="true"
                 app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
                 app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
                 app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
                 app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
                 android:visibility="gone"
         />
 
     </androidx.constraintlayout.widget.ConstraintLayout>
 
 </FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_lines_grid.xml b/app/src/main/res/layout/fragment_lines_grid.xml
index 82417b1..60889a7 100644
--- a/app/src/main/res/layout/fragment_lines_grid.xml
+++ b/app/src/main/res/layout/fragment_lines_grid.xml
@@ -1,138 +1,176 @@
 <?xml version="1.0" encoding="utf-8"?>
 <FrameLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         tools:context=".fragments.LinesGridShowingFragment">
         <!--<androidx.core.widget.NestedScrollView
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:id="@+id/linesScrollView"
                 android:layout_weight="12"
         >-->
         <androidx.constraintlayout.widget.ConstraintLayout
                 android:layout_width="match_parent" android:layout_height="match_parent"
                 android:animateLayoutChanges="true"
         >
+            <ImageView
+                    android:src="@drawable/baseline_chevron_right_24"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content" android:id="@+id/arrowFavorites"
+                    app:layout_constraintLeft_toLeftOf="parent"
+                    app:layout_constraintBottom_toBottomOf="@id/favoritesTitleView"
+                    android:layout_margin="4dp"
+                    android:layout_marginStart="16dp"
+                    android:rotation="90"
+            />
+            <TextView android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:text="@string/favorites_lines"
+                      android:textAppearance="@style/TextAppearance.AppCompat.Body2"
+                      android:textSize="@dimen/subtitle_size"
+                      android:layout_margin="4dp"
+                      android:textColor="@color/black_900"
+                      android:gravity="center"
+                      android:id="@+id/favoritesTitleView"
+                      app:layout_constraintTop_toTopOf="parent"
+                      app:layout_constraintLeft_toRightOf="@id/arrowFavorites"
+                      app:layout_constraintBottom_toTopOf="@id/favoritesRecyclerView"
+                      android:layout_marginLeft="6dp"
+                      app:layout_constraintVertical_bias="0.0"
+                      app:layout_constraintVertical_chainStyle="packed"/>
+            <androidx.recyclerview.widget.RecyclerView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/favoritesRecyclerView"
+                    android:layout_marginLeft="20dp"
+                    android:layout_marginRight="20dp"
+                    android:visibility="visible"
+                    app:layout_constraintTop_toBottomOf="@id/favoritesTitleView"
+                    app:layout_constraintBottom_toTopOf="@id/urbanLinesTitleView"
+                    app:layout_constraintLeft_toLeftOf="parent"
+                    app:layout_constraintVertical_bias="0.0"
+
+            />
             <ImageView
                     android:src="@drawable/baseline_chevron_right_24"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content" android:id="@+id/arrowUrb"
                     app:layout_constraintLeft_toLeftOf="parent"
                     app:layout_constraintBottom_toBottomOf="@id/urbanLinesTitleView"
                     android:layout_margin="4dp"
                     android:layout_marginStart="16dp"
                     android:rotation="0"
             />
             <TextView android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:text="@string/urban_lines"
                       android:textAppearance="@style/TextAppearance.AppCompat.Body2"
                       android:textSize="@dimen/subtitle_size"
                       android:layout_margin="4dp"
                       android:textColor="@color/black_900"
                       android:gravity="center"
                       android:id="@+id/urbanLinesTitleView"
-                      app:layout_constraintTop_toTopOf="parent"
+                      app:layout_constraintTop_toBottomOf="@id/favoritesRecyclerView"
                       app:layout_constraintLeft_toRightOf="@id/arrowUrb"
                       app:layout_constraintBottom_toTopOf="@id/urbanLinesRecyclerView"
                       android:layout_marginLeft="6dp"
                       app:layout_constraintVertical_bias="0.0"
-                      app:layout_constraintVertical_chainStyle="packed"/>
+            />
             <androidx.recyclerview.widget.RecyclerView
                     android:layout_width="match_parent"
                     android:layout_height="0dp"
                     android:id="@+id/urbanLinesRecyclerView"
                     android:layout_marginLeft="10dp"
                     android:layout_marginRight="10dp"
                     android:visibility="visible"
                     android:layout_below="@id/urbanLinesTitleView"
                     app:layout_constraintTop_toBottomOf="@id/urbanLinesTitleView"
                     app:layout_constraintBottom_toTopOf="@id/touristLinesTitleView"
                     app:layout_constraintLeft_toLeftOf="parent"
                     app:layout_constraintVertical_bias="0.0"
 
             />
             <ImageView
                     android:src="@drawable/baseline_chevron_right_24"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content" android:id="@+id/arrowTourist"
                     app:layout_constraintLeft_toLeftOf="parent"
                     app:layout_constraintBottom_toBottomOf="@id/touristLinesTitleView"
                     android:layout_margin="4dp"
                     android:layout_marginStart="16dp"
                     android:rotation="0"
             />
             <TextView android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:text="@string/turist_lines"
                       android:textAppearance="@style/TextAppearance.AppCompat.Body2"
                       android:textSize="@dimen/subtitle_size"
                       android:textColor="@color/black_900"
                       android:layout_margin="4dp"
                       android:layout_marginStart="6dp"
                       android:gravity="center"
                       android:id="@+id/touristLinesTitleView"
                       app:layout_constraintLeft_toRightOf="@id/arrowTourist"
                       app:layout_constraintTop_toBottomOf="@id/urbanLinesRecyclerView"
                       app:layout_constraintBottom_toTopOf="@id/touristLinesRecyclerView"
                       app:layout_constraintVertical_bias="0.0"
                       android:layout_marginLeft="6dp"/>
             <androidx.recyclerview.widget.RecyclerView
                     android:layout_width="match_parent"
                     android:layout_height="0dp"
                     android:id="@+id/touristLinesRecyclerView"
                     android:layout_marginLeft="10dp"
                     android:layout_marginRight="10dp"
                     android:visibility="gone"
                     app:layout_constraintStart_toStartOf="parent"
                     app:layout_constraintTop_toBottomOf="@id/touristLinesTitleView"
                     app:layout_constraintBottom_toTopOf="@id/extraurbanLinesTitleView"
                     app:layout_constraintVertical_bias="0.0"
 
             />
             <ImageView
                     android:src="@drawable/baseline_chevron_right_24"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content" android:id="@+id/arrowExtraurban"
                     app:layout_constraintLeft_toLeftOf="parent"
                     app:layout_constraintBottom_toBottomOf="@id/extraurbanLinesTitleView"
                     android:layout_margin="4dp"
                     android:layout_marginStart="16dp"
                     android:rotation="0"
             />
             <TextView android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:text="@string/extraurban_lines"
                       android:textAppearance="@style/TextAppearance.AppCompat.Body2"
                       android:textSize="@dimen/subtitle_size"
                       android:layout_margin="4dp"
                       android:textColor="@color/black_900"
                       android:gravity="center"
                       android:layout_marginStart="6dp"
                       android:id="@+id/extraurbanLinesTitleView"
                       app:layout_constraintTop_toBottomOf="@id/touristLinesRecyclerView"
                       app:layout_constraintLeft_toRightOf="@id/arrowExtraurban"
                       app:layout_constraintBottom_toTopOf="@id/extraurbanLinesRecyclerView"
                       app:layout_constraintVertical_bias="0.0"
 
             />
             <androidx.recyclerview.widget.RecyclerView
                     android:layout_width="match_parent"
                     android:layout_height="0dp"
                     android:id="@+id/extraurbanLinesRecyclerView"
                     android:layout_marginLeft="10dp"
                     android:layout_marginRight="10dp"
                     android:visibility="gone"
                     android:layout_below="@id/extraurbanLinesTitleView"
 
                     app:layout_constraintLeft_toLeftOf="parent"
                     app:layout_constraintTop_toBottomOf="@id/extraurbanLinesTitleView"
                     app:layout_constraintBottom_toBottomOf="parent"
                     app:layout_constraintVertical_bias="0.0"
 
             />
         </androidx.constraintlayout.widget.ConstraintLayout>
         <!--</androidx.core.widget.NestedScrollView>-->
 </FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index e932b2b..5b635c4 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1,252 +1,255 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
 
     <string name="app_description">Stai utilizzando l\'ultimo ritrovato in materia di rispetto della tua privacy.</string>
     <string name="search">Cerca</string>
     <string name="qrcode">QR Code</string>
     <string name="yes">Si</string>
     <string name="no">No</string>
     <string name="next">Prossimo</string>
     <string name="previous">Precedente</string>
     <string name="title_barcode_scanner_install">Installare Barcode Scanner?</string>
     <string name="message_install_barcode_scanner">Questa azione richiede un\'altra app per scansionare i codici QR. Vuoi installare Barcode Scanner?</string>
     <string name="insert_bus_stop_number">Numero fermata</string>
     <string name="insert_bus_stop_name">Nome fermata</string>
     <string name="insert_bus_stop_number_error">Inserisci il numero della fermata</string>
     <string name="insert_bus_stop_name_error">Inserisci il nome della fermata</string>
     <string name="network_error">Verifica l\'accesso ad Internet!</string>
     <string name="no_bus_stop_have_this_name">Sembra che nessuna fermata abbia questo nome</string>
     <string name="no_arrivals_stop">Nessun passaggio trovato alla fermata</string>
     <string name="parsing_error">Errore di lettura del sito 5T/GTT (dannato sito!)</string>
     <string name="passages">Fermata: %1$s</string>
     <string name="line">Linea</string>
     <string name="lines">Linee</string>
     <string name="urban_lines">Linee urbane</string>
     <string name="extraurban_lines">Linee extraurbane</string>
     <string name="turist_lines">Linee turistiche</string>
     <string name="direction_duep">Direzione:</string>
 
 
     <string name="line_fill">Linea: %1$s</string>
     <string name="lines_fill">Linee: %1$s</string>
     <string name="results">Scegli la fermata…</string>
     <string name="no_passages">Nessun passaggio</string>
     <string name="no_qrcode">Nessun QR code trovato, prova ad usare un\'altra app</string>
     <string name="action_favorites">Preferiti</string>
     <string name="action_help">Aiuto</string>
     <string name="action_about">Informazioni</string>
     <string name="action_about_more">Più informazioni</string>
     <string name="action_wiki">Contribuisci</string>
     <string name="hack_url">https://gitpull.it/w/librebusto/it/</string>
     <string name="action_source">Codice sorgente</string>
     <string name="action_licence">Licenza</string>
     <string name="action_author">Incontra l\'autore</string>
     <string name="added_in_favorites">Fermata aggiunta ai preferiti</string>
     <string name="cant_add_to_favorites">Impossibile aggiungere ai preferiti (memoria piena o database corrotto?)!</string>
     <string name="title_activity_favorites">Preferiti</string>
     <string name="title_activity_map">Mappa</string>
     <string name="tip_add_favorite">Nessun preferito? Arghh!\nSchiaccia sulla stella di una fermata per aggiungerla a questa lista!</string>
     <string name="action_remove_from_favourites">Rimuovi</string>
     <string name="action_rename_bus_stop_username">Rinomina</string>
     <string name="dialog_rename_bus_stop_username_title">Rinomina fermata</string>
     <string name="dialog_rename_bus_stop_username_reset_button">Reset</string>
     <string name="about_activity">Informazioni</string>
     <string name="howDoesItWork"><b>Tocca la stella</b> per aggiungere la fermata ai preferiti\n\n<b>Come leggere gli orari:</b>
         \n<b>&#160;&#160;&#160;12:56*</b> Orario in tempo reale\n<b>&#160;&#160;&#160;12:56</b> &#160; Orario programmato\n\n<b>Trascina giù per aggiornare</b> l\'orario.
         \n<b>Tocca a lungo</b> su <b>Fonte Orari</b> per cambiare sorgente degli orari di arrivo.
     </string>
     <string name="hint_button">OK!</string>
     <string name="about_history">
 <![CDATA[
         <h1>Benvenuto!</h1>
         
         <p>Grazie per aver scelto BusTO, un\'app <b>indipendente</b> da GTT/5T, per spostarsi a Torino attraverso <b>software libero</b>:</p>
 		<br>
 		<p>Perché usare BusTO?</p>
 		<p>
         - Non sei <b>monitorato</b><br>
         - Non ci sono <b>pubblicità</b><br>
         - La tua <b>privacy</b> è al sicuro<br>
         - Inoltre l\'app è molto leggera!<br>
         </p>
         <br>
         <h2>Come funziona?</h2>
         <p>Quest\'app ottiene i passaggi dei bus, le fermate e altre informazioni utili unendo dati forniti dal sito <b>www.gtt.to.it</b>, <i>www.5t.torino.it</i>, <b>muoversiatorino.it</b> "per uso personale" e altre fonti Open Data (aperto.comune.torino.it).</p>
         <br>
 		<p>Ingredienti:<br>
 		- <b>Fabio Mazza</b> attuale rockstar developer anziano.<br>
 		- <b>Andrea Ugo</b> attuale rockstar developer in formazione.<br>
 		- <b>Silviu Chiriac</b> designer del logo 2021.<br>
 		- <b>Marco M</b> formidabile tester e cacciatore di bug.<br>
 		- <b>Ludovico Pavesi</b> ex rockstar developer anziano asd.<br>
 		- <b>Valerio Bozzolan</b> attuale manutentore.<br>
 		- <b>Marco Gagino</b> apprezzato ex collaboratore, ideatore icona e grafica.<br>
 		- <b>JSoup</b> libreria per "<i>web scaping</i>".<br>
 		- <b>Google</b> icone e libreria di supporto per il Material Design.<br>
 		- Tutti i contributori e i beta tester!
 		</p>
         <br>
 		<h2>Licenze</h2>
 		<p>L\'app e il relativo codice sorgente sono distribuiti sotto la licenza <i>GNU General Public License v3+</i>.
 		Ciò <b>significa</b> che puoi usare, studiare, migliorare e ricondividere quest\'app con <b>qualunque mezzo</b> e per <b>qualsiasi scopo</b>: a patto di mantenere sempre questi diritti a tua volta e di dare credito a Valerio Bozzolan.
 		</p>
 
 		<br>
 		<h2>Note</h2>
 		<p>Quest\'applicazione è rilasciata <b>nella speranza che sia utile a tutti</b> ma senza NESSUNA garanzia sul suo funzionamento attuale e/o futuro.</p>
 		<p>Tutti i dati utilizzati dall\'app provengono <b>direttamente</b> da GTT o da simili agenzie pubbliche: se trovi che sono inesatti per qualche motivo, ti invitiamo a rivolgerti a loro.</p>
 		<p>Buon utilizzo! :)</p>
     ]]>
     </string>
     <string name="query_too_short">Nome troppo corto, digita più caratteri e riprova</string>
     <string name="route_towards_destination">%1$s verso %2$s</string>
     <string name="route_towards_unknown">%s (destinazione sconosciuta)</string>
     <string name="internal_error">Errore interno inaspettato, impossibile estrarre dati dal sito GTT/5T</string>
     <string name="action_view_on_map">Visualizza sulla mappa</string>
     <string name="cannot_show_on_map_no_activity">Non trovo un\'applicazione dove mostrarla</string>
     <string name="cannot_show_on_map_no_position">Posizione della fermata non trovata</string>
 
 
     <string name="nearby_stops_message">Fermate vicine</string>
     <string name="position_searching_message">Ricerca della posizione</string>
     <string name="no_stops_nearby">Nessuna fermata nei dintorni</string>
     <string name="main_menu_pref">Preferenze</string>
     <string name="database_update_msg_inapp">Aggiornamento del database&#8230;</string>
     <string name="database_update_msg_notif">Aggiornamento del database</string>
     <string name="database_update_req">Aggiornamento database forzato</string>
     <string name="database_update_req_descr">Tocca per aggiornare ora il database</string>
     <string name="pref_num_elements">Numero minimo di fermate</string>
     <string name="num_stops_nearby_not_number">Il numero di fermate da ricercare non è valido</string>
     <string name="invalid_number">Valore errato, inserisci un numero</string>
     <string name="title_activity_settings">Impostazioni</string>
     <string name="settings_search_radius">Distanza massima di ricerca (m)</string>
     <string name="settings_experimental">Funzionalità sperimentali</string>
     <string name="action_settings">Impostazioni</string>
     <string name="general_settings">Generali</string>
     <string name="pref_recents_group_title">Fermate recenti</string>
     <string name="settings_group_general">Impostazioni generali</string>
     <string name="settings_group_database">Gestione del database</string>
     <string name="settings_reset_database">Comincia aggiornamento manuale del database</string>
 
 
     <string name="enable_position_message_map">Consenti l\'accesso alla posizione per mostrarla sulla mappa</string>
     <string name="enableGpsText">Abilitare il GPS</string>
     <string name="bus_arriving_at">arriva alle</string>
     <string name="arrivals_card_at_the_stop">alla fermata</string>
     <string name="show_arrivals">Mostra arrivi</string>
     <string name="show_stops">Mostra fermate</string>
     <string name="nearby_arrivals_message">Arrivi qui vicino</string>
     <string name="removed_from_favorites">Fermata rimossa dai preferiti</string>
     <!--
     Mixed button strings
     !-->
     <string name="open_telegram"> Canale Telegram</string>
 
     <!--
     Map view buttons strings
     !-->
     <string name="bt_center_map_description">La mia posizione</string>
     <string name="bt_follow_me_description">Segui posizione</string>
 
     <!--
     Arrival times sources
     !-->
     <string name="times_source_fmt">Fonte orari: %1$s</string>
     <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="undetermined_source">Sconosciuta</string>
 
     <string name="arrival_times_choice_title">Fonti orari di arrivo</string>
     <string name="arrival_times_choice_explanation">Scegli le fonti di orari da usare</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>
     <string name="no_passages_title">Nessun passaggio per le linee:</string>
 
 
 
     <string name="default_notification_channel_description">Canale unico delle notifiche</string>
     <string name="database_notification_channel">Database</string>
     <string name="database_notification_channel_desc">Informazioni sul database (aggiornamento)</string>
     <string name="db_trips_download_message">Downloading trips from MaTO server</string>
 
 
     <string name="too_many_permission_asks">Chiesto troppe volte per il permesso %1$s</string>
     <string name="permission_storage_maps_msg">Non si può usare questa funzionalità senza il permesso di archivio</string>
     <string name="storage_permission">di archivio</string>
     <string name="message_crash">Un bug ha fatto crashare l\'app!
         \nPremi \"OK\" per inviare il report agli sviluppatori via email, così potranno scovare e risolvere il tuo bug!
         \nIl report contiene piccole informazioni non sensibili sulla configurazione del tuo telefono e sullo stato dell\'app al momento del crash.
     </string>
     <string name="acra_email_message">L\'applicazione è crashata, e il crash report è stato messo negli allegati. Se vuoi, descrivi cosa stavi facendo prima che si interrompesse: \n</string>
     <string name="nav_arrivals_text">Arrivi</string>
     <string name="nav_map_text">Mappa</string>
     <string name="nav_favorites_text">Preferiti</string>
     <string name="drawer_open">Apri drawer</string>
     <string name="drawer_close">Chiudi drawer</string>
     <string name="experiments">Esperimenti</string>
     <string name="donate_now">Offrici un caffè</string>
     <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>
 
     <!--  Preferences -->
     <string name="requesting_db_update">Richiesto aggiornamento del database</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>
     <string name="pref_lines_click_msg">Mostra arrivi quando tocchi una fermata</string>
     <string name="pref_experimental_msg">Abilita esperimenti</string>
     <string name="pref_shown_startup">Schermata da mostrare all\'avvio</string>
     <string name="pref_shown_startup_def_desc">Tocca per cambiare</string>
 
     <string name="positions_source_pref_title">Fonte posizioni in tempo reale di bus e tram</string>
-
     <array name="positions_source_sel">
         <item>MaTO (aggiornate più spesso, può non funzionare)</item>
         <item>GTFS RT (più stabile)</item>
     </array>
 
+    <string name="favorites_line_add">Linea aggiunta ai preferiti</string>
+    <string name="favorites_line_remove">Linea rimossa dai preferiti</string>
+    <string name="favorites_lines">Preferite</string>
+
     <!-- lines -->
     <string name="long_press_stop_4_options">Tocca a lungo la fermata per le opzioni</string>
     <string name="remove_all_trips">Rimuovi tutti i trip GTFS</string>
     <string name="all_trips_removed">Tutti i trip GTFS sono rimossi dal database</string>
 
     <!-- tutorial -->
     <string name="tutorial_action">Mostra tutorial</string>
     <string name="tutorial_first">
         <![CDATA[Grazie per aver installato BusTO, l\'app gratuita, libera e <b>open source</b> per il trasporto pubblico di Torino. Stai usando un\'app <b>indipendente</b>, senza pubblicità e senza nessun tracciamento.
 ]]> </string>
     <string name="tutorial_search"><![CDATA[Puoi cercare gli orari d\'arrivo ad una fermata inserendo il numero nella casella di ricerca.<br>
 Se ti trovi a una fermata, puoi scansionare il codice QR presente sulla palina toccando l\'icona a sinistra della barra di ricerca.]]>
     </string>
     <string name="tutorial_arrivals">
         <![CDATA[Una volta trovata una fermata, puoi aggiungerla ai <b>preferiti</b> toccando la stella a fianco del nome.]]>
     </string>
     <string name="tutorial_stops">
         <![CDATA[Con la posizione abilitata, puoi vedere le <b>fermate più vicine</b> a te direttamente nella schermata principale...]]>
     </string>
     <string name="tutorial_map">
         <![CDATA[...oppure puoi trovarle sulla mappa, assieme alle <b>posizioni in tempo reale</b> dei bus e tram (in <font color="#2F59CC">blu</font>)]]>
     </string>
     <string name="tutorial_line">
         <![CDATA[Scopri il percorso e le fermate della linea che preferisci, insieme alle posizioni in tempo reale dei bus/tram che la servono. Ricorda di scegliere la direzione che ti interessa!]]>
     </string>
     <string name="tutorial_menu">
         <![CDATA[Tutte queste funzioni si trovano nel menu laterale.<br>
 Guarda nelle <b>Impostazioni</b> per personalizzare l\'app come preferisci, e su <b>Informazioni</b> per sapere di più sull\'app e il team di sviluppo.]]>
     </string>
-    <string name="close_tutorial">Chiudi</string>
+    <string name="close_tutorial">Capito, chiudi il tutorial</string>
 
 
 </resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a570cd9..d2734ca 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,286 +1,289 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
     <string name="app_name" translatable="false">BusTO</string>
     <string name="app_name_full" translatable="false">Libre BusTO</string>
     <string name="app_name_debug" translatable="false">BusTO dev</string>
     <string name="app_name_gitpull" translatable="false">BusTO git</string>
     <string name="app_description">You\'re using the latest in technology when it comes to respecting your privacy.
     </string>
     <string name="search">Search</string>
     <string name="qrcode">Scan QR Code</string>
     <string name="yes">Yes</string>
     <string name="no">No</string>
     <string name="next">Next</string>
     <string name="previous">Previous</string>
 
     <string name="title_barcode_scanner_install">Install Barcode Scanner?</string>
     <string name="message_install_barcode_scanner">This application requires an app to scan the QR codes. Would you like
         to install Barcode Scanner now?
     </string>
 
     <string name="insert_bus_stop_number">Bus stop number</string>
     <string name="insert_bus_stop_name">Bus stop name</string>
     <string name="insert_bus_stop_number_error">Insert bus stop number</string>
     <string name="insert_bus_stop_name_error">Insert bus stop name</string>
     <string name="route_towards_destination">%1$s towards %2$s</string>
     <string name="route_towards_unknown">%s (unknown destination)</string>
     <string name="network_error">Verify your Internet connection!</string>
     <string name="no_bus_stop_have_this_name">Seems that no bus stop have this name</string>
     <string name="no_arrivals_stop">No arrivals found for this stop</string>
     <string name="parsing_error">Error parsing the 5T/GTT website (damn site!)</string>
     <string name="query_too_short">Name too short, type more characters and retry
     </string> <!-- TODO: carry out experiments to determine the best wording for this message and publish a paper with the findings -->
     <string name="passages">Arrivals at: %1$s</string>
     <string name="results">Choose the bus stop…</string>
     <string name="line">Line</string>
     <string name="lines">Lines</string>
     <string name="urban_lines">Urban lines</string>
     <string name="extraurban_lines">Extra urban lines</string>
     <string name="turist_lines">Tourist lines</string>
 
-    <string name="direction_duep">Heading to:</string>
+    <string name="direction_duep">Destination:</string>
     <string name="lines_fill">Lines: %1$s</string>
     <string name="line_fill">Line: %1$s</string>
     <string name="no_passages">No timetable found</string>
     <string name="no_qrcode">No QR code found, try using another app to scan</string>
     <string name="internal_error">Unexpected internal error, cannot extract data from GTT/5T website</string>
     <string name="action_help">Help</string>
     <string name="action_about">About the app</string>
     <string name="action_about_more">More about</string>
     <string name="action_wiki">Contribute</string>
     <string name="hack_url">https://gitpull.it/w/librebusto/en/</string>
     <string name="action_source">Source code</string>
     <string name="action_licence">Licence</string>11
     <string name="action_author">Meet the author</string>
     <string name="added_in_favorites">Bus stop is now in your favorites</string>
     <string name="removed_from_favorites">Bus stop removed from your favorites</string>
+    <string name="favorites_line_add">Added line to favorites</string>
+    <string name="favorites_line_remove">Remove line from favorites</string>
+    <string name="favorites_lines">Favorites</string>
     <string name="action_favorites">Favorites</string>
     <string name="title_activity_favorites">Favorites</string>
     <string name="title_activity_map">Map</string>
     <string name="tip_add_favorite">No favorites? Arghh! Press on a bus stop star to populate this list!</string>
     <string name="action_remove_from_favourites">Delete</string>
     <string name="action_rename_bus_stop_username">Rename</string>
     <string name="dialog_rename_bus_stop_username_title">Rename the bus stop</string>
     <string name="dialog_rename_bus_stop_username_reset_button">Reset</string>
     <string name="about_activity">About the app</string>
     <string name="howDoesItWork">
         <b>Tap the star</b>
         to add the bus stop to the favourites\n\n<b>How to read timelines:</b>\n<b>&#160;&#160;&#160;12:56*</b> Real-time
         arrivals\n<b>&#160;&#160;&#160;12:56</b> &#160; Scheduled arrivals\n\n<b>Pull down to refresh</b> the timetable
         \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="no_passages_title">No arrivals found for lines:</string>
     <string name="about_history">
         <![CDATA[
         <h1>Welcome!</h1>
 
         <p>Thanks for using BusTO, a "politically" <b>independent</b> app useful to move around Torino using a <b>Free/Libre software</b>.</p>
         <br>
         <p>Why use this app?</p>
 		<p>
         - You\'ll never be <b>tracked</b><br>
         - You\'ll never see boring <b>ads</b><br>
         - We\'ll always respect your <b>privacy</b><br>
         - Moreover, it\'s lightweight!<br>
         </p>
         <br>
         <h2>How does it work?</h2>
         <p>This app is able to do all the amazing things it does by pulling data from <b>www.gtt.to.it</b>, <b>www.5t.torino.it</b> or <b>muoversiatorino.it</b> "for personal use", along with open data from the AperTO (aperto.comune.torino.it) website.</p>
         <br>
 		<p>The work of several people is behind this app, in particular:<br>
 		- <b>Fabio Mazza</b>, current senior rockstar developer.<br>
         - <b>Andrea Ugo</b>, current junior rockstar developer.<br>
 		- <b>Silviu Chiriac</b>, designer of the 2021 logo.<br>
 		- <b>Marco M</b>, rockstar tester and bug hunter.<br>
 		- <b>Ludovico Pavesi</b>, previous senior rockstar developer (asd).<br>
 		- <b>Valerio Bozzolan</b>, maintainer and infrastructure (sponsor).<br>
 		- <b>Marco Gagino</b>, contributor and first icon creator.<br>
 		- <b>JSoup</b> web scraper library.<br>
 		- <b>makovkastar</b> floating buttons.<br>
 		- <b>Google</b> Material Design icons and Volley framework.<br>
 		- <b>Android</b> app components.<br>
 		- All the contributors, and the beta testers, too!
 		</p>
 		<br>
 		<h2>Licenses</h2>
 		<p>The app and the related source code are released by Valerio Bozzolan and the other authors under the terms of the <i>GNU General Public License v3+</i>).
 		So everyone is allowed to use, to study, to improve and to share this app by <b>any kind of means</b> and for <b>any purpose</b>: under the conditions of maintaining this rights and of attributing the original work to Valerio Bozzolan.</p>
         <br>
 		<h2>Notes</h2>
 		<p>This app has been developed with the <b>hope to be useful to everyone</b>, but comes without ANY warranty of any kind.</p>
 		<p>The data used by the app comes <b>directly</b> from GTT and other public agencies: if you find any errors, please take it up to them, not to us.</p>
 		<p>This translation is kindly provided by Riccardo Caniato, Marco Gagino and Fabio Mazza.</p>
 		<p>Now you can hack public transport, too! :)</p>
     ]]>
     </string>
 
     <string name="cant_add_to_favorites">Cannot add to favorites (storage full or corrupted database?)!</string>
     <string name="action_view_on_map">View on a map</string>
     <string name="cannot_show_on_map_no_activity">Cannot find any application to show it in</string>
     <string name="cannot_show_on_map_no_position">Cannot find the position of the stop</string>
 
     <string name="list_fragment_debug" translatable="false">ListFragment - BusTO</string>
     <string name="mainSharedPreferences" translatable="false">it.reyboz.bustorino.preferences</string>
     <string name="databaseUpdatingPref" translatable="false">db_is_updating</string>
     <!-- Settings -->
     <string name="nearby_stops_message">Nearby stops</string>
     <string name="nearby_arrivals_message">Nearby connections</string>
     <string name="app_version">App version</string>
     <string name="num_stops_nearby_not_number">The number of stops to show in the recent stops is invalid</string>
     <string name="invalid_number">Invalid value, put a valid number</string>
 
     <string name="position_searching_message">Finding location</string>
     <string name="no_stops_nearby">No stops nearby</string>
     <string name="pref_num_elements">Minimum number of stops</string>
     <string name="main_menu_pref">Preferences</string>
     <string name="title_activity_settings">Settings</string>
     <string name="action_settings">Settings</string>
     <string name="general_settings">General</string>
     <string name="settings_experimental">Experimental features</string>
     <string name="settings_search_radius">Maximum distance (meters)</string>
     <string name="pref_recents_group_title">Recent stops</string>
     <string name="settings_group_general">General settings</string>
     <string name="settings_group_database">Database management</string>
     <string name="settings_reset_database">Launch manual database update</string>
 
     <string name="enable_position_message_map">Allow access to position to show it on the map</string>
     <string name="enableGpsText">Please enable GPS</string>
     <string name="database_update_msg_inapp">Database update in progress&#8230;</string>
     <string name="database_update_msg_notif">Updating the database</string>
     <string name="database_update_req">Force database update</string>
     <string name="database_update_req_descr">Touch to update the app database now</string>
     <string name="bus_arriving_at">is arriving at</string>
     <string name="arrivals_card_at_the_stop">at the stop</string>
     <string name="two_strings_format" translatable="false">%1$s - %2$s</string>
     <string name="show_arrivals">Show arrivals</string>
     <string name="show_stops">Show stops</string>
     <!--
     Mixed button strings
     !-->
     <string name="open_telegram"> Join Telegram channel</string>
     <!--
    Map view buttons strings
    !-->
     <string name="bt_center_map_description">Center on my location</string>
     <string name="bt_follow_me_description">Follow me</string>
 
     <!--
     Arrival times sources
     !-->
     <string name="times_source_fmt">Arrivals source: %1$s</string>
     <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="undetermined_source">Undetermined</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>
     <string-array name="arrival_times_source_list">
         <item>@string/source_mato</item>
         <item>@string/fivetapifetcher</item>
         <item>@string/gttjsonfetcher</item>
         <item>@string/fivetscraper</item>
     </string-array>
     <string name="arrival_times_choice_title">Sources of arrival times</string>
     <string name="arrival_times_choice_explanation">Select which sources of arrival times to use</string>
 
 
     <!--
     Notifications
     -->
     <string name="default_notification_channel" translatable="false">Default</string>
     <string name="default_notification_channel_description">Default channel for notifications</string>
     <string name="database_notification_channel">Database</string>
     <string name="database_notification_channel_desc">Notifications on the update of the database</string>
     <string name="db_trips_download_message">Downloading trips from MaTO server</string>
 
     <string name="too_many_permission_asks">Asked for %1$s permission too many times</string>
     <string name="permission_storage_maps_msg">Cannot use the map with the storage permission!</string>
     <string name="storage_permission">storage</string>
     <string name="message_crash">The application has crashed because you encountered a bug.
         \nIf you want, you can help the developers by sending the crash report via email.
         \nNote that no sensitive data is contained in the report, just small bits of info on your phone and app
         configuration/state.
     </string>
     <string name="acra_email_message">The application crashed and the crash report is in the attachments. Please
         describe what you were doing before the crash: \n
     </string>
     <string name="nav_arrivals_text">Arrivals</string>
     <string name="nav_map_text">Map</string>
     <string name="nav_favorites_text">Favorites</string>
     <string name="drawer_open">Open navigation drawer</string>
     <string name="drawer_close">Close navigation drawer</string>
     <string name="experiments">Experiments</string>
     <string name="donate_now">Buy us a coffee</string>
     <string name="map">Map</string>
     <string name="stop_search_view_title">Search by stop</string>
     <string name="requesting_db_update">Launching database update</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>
     <string name="pref_shown_startup">Section to show on startup</string>
     <string name="pref_shown_startup_def_desc">Touch to change it</string>
     <string name="pref_lines_click_msg">Show arrivals touching on stop</string>
     <string name="pref_experimental_msg">Enable experiments</string>
 
     <!-- lines -->
     <string name="long_press_stop_4_options">Long press the stop for options</string>
 
     <array name="first_screen_shown">
         <item>@string/nav_arrivals_text</item>
         <item>@string/nav_favorites_text</item>
         <item>@string/nav_map_text</item>
         <item>@string/lines</item>
     </array>
 
     <string name="positions_source_pref_title">Source of real time positions for buses and trams</string>
     <array name="positions_source_sel">
         <item>MaTO (updated more frequently, might be offline)</item>
         <item>GTFS RT (more stable, less frequently updated)</item>
     </array>
 
     <string name="remove_all_trips">Remove all GTFS trips info</string>
     <string name="all_trips_removed">All GTFS trips have been removed from the database</string>
 
     <!-- Tutorial strings -->
     <string name="tutorial_action">Show tutorial</string>
     <string name="tutorial_first">
         <![CDATA[Thank you for installing BusTO, the free and <b>open source</b> app for Turin public transport. This is an <b>independent</b> app, with no ads and no tracking whatsoever.]]> </string>
     <string name="tutorial_search"> <![CDATA[You can search for the arrival times at a bus stop by inserting the number in the top, or scanning a QR code when you are at the stop
         touching the icon on the left ]]>
     </string>
     <string name="tutorial_arrivals">
         <![CDATA[Once you have found a bus stop, you can save it in the <b>favorites</b> by touching the star next to its name]]>
     </string>
     <string name="tutorial_stops">
         <![CDATA[You can see the stops near your position directly in the main screen...]]>
     </string>
 
     <string name="tutorial_map">
     <![CDATA[...or you can find them in the map, along with the real-time positions of the buses running in the city (in <font color="#2F59CC">blue</font>)]]>
     </string>
     <string name="tutorial_line">
         <![CDATA[You can also look at the line you want and see where the stops are, together with the vehicles serving the line at the moment. Remember to choose the direction you\'re interested in!]]>
     </string>
     <string name="tutorial_menu">
         <![CDATA[You can find all of this from the lateral menu.
 Look in the <b>Settings</b> to customize the app behaviour, and in the <b>About the app</b> section if you want to know more about the app and the developers.]]>
     </string>
-    <string name="close_tutorial">Close</string>
+    <string name="close_tutorial">OK, close the tutorial</string>
 
 </resources>