diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6bd7566..5291345 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,116 +1,127 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="it.reyboz.bustorino">
 
 
 
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
     <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
 
     <application
             android:allowBackup="true"
             android:icon="@drawable/ic_launcher"
             android:label="@string/app_name"
             android:theme="@style/AppTheme">
         <activity
                 android:name=".ActivityMain"
                 android:label="@string/app_name"
                 android:screenOrientation="portrait"
                 android:windowSoftInputMode="adjustResize">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
 
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
             <intent-filter>
                 <action android:name="android.intent.action.VIEW"/>
 
                 <category android:name="android.intent.category.DEFAULT"/>
                 <category android:name="android.intent.category.BROWSABLE"/>
 
                 <data
                         android:host="www.gtt.to.it"
                         android:pathPrefix="/cms/percorari/arrivi"
                         android:scheme="http"/>
             </intent-filter>
             <intent-filter>
                 <action android:name="android.intent.action.VIEW"/>
 
                 <category android:name="android.intent.category.DEFAULT"/>
                 <category android:name="android.intent.category.BROWSABLE"/>
 
                 <data
                         android:host="gtt.to.it"
                         android:pathPrefix="/cms/percorari/arrivi"
                         android:scheme="http"/>
             </intent-filter>
             <intent-filter>
                 <action android:name="android.intent.action.VIEW"/>
 
                 <category android:name="android.intent.category.DEFAULT"/>
                 <category android:name="android.intent.category.BROWSABLE"/>
 
                 <data
                         android:host="m.gtt.to.it"
                         android:pathPrefix="/m/it/arrivi.jsp"
                         android:scheme="http"/>
             </intent-filter>
         </activity>
         <activity
                 android:name=".ActivityAbout"
                 android:label="@string/about"
                 android:parentActivityName=".ActivityMain"
                 android:theme="@style/AboutTheme">
 
             <!-- API < 16: -->
             <meta-data
                     android:name="android.support.PARENT_ACTIVITY"
                     android:value=".ActivityMain"/>
         </activity>
         <activity
                 android:name=".ActivityFavorites"
                 android:label="@string/title_activity_favorites"
                 android:parentActivityName=".ActivityMain"
                 android:theme="@style/FavTheme">
 
             <!-- API < 16: -->
             <meta-data
                     android:name="android.support.PARENT_ACTIVITY"
                     android:value=".ActivityMain"/>
         </activity>
+        <activity
+            android:name=".ActivityMap"
+            android:label="@string/title_activity_map"
+            android:parentActivityName=".ActivityMain"
+            android:theme="@style/MapTheme">
+
+            <!-- API < 16: -->
+            <meta-data
+                android:name="android.support.PARENT_ACTIVITY"
+                android:value=".ActivityMain"/>
+        </activity>
 
         <provider
                 android:name=".middleware.AppDataProvider"
                 android:authorities="it.reyboz.bustorino.provider"
                 android:enabled="true"
                 android:exported="false">
         </provider>
 
         <service
                 android:name=".middleware.DatabaseUpdateService"
                 android:exported="false">
         </service>
         <!-- Don't show the additional frame on samsung phones -->
         <meta-data
                 android:name="com.samsung.android.icon_container.has_icon_container"
                 android:value="true"/>
 
         <activity
                 android:name=".ActivitySettings"
                 android:label="@string/title_activity_settings"
                 android:parentActivityName=".ActivityMain"
                 android:theme="@style/AppTheme">
             <meta-data
                     android:name="android.support.PARENT_ACTIVITY"
                     android:value="it.reyboz.bustorino.ActivityMain"/>
         </activity>
     </application>
 
 </manifest>
diff --git a/build.gradle b/build.gradle
index 2f5691c..f3436fa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,85 +1,87 @@
 buildscript {
     repositories {
         jcenter()
         maven { url 'https://maven.google.com' }
         google()
 
     }
 
     dependencies {
         classpath 'com.android.tools.build:gradle:3.1.0'
     }
     ext {
         support_ver = "25.4.0"
     }
 }
 allprojects {
     repositories {
         jcenter()
         maven { url 'https://maven.google.com' }
         google()
 
     }
 }
 
 apply plugin: 'com.android.application'
 
 android {
     compileSdkVersion 25
     buildToolsVersion '27.0.3'
 
     defaultConfig {
         applicationId "it.reyboz.bustorino"
         minSdkVersion 9
         targetSdkVersion 25
         versionCode 28
         versionName "1.12"
         vectorDrawables.useSupportLibrary = true
     }
 
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_8
         targetCompatibility JavaVersion.VERSION_1_8
     }
 
     sourceSets {
         main {
             manifest.srcFile 'AndroidManifest.xml'
             java.srcDirs = ['src']
             resources.srcDirs = ['src']
             aidl.srcDirs = ['src']
             renderscript.srcDirs = ['src']
             res.srcDirs = ['res']
             assets.srcDirs = ['assets']
         }
     }
     buildTypes {
         debug {
             applicationIdSuffix ".debug"
             versionNameSuffix "-dev"
         }
     }
 
     lintOptions {
         abortOnError false
     }
 
     repositories {
         jcenter()
         mavenLocal()
     }
 
     dependencies {
         implementation "com.android.support:support-v4:$support_ver"
         implementation "com.android.support:appcompat-v7:$support_ver"
         implementation "com.android.support:design:$support_ver"
         implementation "com.android.support:recyclerview-v7:$support_ver"
         implementation "com.android.support:preference-v7:$support_ver"
         implementation "com.android.support:cardview-v7:$support_ver"
 
 
         implementation 'org.jsoup:jsoup:1.11.3'
         implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1'
         implementation 'com.android.volley:volley:1.1.1'
+
+        implementation 'org.osmdroid:osmdroid-android:6.1.3'
     }
 }
diff --git a/res/drawable/bus_marker.xml b/res/drawable/bus_marker.xml
new file mode 100644
index 0000000..21d020d
--- /dev/null
+++ b/res/drawable/bus_marker.xml
@@ -0,0 +1,12 @@
+<!-- drawable/bus_marker.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:strokeWidth="1"
+        android:strokeColor="#FFF"
+        android:fillColor="@color/metro_red"
+        android:pathData="M12 2C7.58 2 4 2.5 4 6V16A3 3 0 0 0 5 18.22V20A1 1 0 0 0 6 21H7A1 1 0 0 0 8 20V19H14A8 8 0 0 1 13 15.5A5.55 5.55 0 0 1 15.38 11H6V6H18V10A4.07 4.07 0 0 1 18.5 10A5.34 5.34 0 0 1 20 10.22V6C20 2.5 16.42 2 12 2M7.5 14A1.5 1.5 0 1 1 6 15.5A1.5 1.5 0 0 1 7.5 14M18.5 12A3.54 3.54 0 0 0 15 15.5C15 18.1 18.5 22 18.5 22S22 18.1 22 15.5A3.54 3.54 0 0 0 18.5 12M18.5 16.8A1.2 1.2 0 1 1 18.5 14.4A1.29 1.29 0 0 1 19.7 15.6A1.15 1.15 0 0 1 18.5 16.8Z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/map.xml b/res/drawable/map.xml
new file mode 100644
index 0000000..38b32b0
--- /dev/null
+++ b/res/drawable/map.xml
@@ -0,0 +1,8 @@
+<!-- drawable/map.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path android:fillColor="#FFF" android:pathData="M15,19L9,16.89V5L15,7.11M20.5,3C20.44,3 20.39,3 20.34,3L15,5.1L9,3L3.36,4.9C3.15,4.97 3,5.15 3,5.38V20.5A0.5,0.5 0 0,0 3.5,21C3.55,21 3.61,21 3.66,20.97L9,18.9L15,21L20.64,19.1C20.85,19 21,18.85 21,18.62V3.5A0.5,0.5 0 0,0 20.5,3Z" />
+</vector>
diff --git a/res/layout/activity_map.xml b/res/layout/activity_map.xml
new file mode 100644
index 0000000..bd7bbc6
--- /dev/null
+++ b/res/layout/activity_map.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+    <org.osmdroid.views.MapView android:id="@+id/map"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/map_popup.xml b/res/layout/map_popup.xml
new file mode 100644
index 0000000..76a33d8
--- /dev/null
+++ b/res/layout/map_popup.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@color/cardview_light_background">
+
+    <TextView
+        android:id="@+id/bubble_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@color/blue_500" />
+
+    <TextView
+        android:id="@+id/bubble_description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <TextView
+        android:id="@+id/bubble_subdescription"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <ImageView
+        android:id="@+id/bubble_image"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/menu/main.xml b/res/menu/main.xml
index fdacb5c..bb8fa22 100644
--- a/res/menu/main.xml
+++ b/res/menu/main.xml
@@ -1,52 +1,57 @@
 <menu 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"
     tools:context="it.reyboz.bustorino.ActivityMain">
