Bimba.git

commit 2bd8666c6f8ab179d0458716afd67e837629c97e

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

dynamic tabs rollback; static timetable works (slow)

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


diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c767be9fdc8a0fda0f07435c066d24374c4aa82f..039e73a8a82d72abbf1d496d8b84d9f149739718 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -18,10 +18,6 @@             android:name=".datasources.TimetableDownloader"
             android:exported="false" />
 
         <activity
-            android:name=".activities.StopActivity"
-            android:label="@string/title_activity_stop"
-            android:theme="@style/AppTheme" />
-        <activity
             android:name=".activities.SplashActivity"
             android:theme="@style/SplashTheme">
             <intent-filter>
@@ -42,7 +38,16 @@             android:name=".datasources.VmClient"
             android:enabled="true"
             android:exported="false" />
 
-        <activity android:name=".activities.StopSpecifyActivity"></activity>
+        <activity android:name=".activities.StopSpecifyActivity" />
+        <activity
+            android:name=".activities.StopActivity"
+            android:label="@string/title_activity_stop"
+            android:parentActivityName=".activities.StopSpecifyActivity"
+            android:theme="@style/AppTheme">
+            <meta-data
+                android:name="android.support.PARENT_ACTIVITY"
+                android:value="ml.adamsprogs.bimba.activities.StopSpecifyActivity" />
+        </activity>
     </application>
 
 </manifest>
\ No newline at end of file




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 6f9d1b16cef4ef12376f9ab104dfce3163653df4..95d223cddcf4b3c1ab52227267f8e07c214d86b2 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt
@@ -122,9 +122,9 @@         searchView.setOnBindSuggestionCallback { _, _, textView, item, _ ->
             val suggestion = item as StopSuggestion
             val text = suggestion.body.split("\n")
             val colour = when (text[1]) {
-                "A" -> context.getString(R.string.zone_a_colour)
-                "B" -> context.getString(R.string.zone_b_colour)
-                "C" -> context.getString(R.string.zone_c_colour)
+                "A" -> "#${getColour(R.color.zoneA, context).toString(16)}"
+                "B" -> "#${getColour(R.color.zoneB, context).toString(16)}"
+                "C" -> "#${getColour(R.color.zoneC, context).toString(16)}"
                 else -> "#000000"
             }
             val t = "<small><font color=\"$colour\">" + text[1] + "</font></small>"




diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt
index 9731a065e2b2754d6f0d52cdb01b74f74bbd9e90..04da7ada55bdb13f87789f6fec5dccf0a11e118f 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt
@@ -19,7 +19,6 @@                 startActivity(Intent(this, NoDbActivity::class.java))
             else
                 startActivity(Intent(this, DashActivity::class.java))
         } catch(e: Exception) {
-            e.printStackTrace()
             startActivity(Intent(this, NoDbActivity::class.java))
         }
         finish()




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 98728eaddb91d85a24cd3cdeb43b383b15a23182..f8c95ab545ed1b4cad1842a348875cb4ef3b55e8 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt
@@ -1,28 +1,42 @@
 package ml.adamsprogs.bimba.activities
 
-import java.util.*
-import kotlin.collections.*
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.database.DataSetObserver
+import android.support.design.widget.TabLayout
+import android.support.design.widget.Snackbar
+import android.support.v7.app.AppCompatActivity
 
-import android.content.*
+import android.support.v4.app.Fragment
+import android.support.v4.app.FragmentManager
 import android.os.Bundle
-import android.view.*
-import android.support.design.widget.*
-import android.support.v7.app.AppCompatActivity
-import android.support.v7.widget.*
-import android.support.v4.app.*
-import android.support.v4.view.*
+import android.support.v4.app.FragmentStatePagerAdapter
 import android.support.v4.content.res.ResourcesCompat
+import android.support.v4.view.PagerAdapter
+import android.support.v7.widget.DividerItemDecoration
+import android.support.v7.widget.LinearLayoutManager
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
 
-import ml.adamsprogs.bimba.models.*
-import ml.adamsprogs.bimba.*
-import kotlin.concurrent.thread
 import kotlinx.android.synthetic.main.activity_stop.*
+import ml.adamsprogs.bimba.MessageReceiver
+import ml.adamsprogs.bimba.R
 import ml.adamsprogs.bimba.datasources.TimetableDownloader
 import ml.adamsprogs.bimba.datasources.VmClient
 import ml.adamsprogs.bimba.gtfs.AgencyAndId
