Bimba.git

commit 78964c7a827f691188a49a4e36d8945e5ddfdf73

Author: Adam Pioterek <adam.pioterek@protonmail.ch>

offline timetable

%!v(PANIC=String method: strings: negative Repeat count)


diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 249294c52d3e85f02f2c65f0a8458e1e26631220..58ac9e4d29069e199be5ffbd848d746b75ee26e6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -27,7 +27,6 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity android:name=".activities.NoDbActivity" />
         <activity android:name=".activities.EditFavouriteActivity" />
         <activity
             android:name=".activities.SettingsActivity"




diff --git a/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt b/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt
index 81c6bc9779bdbb6cfa7ef4faa87f43af010795a1..c40abec7cc2d1edd1e714a8a4f24dbe6afb6d577 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt
@@ -30,7 +30,7 @@             }
         }
         if (intent?.action == VmService.ACTION_READY) {
             val departures = intent.getStringArrayListExtra(VmService.EXTRA_DEPARTURES)?.map { Departure.fromString(it) }?.toSet()
-            val plateId = intent.getSerializableExtra(VmService.EXTRA_PLATE_ID) as Plate.ID
+            val plateId = intent.getSerializableExtra(VmService.EXTRA_PLATE_ID) as Plate.ID?
             val stopCode = intent.getSerializableExtra(VmService.EXTRA_STOP_CODE) as String
             for (listener in onVmListeners) {
                 listener.onVm(departures, plateId, stopCode)
@@ -59,6 +59,6 @@         fun onTimetableDownload(result: String?)
     }
 
     interface OnVmListener {
-        fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID, stopCode: String)
+        fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID?, stopCode: String)
     }
 }
\ No newline at end of file