-
+    <item
+        android:id="@+id/action_map"
+        android:icon="@drawable/map"
+        android:orderInCategory="1"
+        android:title="map"
+        app:showAsAction="ifRoom" />
     <!--suppress AndroidDomInspection --> <!-- Android Studio can't find ic_star even though it's there. -->
     <item
         android:id="@+id/action_favorites"
         android:icon="@drawable/ic_star"
         android:orderInCategory="1"
         android:title="@string/action_favorites"
         app:showAsAction="ifRoom" />
     <item
         android:orderInCategory="2"
         android:title="@string/action_about">
         <menu>
             <item
                 android:id="@+id/action_about"
                 android:orderInCategory="4"
                 android:title="@string/action_about_more" />
             <item
                 android:id="@+id/action_news"
                 android:orderInCategory="5"
                 android:title="@string/action_news" />
             <item
                 android:id="@+id/action_bugs"
                 android:orderInCategory="6"
                 android:title="@string/action_bugs"
                 app:showAsAction="never" />
             <item
                 android:id="@+id/action_source"
                 android:orderInCategory="7"
                 android:title="@string/action_source"
                 app:showAsAction="never" />
             <item
                 android:id="@+id/action_licence"
                 android:orderInCategory="8"
                 android:title="@string/action_licence"
                 app:showAsAction="never" />
         </menu>
     </item>
     <item
             android:id="@+id/action_settings"
             android:orderInCategory="3"
             android:title="@string/action_settings"
             android:visible="true" />
     <item
         android:id="@+id/action_help"
         android:orderInCategory="4"
         android:title="@string/action_help"
         android:visible="true" />
 </menu>
\ No newline at end of file
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index f05e2ab..3623f2b 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -1,111 +1,113 @@
 <?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="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="parsing_error">Errore di lettura del sito 5T/GTT (dannato sito!)</string>
     <string name="passages">Fermata: %1$s</string>
     <string name="lines">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</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_news">News</string>
     <string name="action_bugs">Invia bug</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! Schiaccia sulla stella di una fermata per aggiungere 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">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.</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>
 		
 		<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>
 
         <h2>Come Funziona?</h2>
         <p>Quest\'app ottiene i passaggi dei bus in tempo reale filtrando i dati forniti pubblicamente sul sito <b>www.gtt.to.it</b> o <i>www.5t.torino.it</i> "per uso personale".</p>
 
 		<p>Ingredienti:<br>
-		- <b>Fabio Mazza</b> attuale rockstar developer.<br>
-		- <b>Ludovico Pavesi</b> ex rockstar developer.<br>
+		- <b>Fabio Mazza</b> attuale rockstar developer anziano.<br>
+		- <b>Andrea Ugo</b> attuale rockstar developer in formazione.<br>
+		- <b>Ludovico Pavesi</b> ex rockstar developer anziano.<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!
 		</p>
 
 		<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>
 		
 		<h2>Note</h2>
 		<p>Quest\'applicazione è rilasciata <b>nella speranza che sia utile a tutti</b> ma senza NESSUNA garanzia.</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 in corso&#8230;</string>
     <string name="no_stops_nearby">Nessuna fermata nei dintorni</string>
     <string name="main_menu_pref">Preferenze</string>
     <string name="database_update_message">Aggiornamento del database&#8230;</string>
     <string name="pref_num_elements">Numero di fermate</string>
     <string name="title_activity_settings">Impostazioni</string>
     <string name="action_settings">Impostazioni</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="enableGpsText">Abilitare il GPS</string>
     <string name="settings_search_radius">Raggio di ricerca</string>
     <string name="settings_experimental">Funzionalità sperimentali</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>
 
 
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 338cbb3..f60e101 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1,120 +1,122 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
     <string name="app_name" translatable="false">BusTO</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="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="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="lines">Lines: %1$s</string>
     <string name="no_passages">No timetable found</string>
     <string name="no_qrcode">No QR code</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</string>
     <string name="action_about_more">More about</string>
     <string name="action_news">News releases</string>
     <string name="action_bugs">Bug submission</string>
     <string name="action_source">Source code</string>
     <string name="action_licence">Licence</string>
     <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="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">About</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
     </string>
     <string name="hint_button">GOT IT!</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>
 
         <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>
 
         <h2>How does it work?</h2>
         <p>This app will show you bus timetables gathering data from <b>www.gtt.to.it</b> or <b>www.5t.torino.it</b> "for personal use".</p>
 
 		<p>Who worked on BusTO:<br>
-		- <b>Fabio Mazza</b> current rockstar developer.<br>
-		- <b>Ludovico Pavesi</b> previous rockstar developer.<br>
-		- <b>Valerio Bozzolan</b> maintainer.<br>
-		- <b>Marco Gagino</b> ex contributor and icon creator.<br>
+		- <b>Fabio Mazza</b> current senior rockstar developer.<br>
+        - <b>Andrea Ugo</b> current junior rockstar developer.<br>
+		- <b>Ludovico Pavesi</b> previous senior rockstar developer.<br>
+		- <b>Valerio Bozzolan</b> maintainer and infrastructure sponsor.<br>
+		- <b>Marco Gagino</b> contributor and icon creator.<br>
 		- <b>JSoup</b> web scraper library.<br>
 		- <b>makovkastar</b> floating buttons.<br>
 		- <b>Google</b> Material Design icons.<br>
 		- All the contributors!
 		</p>
 		
 		<h2>Licenses</h2>
 		<p>The app and the related source code are released by Valerio Bozzolan 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>
 
 		<h2>Notes</h2>
 		<p>This app has been developed <b>hoping to be useful to everyone</b> but without ANY warranty.</p>
 		<p>This translation is kindly provided by Riccardo Caniato and Marco Gagino.</p>
 		<p>Get involved! :)</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>
 
     <string name="nearby_stops_message">Nearby stops</string>
     <string name="nearby_arrivals_message">Nearby connections</string>
 
     <string name="position_searching_message">Finding the position&#8230;</string>
     <string name="no_stops_nearby">No stops nearby</string>
     <string name="pref_num_elements">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="settings_experimental">Experimental features</string>
     <string name="settings_search_radius">Search radius</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="enableGpsText">Please enable GPS</string>
     <string name="database_update_message">Database update in progress&#8230;</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>
 </resources>
diff --git a/res/values/theme.xml b/res/values/theme.xml
index 9766bd6..da27fe5 100644
--- a/res/values/theme.xml
+++ b/res/values/theme.xml
@@ -1,18 +1,24 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
     <style name="FavTheme" parent="Theme.AppCompat.Light.DarkActionBar">
         <item name="colorPrimary">@color/blue_500</item>
         <item name="colorPrimaryDark">@color/blue_700</item>
         <item name="colorAccent">@color/teal_500</item>
     </style>
 
     <style name="AboutTheme" parent="Theme.AppCompat.Light.DarkActionBar">
         <item name="colorPrimary">@color/teal_300</item>
         <item name="colorPrimaryDark">@color/teal_500</item>
         <item name="colorAccent">@color/blue_700</item>
     </style>
 