-import android.support.v4.view.ViewPager
+import ml.adamsprogs.bimba.models.*
+import java.util.*
+import kotlin.concurrent.thread
 
 class StopActivity : AppCompatActivity(), MessageReceiver.OnTimetableDownloadListener, MessageReceiver.OnVmListener, Favourite.OnVmPreparedListener {
+
+    private var sectionsPagerAdapter: SectionsPagerAdapter? = null
+
     companion object {
         const val EXTRA_STOP_ID = "stopId"
         const val EXTRA_FAVOURITE = "favourite"
@@ -31,21 +45,16 @@         const val SOURCE_TYPE_STOP = "stop"
         const val SOURCE_TYPE_FAV = "favourite"
     }
 
-
     private var stopSegment: StopSegment? = null
     private var favourite: Favourite? = null
     private var timetableType = "departure"
-    private var sectionsPagerAdapter: SectionsPagerAdapter? = null
-    private var viewPager: ViewPager? = null
     private lateinit var timetable: Timetable
-    private lateinit var tabLayout: TabLayout
     private val context = this
     private val receiver = MessageReceiver.getMessageReceiver()
     private val vmDepartures = HashMap<Plate.ID, Set<Departure>>()
     private var hasDepartures = false
 
     private lateinit var sourceType: String
-
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_stop)
@@ -54,7 +63,6 @@         timetable = Timetable.getTimetable()
 
         sourceType = intent.getStringExtra(SOURCE_TYPE)
 
-        val toolbar = findViewById<Toolbar>(R.id.toolbar)
         setSupportActionBar(toolbar)
 
         when (sourceType) {
@@ -70,30 +78,16 @@                 favourite!!.addOnVmPreparedListener(this)
             }
         }
 
-        prepareOnDownloadListener()
-
-        viewPager = container
-        tabLayout = tabs
         sectionsPagerAdapter = SectionsPagerAdapter(supportFragmentManager, HashMap<AgencyAndId, ArrayList<Departure>>())
 
-        /*thread {
-            if (sourceType == SOURCE_TYPE_STOP) {
-                sectionsPagerAdapter!!.departures = Departure.createDepartures(stopSegment!!.stop)
-            } else {
-                sectionsPagerAdapter!!.departures = favourite!!.allDepartures()
-            }
-            runOnUiThread {
-                sectionsPagerAdapter?.notifyDataSetChanged()
-            }
-        }*/
-
-        viewPager!!.adapter = sectionsPagerAdapter
-        viewPager!!.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout))
-        tabLayout.addOnTabSelectedListener(TabLayout.ViewPagerOnTabSelectedListener(viewPager))
+        container.adapter = sectionsPagerAdapter
 
-        selectTodayPage()
+        container.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabs))
+        tabs.addOnTabSelectedListener(TabLayout.ViewPagerOnTabSelectedListener(container))
 
         showFab()