diff --git a/app/src/main/java/ml/adamsprogs/bimba/NetworkStateReceiver.kt b/app/src/main/java/ml/adamsprogs/bimba/NetworkStateReceiver.kt
index 70a3a1652bc188fb666fd3841247a52422388b1c..2acd1e1eb9dbb95db986e6907c6e07db549c9792 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/NetworkStateReceiver.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/NetworkStateReceiver.kt
@@ -37,9 +37,14 @@         fun onConnectivityChange(connected: Boolean)
     }
 
     companion object {
-        fun isNetworkAvailable(context: Context): Boolean {
-            val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
-            val activeNetworkInfo = connectivityManager.activeNetworkInfo
+        lateinit var manager: ConnectivityManager
+
+        fun init(context: Context) {
+            manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+        }
+
+        fun isNetworkAvailable(): Boolean {
+            val activeNetworkInfo = manager.activeNetworkInfo
             return activeNetworkInfo != null && activeNetworkInfo.isConnected
         }
     }




diff --git a/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt b/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt
index 0dcddcffc601cb437e27a01a15286715735d045b..da2601cf9bf8bcf25a49956282b825c3730daff1 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt
@@ -1,14 +1,13 @@
 package ml.adamsprogs.bimba
 
-import android.annotation.SuppressLint
 import android.content.*
 import kotlinx.coroutines.experimental.android.UI
 import kotlinx.coroutines.experimental.*
-import ml.adamsprogs.bimba.activities.StopActivity
 import ml.adamsprogs.bimba.datasources.*
 import ml.adamsprogs.bimba.models.*
 import ml.adamsprogs.bimba.models.suggestions.*
 import java.util.*
+import kotlin.collections.HashMap
 
 //todo make singleton
 class ProviderProxy(context: Context? = null) {
@@ -16,6 +15,7 @@     private val vmStopsClient = VmClient.getVmStopClient()
     private var timetable: Timetable = Timetable.getTimetable(context)
     private var suggestions = emptyList<GtfsSuggestion>()
     private val requests = HashMap<String, Request>()
+
     var mode = if (timetable.isEmpty()) MODE_VM else MODE_FULL
 
     companion object {
@@ -25,10 +25,11 @@     }
 
     fun getSuggestions(query: String = "", callback: (List<GtfsSuggestion>) -> Unit) {
         launch(UI) {
-            suggestions = withContext(DefaultDispatcher) {
-                getStopSuggestions(query) //+ getLineSuggestions(query) //todo<p:v+1> + bike stations, train stations, &c
+            val filtered = withContext(DefaultDispatcher) {
+                suggestions = getStopSuggestions(query) //+ getLineSuggestions(query) //todo<p:v+1> + bike stations, train stations, &c
+                filterSuggestions(query)
             }
-            callback(filterSuggestions(query))
+            callback(filtered)
         }
     }
 
@@ -65,14 +66,14 @@     }
 
     fun getSheds(name: String, callback: (Map<String, Set<String>>) -> Unit) {
         launch(UI) {
-            val vmSheds = withContext(DefaultDispatcher) {
-                vmStopsClient.getSheds(name)
-            }
+            val sheds = withContext(DefaultDispatcher) {
+                val vmSheds = vmStopsClient.getSheds(name)
 
-            val sheds = if (vmSheds.isEmpty() and !timetable.isEmpty()) {
-                timetable.getHeadlinesForStop(name)
-            } else {
-                vmSheds
+                if (vmSheds.isEmpty() and !timetable.isEmpty()) {
+                    timetable.getHeadlinesForStop(name)
+                } else {
+                    vmSheds
+                }
             }
 
             callback(sheds)
@@ -91,7 +92,7 @@         requests[uuid] = Request(listener, stopSegments)
         return uuid
     }
 
-    fun subscribeForDepartures(stopCode: String, listener: StopActivity, context: StopActivity): String {
+    fun subscribeForDepartures(stopCode: String, listener: OnDeparturesReadyListener, context: Context): String {
         val intent = Intent(context, VmService::class.java)
         intent.putExtra("stop", stopCode)
         intent.action = "request"
@@ -102,11 +103,35 @@         requests[uuid] = Request(listener, setOf(StopSegment(stopCode, null)))
         return uuid
     }
 
-    private fun constructSegmentDepartures(stopSegments: Set<StopSegment>): Set<Departure> {
-        if (timetable.isEmpty())
-            return emptySet()
-        else
-            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+    private fun constructSegmentDepartures(stopSegments: Set<StopSegment>): Deferred<Map<String, List<Departure>>> {
+        return async {
+            if (timetable.isEmpty())
+                emptyMap()
+            else {
+                timetable.getStopDeparturesBySegments(stopSegments)
+            }
+        }
+    }
+
+    private fun filterDepartures(departures: Map<String, List<Departure>>): List<Departure> {
+        val now = Calendar.getInstance().secondsAfterMidnight()
+        val lines = HashMap<String, Int>()
+        val twoDayDepartures = (timetable.getServiceForToday()?.let {
+            departures[it]
+        } ?: emptyList()) +
+                (timetable.getServiceForTomorrow()?.let { service ->
+                    departures[service]!!.map { it.copy().apply { tomorrow = true } }
+                } ?: emptyList())
+
+        return twoDayDepartures
+                .filter { it.timeTill(now) >= 0 }
+                .filter {
+                    val existed = lines[it.line] ?: 0
+                    if (existed < 3) {
+                        lines[it.line] = existed + 1
+                        true
+                    } else false
+                }
     }
 
     fun unsubscribeFromDepartures(uuid: String, context: Context) {
@@ -119,65 +144,55 @@         timetable = Timetable.getTimetable(context, true)
         mode = MODE_FULL
     }
 
-    fun getFullTimetable(stopCode: String): Map<Int, List<Departure>> {
-        val departures = if (timetable.isEmpty())
+    fun getFullTimetable(stopCode: String): Map<String, List<Departure>> {
+        return if (timetable.isEmpty())
             emptyMap()
         else
             timetable.getStopDepartures(stopCode)
 
-        return convertCalendarModes(departures)
     }
 
-    fun getFullTimetable(stopSegments: Set<StopSegment>): Map<Int, List<Departure>> {
-        val departures = if (timetable.isEmpty())
+    fun getFullTimetable(stopSegments: Set<StopSegment>): Map<String, List<Departure>> {
+        return if (timetable.isEmpty())
             emptyMap()
         else
             timetable.getStopDeparturesBySegments(stopSegments)
 
-        return convertCalendarModes(departures)
-    }
-
-    @SuppressLint("UseSparseArrays")
-    private fun convertCalendarModes(raw: Map<String, List<Departure>>): Map<Int, List<Departure>> {
-        val sunday = timetable.getServiceFor(Calendar.SUNDAY)
-        val saturday = timetable.getServiceFor(Calendar.SATURDAY)
-
-        val departures = HashMap<Int, List<Departure>>()
-        departures[StopActivity.MODE_WORKDAYS] =
-                try {
-                    raw.filter { it.key != saturday && it.key != sunday }.toList()[0].second
-                } catch (e: IndexOutOfBoundsException) {
-                    ArrayList<Departure>()
-                }
-
-        departures[StopActivity.MODE_SATURDAYS] = raw[saturday] ?: ArrayList()
-        departures[StopActivity.MODE_SUNDAYS] = raw[sunday] ?: ArrayList()
-
-        return departures
     }
 
     interface OnDeparturesReadyListener {
-        fun onDeparturesReady(departures: Set<Departure>, plateId: Plate.ID)
+        fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?)
     }
 
     inner class Request(private val listener: OnDeparturesReadyListener, private val segments: Set<StopSegment>) : MessageReceiver.OnVmListener {
         private val receiver = MessageReceiver.getMessageReceiver()
         private val receivedPlates = HashSet<Plate.ID>()
 
+        private var cache: Deferred<Map<String, List<Departure>>>? = null
+
         init {
-            receiver.addOnVmListener(this)
+            receiver.addOnVmListener(this@Request)
+            launch(UI) {
+                cache = constructSegmentDepartures(segments)
+            }
         }
 
-        override fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID, stopCode: String) {
-            if (segments.any { plateId in it }) {
-                if (vmDepartures != null) {
-                    listener.onDeparturesReady(vmDepartures, plateId)
-                    if (plateId !in receivedPlates)
-                        receivedPlates.add(plateId)
+        override fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID?, stopCode: String) {
+            launch(UI) {
+                if (plateId == null) {
+                    listener.onDeparturesReady(filterDepartures(cache!!.await()), null)
                 } else {
-                    receivedPlates.remove(plateId)
-                    if (receivedPlates.isEmpty())
-                        listener.onDeparturesReady(constructSegmentDepartures(segments), plateId)
+                    if (segments.any { plateId in it }) {
+                        if (vmDepartures != null) {
+                            listener.onDeparturesReady(vmDepartures.toList(), plateId)
+                            if (plateId !in receivedPlates)
+                                receivedPlates.add(plateId)
+                        } else {
+                            receivedPlates.remove(plateId)
+                            if (receivedPlates.isEmpty())
+                                listener.onDeparturesReady(filterDepartures(cache!!.await()), null)
+                        }
+                    }
                 }
             }
         }




diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt
index 35d8e4cf583bf4dc49428fff87ec963d663ce135..6c66c23ee44a8cd848b46d5f53c4130f8624a468 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt
@@ -59,6 +59,7 @@         setSupportActionBar(toolbar)
 
         providerProxy = ProviderProxy(this)
         timetable = Timetable.getTimetable()
+        NetworkStateReceiver.init(this)
 
         getSuggestions()
 
@@ -75,7 +76,7 @@         //drawer.setCheckedItem(R.id.drawer_home)
         drawerView.setNavigationItemSelectedListener { item ->
             when (item.itemId) {
                 R.id.drawer_refresh -> {
-                    startDownloaderService()
+                    startDownloaderService(true)
                 }
                 R.id.drawer_help -> {
                     startActivity(Intent(context, HelpActivity::class.java))
@@ -235,7 +236,7 @@         favouritesList.itemAnimator = DefaultItemAnimator()
         favouritesList.layoutManager = layoutManager
     }
 
-    override fun onDeparturesReady(departures: Set<Departure>, plateId: Plate.ID) {
+    override fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?) {
         favouritesList.adapter.notifyDataSetChanged()
     }
 
@@ -253,8 +254,8 @@         registerReceiver(receiver, filter)
         receiver.addOnTimetableDownloadListener(context as MessageReceiver.OnTimetableDownloadListener)
     }
 
-    private fun startDownloaderService() {
-        if (getDefaultSharedPreferences(this).getBoolean("automatic timetable updates", false))
+    private fun startDownloaderService(force: Boolean = false) {
+        if (getDefaultSharedPreferences(this).getBoolean("automatic timetable updates", false) or force)
             startService(Intent(context, TimetableDownloader::class.java))
     }
 




diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/NoDbActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/NoDbActivity.kt
deleted file mode 100644
index 12157af53cd4f527780038c4c7495da4cb8c1c92..0000000000000000000000000000000000000000
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/NoDbActivity.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-package ml.adamsprogs.bimba.activities
-
-import android.content.Context
-import android.content.Intent
-import android.support.v7.app.AppCompatActivity
-import android.os.Bundle
-import android.content.IntentFilter
-import ml.adamsprogs.bimba.*
-import kotlinx.android.synthetic.main.activity_nodb.*
-import ml.adamsprogs.bimba.datasources.TimetableDownloader
-import ml.adamsprogs.bimba.models.Timetable
-
-class NoDbActivity : AppCompatActivity(), NetworkStateReceiver.OnConnectivityChangeListener, MessageReceiver.OnTimetableDownloadListener {
-    private val networkStateReceiver = NetworkStateReceiver()
-    private val timetableDownloadReceiver = MessageReceiver.getMessageReceiver()
-    private var serviceRunning = false
-    private var askedForNetwork = false
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContentView(R.layout.activity_nodb)
-        val editor = getSharedPreferences("ml.adamsprogs.bimba.prefs", Context.MODE_PRIVATE).edit()
-        editor.putString("etag", "")
-        editor.apply()
-
-        var filter = IntentFilter(TimetableDownloader.ACTION_DOWNLOADED)
-        filter.addCategory(Intent.CATEGORY_DEFAULT)
-        registerReceiver(timetableDownloadReceiver, filter)
-        timetableDownloadReceiver.addOnTimetableDownloadListener(this)
-
-        if (!NetworkStateReceiver.isNetworkAvailable(this)) {
-            askedForNetwork = true
-            no_db_caption.text = getString(R.string.no_db_connect)
-            filter = IntentFilter("android.net.conn.CONNECTIVITY_CHANGE")
-            registerReceiver(networkStateReceiver, filter)
-            networkStateReceiver.addOnConnectivityChangeListener(this)
-        } else
-            downloadTimetable()
-
-        skip_button.setOnClickListener {
-            /*
-            val editor = getSharedPreferences("ml.adamsprogs.bimba.prefs", Context.MODE_PRIVATE).edit()
-            editor.putBoolean(Timetable.ONLY_ONLINE, true)
-            editor.apply()*/
-            startActivity(Intent(this, DashActivity::class.java))
-            finish()
-        }
-    }
-
-    override fun onResume() {
-        super.onResume()
-        try {
-            val timetable = Timetable.getTimetable(this, true)
-            if (!timetable.isEmpty()) {
-                startActivity(Intent(this, DashActivity::class.java))
-                finish()
-            }
-        } catch (e:Exception){}
-        var filter = IntentFilter(TimetableDownloader.ACTION_DOWNLOADED)
-        filter.addCategory(Intent.CATEGORY_DEFAULT)
-        registerReceiver(timetableDownloadReceiver, filter)
-        if (!NetworkStateReceiver.isNetworkAvailable(this)) {
-            askedForNetwork = true
-            no_db_caption.text = getString(R.string.no_db_connect)
-            filter = IntentFilter("android.net.conn.CONNECTIVITY_CHANGE")
-            registerReceiver(networkStateReceiver, filter)
-            networkStateReceiver.addOnConnectivityChangeListener(this)
-        } else if (!serviceRunning)
-            downloadTimetable()
-    }
-
-    private fun downloadTimetable() {
-        no_db_caption.text = getString(R.string.no_db_downloading)
-        serviceRunning = true
-        intent = Intent(this, TimetableDownloader::class.java)
-        intent.putExtra(TimetableDownloader.EXTRA_FORCE, true)
-        startService(intent)
-    }
-
-    override fun onConnectivityChange(connected: Boolean) {
-        if (connected && !serviceRunning)
-            downloadTimetable()
-        /*if (!connected)
-            serviceRunning = false*/
-    }
-
-    override fun onTimetableDownload(result: String?) {
-        when (result) {
-            TimetableDownloader.RESULT_FINISHED -> {
-                timetableDownloadReceiver.removeOnTimetableDownloadListener(this)
-                networkStateReceiver.removeOnConnectivityChangeListener(this)
-                startActivity(Intent(this, DashActivity::class.java))
-                finish()
-            }
-            else -> no_db_caption.text = getString(R.string.error_try_later)
-        }
-    }
-
-    override fun onPause() {
-        super.onPause()
-        unregisterReceiver(timetableDownloadReceiver)
-        if (askedForNetwork)
-            unregisterReceiver(networkStateReceiver)
-    }
-}




diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt
index e9e1c623ac6479d893b707dd64b4fc7bf1314e99..4fb95b7576a4f220f6a667fc0a1ed2dd38c493fa 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt
@@ -4,8 +4,6 @@ import android.content.*
 import android.support.design.widget.*
 import android.os.Bundle
 import android.view.*
-import android.support.v4.app.*
-import android.support.v4.view.PagerAdapter
 import android.support.v4.content.res.ResourcesCompat
 import android.support.v7.app.AppCompatActivity
 import android.support.v7.widget.*
@@ -19,8 +17,6 @@ import ml.adamsprogs.bimba.models.*
 import ml.adamsprogs.bimba.models.adapters.DeparturesAdapter
 
 class StopActivity : AppCompatActivity(), MessageReceiver.OnTimetableDownloadListener, ProviderProxy.OnDeparturesReadyListener {
-
-    private var sectionsPagerAdapter: SectionsPagerAdapter? = null
 
     companion object {
         const val EXTRA_STOP_CODE = "stopCode"
@@ -44,9 +40,10 @@     private var timetableType = "departure"
     private val context = this
     private val receiver = MessageReceiver.getMessageReceiver()
     private lateinit var providerProxy: ProviderProxy
-    private val departures = HashMap<Plate.ID, Set<Departure>>()
-    private val fullDepartures = HashMap<Int, List<Departure>>()
+    private val departures = HashMap<Plate.ID, List<Departure>>()
+    private val fullDepartures = HashMap<String, List<Departure>>()
     private lateinit var subscriptionId: String
+    private lateinit var adapter: DeparturesAdapter
 
 
     private lateinit var sourceType: String
@@ -73,14 +70,19 @@         }
 
         showFab()
 
-        sectionsPagerAdapter = SectionsPagerAdapter(supportFragmentManager, null)
+        val layoutManager = LinearLayoutManager(this)
+        departuresList.addItemDecoration(DividerItemDecoration(departuresList.context, layoutManager.orientation))
+        departuresList.adapter = DeparturesAdapter(this, emptyList(), true)
+        adapter = departuresList.adapter as DeparturesAdapter
+        departuresList.layoutManager = layoutManager
 
-        container.adapter = sectionsPagerAdapter
-
-        container.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabs))
-        tabs.addOnTabSelectedListener(TabLayout.ViewPagerOnTabSelectedListener(container))
-
-        selectTodayPage()
+        departuresList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {}
+            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+                updateFabVisibility(dy)
+                super.onScrolled(recyclerView, dx, dy)
+            }
+        })
 
         prepareOnDownloadListener()
         subscribeForDepartures()
@@ -136,26 +138,28 @@         } else
             favourite!!.subscribeForDepartures(this, context)
     }
 
-    override fun onDeparturesReady(departures: Set<Departure>, plateId: Plate.ID) {
-        this.departures[plateId] = HashSet()
-        (this.departures[plateId]as HashSet).addAll(departures)
+    override fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?) {
+        if (plateId == null) {
+            this.departures.clear()
+            this.departures[Plate.ID.dummy] = departures
+        } else {
+            this.departures.remove(Plate.ID.dummy)
+            this.departures[plateId] = departures
+        }
         if (timetableType == TIMETABLE_TYPE_FULL)
             return
         refreshAdapter()
     }
 
     private fun refreshAdapter() {
-        if (timetableType == TIMETABLE_TYPE_FULL)
-            sectionsPagerAdapter!!.departures = fullDepartures
-        else {
-            val departures = HashMap<Int, List<Departure>>()
+        if (timetableType == TIMETABLE_TYPE_FULL) {
+            // todo adapter.departures = fullDepartures
+        } else {
             val now = Calendar.getInstance()
-            val tab = now.getMode()
             val seconds = now.secondsAfterMidnight()
-            departures[tab] = this.departures.flatMap { it.value }.sortedBy { it.timeTill(seconds) }
-            sectionsPagerAdapter!!.departures = departures
+            adapter.departures = this.departures.flatMap { it.value }.sortedBy { it.timeTill(seconds) }
         }
-        sectionsPagerAdapter!!.notifyDataSetChanged()
+        adapter.notifyDataSetChanged()
     }
 
     override fun onTimetableDownload(result: String?) {
@@ -172,10 +176,6 @@         }
         providerProxy.refreshTimetable(this)
     }
 
-    private fun selectTodayPage() {
-        tabs.getTabAt(sectionsPagerAdapter!!.todayTab())!!.select()
-    }
-
     override fun onCreateOptionsMenu(menu: Menu): Boolean {
         if (providerProxy.mode == ProviderProxy.MODE_FULL)
             menuInflater.inflate(R.menu.menu_stop, menu)
@@ -189,7 +189,7 @@         if (id == R.id.action_change_type) {
             if (timetableType == TIMETABLE_TYPE_DEPARTURE) {
                 timetableType = TIMETABLE_TYPE_FULL
                 item.icon = (ResourcesCompat.getDrawable(resources, R.drawable.ic_timetable_departure, this.theme))
-                sectionsPagerAdapter?.relativeTime = false
+                adapter.relativeTime = false
                 if (fullDepartures.isEmpty())
                     if (sourceType == SOURCE_TYPE_STOP)
                         fullDepartures.putAll(providerProxy.getFullTimetable(stopCode))
@@ -199,7 +199,7 @@                 refreshAdapter()
             } else {
                 timetableType = TIMETABLE_TYPE_DEPARTURE
                 item.icon = (ResourcesCompat.getDrawable(resources, R.drawable.ic_timetable_full, this.theme))
-                sectionsPagerAdapter?.relativeTime = true
+                adapter.relativeTime = true
                 refreshAdapter()
             }
             return true
@@ -216,74 +216,5 @@             providerProxy.unsubscribeFromDepartures(subscriptionId, this)
         else
             favourite!!.unsubscribeFromDepartures(subscriptionId, this)
         unregisterReceiver(receiver)
-    }
-
-    inner class SectionsPagerAdapter(fm: FragmentManager, var departures: Map<Int, List<Departure>>?) : FragmentStatePagerAdapter(fm) {
-        var relativeTime = true
-
-        override fun getItem(position: Int): Fragment {
-            if (departures == null)
-                return PlaceholderFragment.newInstance(null, relativeTime) { updateFabVisibility(it) }
-            if (departures!!.isEmpty())
-                return PlaceholderFragment.newInstance(ArrayList(), relativeTime) { updateFabVisibility(it) }
-            val list: List<Departure> = departures!![position] ?: ArrayList()
-            return PlaceholderFragment.newInstance(list, relativeTime) { updateFabVisibility(it) }
-        }
-
-        override fun getCount() = 3
-
-        override fun getItemPosition(obj: Any): Int {
-            return PagerAdapter.POSITION_NONE
-        }
-
-        fun todayTab(): Int {
-            return Calendar.getInstance().getMode()
-        }
-    }
-
-    class PlaceholderFragment : Fragment() {
-        lateinit var updater: (Int) -> Unit
-        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
-            val rootView = inflater.inflate(R.layout.fragment_stop, container, false)
-
-            val layoutManager = LinearLayoutManager(activity)
-            val departuresList: RecyclerView = rootView.findViewById(R.id.departuresList)
-            val departures = arguments?.getStringArrayList("departures")?.map { Departure.fromString(it) }
-            if (departures != null && departures.isNotEmpty())
-                departuresList.addItemDecoration(DividerItemDecoration(departuresList.context, layoutManager.orientation))
-
-
-            departuresList.adapter = DeparturesAdapter(activity as Context, departures,
-                    arguments?.get("relativeTime") as Boolean)
-            departuresList.layoutManager = layoutManager
-
-            departuresList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
-                override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {}
-                override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
-                    updater(dy)
-                    super.onScrolled(recyclerView, dx, dy)
-                }
-            })
-            return rootView
-        }
-
-        companion object {
-            fun newInstance(departures: List<Departure>?, relativeTime: Boolean, updater: (Int) -> Unit): PlaceholderFragment {
-                val fragment = PlaceholderFragment()
-                fragment.updater = updater
-                val args = Bundle()
-                if (departures != null) {
-                    if (departures.isNotEmpty()) {
-                        val d = ArrayList<String>()
-                        departures.mapTo(d) { it.toString() }
-                        args.putStringArrayList("departures", d)
-                    } else
-                        args.putStringArrayList("departures", ArrayList<String>())
-                }
-                args.putBoolean("relativeTime", relativeTime)
-                fragment.arguments = args
-                return fragment
-            }
-        }
     }
 }