+    <style name="MapTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <item name="colorPrimary">@color/orange_500</item>
+        <item name="colorPrimaryDark">@color/orange_700</item>
+        <item name="colorAccent">@color/teal_500</item>
+    </style>
+
     <style name="preferenceTheme" parent="PreferenceThemeOverlay"></style>
 
 </resources>
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/ActivityFavorites.java b/src/it/reyboz/bustorino/ActivityFavorites.java
index 75ca998..2587bd7 100644
--- a/src/it/reyboz/bustorino/ActivityFavorites.java
+++ b/src/it/reyboz/bustorino/ActivityFavorites.java
@@ -1,304 +1,310 @@
 /*
 	BusTO - Arrival times for Turin public transports.
     Copyright (C) 2014  Valerio Bozzolan
 
     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;
 
 import android.database.Cursor;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.Loader;
 import android.widget.*;
 import it.reyboz.bustorino.backend.Stop;
 import it.reyboz.bustorino.adapters.StopAdapter;
 import it.reyboz.bustorino.middleware.AsyncStopFavoriteAction;
 import it.reyboz.bustorino.middleware.StopsDB;
 import it.reyboz.bustorino.middleware.UserDB;
 
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.support.v4.app.NavUtils;
 import android.support.v7.app.ActionBar;
 import android.support.v7.app.AppCompatActivity;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.content.Intent;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.Bundle;
 
+import org.osmdroid.util.GeoPoint;
+
 import java.util.List;
 
 public class ActivityFavorites extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
     private ListView favoriteListView;
     private SQLiteDatabase userDB;
     private EditText bus_stop_name;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_favorites);
 
         // this should be done in onStarted and closed in onStop, but apparently onStarted is never run.
         this.userDB = new UserDB(getApplicationContext()).getWritableDatabase();
 
         ActionBar ab = getSupportActionBar();
         assert ab != null;
         ab.setIcon(R.drawable.ic_launcher);
         ab.setDisplayHomeAsUpEnabled(true); // Back button
 
         favoriteListView = (ListView) findViewById(R.id.favoriteListView);
 
         createFavoriteList();
     }
 
     @Override
     public void onCreateContextMenu(ContextMenu menu, View v,
                                     ContextMenuInfo menuInfo) {
         super.onCreateContextMenu(menu, v, menuInfo);
         if (v.getId() == R.id.favoriteListView) {
             MenuInflater inflater = getMenuInflater();
             inflater.inflate(R.menu.menu_favourites_entry, menu);
         }
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
             // Respond to the action bar's Up/Home button
             case android.R.id.home:
                 NavUtils.navigateUpFromSameTask(this);
                 return true;
         }
         return super.onOptionsItemSelected(item);
     }
 
     @Override
     public boolean onContextItemSelected(MenuItem item) {
         AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
                 .getMenuInfo();
 
         Stop busStop = (Stop) favoriteListView.getItemAtPosition(info.position);
 
         switch (item.getItemId()) {
             case R.id.action_favourite_entry_delete:
 
                 // remove the stop from the favorites in background
                 new AsyncStopFavoriteAction(getApplicationContext(), AsyncStopFavoriteAction.Action.REMOVE) {
 
                     /**
                      * Callback fired when everything was done
                      *
                      * @param result
                      */
                     @Override
                     protected void onPostExecute(Boolean result) {
                         super.onPostExecute(result);
 
                         // update the favorite list
                         createFavoriteList();
                     }
                 }.execute(busStop);
 
                 return true;
             case R.id.action_rename_bus_stop_username:
                 showBusStopUsernameInputDialog(busStop);
                 return true;
             case R.id.action_view_on_map:
                 final String theGeoUrl = busStop.getGeoURL();
                 if(theGeoUrl==null){
                     //doesn't have a position
                     Toast.makeText(getApplicationContext(),R.string.cannot_show_on_map_no_position,Toast.LENGTH_SHORT).show();
                     return true;
                 }
-                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(theGeoUrl));
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                if(intent.resolveActivity(getPackageManager())!=null)
-                    startActivity(intent);
-                else {
-                    Toast.makeText(getApplicationContext(),R.string.cannot_show_on_map_no_activity,Toast.LENGTH_SHORT).show();
-                }
+
+                // start ActivityMap with these extras in intent
+                Intent intent = new Intent(ActivityFavorites.this, ActivityMap.class);
+                Bundle b = new Bundle();
+                b.putDouble("lat", busStop.getLatitude());
+                b.putDouble("lon", busStop.getLongitude());
+                b.putString("name", busStop.getStopDefaultName());
+                b.putString("ID", busStop.ID);
+                intent.putExtras(b);
+
+                startActivity(intent);
                 return true;
             default:
                 return super.onContextItemSelected(item);
         }
     }
 
     void createFavoriteList() {
         // TODO: memoize default list, query only user names every time?
         new AsyncGetFavorites(getApplicationContext(), this.userDB).execute();
     }
 
     public void showBusStopUsernameInputDialog(final Stop busStop) {
         AlertDialog.Builder builder = new AlertDialog.Builder(this);
 
         LayoutInflater inflater = this.getLayoutInflater();
         View renameDialogLayout = inflater.inflate(R.layout.rename_dialog, null);
 
         bus_stop_name = (EditText) renameDialogLayout.findViewById(R.id.rename_dialog_bus_stop_name);
         bus_stop_name.setText(busStop.getStopDisplayName());
         bus_stop_name.setHint(busStop.getStopDefaultName());
 
         builder.setTitle(getString(R.string.dialog_rename_bus_stop_username_title));
         builder.setView(renameDialogLayout);
         builder.setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
                 String busStopUsername = bus_stop_name.getText().toString();
                 String oldUserName = busStop.getStopUserName();
 
                 // changed to none
                 if(busStopUsername.length() == 0) {
                     // unless it was already empty, set new
                     if(oldUserName != null) {
                         busStop.setStopUserName(null);
                         UserDB.updateStop(busStop, userDB);
                         createFavoriteList();
                     }
                 } else { // changed to something
                     // something different?
                     if(oldUserName == null || !busStopUsername.equals(oldUserName)) {
                         busStop.setStopUserName(busStopUsername);
                         UserDB.updateStop(busStop, userDB);
                         createFavoriteList();
                     }
                 }
             }
         });
         builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
                 dialog.cancel();
             }
         });
         builder.setNeutralButton(R.string.dialog_rename_bus_stop_username_reset_button, new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
                 // delete user name from database
                 busStop.setStopUserName(null);
                 UserDB.updateStop(busStop, userDB);
 
                 createFavoriteList();
             }
         });
         builder.show();
     }
 
     /**
      * This one runs. onStart instead gets ignored for no reason whatsoever.
      *
      * @see <a href="https://i.stack.imgur.com/SAX9I.png">Android Activity Lifecycle</a>
      */
     @Override
     protected void onStop() {
         super.onStop();
         this.userDB.close();
     }
 
     @Override
     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
         return null;
     }
 
     @Override
     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
 
     }
 
     @Override
     public void onLoaderReset(Loader<Cursor> loader) {
 
     }
 
     private class AsyncGetFavorites extends AsyncTask<Void, Void, List<Stop>> {
         private Context c;
         private SQLiteDatabase userDB;
 
         AsyncGetFavorites(Context c, SQLiteDatabase userDB) {
             this.c = c;
             this.userDB = userDB;
         }
 
         @Override
         protected List<Stop> doInBackground(Void... voids) {
             StopsDB stopsDB = new StopsDB(c);
             stopsDB.openIfNeeded();
             List<Stop> busStops = UserDB.getFavorites(this.userDB, stopsDB);
             stopsDB.closeIfNeeded();
 
             return busStops;
         }
 
         @Override
         protected void onPostExecute(List<Stop> busStops) {
             // If no data is found show a friendly message
             if (busStops.size() == 0) {
                 favoriteListView.setVisibility(View.INVISIBLE);
                 TextView favoriteTipTextView = (TextView) findViewById(R.id.favoriteTipTextView);
                 assert favoriteTipTextView != null;
                 favoriteTipTextView.setVisibility(View.VISIBLE);
                 ImageView angeryBusImageView = (ImageView) findViewById(R.id.angeryBusImageView);
                 angeryBusImageView.setVisibility(View.VISIBLE);
             }
 
             /* There's a nice method called notifyDataSetChanged() to avoid building the ListView
              * all over again. This method exists in a billion answers on Stack Overflow, but
              * it's nowhere to be seen around here, Android Studio can't find it no matter what.
              * Anyway, it only works from Android 2.3 onward (which is why it refuses to appear, I
              * guess) and requires to modify the list with .add() and .clear() and some other
              * methods, so to update a single stop we need to completely rebuild the list for no
              * reason. It would probably end up as "slow" as throwing away the old ListView and
              * redrwaing everything.
              */
 
             // Show results
             favoriteListView.setAdapter(new StopAdapter(this.c, busStops));
             favoriteListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                             /**
                              * Casting because of Javamerda
                              * @url http://stackoverflow.com/questions/30549485/androids-list-view-parameterized-type-in-adapterview-onitemclicklistener
                              */
                             Stop busStop = (Stop) parent.getItemAtPosition(position);
 
                             Intent intent = new Intent(ActivityFavorites.this,
                                     ActivityMain.class);
 
                             Bundle b = new Bundle();
                             // TODO: is passing a serialized object a good idea? Or rather, is it reasonably fast?
                             //b.putSerializable("bus-stop-serialized", busStop);
                             b.putString("bus-stop-ID", busStop.ID);
                             b.putString("bus-stop-display-name", busStop.getStopDisplayName());
                             intent.putExtras(b);
                             //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
                             // Intent.FLAG_ACTIVITY_CLEAR_TASK isn't supported in API < 11 and we're targeting API 7...
                             intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
 
                             startActivity(intent);
 
                             finish();
                         }
                     });
             registerForContextMenu(favoriteListView);
         }
     }
 }