+
+        prepareOnDownloadListener()
     }
 
     private fun getFavouriteDepartures() {
@@ -103,31 +97,12 @@         }
     }
 
     private fun refreshAdapter(departures: Map<AgencyAndId, List<Departure>>) {
-        tabLayout.removeAllTabs()
-        sectionsPagerAdapter?.notifyDataSetChanged()
-        departures.keys.sortedBy {
-            timetable.calendarToMode(AgencyAndId(it.id)).sorted()[0]
-        }.forEach {
-                    val tab = tabLayout.newTab()
-                    tab.text = timetable.calendarToMode(it)
-                            .joinToString { resources.getStringArray(R.array.daysOfWeekShort)[it] }
-                    tabLayout.addTab(tab)
-                    sectionsPagerAdapter?.notifyDataSetChanged()
-                }
-        println("refreshing:")
-        departures[AgencyAndId("4")]?.forEach {
-            println("${it.lineText} -> ${it.direction} @ ${it.time}")
-        }
+
         sectionsPagerAdapter?.departures = departures
-        try {
+        runOnUiThread {
             sectionsPagerAdapter?.notifyDataSetChanged()
-        } catch (e: Exception) {
-            runOnUiThread {
-                sectionsPagerAdapter?.notifyDataSetChanged()
-            }
+            selectTodayPage()
         }
-
-        selectTodayPage()
     }
 
     override fun onVmPrepared() {
@@ -188,7 +163,7 @@             else
                 this.vmDepartures.remove(plateId)
             val departures = HashMap<AgencyAndId, List<Departure>>()
             if (this.vmDepartures.isNotEmpty()) {
-                departures[timetable.getServiceForToday()] = this.vmDepartures.flatMap { it.value }.sortedBy { it.timeTill() }
+                departures[timetable.getServiceForToday()] = this.vmDepartures.flatMap { it.value }.sortedBy { it.timeTill(true) }
                 refreshAdapter(departures)
             } else {
                 refreshAdapter(Departure.createDepartures(stopSegment!!.stop))
@@ -212,9 +187,9 @@         }
         //todo refresh
     }
 
-    private fun selectTodayPage() { //fixme does not work
+    private fun selectTodayPage() {
         val today = (Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1) % 7
-        tabLayout.getTabAt(sectionsPagerAdapter!!.todayTab(today))
+        tabs.getTabAt(sectionsPagerAdapter!!.todayTab(today))
     }
 
     override fun onCreateOptionsMenu(menu: Menu): Boolean {
@@ -263,8 +238,43 @@             favourite!!.deregisterOnVm(receiver, context)
         unregisterReceiver(receiver)
     }
 
-    class PlaceholderFragment : Fragment() {
+    inner class SectionsPagerAdapter(fm: FragmentManager, var departures: Map<AgencyAndId, List<Departure>>) : FragmentStatePagerAdapter(fm) {
+        var relativeTime = true
+        private var modes = ArrayList<AgencyAndId>()
 
+        init {
+            this.registerDataSetObserver(object : DataSetObserver() {
+                override fun onChanged() {
+                    departures.keys.sortedBy { timetable.calendarToMode(it)[0] }.mapTo(modes) { it }
+                }
+            })
+        }
+
+        override fun getItem(position: Int): Fragment {
+            departures.keys.sortedBy { timetable.calendarToMode(it)[0] }.mapTo(modes) { it }
+            val list = if (departures.isEmpty())
+                ArrayList()
+            else
+                departures[modes[position]]!!
+            return PlaceholderFragment.newInstance(list, relativeTime)
+        }
+
+        override fun getCount() = 5
+
+        override fun getItemPosition(obj: Any): Int {
+            return PagerAdapter.POSITION_NONE
+        }
+
+        fun todayTab(today: Int): Int {
+            if (modes.isEmpty())
+                return 0
+            return modes.indexOf(modes.filter {
+                timetable.calendarToMode(it).contains(today)
+            }[0])
+        }
+    }
+
+    class PlaceholderFragment : Fragment() {
         override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
             val rootView = inflater.inflate(R.layout.fragment_stop, container, false)
 
@@ -273,6 +283,8 @@             val departuresList: RecyclerView = rootView.findViewById(R.id.departuresList)
             departuresList.addItemDecoration(DividerItemDecoration(departuresList.context, layoutManager.orientation))
 
             val departures = arguments?.getStringArrayList("departures")!!.map { Departure.fromString(it) }
+
+
             departuresList.adapter = DeparturesAdapter(activity as Context, departures,
                     arguments?.get("relativeTime") as Boolean)
             departuresList.layoutManager = layoutManager
@@ -280,18 +292,10 @@             return rootView
         }
 
         companion object {
-            private const val ARG_SECTION_NUMBER = "section_number"
-
-            fun newInstance(sectionNumber: Int, departures: List<Departure>, relativeTime: Boolean):
-                    PlaceholderFragment {
+            fun newInstance(departures: List<Departure>, relativeTime: Boolean): PlaceholderFragment {
                 val fragment = PlaceholderFragment()
                 val args = Bundle()
-                args.putInt(ARG_SECTION_NUMBER, sectionNumber)
-                println("newInstance:")
-                departures.forEach {
-                    println("${it.lineText} -> ${it.direction} @ ${it.time}")
-                }
-                if (departures.isEmpty()) {
+                if (departures.isNotEmpty()) {
                     val d = ArrayList<String>()
                     departures.mapTo(d) { it.toString() }
                     args.putStringArrayList("departures", d)
@@ -302,59 +306,5 @@                 fragment.arguments = args
                 return fragment
             }
         }
-    }
-
-    inner class SectionsPagerAdapter(fm: FragmentManager, departures: Map<AgencyAndId, List<Departure>>) : FragmentStatePagerAdapter(fm) { //todo swipe
-
-        var departures: Map<AgencyAndId, List<Departure>> = departures
-            set(value) {
-                println("setting:")
-                value[AgencyAndId("4")]?.forEach {
-                    println("${it.lineText} -> ${it.direction} @ ${it.time}")
-                }
-                field = value
-                println("set:")
-                this.departures[AgencyAndId("4")]?.forEach {
-                    println("${it.lineText} -> ${it.direction} @ ${it.time}")
-                }
-            }
-
-        private var modes = ArrayList<AgencyAndId>()
-
-        init {
-            val tab = tabLayout.newTab()
-            tab.text = getString(R.string.today)
-            tabLayout.addTab(tab)
-            sectionsPagerAdapter?.notifyDataSetChanged()
-        }
-
-        var relativeTime = true
-
-        fun todayTab(today: Int): Int {
-            if (modes.isEmpty())
-                return 0
-            return modes.indexOf(modes.filter {
-                timetable.calendarToMode(it).contains(today)
-            }[0])
-        }
-
-        override fun getItemPosition(obj: Any): Int {
-            return PagerAdapter.POSITION_NONE
-        }
-
-        override fun getItem(position: Int): Fragment {
-            //fixme doesn't refresh after getting departures/switching. Thinks `departures` is empty. May be connected with swipe
-            println("adapter:")
-            departures[AgencyAndId("4")]?.forEach {
-                println("${it.lineText} -> ${it.direction} @ ${it.time}")
-            }
-            val list = if (departures.isEmpty())
-                ArrayList()
-            else
-                departures[modes[position]]!!
-            return PlaceholderFragment.newInstance(position + 1, list, relativeTime)
-        }
-
-        override fun getCount() = if (departures.isEmpty()) 1 else modes.size
     }
 }




diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/StopSpecifyActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/StopSpecifyActivity.kt
index ae47d0daee70f338262a9699d121ec57fe1411a0..2e72955d6dc31befe602c2bd85c587c98c4281e4 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/StopSpecifyActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/activities/StopSpecifyActivity.kt
@@ -11,7 +11,6 @@ import ml.adamsprogs.bimba.gtfs.AgencyAndId
 import ml.adamsprogs.bimba.models.Timetable
 import android.content.Context
 import android.widget.TextView
-import android.support.v7.widget.DividerItemDecoration
 import android.support.v7.widget.LinearLayoutManager
 import android.support.v7.widget.RecyclerView
 import android.view.LayoutInflater




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 3bdb006976f4f1b60cf5e7c8797ca97445ed6526..0bd7aabfb4cc0d0e29efa6888c121c73b577f397 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt
@@ -219,7 +219,7 @@
         val departuresForPlate = HashMap<AgencyAndId, HashSet<Departure>>()
         departuresForPlate[timetable.getServiceForToday()] = departures
         val vm = vms[plateId.stop] ?: HashSet()
-        vm.remove(vm.filter { it.id == plateId }[0])
+        vm.remove(vm.filter { it.id == plateId }[0]) //fixme outOfBound when vm is still empty (1st time)
         vm.add(Plate(plateId, departuresForPlate))
         vms[plateId.stop] = vm
         if (departures.isEmpty())




diff --git a/app/src/main/java/ml/adamsprogs/bimba/extensions.kt b/app/src/main/java/ml/adamsprogs/bimba/extensions.kt
index 678772f07cfc39e9a715cc272674929ecf5cff0e..7a3713d35fc96dc9be71a438ca4ad26db214e2d4 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/extensions.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/extensions.kt
@@ -1,5 +1,7 @@
 package ml.adamsprogs.bimba
 
+import android.content.Context
+import android.os.Build
 import java.text.SimpleDateFormat
 import java.util.*
 
@@ -43,4 +45,12 @@     val date = dateFormat.parse(iso)
     //date.hours = date.getHours() - 1 //fixme why?
     calendar.time = date
     return calendar
+}
+
+fun getColour(id: Int, context: Context): Int {
+    @Suppress("DEPRECATION")
+    (return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+        context.resources.getColor(R.color.colorAccent, null)
+    else
+        context.resources.getColor(R.color.colorAccent))
 }
\ 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 ba7a0ef79c1445d5c974c185c741ecec40f2c620..a7a5816b52eda2272d833886f5685011bcb8e908 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt
@@ -2,6 +2,7 @@ package ml.adamsprogs.bimba.models
 
 import ml.adamsprogs.bimba.rollTime
 import ml.adamsprogs.bimba.gtfs.AgencyAndId
+import java.io.Serializable
 import java.util.*
 import kotlin.collections.ArrayList
 import kotlin.collections.HashMap
@@ -24,51 +25,51 @@         return Departure.fromString(this.toString())
     }
 
     companion object {
-        private fun filterDepartures(departures: List<Departure>): ArrayList<Departure> {
+        private fun filterDepartures(departures: List<Departure>, relative: Boolean = true): Array<Serializable> {
             val filtered = ArrayList<Departure>()
             val lines = HashMap<AgencyAndId, Int>()
-            val sortedDepartures = departures.sortedBy { it.timeTill() }
+            val sortedDepartures = departures.sortedBy { it.timeTill(relative) }
             for (departure in sortedDepartures) {
                 var lineExistedTimes = lines[departure.line]
-                if (departure.timeTill() >= 0 && lineExistedTimes ?: 0 < 3) {
+                if (departure.timeTill(relative) >= 0 && lineExistedTimes ?: 0 < 3) {
                     lineExistedTimes = (lineExistedTimes ?: 0) + 1
                     lines[departure.line] = lineExistedTimes
                     filtered.add(departure)
                 }
             }
-            return filtered
+            return arrayOf(filtered, lines.all { it.value >= 3 })
         }
 
         fun createDepartures(stopId: AgencyAndId): Map<AgencyAndId, List<Departure>> {
             val timetable = Timetable.getTimetable()
             val departures = timetable.getStopDepartures(stopId)
-            return createDepartures(departures)
+
+            return rollDepartures(departures)
         }
 
-        fun createDepartures(departures: Map<AgencyAndId, List<Departure>>): Map<AgencyAndId, List<Departure>> { //todo if departure.timeTill < 0 -> show ‘just departed’
-            val moreDepartures = HashMap<AgencyAndId, ArrayList<Departure>>()
-            for ((k, v) in departures) {
-                moreDepartures[k] = ArrayList()
-                for (departure in v)
-                    moreDepartures[k]!!.add(departure.copy())
-            }
-            val rolledDepartures = HashMap<AgencyAndId, ArrayList<Departure>>()
-
-            for ((_, tomorrowDepartures) in moreDepartures) {
-                tomorrowDepartures.forEach { it.tomorrow = true }
+        fun rollDepartures(departures: Map<AgencyAndId, List<Departure>>): Map<AgencyAndId, List<Departure>> { //todo if departure.timeTill < 0 -> show ‘just departed’
+            val rolledDepartures = HashMap<AgencyAndId, 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]!!, false)
+                    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)
+                    @Suppress("UNCHECKED_CAST")
+                    rolledDepartures[it] = (result as List<Departure>).sortedBy { it.timeTill(true) }
+                }
             }
-
-            for ((mode, _) in departures) {
-                rolledDepartures[mode] = (departures[mode] as ArrayList<Departure> +
-                        moreDepartures[mode] as ArrayList<Departure>) as ArrayList<Departure>
-                rolledDepartures[mode] = filterDepartures(rolledDepartures[mode]!!)
-            }
-
-            println("rolled:")
-            rolledDepartures[AgencyAndId("4")]?.forEach {
-                println("${it.lineText} -> ${it.direction} @ ${it.time}")
-            }
-
             return rolledDepartures
         }
 
@@ -84,9 +85,11 @@                     array[7] == "true", array[8] == "true")
         }
     }
 