diff --git a/app/src/main/java/ml/adamsprogs/bimba/collections/FavouriteStorage.kt b/app/src/main/java/ml/adamsprogs/bimba/collections/FavouriteStorage.kt
index 0f6675399ffa2dc686306220cb12ecc95d9b0cc2..2c772c3a6ebe559d74358439cc0db557ae01621e 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/collections/FavouriteStorage.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/collections/FavouriteStorage.kt
@@ -114,7 +114,7 @@     fun merge(names: List, context: Context) {
         if (names.size < 2)
             return
 
-        val newCache = HashMap<Int, ArrayList<Departure>>()
+        val newCache = HashMap<String, ArrayList<Departure>>()
         names.forEach {
             favourites[it]!!.fullTimetable().forEach {
                 if (newCache[it.key] == null)




diff --git a/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt b/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt
index 88cb8c58bb4adcab4ec092cfd9ecdd508ab369a3..65495c91d65aefb89125ea4037822a40cdb2369c 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt
@@ -31,7 +31,7 @@
         if (intent != null) {
             notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
             val prefs = this.getSharedPreferences("ml.adamsprogs.bimba.prefs", Context.MODE_PRIVATE)!!
-            if (!NetworkStateReceiver.isNetworkAvailable(this)) {
+            if (!NetworkStateReceiver.isNetworkAvailable()) {
                 sendResult(RESULT_NO_CONNECTIVITY)
                 return
             }




diff --git a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt
index 81820ca45a92a47eb8f3c126748eddcf2721644c..a795febdae7c43b37f01e3c5eea0da38bdef61df 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt
@@ -2,6 +2,7 @@ package ml.adamsprogs.bimba.datasources
 
 import com.google.gson.*
 import kotlinx.coroutines.experimental.*
+import ml.adamsprogs.bimba.NetworkStateReceiver
 import ml.adamsprogs.bimba.models.suggestions.*
 import okhttp3.*
 import java.io.IOException
@@ -72,6 +73,9 @@         return names.map { StopSuggestion(it, "", "") }
     }
 
     suspend fun makeRequest(method: String, data: String): JsonObject {
+        if (!NetworkStateReceiver.isNetworkAvailable())
+            return JsonObject()
+
         val client = OkHttpClient()
         val url = "http://www.peka.poznan.pl/vm/method.vm?ts=${Calendar.getInstance().timeInMillis}"
         val body = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded; charset=UTF-8"),




diff --git a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmService.kt b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmService.kt
index d9829c86ccecc42d4ab362ade5e6e9a36be5511b..06aa77db42b01d4c0e94ee094007eb064c51e9dd 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmService.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmService.kt
@@ -113,7 +113,7 @@         }
     }
 
     private suspend fun downloadVM(stopCode: String) {
-        if (!NetworkStateReceiver.isNetworkAvailable(this)) {
+        if (!NetworkStateReceiver.isNetworkAvailable()) {
             vms[stopCode] = emptySet()
             sendResult(stopCode, null, null)
             return




diff --git a/app/src/main/java/ml/adamsprogs/bimba/extensions.kt b/app/src/main/java/ml/adamsprogs/bimba/extensions.kt
index e398189fe5652b37dd473d40edf9927cf620e31a..a349dc85e76661e7745d7245c11cf93a3d450e75 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/extensions.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/extensions.kt
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
 import android.content.Context
 import android.graphics.drawable.Drawable
 import android.os.Build
+import android.text.format.DateFormat
 import ml.adamsprogs.bimba.activities.StopActivity
 import java.io.*
 import java.text.SimpleDateFormat
@@ -106,3 +107,23 @@         bytes = read(buffer)
     }
     return bytesCopied
 }
+
+internal fun Calendar.toNiceString(context: Context, withTime: Boolean = false): String {
+    val dateFormat = DateFormat.getMediumDateFormat(context)
+    val timeFormat = DateFormat.getTimeFormat(context)
+    val now = Calendar.getInstance()
+    val date = if (get(Calendar.YEAR) == now.get(Calendar.YEAR)) {
+        when {
+            get(Calendar.DAY_OF_YEAR) == now.get(Calendar.DAY_OF_YEAR) -> timeFormat.format(time)
+            now.apply { add(Calendar.DATE, -1) }.get(Calendar.DAY_OF_YEAR) == get(Calendar.DAY_OF_YEAR) -> "Yesterday"
+            else -> DateFormat.format("d MMM" as CharSequence, this.time) as String
+        }
+    } else
+        dateFormat.format(this.time)
+
+    return if (withTime) {
+        val time = timeFormat.format(this.time)
+        "$date, $time"
+    } else
+        date
+}
\ No newline at end of file




diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt
index 35aeba4e97ff3df8a71f5742d7b49f2fb8a2bb8e..9661f529ba9bd5d83a822b47ec43e022c1daa537 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt
@@ -48,33 +48,6 @@
             return rollDepartures(departures)
         }*/
 
-        fun rollDepartures(departures: Map<Int, List<Departure>>): Map<Int, List<Departure>> { //todo<p:2> it'd be nice to roll from tomorrow's real mode (Fri->Sat, Sat->Sun, Sun->Mon)
-            val rolledDepartures = HashMap<Int, List<Departure>>()
-            departures.keys.forEach {
-                val (filtered, isFull) = filterDepartures(departures[it]!!)
-                if (isFull as Boolean) {
-                    @Suppress("UNCHECKED_CAST")
-                    rolledDepartures[it] = filtered as List<Departure>
-                } else {
-                    val (filteredTomorrow, _) = filterDepartures(departures[it]!!, 0)
-                    val departuresTomorrow = ArrayList<Departure>()
-                    @Suppress("UNCHECKED_CAST")
-                    (filteredTomorrow as List<Departure>).forEach {
-                        val departure = it.copy()
-                        departure.tomorrow = true
-                        departuresTomorrow.add(departure)
-                    }
-                    val (result, _) =
-                            @Suppress("UNCHECKED_CAST")
-                            filterDepartures((filtered as List<Departure>) + departuresTomorrow)
-                    val now = Calendar.getInstance().secondsAfterMidnight()
-                    @Suppress("UNCHECKED_CAST")
-                    rolledDepartures[it] = (result as List<Departure>).sortedBy { it.timeTill(now) }
-                }
-            }
-            return rolledDepartures
-        }
-
         fun fromString(string: String): Departure {
             val array = string.split("|")
             if (array.size != 9)




diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt
index 87df063adc773a421f92d0c37b6d34c110c8af90..ebfe9480bedde0b7c07472f0df0e8cb27b2e01e8 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt
@@ -15,8 +15,8 @@     var name: String
         private set
     var segments: HashSet<StopSegment>
         private set
-    private var fullDepartures: Map<Int, List<Departure>> = HashMap()
-    private var cache: Set<Departure> = HashSet()
+    private var fullDepartures: Map<String, List<Departure>> = HashMap()
+    private var cache: List<Departure> = ArrayList()
 
     val size
         get() = segments.sumBy {
@@ -39,17 +39,17 @@         val mapDir = File(parcel.readString())
 
         val mapString = mapDir.readText()
 
-        val map = HashMap<Int, List<Departure>>()
+        val map = HashMap<String, List<Departure>>()
         mapString.safeSplit("%")!!.forEach { it ->
             val (k, v) = it.split("#")
-            map[k.toInt()] = v.split("&").map { Departure.fromString(it) }
+            map[k] = v.split("&").map { Departure.fromString(it) }
         }
         this.fullDepartures = map
         mapDir.delete()
         providerProxy = ProviderProxy()
     }
 
-    constructor(name: String, segments: HashSet<StopSegment>, cache: Map<Int, List<Departure>>, context: Context) {
+    constructor(name: String, segments: HashSet<StopSegment>, cache: Map<String, List<Departure>>, context: Context) {
         this.fullDepartures = cache
         this.name = name
         this.segments = segments
@@ -121,14 +121,14 @@             else
                 cache.sortedBy { it.time }[0]
 
 
-    fun fullTimetable(): Map<Int, List<Departure>> {
+    fun fullTimetable(): Map<String, List<Departure>> {
         if (fullDepartures.isEmpty())
             fullDepartures = providerProxy.getFullTimetable(segments)
         return fullDepartures
     }
 
     private fun removeFromCache(plate: Plate.ID) {
-        val map = HashMap<Int, List<Departure>>()
+        val map = HashMap<String, List<Departure>>()
         fullDepartures
         fullDepartures.forEach { it ->
             map[it.key] = it.value.filter { plate.line != it.line || plate.headsign != it.headsign }
@@ -141,7 +141,7 @@         this.listener = listener
         return providerProxy.subscribeForDepartures(segments, this, context)
     }
 
-    override fun onDeparturesReady(departures: Set<Departure>, plateId: Plate.ID) {
+    override fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?) {
         cache = departures
         listener.onDeparturesReady(departures, plateId)
     }




diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Plate.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Plate.kt
index 8fb050a63053bf54097da8d1048f746fe74bae98..d4f7d8cc0dd3dc9e147c63f4182cbc1e009c64e7 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/Plate.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/Plate.kt
@@ -54,6 +54,8 @@     }
 
     data class ID(val line: String, val stop: String, val headsign: String) : Serializable {
         companion object {
+            val dummy = Plate.ID("", "", "")
+
             fun fromString(string: String): ID {
                 val (line, stop, headsign) = string.split("|")
                 return ID(line,




diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt
index f00bd5680b147a5006f477d30922a8ba5b6df40a..f6b9dcfe6915fa4e06b2980cf7d5a341d0e0a96a 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt
@@ -3,9 +3,10 @@
 import android.annotation.SuppressLint
 import android.content.Context
 import android.database.*
-import android.database.sqlite.SQLiteCantOpenDatabaseException
 import android.database.sqlite.SQLiteDatabase
 import android.database.sqlite.SQLiteException
+import android.util.SparseArray
+import android.util.SparseBooleanArray
 import ml.adamsprogs.bimba.*
 import ml.adamsprogs.bimba.models.gtfs.*
 import ml.adamsprogs.bimba.models.suggestions.*
@@ -41,7 +42,7 @@             val filesDir = context.getSecondaryExternalFilesDir()
             val dbFile = File(filesDir, "timetable.db")
             timetable.db = try {
                 SQLiteDatabase.openDatabase(dbFile.path, null, SQLiteDatabase.OPEN_READONLY)
-            } catch(e: SQLiteException) {
+            } catch (e: SQLiteException) {
                 null
             }
             this.timetable = timetable
@@ -101,23 +102,27 @@
     fun getHeadlinesForStop(stop: String): Map<String, Set<String>> {
         val headsigns = HashMap<String, HashSet<String>>()
 
-        var cursor = db!!.rawQuery("select stop_code from stops where stop_name = ?",
+        var cursor = db!!.rawQuery("select stop_id, stop_code from stops where stop_name = ?",
                 arrayOf(stop))
-        val stopCodes = ArrayList<String>()
+        val stopIds = ArrayList<String>()
+        val stopCodes = SparseArray<String>()
         while (cursor.moveToNext()) {
-            stopCodes.add(cursor.getString(0))
+            cursor.getInt(0).let {
+                stopIds.add(it.toString())
+                stopCodes.put(it, cursor.getString(1))
+            }
         }
 
         cursor.close()
 
-        val where = stopCodes.joinToString(" or ", "where ") { "stop_code = ?" }
+        val where = stopIds.joinToString(" or ", "where ") { "stop_id = ?" }
 
-        cursor = db!!.rawQuery("select stop_code, route_id, trip_headsign " +
+        cursor = db!!.rawQuery("select stop_id, route_id, trip_headsign " +
                 "from stop_times natural join trips " +
-                where, stopCodes.toTypedArray())
+                where, stopIds.toTypedArray())
 
         while (cursor.moveToNext()) {
-            val stopCode = cursor.getString(0)
+            val stopCode = stopCodes[cursor.getInt(0)]
             val route = cursor.getString(1)
             val headsign = cursor.getString(2)
             if (stopCode !in headsigns)
@@ -193,14 +198,21 @@
         return map
     }
 
-    fun getStopDeparturesBySegments(segments: Set<StopSegment>): Map<String, List<Departure>> {
+    fun getStopDeparturesBySegments(segments: Set<StopSegment>): Map<String, List<Departure>> { // todo optimisation!!!
+        val stopCodes = HashMap<String, Int>()
+        var cursor = db!!.rawQuery("select stop_id, stop_code from stops", emptyArray())
+        while (cursor.moveToNext()) {
+            stopCodes[cursor.getString(1)] = cursor.getInt(0)
+        }
+        cursor.close()
+
         val wheres = segments.flatMap {
-            it.plates?.map {
-                "(stop_code = ${it.stop} and route_id = '${it.line}' and trip_headsign = '${it.headsign}')"
-            } ?: listOf()
+            it.plates?.map { plate ->
+                "(stop_id = ${stopCodes[plate.stop]} and route_id = '${plate.line}' and trip_headsign = '${plate.headsign}')"
+            } ?: listOf("stop_id = ${stopCodes[it.stop]}")
         }.joinToString(" or ")
 
-        val cursor = db!!.rawQuery("select route_id, service_id, departure_time, " +
+        cursor = db!!.rawQuery("select route_id, service_id, departure_time, " +
                 "wheelchair_accessible, stop_sequence, trip_id, trip_headsign, route_desc " +
                 "from stop_times natural join trips natural join routes where $wheres", null)
 
@@ -346,37 +358,38 @@         cursor.close()
         return validTill
     }
 
-    fun getServiceForToday(): String {
-        val today = JCalendar.getInstance().get(JCalendar.DAY_OF_WEEK)
+    fun getServiceForToday(): String? {
+        val today = JCalendar.getInstance()
         return getServiceFor(today)
     }
 
-    fun getServiceForTomorrow(): String {
+    fun getServiceForTomorrow(): String? {
         val tomorrow = JCalendar.getInstance()
         tomorrow.add(JCalendar.DAY_OF_MONTH, 1)
-        val tomorrowDoW = tomorrow.get(JCalendar.DAY_OF_WEEK)
-        return getServiceFor(tomorrowDoW)
+        return getServiceFor(tomorrow)
     }
 
-    fun getServiceFor(day: Int): String {
-        val dayColumn = arrayOf("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday")[((day + 5) % 7)]
-        val cursor = db!!.rawQuery("select service_id from calendar where $dayColumn = 1", null)
+    private fun getServiceFor(day: JCalendar): String? {
+        val dayColumn = arrayOf("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday")[((day.get(JCalendar.DAY_OF_WEEK) + 5) % 7)]
+        val cursor = db!!.rawQuery("select service_id from calendar where $dayColumn = 1 and start_date < ? and ? < end_date", arrayOf(day.toIsoDate(), day.toIsoDate()))
 
-        val service: Int
-        cursor.moveToNext()
-        try {
-            service = cursor.getInt(0)
-            cursor.close()
-            return service.toString()
+        cursor.moveToFirst()
+        return try {
+            cursor.getInt(0).let {
+                cursor.close()
+                it.toString()
+            }
         } catch (e: CursorIndexOutOfBoundsException) {
-            throw IllegalArgumentException()
+            cursor.close()
+            null
         }
     }
 
-    fun getPlatesForStop(stop: String): Set<Plate.ID> {
+    private fun getPlatesForStop(stop: String): Set<Plate.ID> {
+
         val plates = HashSet<Plate.ID>()
         val cursor = db!!.rawQuery("select route_id, trip_headsign " +
-                "from stop_times natural join trips where stop_code = ? " +
+                "from stop_times natural join trips natural join stops where stop_code = ? " +
                 "group by route_id, trip_headsign", arrayOf(stop))
 
         while (cursor.moveToNext()) {
@@ -440,6 +453,45 @@             }
         }
 
         return graphs
+    }
+
+    fun getServiceDescription(service: String, context: Context): String {
+        val dayNames = SparseArray<String>()
+        dayNames.put(1, context.getString(R.string.Mon))
+        dayNames.put(1, context.getString(R.string.Tue))
+        dayNames.put(1, context.getString(R.string.Wed))
+        dayNames.put(1, context.getString(R.string.Thu))
+        dayNames.put(1, context.getString(R.string.Fri))
+        dayNames.put(1, context.getString(R.string.Sat))
+        dayNames.put(1, context.getString(R.string.Sun))
+
+        val cursor = db!!.rawQuery("select * from calendar where service_id = ?", arrayOf(service))
+        val days = SparseBooleanArray()
+        for (i in 1..7) {
+            days.append(i, cursor.getString(i) == "1")
+        }
+        val description = ArrayList<String>()
+        var start = 0
+
+        for (i in 1..7) {
+            if (!days[i] and (start > 0)) {
+                when {
+                    i - start == 1 -> description.add(dayNames[start])
+                    i - start == 2 -> description.add("${dayNames[start]}, ${dayNames[start + 1]}")
+                    i - start > 2 -> description.add("${dayNames[start]}–${dayNames[i - 1]}")
+                }
+                start = 0
+            }
+            if (days[i] and (start == 0))
+                start = i
+        }
+
+        val startDate = calendarFromIsoD(cursor.getString(8)).toNiceString(context)
+        val endDate = calendarFromIsoD(cursor.getString(9)).toNiceString(context)
+
+        cursor.close()
+
+        return "${description.joinToString { it }} ($startDate–$endDate)"
     }
 
     class TripGraph {




diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/adapters/DeparturesAdapter.kt b/app/src/main/java/ml/adamsprogs/bimba/models/adapters/DeparturesAdapter.kt
index 13a2f5765c5ec92fd13aa9ec8f05681c5f3ce0ed..c8915edf9e1b3e8b8444f3661e989ce6f3dc2810 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/adapters/DeparturesAdapter.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/adapters/DeparturesAdapter.kt
@@ -16,7 +16,7 @@ import ml.adamsprogs.bimba.models.Departure
 import ml.adamsprogs.bimba.rollTime
 import java.util.*
 
-class DeparturesAdapter(val context: Context, private val departures: List<Departure>?, private val relativeTime: Boolean) :
+class DeparturesAdapter(val context: Context, var departures: List<Departure>?, var relativeTime: Boolean) :
         RecyclerView.Adapter<DeparturesAdapter.ViewHolder>() {
 
     companion object {
@@ -26,31 +26,32 @@         const val VIEW_TYPE_EMPTY: Int = 2
     }
 
     override fun getItemCount(): Int {
-        if (departures == null || departures.isEmpty())
+        if (departures == null || departures!!.isEmpty())
             return 1
-        return departures.size
+        return departures!!.size
     }
 
     override fun getItemViewType(position: Int): Int {
         return when {
-            departures == null -> VIEW_TYPE_EMPTY
-            departures.isEmpty() -> VIEW_TYPE_LOADING
+            departures == null -> VIEW_TYPE_LOADING //empty
+            departures!!.isEmpty() -> VIEW_TYPE_LOADING
             else -> VIEW_TYPE_CONTENT
         }
     }
 
     override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        // todo migotanie ikon
         if (departures == null) {
             return
         }
         val line = holder.lineTextView
         val time = holder.timeTextView
         val direction = holder.directionTextView
-        if (departures.isEmpty()) {
+        if (departures!!.isEmpty()) {
             time.text = context.getString(R.string.no_departures)
             return
         }
-        val departure = departures[position]
+        val departure = departures!![position]
         val now = Calendar.getInstance()
         val departureTime = Calendar.getInstance().rollTime(departure.time)
         if (departure.tomorrow)




diff --git a/app/src/main/res/layout/activity_stop.xml b/app/src/main/res/layout/activity_stop.xml
index cad4b10f71491458bd9cc388e9db098c409a41da..0aefb1bbdb0b77ef1893e948cfa161bb4e1bfb58 100644
--- a/app/src/main/res/layout/activity_stop.xml
+++ b/app/src/main/res/layout/activity_stop.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.constraint.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:id="@+id/stop_layout"
@@ -7,6 +7,7 @@     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:fitsSystemWindows="true"
     tools:context="ml.adamsprogs.bimba.activities.StopActivity">
+
 
     <android.support.design.widget.AppBarLayout
         android:id="@+id/appbar"
@@ -27,36 +28,17 @@             app:title="@string/app_name">
 
         </android.support.v7.widget.Toolbar>
 
-        <android.support.design.widget.TabLayout
-            android:id="@+id/tabs"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
 
-            <android.support.design.widget.TabItem
-                android:id="@+id/tabItem"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/tab_workday_text" />
-
-            <android.support.design.widget.TabItem
-                android:id="@+id/tabItem4"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/tab_saturday_text" />
-
-            <android.support.design.widget.TabItem
-                android:id="@+id/tabItem5"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/tab_sunday_text" />
-        </android.support.design.widget.TabLayout>
     </android.support.design.widget.AppBarLayout>
 
-    <android.support.v4.view.ViewPager
-        android:id="@+id/container"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/departuresList"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/appbar" />
 
     <android.support.design.widget.FloatingActionButton
         android:id="@+id/fab"
@@ -64,6 +46,9 @@         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="end|bottom"
         android:layout_margin="@dimen/fab_margin"
+        android:layout_marginBottom="16dp"
+        android:layout_marginEnd="16dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
         app:srcCompat="@drawable/ic_favourite" />
-
-</android.support.design.widget.CoordinatorLayout>
+</android.support.constraint.ConstraintLayout>




diff --git a/app/src/main/res/layout/fragment_stop.xml b/app/src/main/res/layout/fragment_stop.xml
deleted file mode 100644
index 4cadd826bbf25470cfb9f7417b425acadc0c9722..0000000000000000000000000000000000000000
--- a/app/src/main/res/layout/fragment_stop.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<android.support.constraint.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:id="@+id/constraintLayout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    tools:context="ml.adamsprogs.bimba.activities.StopActivity$PlaceholderFragment">
-
-    <!-- todo landscape version -->
-    <android.support.v7.widget.RecyclerView
-        android:id="@+id/departuresList"
-        android:layout_width="368dp"
-        android:layout_height="516dp"
-        android:layout_marginBottom="8dp"
-        android:layout_marginStart="8dp"
-        android:layout_marginTop="8dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintVertical_bias="0.485"
-        app:layout_constraintEnd_toEndOf="parent"
-        android:layout_marginEnd="8dp" />
-
-</android.support.constraint.ConstraintLayout>
\ No newline at end of file




diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 59bae428d441c18d3f8b73074d7e738ee2d1c448..fc6cb018421f5b01bd650be84078c6b1522df076 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -90,4 +90,12 @@     key_timetable_source_url
     <string name="title_timetable_source_url">Timetable source</string>
     <string name="title_activity_settings">Settings</string>
     <string name="settings">Settings</string>
+
+    <string name="Mon">Mon</string>
+    <string name="Tue">Tue</string>
+    <string name="Wed">Wed</string>
+    <string name="Thu">Thu</string>
+    <string name="Fri">Fri</string>
+    <string name="Sat">Sat</string>
+    <string name="Sun">Sun</string>
 </resources>




diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 74a1403bc6cf2643facca4506c5f1b23cd015a27..bc754c49306b35a193dcb649178a347de04d8599 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -73,4 +73,11 @@     Quelle des Fahrplans
     <string name="title_activity_settings">Einstellungen</string>
     <string name="settings">Einstellungen</string>
     <string name="timetable_decompressing">Fahrplan wird entpackt</string>
+    <string name="Mon">Mo.</string>
+    <string name="Tue">Di.</string>
+    <string name="Wed">Mi.</string>
+    <string name="Thu">Do.</string>
+    <string name="Fri">Fr.</string>
+    <string name="Sat">Sa.</string>
+    <string name="Sun">So.</string>
 </resources>




diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index e18d6d65bf573b10800637ce426adbf393ea9bda..2f555258b4a51b199fde26df685ba07788e5bc20 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -71,4 +71,11 @@     Fonte dell’Orario
     <string name="title_activity_settings">Impostazioni</string>
     <string name="settings">Impostazioni</string>
     <string name="timetable_decompressing">Decomprimendo l’orario</string>
+    <string name="Mon">lun</string>
+    <string name="Tue">mar</string>
+    <string name="Wed">mer</string>
+    <string name="Thu">gio</string>
+    <string name="Fri">ven</string>
+    <string name="Sat">sab</string>
+    <string name="Sun">dom</string>
 </resources>




diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 3693fadac99d212054a413ec878bf37f093d5c77..82150b353f9a2b6c74446ff0d4415c6b8b4a12db 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -74,4 +74,11 @@     Bron van de dienstregeling
     <string name="title_activity_settings">Instellingen</string>
     <string name="settings">Instellingen</string>
     <string name="timetable_decompressing">Bezig met uitpakken van dienstregeling</string>
+    <string name="Mon">ma</string>
+    <string name="Tue">di</string>
+    <string name="Wed">wo</string>
+    <string name="Thu">do</string>
+    <string name="Fri">vr</string>
+    <string name="Sat">za</string>
+    <string name="Sun">zo</string>
 </resources>




diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 15370f10365138c4426f4c71c7ab5af1bc7370b0..ce9634e0554f906aa47db97f48b47415abfe3742 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -73,4 +73,11 @@     Źródło rozkładu
     <string name="title_activity_settings">Ustawienia</string>
     <string name="settings">Ustawienia</string>
     <string name="timetable_decompressing">Rozpakowywanie rozkładu</string>
+    <string name="Mon">pon.</string>
+    <string name="Tue">wt.</string>
+    <string name="Wed">śr.</string>
+    <string name="Thu">czw.</string>
+    <string name="Fri">pt.</string>
+    <string name="Sat">sob.</string>
+    <string name="Sun">niedz.</string>
 </resources>
\ No newline at end of file