diff --git a/src/it/reyboz/bustorino/ActivityMain.java b/src/it/reyboz/bustorino/ActivityMain.java
index 5bccbaf..3ba0a3d 100644
--- a/src/it/reyboz/bustorino/ActivityMain.java
+++ b/src/it/reyboz/bustorino/ActivityMain.java
@@ -1,903 +1,906 @@
 /*
 	BusTO - Arrival times for Turin public transports.
     Copyright (C) 2014  Valerio Bozzolan
 
     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;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.database.sqlite.SQLiteDatabase;
 import android.location.*;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.annotation.NonNull;
 import android.support.design.widget.Snackbar;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
 import android.support.v4.app.NavUtils;
 import android.support.v4.widget.SwipeRefreshLayout;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.*;
 
 import com.google.zxing.integration.android.IntentIntegrator;
 import com.google.zxing.integration.android.IntentResult;
 import android.support.design.widget.FloatingActionButton;
 
 import it.reyboz.bustorino.backend.*;
 import it.reyboz.bustorino.fragments.*;
 import it.reyboz.bustorino.middleware.*;
 
 import java.util.List;
 
 public class ActivityMain extends GeneralActivity implements FragmentListener {
 
     /*
      * Layout elements
      */
     private EditText busStopSearchByIDEditText;
     private EditText busStopSearchByNameEditText;
     private ProgressBar progressBar;
     private TextView howDoesItWorkTextView;
     private Button hideHintButton;
     private MenuItem actionHelpMenuItem;
     private SwipeRefreshLayout swipeRefreshLayout;
     private FloatingActionButton floatingActionButton;
     private FragmentManager framan;
     private Snackbar snackbar;
 
     /*
      * Search mode
      */
     private static final int SEARCH_BY_NAME = 0;
     private static final int SEARCH_BY_ID = 1;
     private static final int SEARCH_BY_ROUTE = 2; // TODO: implement this -- https://gitpull.it/T12
     private int searchMode;
     private ImageButton addToFavorites;
 
     /*
      * Options
      */
     private final String OPTION_SHOW_LEGEND = "show_legend";
     private final String LOCATION_PERMISSION_GIVEN = "loc_permission";
     /*
      * Status
      */
     private DBStatusManager prefsManager;
     private DBStatusManager.OnDBUpdateStatusChangeListener updatelistener;
     private static final String DEBUG_TAG = "BusTO - MainActivity";
     /* // useful for testing:
     public class MockFetcher implements ArrivalsFetcher {
         @Override
         public Palina ReadArrivalTimesAll(String routeID, AtomicReference<result> res) {
             SystemClock.sleep(5000);
             res.set(result.SERVER_ERROR);
             return new Palina();
         }
     }
     private ArrivalsFetcher[] ArrivalFetchers = {new MockFetcher(), new MockFetcher(), new MockFetcher(), new MockFetcher(), new MockFetcher()};*/
 
     private RecursionHelper<ArrivalsFetcher> ArrivalFetchersRecursionHelper = new RecursionHelper<>(new ArrivalsFetcher[]{new GTTJSONFetcher(), new FiveTScraperFetcher()});
     private RecursionHelper<StopsFinderByName> StopsFindersByNameRecursionHelper = new RecursionHelper<>(new StopsFinderByName[]{new GTTStopsFetcher(), new FiveTStopsFetcher()});
     /*
      * Position
      */
     //Fine location criteria
     private final Criteria cr = new Criteria();
     private boolean pendingNearbyStopsRequest = false;
     private LocationManager locmgr;
     /*
      * Database Access
      */
     private StopsDB stopsDB;
     private UserDB userDB;
     private FragmentHelper fh;
 
     ///////////////////////////////// EVENT HANDLERS ///////////////////////////////////////////////
 
     /*
      * @see swipeRefreshLayout
      */
     private Handler handler = new Handler();
     private final Runnable refreshing = new Runnable() {
         public void run() {
             if (framan.findFragmentById(R.id.resultFrame) instanceof ArrivalsFragment) {
                 ArrivalsFragment fragment = (ArrivalsFragment) framan.findFragmentById(R.id.resultFrame);
                 String stopName = fragment.getStopID();
                 new AsyncDataDownload(AsyncDataDownload.RequestType.ARRIVALS, fh).execute(stopName);
             } else
                 new AsyncDataDownload(AsyncDataDownload.RequestType.ARRIVALS, fh).execute();
         }
     };
 
     //// MAIN METHOD ///
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         framan = getSupportFragmentManager();
         this.stopsDB = new StopsDB(getApplicationContext());
         this.userDB = new UserDB(getApplicationContext());
         setContentView(R.layout.activity_main);
         busStopSearchByIDEditText = (EditText) findViewById(R.id.busStopSearchByIDEditText);
         busStopSearchByNameEditText = (EditText) findViewById(R.id.busStopSearchByNameEditText);
         progressBar = (ProgressBar) findViewById(R.id.progressBar);
         howDoesItWorkTextView = (TextView) findViewById(R.id.howDoesItWorkTextView);
         hideHintButton = (Button) findViewById(R.id.hideHintButton);
         swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.listRefreshLayout);
         floatingActionButton = (FloatingActionButton) findViewById(R.id.floatingActionButton);
 
         framan.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
             @Override
             public void onBackStackChanged() {
                 Log.d("MainActivity, BusTO", "BACK STACK CHANGED");
             }
         });
 
         busStopSearchByIDEditText.setSelectAllOnFocus(true);
         busStopSearchByIDEditText
                 .setOnEditorActionListener(new TextView.OnEditorActionListener() {
                     @Override
                     public boolean onEditorAction(TextView v, int actionId,
                                                   KeyEvent event) {
                         // IME_ACTION_SEARCH alphabetical option
                         if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                             onSearchClick(v);
                             return true;
                         }
                         return false;
                     }
                 });
         busStopSearchByNameEditText
                 .setOnEditorActionListener(new TextView.OnEditorActionListener() {
                     @Override
                     public boolean onEditorAction(TextView v, int actionId,
                                                   KeyEvent event) {
                         // IME_ACTION_SEARCH alphabetical option
                         if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                             onSearchClick(v);
                             return true;
                         }
                         return false;
                     }
                 });
 
         // Called when the layout is pulled down
         swipeRefreshLayout
                 .setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                     @Override
                     public void onRefresh() {
                         handler.post(refreshing);
                     }
                 });
 
         /**
          * @author Marco Gagino!!!
          */
         //swipeRefreshLayout.setColorSchemeColors(R.color.blue_500, R.color.orange_500); // setColorScheme is deprecated, setColorSchemeColors isn't
         swipeRefreshLayout.setColorSchemeResources(R.color.blue_500, R.color.orange_500);
         fh = new FragmentHelper(this, R.id.listRefreshLayout, R.id.resultFrame);
         setSearchModeBusStopID();
 
         //---------------------------- START INTENT CHECK QUEUE ------------------------------------
 
         // Intercept calls from URL intent
         boolean tryedFromIntent = false;
 
         String busStopID = null;
         String busStopDisplayName = null;
         Uri data = getIntent().getData();
         if (data != null) {
             busStopID = getBusStopIDFromUri(data);
             tryedFromIntent = true;
         }
 
         // Intercept calls from other activities
         if (!tryedFromIntent) {
             Bundle b = getIntent().getExtras();
             if (b != null) {
                 busStopID = b.getString("bus-stop-ID");
                 busStopDisplayName = b.getString("bus-stop-display-name");
 
                 /**
                  * I'm not very sure if you are coming from an Intent.
                  * Some launchers work in strange ways.
                  */
                 tryedFromIntent = busStopID != null;
             }
         }
 
         //---------------------------- END INTENT CHECK QUEUE --------------------------------------
 
         if (busStopID == null) {
             // Show keyboard if can't start from intent
             // JUST DON'T
             // showKeyboard();
 
             // You haven't obtained anything... from an intent?
             if (tryedFromIntent) {
 
                 // This shows a luser warning
                 ArrivalFetchersRecursionHelper.reset();
                 Toast.makeText(getApplicationContext(),
                         R.string.insert_bus_stop_number_error, Toast.LENGTH_SHORT).show();
             }
         } else {
             // If you are here an intent has worked successfully
             setBusStopSearchByIDEditText(busStopID);
             /*
             //THIS PART SHOULDN'T BE NECESSARY SINCE THE LAST SUCCESSFULLY SEARCHED BUS
             // STOP IS ADDED AUTOMATICALLY
             Stop nextStop = new Stop(busStopID);
             // forcing it as user name even though it could be standard name, it doesn't really matter
             nextStop.setStopUserName(busStopDisplayName);
             //set stop as last succe
             fh.setLastSuccessfullySearchedBusStop(nextStop);
             */
             createFragmentForStop(busStopID);
         }
         //Try (hopefully) database update
         //TODO: Start the service in foreground, check last time it ran before
         DatabaseUpdateService.startDBUpdate(getApplicationContext());
         /*
         Set database update
          */
         updatelistener = new DBStatusManager.OnDBUpdateStatusChangeListener() {
             @Override
             public boolean defaultStatusValue() {
                 return true;
             }
 
             @Override
             public void onDBStatusChanged(boolean updating) {
 
                 if (updating) {
                     createDefaultSnackbar();
                 } else if (snackbar != null) {
                     snackbar.dismiss();
                     snackbar = null;
                 }
 
 
             }
         };
         prefsManager = new DBStatusManager(getApplicationContext(), updatelistener);
         prefsManager.registerListener();
 
         //locationHandler = new GPSLocationAdapter(getApplicationContext());
         //--------- NEARBY STOPS--------//
         //SETUP LOCATION
         locmgr = (LocationManager) getSystemService(LOCATION_SERVICE);
         cr.setAccuracy(Criteria.ACCURACY_FINE);
         cr.setAltitudeRequired(false);
         cr.setBearingRequired(false);
         cr.setCostAllowed(true);
         cr.setPowerRequirement(Criteria.NO_REQUIREMENT);
         //We want the nearby bus stops!
         handler.post(new NearbyStopsRequester());
         //If there are no providers available, then, wait for them
 
 
         Log.d("MainActivity", "Created");
 
     }
 
     /**
      * Reload bus stop timetable when it's fulled resumed from background.
      */
     /**
      * @Override protected void onPostResume() {
      * super.onPostResume();
      * Log.d("ActivityMain", "onPostResume fired. Last successfully bus stop ID: " + fh.getLastSuccessfullySearchedBusStop());
      * if (searchMode == SEARCH_BY_ID && fh.getLastSuccessfullySearchedBusStop() != null) {
      * setBusStopSearchByIDEditText(fh.getLastSuccessfullySearchedBusStop().ID);
      * //new asyncWgetBusStopFromBusStopID(lastSuccessfullySearchedBusStop.ID, ArrivalFetchersRecursionHelper, lastSuccessfullySearchedBusStop);
      * new AsyncDataDownload(AsyncDataDownload.RequestType.ARRIVALS,fh).execute();
      * } else {
      * //we have new activity or we don't have a new searched stop.
      * //Let's search stops nearby
      * LocationManager locManager = (LocationManager) getSystemService(LOCATION_SERVICE);
      * Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.resultFrame);
      * <p>
      * <p>
      * }
      * //show the FAB since it remains hidden
      * floatingActionButton.show();
      * <p>
      * }
      **/
 
 
     @Override
     protected void onPause() {
         super.onPause();
         fh.stopLastRequestIfNeeded();
         fh.setBlockAllActivities(true);
         if (updatelistener != null && prefsManager != null) prefsManager.unregisterListener();
         locmgr.removeUpdates(locListener);
     }
 
     @Override
     protected void onResume() {
         super.onResume();
         fh.setBlockAllActivities(false);
         if (updatelistener != null && prefsManager != null) {
             prefsManager.registerListener();
             if (prefsManager.isDBUpdating(true)) {
                 createDefaultSnackbar();
             }
         }
         if (pendingNearbyStopsRequest)
             handler.post(new NearbyStopsRequester());
     }
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         // Inflate the menu; this adds items to the action bar if it is present.
         getMenuInflater().inflate(R.menu.main, menu);
 
         actionHelpMenuItem = menu.findItem(R.id.action_help);
         return true;
     }
 
     /**
      * Callback fired when a MenuItem is selected
      *
      * @param item
      * @return
      */
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         // Handle action bar item clicks here. The action bar will
         // automatically handle clicks on the Home/Up button, so long
         // as you specify a parent activity in AndroidManifest.xml.
         switch (item.getItemId()) {
             case android.R.id.home:
                 // Respond to the action bar's Up/Home button
                 NavUtils.navigateUpFromSameTask(this);
                 return true;
             case R.id.action_help:
                 showHints();
                 return true;
             case R.id.action_favorites:
                 startActivity(new Intent(ActivityMain.this, ActivityFavorites.class));
                 return true;
+            case R.id.action_map:
+                startActivity(new Intent(ActivityMain.this, ActivityMap.class));
+                return true;
             case R.id.action_about:
                 startActivity(new Intent(ActivityMain.this, ActivityAbout.class));
                 return true;
             case R.id.action_news:
                 openIceweasel("https://gitpull.it/w/librebusto/#how-to-get-news");
                 return true;
             case R.id.action_bugs:
                 openIceweasel("https://gitpull.it/w/librebusto/#how-to-create-a-bug-feature");
                 return true;
             case R.id.action_source:
                 openIceweasel("https://gitpull.it/w/librebusto/#how-to-hack-busto");
                 return true;
             case R.id.action_licence:
                 openIceweasel("https://www.gnu.org/licenses/gpl-3.0.html");
                 return true;
             case R.id.action_settings:
                 Log.d("MAINBusTO", "Pressed button preferences");
                 startActivity(new Intent(ActivityMain.this, ActivitySettings.class));
         }
         return super.onOptionsItemSelected(item);
     }
 
     /**
      * OK this is pure shit
      *
      * @param v View clicked
      */
     public void onSearchClick(View v) {
         if (searchMode == SEARCH_BY_ID) {
             String busStopID = busStopSearchByIDEditText.getText().toString();
             //OLD ASYNCTASK
             //new asyncWgetBusStopFromBusStopID(busStopID, ArrivalFetchersRecursionHelper, lastSuccessfullySearchedBusStop);
 
             if (busStopID == null || busStopID.length() <= 0) {
                 showMessage(R.string.insert_bus_stop_number_error);
                 toggleSpinner(false);
             } else {
                 new AsyncDataDownload(AsyncDataDownload.RequestType.ARRIVALS, fh).execute(busStopID);
                 Log.d("MainActiv", "Started search for arrivals of stop " + busStopID);
             }
         } else { // searchMode == SEARCH_BY_NAME
             String query = busStopSearchByNameEditText.getText().toString();
             //new asyncWgetBusStopSuggestions(query, stopsDB, StopsFindersByNameRecursionHelper);
             new AsyncDataDownload(AsyncDataDownload.RequestType.STOPS, fh).execute(query);
         }
     }
 
     /**
      * PERMISSION STUFF
      **/
     @Override
     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
         switch (requestCode) {
             case PERMISSION_REQUEST_POSITION:
                 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                     setOption(LOCATION_PERMISSION_GIVEN, true);
                     //if we sent a request for a new NearbyStopsFragment
                     if (pendingNearbyStopsRequest) {
                         pendingNearbyStopsRequest = false;
                         handler.post(new NearbyStopsRequester());
                     }
 
                 } else {
                     //permission denied
                     setOption(LOCATION_PERMISSION_GIVEN, false);
                 }
                 //add other cases for permissions
         }
 
     }
 
 
     @Override
     public void createFragmentForStop(String ID) {
         //new asyncWgetBusStopFromBusStopID(ID, ArrivalFetchersRecursionHelper,lastSuccessfullySearchedBusStop);
         if (ID == null || ID.length() <= 0) {
             // we're still in UI thread, no need to mess with Progress
             showMessage(R.string.insert_bus_stop_number_error);
             toggleSpinner(false);
         } else {
             new AsyncDataDownload(AsyncDataDownload.RequestType.ARRIVALS, fh).execute(ID);
             Log.d("MainActiv", "Started search for arrivals of stop " + ID);
         }
     }
 
 
     /**
      * QR scan button clicked
      *
      * @param v View QRButton clicked
      */
     public void onQRButtonClick(View v) {
         IntentIntegrator integrator = new IntentIntegrator(this);
         integrator.initiateScan();
     }
 
     /**
      * Receive the Barcode Scanner Intent
      */
     public void onActivityResult(int requestCode, int resultCode, Intent intent) {
         IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
 
         Uri uri;
         try {
             uri = Uri.parse(scanResult != null ? scanResult.getContents() : null); // this apparently prevents NullPointerException. Somehow.
         } catch (NullPointerException e) {
             Toast.makeText(getApplicationContext(),
                     R.string.no_qrcode, Toast.LENGTH_SHORT).show();
             return;
         }
 
         String busStopID = getBusStopIDFromUri(uri);
         busStopSearchByIDEditText.setText(busStopID);
         createFragmentForStop(busStopID);
     }
 
     public void onHideHint(View v) {
         hideHints();
         setOption(OPTION_SHOW_LEGEND, false);
     }
 
     public void onToggleKeyboardLayout(View v) {
         if (searchMode == SEARCH_BY_NAME) {
             setSearchModeBusStopID();
             if (busStopSearchByIDEditText.requestFocus()) {
                 showKeyboard();
             }
         } else { // searchMode == SEARCH_BY_ID
             setSearchModeBusStopName();
             if (busStopSearchByNameEditText.requestFocus()) {
                 showKeyboard();
             }
         }
     }
 
     private void createDefaultSnackbar() {
         if (snackbar == null) {
             snackbar = Snackbar.make(findViewById(R.id.searchButton), R.string.database_update_message, Snackbar.LENGTH_INDEFINITE);
         }
         snackbar.show();
     }
     ///////////////////////////////// POSITION STUFF//////////////////////////////////////////////
 
     private void resolveStopRequest(String provider) {
         Log.d(DEBUG_TAG, "Provider " + provider + " got enabled");
         if (locmgr != null && pendingNearbyStopsRequest && locmgr.getProvider(provider).meetsCriteria(cr)) {
             pendingNearbyStopsRequest = false;
             handler.post(new NearbyStopsRequester());
         }
     }
 
     final LocationListener locListener = new LocationListener() {
         @Override
         public void onLocationChanged(Location location) {
             Log.d(DEBUG_TAG, "Location changed");
         }
 
         @Override
         public void onStatusChanged(String provider, int status, Bundle extras) {
             Log.d(DEBUG_TAG, "Location provider status: " + status);
             if (status == LocationProvider.AVAILABLE) {
                 resolveStopRequest(provider);
             }
         }
 
         @Override
         public void onProviderEnabled(String provider) {
             resolveStopRequest(provider);
         }
 
         @Override
         public void onProviderDisabled(String provider) {
 
         }
     };
 
     class NearbyStopsRequester implements Runnable {
         @SuppressLint("MissingPermission")
         @Override
         public void run() {
             final boolean canRunPosition = Build.VERSION.SDK_INT < Build.VERSION_CODES.M || getOption(LOCATION_PERMISSION_GIVEN, false);
 
             if (!canRunPosition) {
                 pendingNearbyStopsRequest = true;
                 assertLocationPermissions();
                 return;
             } else setOption(LOCATION_PERMISSION_GIVEN, true);
 
             LocationManager locManager = (LocationManager) getSystemService(LOCATION_SERVICE);
             if (locManager == null) {
                 Log.e(DEBUG_TAG, "location manager is nihil, cannot create NearbyStopsFragment");
                 return;
             }
             if (anyLocationProviderMatchesCriteria(locManager, cr, true) && fh.getLastSuccessfullySearchedBusStop() == null) {
                 //Go ahead with the request
                 Log.d("mainActivity", "Recreating stop fragment");
                 swipeRefreshLayout.setVisibility(View.VISIBLE);
                 NearbyStopsFragment fragment = NearbyStopsFragment.newInstance(NearbyStopsFragment.TYPE_STOPS);
                 Fragment oldFrag = framan.findFragmentById(R.id.resultFrame);
                 FragmentTransaction ft = framan.beginTransaction();
                 if (oldFrag != null)
                     ft.remove(oldFrag);
                 ft.add(R.id.resultFrame, fragment, "nearbyStop_correct");
                 ft.commit();
                 framan.executePendingTransactions();
                 pendingNearbyStopsRequest = false;
             } else if (!anyLocationProviderMatchesCriteria(locManager, cr, true)) {
                 //Wait for the providers
                 Log.d(DEBUG_TAG, "Queuing position request");
                 pendingNearbyStopsRequest = true;
                 locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10, 0.1f, locListener);
             }
 
         }
     }
 
     private boolean anyLocationProviderMatchesCriteria(LocationManager mng, Criteria cr, boolean enabled) {
         List<String> providers = mng.getProviders(cr, enabled);
         Log.d(DEBUG_TAG, "Getting enabled location providers: ");
         for (String s : providers) {
             Log.d(DEBUG_TAG, "Provider " + s);
         }
         return providers.size() > 0;
     }
 
 
     ///////////////////////////////// OTHER STUFF //////////////////////////////////////////////////
 
     /**
      * Get the last successfully searched bus stop or NULL
      *
      * @return
      */
     @Override
     public Stop getLastSuccessfullySearchedBusStop() {
         return fh.getLastSuccessfullySearchedBusStop();
     }
 
     /**
      * Get the last successfully searched bus stop ID or NULL
      *
      * @return
      */
     @Override
     public String getLastSuccessfullySearchedBusStopID() {
         Stop stop = getLastSuccessfullySearchedBusStop();
         return stop == null ? null : stop.ID;
     }
 
     /**
      * Update the star "Add to favorite" icon
      */
     @Override
     public void updateStarIconFromLastBusStop() {
 
         // no favorites no party!
         addToFavorites = (ImageButton) findViewById(R.id.addToFavorites);
         if (addToFavorites == null) {
             Log.d("MainActivity", "Why the fuck the star is not here?!");
             return;
         }
 
         // check if there is a last Stop
         String stopID = getLastSuccessfullySearchedBusStopID();
         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);
         }
     }
 
     /**
      * Check if the last Bus Stop is in the favorites
      *
      * @return
      */
     public boolean isStopInFavorites(String busStopId) {
         boolean found = false;
 
         // no stop no party
         if (busStopId != null) {
             SQLiteDatabase userDB = new UserDB(getApplicationContext()).getReadableDatabase();
             found = UserDB.isStopInFavorites(userDB, busStopId);
         }
 
         return found;
     }
 
     /**
      * Add the last Stop to favorites
      */
     @Override
     public void toggleLastStopToFavorites() {
         Stop stop = getLastSuccessfullySearchedBusStop();
         if (stop != null) {
 
             // toggle the status in background
             new AsyncStopFavoriteAction(getApplicationContext(), AsyncStopFavoriteAction.Action.TOGGLE) {
 
                 /**
                  * Callback fired when the Stop is saved in the favorites
                  * @param result
                  */
                 @Override
                 protected void onPostExecute(Boolean result) {
                     super.onPostExecute(result);
 
 
                     // update the star icon
                     updateStarIconFromLastBusStop();
                 }
 
             }.execute(stop);
         } else {
             // this case have no sense, but just immediately update the favorite icon
             updateStarIconFromLastBusStop();
         }
     }
 
     @Override
     public void showFloatingActionButton(boolean yes) {
         if (yes) floatingActionButton.show();
         else floatingActionButton.hide();
     }
 
     @Override
     public void enableRefreshLayout(boolean yes) {
         swipeRefreshLayout.setEnabled(yes);
     }
 
     ////////////////////////////////////// GUI HELPERS /////////////////////////////////////////////
     @Override
     public void showKeyboard() {
         InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
         View view = searchMode == SEARCH_BY_ID ? busStopSearchByIDEditText : busStopSearchByNameEditText;
         imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
     }
 
     @Override
     public void showMessage(int messageID) {
         Toast.makeText(getApplicationContext(), messageID, Toast.LENGTH_SHORT).show();
     }
 
     private void setSearchModeBusStopID() {
         searchMode = SEARCH_BY_ID;
         busStopSearchByNameEditText.setVisibility(View.GONE);
         busStopSearchByNameEditText.setText("");
         busStopSearchByIDEditText.setVisibility(View.VISIBLE);
         floatingActionButton.setImageResource(R.drawable.alphabetical);
     }
 
     private void setSearchModeBusStopName() {
         searchMode = SEARCH_BY_NAME;
         busStopSearchByIDEditText.setVisibility(View.GONE);
         busStopSearchByIDEditText.setText("");
         busStopSearchByNameEditText.setVisibility(View.VISIBLE);
         floatingActionButton.setImageResource(R.drawable.numeric);
     }
 
     /**
      * Having that cursor at the left of the edit text makes me cancer.
      *
      * @param busStopID bus stop ID
      */
     private void setBusStopSearchByIDEditText(String busStopID) {
         busStopSearchByIDEditText.setText(busStopID);
         busStopSearchByIDEditText.setSelection(busStopID.length());
     }
 
     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);
     }
 
     //TODO: toggle spinner from mainActivity
     @Override
     public void toggleSpinner(boolean enable) {
         if (enable) {
             //already set by the RefreshListener when needed
             //swipeRefreshLayout.setRefreshing(true);
             progressBar.setVisibility(View.VISIBLE);
         } else {
             swipeRefreshLayout.setRefreshing(false);
             progressBar.setVisibility(View.GONE);
         }
     }
 
     private void prepareGUIForBusLines() {
         swipeRefreshLayout.setEnabled(true);
         swipeRefreshLayout.setVisibility(View.VISIBLE);
         actionHelpMenuItem.setVisible(true);
     }
 
     private void prepareGUIForBusStops() {
         swipeRefreshLayout.setEnabled(false);
         swipeRefreshLayout.setVisibility(View.VISIBLE);
         actionHelpMenuItem.setVisible(false);
     }
 
 
     /**
      * This provides a temporary fix to make the transition
      * to a single asynctask go smoother
      *
      * @param fragmentType the type of fragment created
      */
     @Override
     public void readyGUIfor(FragmentKind fragmentType) {
         hideKeyboard();
         //if we are getting results, already, stop waiting for nearbyStops
         if (pendingNearbyStopsRequest && (fragmentType == FragmentKind.ARRIVALS || fragmentType == FragmentKind.STOPS)) {
             locmgr.removeUpdates(locListener);
             pendingNearbyStopsRequest = false;
         }
         if (fragmentType == null) Log.e("ActivityMain", "Problem with fragmentType");
         else
             switch (fragmentType) {
                 case ARRIVALS:
                     prepareGUIForBusLines();
                     if (getOption(OPTION_SHOW_LEGEND, true)) {
                         showHints();
                     }
                     break;
                 case STOPS:
                     prepareGUIForBusStops();
                     break;
                 default:
                     Log.e("BusTO Activity", "Called readyGUI with unsupported type of Fragment");
                     return;
             }
         // Shows hints
 
     }
 
     /**
      * Open an URL in the default browser.
      *
      * @param url URL
      */
     public void openIceweasel(String url) {
         Intent browserIntent1 = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
         startActivity(browserIntent1);
     }
 
     ///////////////////// INTENT HELPER ////////////////////////////////////////////////////////////
 
     /**
      * Try to extract the bus stop ID from a URi
      *
      * @param uri The URL
      * @return bus stop ID or null
      */
     public static String getBusStopIDFromUri(Uri uri) {
         String busStopID;
 
         // everithing catches fire when passing null to a switch.
         String host = uri.getHost();
         if (host == null) {
             Log.e("ActivityMain", "Not an URL: " + uri);
             return null;
         }
 
         switch (host) {
             case "m.gtt.to.it":
                 // http://m.gtt.to.it/m/it/arrivi.jsp?n=1254
                 busStopID = uri.getQueryParameter("n");
                 if (busStopID == null) {
                     Log.e("ActivityMain", "Expected ?n from: " + uri);
                 }
                 break;
             case "www.gtt.to.it":
             case "gtt.to.it":
                 // http://www.gtt.to.it/cms/percorari/arrivi?palina=1254
                 busStopID = uri.getQueryParameter("palina");
                 if (busStopID == null) {
                     Log.e("ActivityMain", "Expected ?palina from: " + uri);
                 }
                 break;
             default:
                 Log.e("ActivityMain", "Unexpected intent URL: " + uri);
                 busStopID = null;
         }
         return busStopID;
     }
 
     public void changeStarType(String stopID) {
         if (isStopInFavorites(stopID)) {
             changeStarFilled();
         } else {
             changeStarOutline();
         }
     }
 
     public void changeStarFilled() {
         addToFavorites.setImageResource(R.drawable.ic_star_filled);
     }
 
     public void changeStarOutline() {
         addToFavorites.setImageResource(R.drawable.ic_star_outline);
     }
 
 }
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/ActivityMap.java b/src/it/reyboz/bustorino/ActivityMap.java
new file mode 100644
index 0000000..8609bdb
--- /dev/null
+++ b/src/it/reyboz/bustorino/ActivityMap.java
@@ -0,0 +1,212 @@
+package it.reyboz.bustorino;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.preference.PreferenceManager;
+
+import org.osmdroid.api.IMapController;
+import org.osmdroid.config.Configuration;
+import org.osmdroid.events.DelayedMapListener;
+import org.osmdroid.events.MapListener;
+import org.osmdroid.events.ScrollEvent;
+import org.osmdroid.events.ZoomEvent;
+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.Marker;
+import org.osmdroid.views.overlay.infowindow.InfoWindow;
+
+import it.reyboz.bustorino.backend.Stop;
+import it.reyboz.bustorino.map.CustomInfoWindow;
+import it.reyboz.bustorino.middleware.StopsDB;
+
+public class ActivityMap extends AppCompatActivity {
+
+    private static MapView map = null;
+    public Context ctx;
+
+    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
+    @Override public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        //handle permissions first, before map is created. not depicted here
+
+        //load/initialize the osmdroid configuration, this can be done
+        ctx = getApplicationContext();
+        Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx));
+        //setting this before the layout is inflated is a good idea
+        //it 'should' ensure that the map has a writable location for the map cache, even without permissions
+        //if no tiles are displayed, you can try overriding the cache path using Configuration.getInstance().setCachePath
+        //see also StorageUtils
+        //note, the load method also sets the HTTP User Agent to your application's package name, abusing osm's tile servers will get you banned based on this string
+
+        //inflate and create the map
+        setContentView(R.layout.activity_map);
+
+        map = (MapView) findViewById(R.id.map);
+        map.setTileSource(TileSourceFactory.MAPNIK);
+
+        // add ability to zoom with 2 fingers
+        map.setMultiTouchControls(true);
+
+        // take the parameters if it's called from other Activities
+        Bundle b = getIntent().getExtras();
+        GeoPoint marker = null;
+        String name = null;
+        String ID = null;
+        if(b != null) {
+            double lat = b.getDouble("lat");
+            double lon = b.getDouble("lon");
+            marker = new GeoPoint(lat, lon);
+            name = b.getString("name");
+            ID = b.getString("ID");
+        }
+        startMap(marker, name, ID);
+
+        // on drag and zoom reload the markers
+        map.addMapListener(new DelayedMapListener(new MapListener() {
+
+            @Override
+            public boolean onScroll(ScrollEvent paramScrollEvent) {
+                loadMarkers();
+                return true;
+            }
+
+            @Override
+            public boolean onZoom(ZoomEvent event) {
+                loadMarkers();
+                return true;
+            }
+
+        }));
+    }
+
+    public void startMap(GeoPoint marker, String name, String ID) {
+
+        // move the map on the marker position or on a default view point: Turin, Piazza Castello
+        // and set the start zoom
+        IMapController mapController = map.getController();
+        GeoPoint startPoint;
+        if (marker != null) {
+            startPoint = marker;
+            mapController.setZoom(20.0);
+        } else {
+            startPoint = new GeoPoint(45.0708, 7.6858);
+            mapController.setZoom(18.0);
+        }
+        // set the minimum zoom level
+        map.setMinZoomLevel(15.5);
+        mapController.setCenter(startPoint);
+
+        loadMarkers();
+        if (marker != null) {
+            // make a marker with the info window open for the searched marker
+            makeMarker(startPoint, name , ID, true);
+        }
+
+    }
+
+    public void makeMarker(GeoPoint geoPoint, String stopName, String ID, boolean isStartMarker) {
+
+        // add a marker
+        Marker marker = new Marker(map);
+
+        // set custom info window as info window
+        CustomInfoWindow popup = new CustomInfoWindow(map, ID, stopName);
+        marker.setInfoWindow(popup);
+
+        // make the marker clickable
+        marker.setOnMarkerClickListener((thisMarker, mapView) -> {
+            if (thisMarker.isInfoWindowOpen()) {
+                // on second click
+
+                // create an intent with these extras
+                Intent intent = new Intent(ActivityMap.this, ActivityMain.class);
+                Bundle b = new Bundle();
+                b.putString("bus-stop-ID", ID);
+                b.putString("bus-stop-display-name", stopName);
+                intent.putExtras(b);
+                intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+
+                // start ActivityMain with the previous intent
+                startActivity(intent);
+            } else {
+                // on first click
+
+                // hide all opened info window
+                InfoWindow.closeAllInfoWindowsOn(map);
+                // show this particular info window
+                thisMarker.showInfoWindow();
+                // move the map to its position
+                map.getController().animateTo(thisMarker.getPosition());
+            }
+
+            return true;
+        });
+
+        // set its position
+        marker.setPosition(geoPoint);
+        marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
+        // display the marker
+        map.getOverlays().add(marker);
+        // add to it an icon
+        marker.setIcon(getResources().getDrawable(R.drawable.bus_marker));
+        // add to it a title
+        marker.setTitle(stopName);
+
+        // show popup info window of the searched marker
+        if (isStartMarker) {
+            marker.showInfoWindow();
+        }
+
+    }
+
+    public void loadMarkers() {
+
+        // get rid of the previous markers
+        map.getOverlays().clear();
+
+        // get the top, bottom, left and right screen's coordinate
+        BoundingBox bb = map.getBoundingBox();
+        double latFrom = bb.getLatSouth();
+        double latTo = bb.getLatNorth();
+        double lngFrom = bb.getLonWest();
+        double lngTo = bb.getLonEast();
+
+        // get the stops located in those coordinates
+        StopsDB stopsDB = new StopsDB(ctx);
+        stopsDB.openIfNeeded();
+        Stop[] stops = stopsDB.queryAllInsideMapView(latFrom, latTo, lngFrom, lngTo);
+        stopsDB.closeIfNeeded();
+
+        // add new markers of those stops
+        for (Stop stop : stops) {
+            GeoPoint marker = new GeoPoint(stop.getLatitude(), stop.getLongitude());
+            makeMarker(marker, stop.getStopDefaultName(), stop.ID, false);
+        }
+
+    }
+
+    public void onResume(){
+        super.onResume();
+        //this will refresh the osmdroid configuration on resuming.
+        //if you make changes to the configuration, use
+        //SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+        //Configuration.getInstance().load(this, PreferenceManager.getDefaultSharedPreferences(this));
+        map.onResume(); //needed for compass, my location overlays, v6.0.0 and up
+    }
+
+    public void onPause(){
+        super.onPause();
+        //this will refresh the osmdroid configuration on resuming.
+        //if you make changes to the configuration, use
+        //SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+        //Configuration.getInstance().save(this, prefs);
+        map.onPause();  //needed for compass, my location overlays, v6.0.0 and up
+    }
+}
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/map/CustomInfoWindow.java b/src/it/reyboz/bustorino/map/CustomInfoWindow.java
new file mode 100644
index 0000000..c1fcab5
--- /dev/null
+++ b/src/it/reyboz/bustorino/map/CustomInfoWindow.java
@@ -0,0 +1,42 @@
+package it.reyboz.bustorino.map;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+
+import org.osmdroid.views.MapView;
+import org.osmdroid.views.overlay.infowindow.BasicInfoWindow;
+
+import it.reyboz.bustorino.ActivityMain;
+import it.reyboz.bustorino.R;
+
+public class CustomInfoWindow extends BasicInfoWindow {
+
+    @SuppressLint("ClickableViewAccessibility")
+    public CustomInfoWindow(MapView mapView, String ID, String stopName) {
+        // get the personalized layout
+        super(R.layout.map_popup, mapView);
+
+        // make clickable
+        mView.setOnTouchListener((View v, MotionEvent e) -> {
+            if (e.getAction() == MotionEvent.ACTION_UP) {
+                // on click
+
+                // create an intent with these extras
+                Intent intent = new Intent(mapView.getContext(), ActivityMain.class);
+                Bundle b = new Bundle();
+                b.putString("bus-stop-ID", ID);
+                b.putString("bus-stop-display-name", stopName);
+                intent.putExtras(b);
+                intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+
+                // start ActivityMain with the previous intent
+                mapView.getContext().startActivity(intent);
+            }
+            return true;
+        });
+    }
+
+}