-    fun timeTill(): Long {
+    fun timeTill(relative: Boolean = true): Long {
         val time = Calendar.getInstance().rollTime(this.time)
-        val now = Calendar.getInstance()
+        var now = Calendar.getInstance()
+        if(!relative)
+            now = now.rollTime(0)
         if (this.tomorrow)
             time.add(Calendar.DAY_OF_MONTH, 1)
         return (time.timeInMillis - now.timeInMillis) / (1000 * 60)




diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/DeparturesAdapter.kt b/app/src/main/java/ml/adamsprogs/bimba/models/DeparturesAdapter.kt
index e76c76a5dc1b270d7cbea8e7cb86cc2da2851f05..9396ef56f99e887052ee82ea9a8634eb5799a0ea 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/DeparturesAdapter.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/DeparturesAdapter.kt
@@ -50,7 +50,8 @@         val departureIn = (departureTime.timeInMillis - now.timeInMillis) / (1000 * 60)
         val timeString: String
 
         timeString = if (departureIn > 60 || departureIn < 0 || !relativeTime)
-            context.getString(R.string.departure_at, "${departureTime.get(Calendar.HOUR_OF_DAY)}:${departureTime.get(Calendar.MINUTE)}")
+            //todo shall we pad hour too?
+            context.getString(R.string.departure_at, "${departureTime.get(Calendar.HOUR_OF_DAY)}:${String.format("%02d", departureTime.get(Calendar.MINUTE))}")
         else if (departureIn > 0 && !departure.onStop)
             context.getString(Declinator.decline(departureIn), departureIn.toString())
         else




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 8a7323530034fe0725df76b3698531708cec54c2..54880ff5ae53f5698aef244c4a2827c8fd8e3e89 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt
@@ -62,7 +62,7 @@
     private fun filterVmDepartures() {
         this.vmDepartures.forEach {
             val newSet = it.value
-                    .filter { it.timeTill() >= 0 }.toSet()
+                    .filter { it.timeTill(true) >= 0 }.toSet()
             this.vmDepartures[it.key] = newSet
         }
     }
@@ -135,7 +135,7 @@
         if (vmDepartures.isNotEmpty()) {
             return vmDepartures.flatMap { it.value }
                     .minBy {
-                        it.timeTill()
+                        it.timeTill(true)
                     }
         }
 
@@ -145,8 +145,8 @@         if (twoDayDepartures.isEmpty())
             return null
 
         return twoDayDepartures
-                .filter { it.timeTill() >= 0 }
-                .minBy { it.timeTill() }
+                .filter { it.timeTill(true) >= 0 }
+                .minBy { it.timeTill(true) }
     }
 
     private fun nowDepartures(): ArrayList<Departure> {
@@ -175,7 +175,7 @@             val today = timetable.getServiceForToday()
             departures[today] = vmDepartures.flatMap { it.value } as ArrayList<Departure>
         }
 
-        return Departure.createDepartures(departures)
+        return Departure.rollDepartures(departures)
     }
 
     fun fullTimetable(): Map<AgencyAndId, List<Departure>> {




diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/FavouritesAdapter.kt b/app/src/main/java/ml/adamsprogs/bimba/models/FavouritesAdapter.kt
index 8c62986865d0808f53d12ac353416e220ed4ad92..76ae3f570fae1f4eb659ca1d82345507e86dfa0e 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/FavouritesAdapter.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/FavouritesAdapter.kt
@@ -3,7 +3,6 @@
 import android.app.Activity
 import android.content.Context
 import android.content.Intent
-import android.os.Build
 import android.support.design.widget.AppBarLayout
 import android.support.v4.content.res.ResourcesCompat
 import android.support.v7.widget.*
@@ -18,6 +17,7 @@ import android.util.TypedValue
 import com.arlib.floatingsearchview.FloatingSearchView
 import ml.adamsprogs.bimba.Declinator
 import ml.adamsprogs.bimba.activities.StopActivity
+import ml.adamsprogs.bimba.getColour
 import kotlin.collections.ArrayList
 
 class FavouritesAdapter(val context: Context, var favourites: List<Favourite>, private val onMenuItemClickListener: FavouritesAdapter.OnMenuItemClickListener) :
@@ -61,7 +61,7 @@             }
             val nextDepartureText: String
             val nextDepartureLineText: String
             if (nextDeparture != null) {
-                val interval = nextDeparture.timeTill()
+                val interval = nextDeparture.timeTill(true)
                 if (interval < 0)
                     return@thread
                 nextDepartureText = context.getString(Declinator.decline(interval), interval.toString())
@@ -125,11 +125,7 @@
     private fun select(view: CardView, position: Int) {
         growSelected(position)
 
-        @Suppress("DEPRECATION")
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
-            view.setCardBackgroundColor(context.resources.getColor(R.color.colorAccent, null))
-        else
-            view.setCardBackgroundColor(context.resources.getColor(R.color.colorAccent))
+        view.setCardBackgroundColor(getColour(R.color.colorAccent, context))
         selected[position] = true
         setSelecting()
     }




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 d6106c90f3bd0b2ec63a2fca13b377adbfc1986e..86f05318d4ee75a376e7977751aac467ba9e115f 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt
@@ -39,6 +39,7 @@
     //private lateinit var cacheManager: CacheManager
     private var _stops: List<StopSuggestion>? = null
     private lateinit var filesDir: File
+    private val tripsCache = HashMap<String, Array<String>>()
 
     fun refresh() {
         //cacheManager.recreate(getStopDeparturesByPlates(cacheManager.keys().toSet()))
@@ -91,6 +92,7 @@         }
 
         val stopsFile = File(filesDir, "gtfs_files/trips.txt")
         parser.parseAll(stopsFile).forEach {
+            tripsCache[it[2]] = it
             routes[it[2]] = Pair(it[0], it[3])
         }
 
@@ -177,7 +179,6 @@                     toGet.add(it)
                 }
 
         getStopDeparturesByPlates(toGet, trips).forEach { plates.add(it) }
-
         return Plate.join(plates)
     }
 
@@ -195,7 +196,8 @@
         return plates.map { getStopDeparturesByPlate(it, trips) }.toSet()
     }
 
-    private fun getStopDeparturesByPlate(plate: Plate, trips: Map<String, Trip>): Plate {
+    private fun getStopDeparturesByPlate(plate: Plate, trips: Map<String, Trip>): Plate { //fixme takes too long
+        println("getStopDeparturesByPlate: ${JCalendar.getInstance().timeInMillis}")
         val resultPlate = Plate(Plate.ID(plate.id), HashMap())
         val stopTimes = HashMap<String, Map<String, Any>>()
         val stopTimesFile = File(filesDir, "gtfs_files/stop_times_${plate.id.stop.id}.txt")
@@ -231,6 +233,7 @@                     resultPlate.departures[serviceId] = HashSet()
                 resultPlate.departures[serviceId]!!.add(dep)
             }
         }
+        println("</>: ${JCalendar.getInstance().timeInMillis}")
         return resultPlate
     }
 
@@ -281,31 +284,14 @@         return explanations
     }
 
     private fun getRouteForTrip(trip: Trip): Route {
-        val routesFile = File(filesDir, "gtfs_files/trips.txt")
-        var mapReader = CsvMapReader(FileReader(routesFile), CsvPreference.STANDARD_PREFERENCE)
-        var header = mapReader.getHeader(true)
-
-        var routeId = ""
-        var row: Map<String, Any>? = null
-        var processors = Array<CellProcessor?>(header.size, { null })
-        while ({ row = mapReader.read(header, processors); row }() != null) {
-            if ((row!!["trip_id"] as String) == trip.rawId) {
-                mapReader.close()
-                routeId = row!!["route_id"] as String
-                break
-            }
-        }
-        if (routeId == "") {
-            mapReader.close()
-            throw IllegalArgumentException("Trip ${trip.rawId} not in store")
-        }
+        val routeId = tripsCache[trip.rawId]!![0]
 
         val tripsFile = File(filesDir, "gtfs_files/routes.txt")
-        mapReader = CsvMapReader(FileReader(tripsFile), CsvPreference.STANDARD_PREFERENCE)
-        header = mapReader.getHeader(true)
+        val mapReader = CsvMapReader(FileReader(tripsFile), CsvPreference.STANDARD_PREFERENCE)
+        val header = mapReader.getHeader(true)
 
         var routeRow: Map<String, Any>? = null
-        processors = Array(header.size, { null })
+        val processors = Array<CellProcessor?>(header.size, { null })
         while ({ routeRow = mapReader.read(header, processors); routeRow }() != null) {
             if ((routeRow!!["route_id"] as String) == routeId) {
                 mapReader.close()
@@ -343,41 +329,24 @@
     fun getTripsForStop(stopId: AgencyAndId): HashMap<String, Trip> { //todo actually, we have trips at the start. Why not cache?
         val tripIds = HashSet<String>()
         val stopTimesFile = File(filesDir, "gtfs_files/stop_times_${stopId.id}.txt")
-        var mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE)
-        var header = mapReader.getHeader(true)
+        val mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE)
+        val header = mapReader.getHeader(true)
 
         var stopTimesRow: Map<String, Any>? = null
-        var processors = Array<CellProcessor?>(header.size, { null })
+        val processors = Array<CellProcessor?>(header.size, { null })
         while ({ stopTimesRow = mapReader.read(header, processors); stopTimesRow }() != null) {
             val tripId = stopTimesRow!!["trip_id"] as String
             tripIds.add(tripId)
         }
         mapReader.close()
 
-
-        val trips = HashMap<String, Trip>()
-        val tripsFile = File(filesDir, "gtfs_files/trips.txt")
-        mapReader = CsvMapReader(FileReader(tripsFile), CsvPreference.STANDARD_PREFERENCE)
-        header = mapReader.getHeader(true)
-
-        var tripsRow: Map<String, Any>? = null
-        processors = Array(header.size, { null })
-        while ({ tripsRow = mapReader.read(header, processors); tripsRow }() != null) {
-            val tripId = tripsRow!!["trip_id"] as String
-            trips[tripId] = Trip(AgencyAndId(tripsRow!!["route_id"] as String),
-                    AgencyAndId(tripsRow!!["service_id"] as String),
-                    createTripId(tripId),
-                    tripsRow!!["trip_headsign"] as String,
-                    Integer.parseInt(tripsRow!!["direction_id"] as String),
-                    AgencyAndId(tripsRow!!["shape_id"] as String),
-                    tripsRow!!["wheelchair_accessible"] as String == "1")
-        }
-        mapReader.close()
-
         val filteredTrips = HashMap<String, Trip>()
 
         tripIds.forEach {
-            filteredTrips[it] = trips[it]!!
+            filteredTrips[it] = Trip(AgencyAndId(tripsCache[it]!![0]),
+                    AgencyAndId(tripsCache[it]!![1]), createTripId(tripsCache[it]!![2]),
+                    tripsCache[it]!![3], Integer.parseInt(tripsCache[it]!![4]),
+                    AgencyAndId(tripsCache[it]!![5]), tripsCache[it]!![6] == "1")
         }
         return filteredTrips
     }
@@ -483,37 +452,22 @@         throw IllegalArgumentException("Route $number not in store")
     }
 
     fun getPlatesForStop(stop: AgencyAndId): Set<Plate.ID> {
-        val plates = HashMap<String, Plate.ID>()
         val tripIds = HashSet<String>()
         val stopTimesFile = File(filesDir, "gtfs_files/stop_times_${stop.id}.txt")
-        var mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE)
-        var header = mapReader.getHeader(true)
+        val mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE)
+        val header = mapReader.getHeader(true)
 
         var stopTimesRow: Map<String, Any>? = null
-        var processors = Array<CellProcessor?>(header.size, { null })
+        val processors = Array<CellProcessor?>(header.size, { null })
         while ({ stopTimesRow = mapReader.read(header, processors); stopTimesRow }() != null) {
             val tripId = stopTimesRow!!["trip_id"] as String
             tripIds.add(tripId)
         }
         mapReader.close()
 
-
-        val tripsFile = File(filesDir, "gtfs_files/trips.txt")
-        mapReader = CsvMapReader(FileReader(tripsFile), CsvPreference.STANDARD_PREFERENCE)
-        header = mapReader.getHeader(true)
-
-        var tripsRow: Map<String, Any>? = null
-        processors = Array(header.size, { null })
-        while ({ tripsRow = mapReader.read(header, processors); tripsRow }() != null) {
-            val tripId = tripsRow!!["trip_id"] as String
-            plates[tripId] = Plate.ID(AgencyAndId(tripsRow!!["route_id"] as String), stop, tripsRow!!["trip_headsign"] as String)
-
-        }
-        mapReader.close()
-
         val filteredPlates = HashSet<Plate.ID>()
         tripIds.forEach {
-            filteredPlates.add(plates[it]!!)
+            filteredPlates.add(Plate.ID(AgencyAndId(tripsCache[it]!![0]), stop, tripsCache[it]!![3]))
         }
 
         return filteredPlates




diff --git a/app/src/main/res/layout/activity_stop.xml b/app/src/main/res/layout/activity_stop.xml
index 6de28177cef9c063f6323d9a2e1dd99572646e63..670a7d4526703b96c1085c7e07604c3f57960a6a 100644
--- a/app/src/main/res/layout/activity_stop.xml
+++ b/app/src/main/res/layout/activity_stop.xml
@@ -5,7 +5,6 @@     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/main_content"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:animateLayoutChanges="true"
     android:fitsSystemWindows="true"
     tools:context="ml.adamsprogs.bimba.activities.StopActivity">
 
@@ -16,21 +15,53 @@         android:layout_height="wrap_content"
         android:paddingTop="@dimen/appbar_padding_top"
         android:theme="@style/AppTheme.AppBarOverlay">
 
-        <!--suppress AndroidDomInspection -->
         <android.support.v7.widget.Toolbar
             android:id="@+id/toolbar"
             android:layout_width="match_parent"
             android:layout_height="?attr/actionBarSize"
             android:layout_weight="1"
-            android:background="@color/colorPrimary"
+            android:background="?attr/colorPrimary"
             app:layout_scrollFlags="scroll|enterAlways"
             app:popupTheme="@style/AppTheme.PopupOverlay"
-            app:title="@string/app_name" />
+            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: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/mon" />
+
+            <android.support.design.widget.TabItem
+                android:id="@+id/tabItem2"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/tue__thu" />
+
+            <android.support.design.widget.TabItem
+                android:id="@+id/tabItem3"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/fri" />
+
+            <android.support.design.widget.TabItem
+                android:id="@+id/tabItem4"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/sat" />
+
+            <android.support.design.widget.TabItem
+                android:id="@+id/tabItem5"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/sun" />
+        </android.support.design.widget.TabLayout>
     </android.support.design.widget.AppBarLayout>
 
     <android.support.v4.view.ViewPager




diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index cc3dac1c13fa07f11275b532af588802f03da62b..b8faa65871949ede9b611e8111910a00f227299a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -93,8 +93,10 @@         Sat
         <item>Sun</item>
     </string-array>
 
-    <string name="zone_a_colour" translatable="false">#00a650</string>
-    <string name="zone_b_colour" translatable="false">#ed1c24</string>
-    <string name="zone_c_colour" translatable="false">#ffc107</string>
     <string name="today">Today</string>
+    <string name="mon">Mon</string>
+    <string name="tue__thu">Tue, …, Thu</string>
+    <string name="fri">Fri</string>
+    <string name="sat">Sat</string>
+    <string name="sun">Sun</string>
 </resources>