Bimba.git

commit 730a199d37b81a46bc2b93c28a4a33e6483ac160

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

adding favourites

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


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 cb15aee6ae6921f2590a7a42700164f5dde6ce70..648ec173b99655b90e453de672ced8c44999e08f 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt
@@ -25,10 +25,14 @@ import ml.adamsprogs.bimba.datasources.VmClient
 import ml.adamsprogs.bimba.models.suggestions.GtfsSuggestion
 import ml.adamsprogs.bimba.models.suggestions.LineSuggestion
 import ml.adamsprogs.bimba.models.suggestions.StopSuggestion
+import android.support.v7.widget.DefaultItemAnimator
+import android.content.Intent
 
 //todo cards https://enoent.fr/blog/2015/01/18/recyclerview-basics/
+//todo searchView integration
 class DashActivity : AppCompatActivity(), MessageReceiver.OnTimetableDownloadListener,
-        FavouritesAdapter.OnMenuItemClickListener, Favourite.OnVmPreparedListener {
+        FavouritesAdapter.OnMenuItemClickListener, Favourite.OnVmPreparedListener,
+        FavouritesAdapter.ViewHolder.OnClickListener {
     val context: Context = this
     private val receiver = MessageReceiver.getMessageReceiver()
     lateinit var timetable: Timetable
@@ -38,6 +42,13 @@     private lateinit var drawerView: NavigationView
     lateinit var favouritesList: RecyclerView
     lateinit var searchView: FloatingSearchView
     private lateinit var favourites: FavouriteStorage
+    private lateinit var adapter: FavouritesAdapter
+    private val actionModeCallback = ActionModeCallback()
+    private var actionMode: ActionMode? = null
+
+    companion object {
+        const val REQUEST_EDIT_FAVOURITE = 1
+    }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -45,7 +56,6 @@         setContentView(R.layout.activity_dash)
 
         AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO)
         setSupportActionBar(toolbar)
-        supportActionBar?.title = getString(R.string.merge_favourites)
 
         getSuggestions()
 
@@ -115,7 +125,7 @@                     val intent = Intent(context, StopSpecifyActivity::class.java)
                     intent.putExtra(StopSpecifyActivity.EXTRA_STOP_IDS, searchSuggestion.ids.joinToString(",") { it.id })
                     intent.putExtra(StopSpecifyActivity.EXTRA_STOP_NAME, searchSuggestion.name)
                     startActivity(intent)
-                } else if (searchSuggestion is LineSuggestion){
+                } else if (searchSuggestion is LineSuggestion) {
                     val intent = Intent(context, LineSpecifyActivity::class.java)
                     intent.putExtra(LineSpecifyActivity.EXTRA_LINE_ID, searchSuggestion.name)
                     startActivity(intent)
@@ -144,7 +154,9 @@     private fun prepareFavourites() {
         favourites = FavouriteStorage.getFavouriteStorage(context)
         val layoutManager = LinearLayoutManager(context)
         favouritesList = favourites_list
-        favouritesList.adapter = FavouritesAdapter(context, favourites.favouritesList, this)
+        adapter = FavouritesAdapter(context, favourites, this, this)
+        favouritesList.adapter = adapter
+        favouritesList.itemAnimator = DefaultItemAnimator()
         favouritesList.layoutManager = layoutManager
     }
 
@@ -164,9 +176,7 @@         filter.addAction(VmClient.ACTION_READY)
         filter.addCategory(Intent.CATEGORY_DEFAULT)
         registerReceiver(receiver, filter)
         receiver.addOnTimetableDownloadListener(context as MessageReceiver.OnTimetableDownloadListener)
-        favourites.favouritesList.forEach {
-            it.registerOnVm(receiver, context)
-        }
+        favourites.registerOnVm(receiver, context)
     }
 
     private fun startDownloaderService() {
@@ -185,38 +195,17 @@     }
 
     override fun onResume() {
         super.onResume()
-        (favouritesList.adapter as FavouritesAdapter).favourites = favourites.favouritesList
+        adapter.favourites = favourites
         favouritesList.adapter.notifyDataSetChanged()
     }
 
     override fun onDestroy() {
         super.onDestroy()
         receiver.removeOnTimetableDownloadListener(context as MessageReceiver.OnTimetableDownloadListener)
-        favourites.favouritesList.forEach {
-            it.deregisterOnVm(receiver, context)
-        }
+        favourites.deregisterOnVm(receiver, context)
         unregisterReceiver(receiver)
     }
 
-    override fun onCreateOptionsMenu(menu: Menu): Boolean {
-        menuInflater.inflate(R.menu.menu_favourite_merge, menu)
-        return true
-    }
-
-    override fun onOptionsItemSelected(item: MenuItem): Boolean {
-        val id = item.itemId
-
-        if (id == R.id.action_merge) {
-            val names = (favouritesList.adapter as FavouritesAdapter).selectedNames
-            favourites.merge(names)
-            (favouritesList.adapter as FavouritesAdapter).favourites = favourites.favouritesList
-            favouritesList.adapter.notifyDataSetChanged()
-            (favouritesList.adapter as FavouritesAdapter).stopSelecting(names[0])
-        }
-
-        return super.onOptionsItemSelected(item)
-    }
-
     fun deAccent(str: String): String {
         var result = str.replace('ę', 'e')
         result = result.replace('ó', 'o')
@@ -248,18 +237,35 @@         }
     }
 
     override fun edit(name: String): Boolean {
+        val positionBefore = favourites.indexOf(name)
         val intent = Intent(this, EditFavouriteActivity::class.java)
-        intent.putExtra(EditFavouriteActivity.EXTRA_FAVOURITE, favourites.favourites[name])
-        startActivity(intent)
-        (favouritesList.adapter as FavouritesAdapter).favourites = favourites.favouritesList
-        favouritesList.adapter.notifyDataSetChanged()
+        intent.putExtra(EditFavouriteActivity.EXTRA_FAVOURITE, favourites[name])
+        intent.putExtra(EditFavouriteActivity.EXTRA_POSITION_BEFORE, positionBefore)
+        startActivityForResult(intent, REQUEST_EDIT_FAVOURITE)
         return true
     }
 
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
+        if (requestCode == REQUEST_EDIT_FAVOURITE) {
+            if (resultCode == Activity.RESULT_OK) {
+                val name = data.getStringExtra(EditFavouriteActivity.EXTRA_NEW_NAME)
+                val positionBefore = data.getIntExtra(EditFavouriteActivity.EXTRA_POSITION_BEFORE, -1)
+                //adapter.favourites = favourites.favouritesList
+                if (positionBefore == -1)
+                    favouritesList.adapter.notifyDataSetChanged()
+                else {
+                    val positionAfter = favourites.indexOf(name)
+                    favouritesList.adapter.notifyItemChanged(positionBefore)
+                    favouritesList.adapter.notifyItemMoved(positionBefore, positionAfter)
+                }
+            }
+        }
+    }
+
     override fun delete(name: String): Boolean {
         favourites.delete(name)
-        (favouritesList.adapter as FavouritesAdapter).favourites = favourites.favouritesList
-        favouritesList.adapter.notifyDataSetChanged()
+        //adapter.favourites = favourites.favouritesList
+        favouritesList.adapter.notifyItemRemoved(favourites.indexOf(name))
         return true
     }
 
@@ -267,5 +273,76 @@     @SuppressLint("MissingSuperCall")
     override fun onSaveInstanceState(outState: Bundle) {
         //hack below line to be commented to prevent crash on nougat.
         //super.onSaveInstanceState(outState);
+    }
+
+    override fun onItemClicked(position: Int) {
+        if (actionMode != null) {
+            toggleSelection(position)
+        }
+
+        //todo else -> StopActivity
+    }
+
+    override fun onItemLongClicked(position: Int): Boolean {
+        if (actionMode == null) {
+            actionMode = startActionMode(actionModeCallback)
+        }
+
+        toggleSelection(position)
+
+        return true
+    }
+
+    private fun toggleSelection(position: Int) {
+        adapter.toggleSelection(position)
+        val count = adapter.getSelectedItemCount()
+
+        if (count == 0) {
+            actionMode?.finish()
+        } else {
+            actionMode?.title = getString(R.string.merge_favourites)
+            actionMode?.invalidate()
+        }
+    }
+
+    private fun clearSelection() {
+        adapter.clearSelection()
+        actionMode?.finish()
+    }
+
+    private inner class ActionModeCallback : ActionMode.Callback {
+        override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
+            menuInflater.inflate(R.menu.menu_favourite_merge, menu)
+            return true
+        }
+
+        override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
+            return false
+        }
+
+        override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
+            return when (item.itemId) {
+                R.id.action_merge -> {
+                    val selectedPositions = adapter.getSelectedItems()
+                    val selectedNames = selectedPositions.map { favourites[it]?.name }.filter { it != null }.map { it!! }
+                    favourites.merge(selectedNames)
+
+                    adapter.notifyItemChanged(selectedPositions.min()!!)
+                    (1 until selectedPositions.size).forEach {
+                        adapter.notifyItemRemoved(it)
+                    }
+
+                    clearSelection()
+                    true
+                }
+
+                else -> false
+            }
+        }
+
+        override fun onDestroyActionMode(mode: ActionMode) {
+            (favouritesList.adapter as FavouritesAdapter).clearSelection()
+            actionMode = null
+        }
     }
 }




diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/EditFavouriteActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/EditFavouriteActivity.kt
index 5a7c10bbf2e68fd18f4f29a9081d1e15bbc87a67..06d645b1b8e90386e934e7ed8b43289a5553cdf3 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/EditFavouriteActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/activities/EditFavouriteActivity.kt
@@ -10,21 +10,29 @@ import ml.adamsprogs.bimba.models.Favourite
 import ml.adamsprogs.bimba.models.FavouriteEditRowAdapter
 import ml.adamsprogs.bimba.models.FavouriteStorage
 import kotlinx.android.synthetic.main.activity_edit_favourite.*
+import android.app.Activity
+import android.content.Intent
+
+
 
 class EditFavouriteActivity : AppCompatActivity() {
     companion object {
         const val EXTRA_FAVOURITE = "favourite"
+        const val EXTRA_POSITION_BEFORE = "position_before"
+        const val EXTRA_NEW_NAME = "new_name"
     }
 
     private lateinit var favourites: FavouriteStorage
     private lateinit var nameEdit: EditText
     private var favourite: Favourite? = null
+    private var positionBefore: Int? = null
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_edit_favourite)
 
         favourite = intent.getParcelableExtra(EXTRA_FAVOURITE)
+        positionBefore = intent.getIntExtra(EXTRA_POSITION_BEFORE, -1)
         if (favourite == null)
             finish()
         favourites = FavouriteStorage.getFavouriteStorage(this)
@@ -43,6 +51,10 @@     }
 
     override fun onBackPressed() {
         favourites.rename(favourite?.name!!, nameEdit.text.toString())
+        val returnIntent = Intent()
+        returnIntent.putExtra(EXTRA_POSITION_BEFORE, positionBefore)
+        returnIntent.putExtra(EXTRA_NEW_NAME, nameEdit.text.toString())
+        setResult(Activity.RESULT_OK, returnIntent)
         super.onBackPressed()
     }
 }




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 76890245ec5398ab355585494c2b088ec45fd84c..79ee257a0ec0231d16868e4e484c4eb4764a1e62 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt
@@ -3,6 +3,7 @@
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
+import android.icu.util.JapaneseCalendar
 import android.support.design.widget.TabLayout
 import android.support.design.widget.Snackbar
 import android.support.v7.app.AppCompatActivity
@@ -140,11 +141,8 @@         }
 
         fab.setOnClickListener {
             if (!favourites.has(stopSymbol)) {
-                val items = HashSet<Plate>()
-                timetable.getTripsForStop(stopSegment!!.stop).values.forEach {
-                    val o = Plate(Plate.ID(it.routeId, stopSegment!!.stop, it.headsign), null)
-                    items.add(o)
-                }
+                val items = HashSet<StopSegment>()
+                items.add(stopSegment!!)
                 favourites.add(stopSymbol, items)
                 fab.setImageDrawable(ResourcesCompat.getDrawable(context.resources, R.drawable.ic_favourite, this.theme))
             } else {
@@ -256,8 +254,8 @@             if (departures == null)
                 return PlaceholderFragment.newInstance(null, relativeTime)
             if (departures!!.isEmpty())
                 return PlaceholderFragment.newInstance(ArrayList(), relativeTime)
-            val sat = timetable.getServiceFor("saturday")
-            val sun = timetable.getServiceFor("sunday")
+            val sat = timetable.getServiceFor(Calendar.getInstance().get(Calendar.DAY_OF_WEEK))
+            val sun = timetable.getServiceFor(Calendar.getInstance().get(Calendar.DAY_OF_WEEK))
             val list: List<Departure> = when (position) {
                 1 -> departures!![sat] ?: ArrayList()
                 2 -> departures!![sun] ?: ArrayList()




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 a6a5efe8c0ee0cb500afdd641bc0abccc13c3bc3..48ea70cbb6bfa857d14247788331f2775aac048c 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt
@@ -38,7 +38,7 @@         }
     }
     private val requests = HashMap<AgencyAndId, Set<Request>>()
     private val vms = HashMap<AgencyAndId, HashSet<Plate>>() //HashSet<Departure>?
-    private val timetable = Timetable.getTimetable()
+    private val timetable = Timetable.getTimetable(this)
 
 
     override fun onCreate() {




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 c3eec8df843bb466a90f94a2a2662a69602ad8ca..1381ac50abf19a581df0055d375aaeeb96ee03de 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt
@@ -16,13 +16,15 @@ class Favourite : Parcelable, MessageReceiver.OnVmListener {
     private var isRegisteredOnVmListener: Boolean = false
     var name: String
         private set
-    var timetables: HashSet<Plate>
+    var timetables: HashSet<StopSegment>
         private set
     private var vmDepartures = HashMap<Plate.ID, Set<Departure>>()
     val timetable = Timetable.getTimetable()
 
     val size
-        get() = this.timetables.size
+        get() = timetables.sumBy {
+            it.size
+        }
 
     private val onVmPreparedListeners = HashSet<OnVmPreparedListener>()
 
@@ -35,15 +37,17 @@         onVmPreparedListeners.remove(listener)
     }
 
     constructor(parcel: Parcel) {
-        val array = ArrayList<String>()
-        parcel.readStringList(array)
-        val timetables = HashSet<Plate>()
-        array.mapTo(timetables) { Plate.fromString(it) }
         this.name = parcel.readString()
-        this.timetables = timetables
+        @Suppress("UNCHECKED_CAST")
+        val set = HashSet<StopSegment>()
+        val array = parcel.readParcelableArray(StopSegment::class.java.classLoader)
+        array.forEach {
+            set.add(it as StopSegment)
+        }
+        this.timetables = set
     }
 
-    constructor(name: String, timetables: HashSet<Plate>) {
+    constructor(name: String, timetables: HashSet<StopSegment>) {
         this.name = name
         this.timetables = timetables
 
@@ -54,9 +58,9 @@         return Parcelable.CONTENTS_FILE_DESCRIPTOR
     }
 
     override fun writeToParcel(dest: Parcel?, flags: Int) {
-        val parcel = timetables.map { it.toString() }
-        dest?.writeStringList(parcel)
         dest?.writeString(name)
+        val parcelableSegments = timetables.map { it }.toTypedArray()
+        dest?.writeParcelableArray(parcelableSegments, flags)
     }
 
     private fun filterVmDepartures() {
@@ -67,8 +71,10 @@             this.vmDepartures[it.key] = newSet
         }
     }
 
-    fun delete(plate: Plate) {
-        timetables.remove(timetables.find { it.id == plate.id })
+    fun delete(plateId: Plate.ID) {
+        timetables.forEach {
+            it.remove(plateId)
+        }
     }
 
     fun registerOnVm(receiver: MessageReceiver, context: Context) {
@@ -76,16 +82,10 @@         if (!isRegisteredOnVmListener) {
             receiver.addOnVmListener(this)
             isRegisteredOnVmListener = true
 
-            val segments = HashMap<AgencyAndId, StopSegment>()
-            timetables.forEach {
-                if (segments[it.id.stop] == null)
-                    segments[it.id.stop] = StopSegment(it.id.stop, HashSet())
-                segments[it.id.stop]!!.plates = segments[it.id.stop]!!.plates!!.plus(it.id)
-            }
 
-            segments.forEach {
+            timetables.forEach {
                 val intent = Intent(context, VmClient::class.java)
-                intent.putExtra("stop", it.value)
+                intent.putExtra("stop", it)
                 intent.action = "request"
                 context.startService(intent)
             }
@@ -97,16 +97,9 @@         if (isRegisteredOnVmListener) {
             receiver.removeOnVmListener(this)
             isRegisteredOnVmListener = false
 
-            val segments = HashMap<AgencyAndId, StopSegment>()
             timetables.forEach {
-                if (segments[it.id.stop] == null)
-                    segments[it.id.stop] = StopSegment(it.id.stop, HashSet())
-                segments[it.id.stop]!!.plates = segments[it.id.stop]!!.plates!!.plus(it.id)
-            }
-
-            segments.forEach {
                 val intent = Intent(context, VmClient::class.java)
-                intent.putExtra("stop", it.value)
+                intent.putExtra("stop", it)
                 intent.action = "remove"
                 context.startService(intent)
             }
@@ -153,14 +146,22 @@     private fun nowDepartures(): ArrayList {
         val today = timetable.getServiceForToday()
         val tomorrowCal = Calendar.getInstance()
         tomorrowCal.add(Calendar.DAY_OF_MONTH, 1)
-        val tomorrow = timetable.getServiceForTomorrow()
+        val tomorrow = try {
+            timetable.getServiceForTomorrow()
+        } catch (e: IllegalArgumentException) {
+            -1
+        }
+
+        val departures = fullTimetable()
 
-        val departures = timetable.getStopDepartures(timetables)
+        println(departures.keys.joinToString(","))
         val todayDepartures = departures[today]!!
         val tomorrowDepartures = ArrayList<Departure>()
         val twoDayDepartures = ArrayList<Departure>()
-        departures[tomorrow]!!.mapTo(tomorrowDepartures) { it.copy() }
-        tomorrowDepartures.forEach { it.tomorrow = true }
+        if (tomorrow != -1) {
+            departures[tomorrow]!!.mapTo(tomorrowDepartures) { it.copy() }
+            tomorrowDepartures.forEach { it.tomorrow = true }
+        }
 
         todayDepartures.forEach { twoDayDepartures.add(it) }
         tomorrowDepartures.forEach { twoDayDepartures.add(it) }
@@ -175,16 +176,25 @@             departures[today] = vmDepartures.flatMap { it.value } as ArrayList
             return departures
         }
 
-        val departures = timetable.getStopDepartures(timetables)
+        val departures = fullTimetable()
         return Departure.rollDepartures(departures)
     }
 
     fun fullTimetable(): Map<AgencyAndId, List<Departure>> {
-        return timetable.getStopDepartures(timetables)
+        val departureSet = HashSet<Map<AgencyAndId, List<Departure>>>()
+        timetables.forEach { departureSet.add(timetable.getStopDeparturesBySegment(it)) }
+        val departures = HashMap<AgencyAndId, List<Departure>>()
+        departureSet.forEach {
+            val map = it
+            it.keys.forEach {
+                departures[it] = (departures[it] ?: ArrayList()) + (map[it] ?: ArrayList())
+            }
+        }
+        return departures
     }
 
     override fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID) {
-        if (timetables.any { it.id == plateId }) {
+        if (timetables.any { it.contains(plateId) }) {
             if (vmDepartures == null)
                 this.vmDepartures.remove(plateId)
             else




diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteEditRowAdapter.kt b/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteEditRowAdapter.kt
index 0bdfe6fac3c60ab416514ab4e8c3482cf3cbddd0..841aa4e21c7a3ee97675f16aafab2db90aadb447 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteEditRowAdapter.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteEditRowAdapter.kt
@@ -17,17 +17,17 @@
     override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
         val timetable = Timetable.getTimetable()
         val favourites = FavouriteStorage.getFavouriteStorage()
-        val id = favourite.timetables.sortedBy { "${it.id.line}${it.id.stop}" }[position].id
+        val id = favourite.timetables.flatMap { it.plates!! }.sortedBy { "${it.line}${it.stop}"}[position]
         val plate = Plate(id,null)
         val favouriteElement = "${timetable.getStopName(plate.id.stop)} ( ${timetable.getStopCode(plate.id.stop)}):\n${timetable.getLineNumber(plate.id.line)} → ${plate.id.headsign}"
         holder?.rowTextView?.text = favouriteElement
-        holder?.splitButton?.setOnClickListener {
-            favourites.detach(favourite.name, plate, favouriteElement)
-            favourite = favourites.favourites[favourite.name]!!
-            notifyDataSetChanged()
-        }
+//        holder?.splitButton?.setOnClickListener {
+//            favourites.detach(favourite.name, id, favouriteElement)
+//            favourite = favourites.favourites[favourite.name]!!
+//            notifyDataSetChanged()
+//        }
         holder?.deleteButton?.setOnClickListener {
-            favourites.delete(favourite.name, plate)
+            favourites.delete(favourite.name, id)
             favourite = favourites.favourites[favourite.name]!!
             notifyDataSetChanged()
         }
@@ -43,7 +43,7 @@     }
 
     inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
         val rowTextView:TextView = itemView.findViewById(R.id.favourite_edit_row)
-        val splitButton:ImageView = itemView.findViewById(R.id.favourite_edit_split)
+//        val splitButton:ImageView = itemView.findViewById(R.id.favourite_edit_split)
         val deleteButton:ImageView = itemView.findViewById(R.id.favourite_edit_delete)
     }
 }
\ No newline at end of file




diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteStorage.kt b/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteStorage.kt
index f2df3265ab1547b79f57a69f691cca4c137254bf..fc3f1e7a09584272032fc4d917b7b6f7eb362d33 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteStorage.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteStorage.kt
@@ -4,7 +4,9 @@ import android.content.Context
 import android.content.SharedPreferences
 import com.google.gson.Gson
 import com.google.gson.JsonArray
+import com.google.gson.JsonElement
 import com.google.gson.JsonObject
+import ml.adamsprogs.bimba.MessageReceiver
 import ml.adamsprogs.bimba.models.gtfs.AgencyAndId
 
 
@@ -26,20 +28,22 @@     }
 
     val favourites = HashMap<String, Favourite>()
     private val preferences: SharedPreferences = context.getSharedPreferences("ml.adamsprogs.bimba.prefs", Context.MODE_PRIVATE)
-    val favouritesList: List<Favourite>
-        get() {
-            return favourites.values.toList().sortedBy { it.name }
-        }
 
     init {
         val favouritesString = preferences.getString("favourites", "{}")
         val favouritesMap = Gson().fromJson(favouritesString, JsonObject::class.java)
         for ((name, jsonTimetables) in favouritesMap.entrySet()) {
-            val timetables = HashSet<Plate>()
+            val timetables = HashSet<StopSegment>()
             jsonTimetables.asJsonArray.mapTo(timetables) {
-                Plate(Plate.ID(AgencyAndId.convertFromString(it.asJsonObject["line"].asString),
-                        AgencyAndId.convertFromString(it.asJsonObject["stop"].asString),
-                        it.asJsonObject["headsign"].asString), null)
+                val stopSegment = StopSegment(AgencyAndId(it.asJsonObject["stop"].asString), null)
+                val plates = HashSet<Plate.ID>()
+                it.asJsonObject["plates"].asJsonArray.mapTo(plates) {
+                    Plate.ID(AgencyAndId(it.asJsonObject["line"].asString),
+                            AgencyAndId(it.asJsonObject["stop"].asString),
+                            it.asJsonObject["headsign"].asString)
+                }
+                stopSegment.plates = plates
+                stopSegment
             }
             favourites[name] = Favourite(name, timetables)
         }
@@ -49,7 +53,7 @@     override fun iterator(): Iterator = favourites.values.iterator()
 
     fun has(name: String): Boolean = favourites.contains(name)
 
-    fun add(name: String, timetables: HashSet<Plate>) {
+    fun add(name: String, timetables: HashSet<StopSegment>) {
         if (favourites[name] == null) {
             favourites[name] = Favourite(name, timetables)
             serialize()
@@ -68,7 +72,7 @@         favourites.remove(name)
         serialize()
     }
 
-    fun delete(name: String, plate: Plate) {
+    fun delete(name: String, plate: Plate.ID) {
         favourites[name]?.delete(plate)
         serialize()
     }
@@ -78,11 +82,18 @@         val rootObject = JsonObject()
         for ((name, favourite) in favourites) {
             val timetables = JsonArray()
             for (timetable in favourite.timetables) {
-                val element = JsonObject()
-                element.addProperty("stop", timetable.id.stop.toString())
-                element.addProperty("line", timetable.id.line.toString())
-                element.addProperty("headsign", timetable.id.headsign)
-                timetables.add(element)
+                val segment = JsonObject()
+                segment.addProperty("stop", timetable.stop.id)
+                val plates = JsonArray()
+                for (plate in timetable.plates ?: HashSet()) {
+                    val element = JsonObject()
+                    element.addProperty("stop", plate.stop.id)
+                    element.addProperty("line", plate.line.id)
+                    element.addProperty("headsign", plate.headsign)
+                    plates.add(element)
+                }
+                segment.add("plates", plates)
+                timetables.add(segment)
             }
             rootObject.add(name, timetables)
         }
@@ -93,16 +104,18 @@         editor.apply()
 
     }
 
-    fun detach(name: String, plate: Plate, newName: String) {
-        val array = HashSet<Plate>()
-        array.add(plate)
-        favourites[newName] = Favourite(newName, array)
+    fun detach(name: String, plate: Plate.ID, newName: String) {
+        val plates = HashSet<Plate.ID>()
+        plates.add(plate)
+        val segments = HashSet<StopSegment>()
+        segments.add(StopSegment(plate.stop, plates))
+        favourites[newName] = Favourite(newName, segments)
         serialize()
 
         delete(name, plate)
     }
 
-    fun merge(names: ArrayList<String>) {
+    fun merge(names: List<String>) {
         if (names.size < 2)
             return
         val newFavourite = Favourite(names[0], HashSet())
@@ -122,4 +135,32 @@         favourites.remove(oldName)
         favourites[newName] = favourite
         serialize()
     }
+
+    fun registerOnVm(receiver: MessageReceiver, context: Context) {
+        favourites.values.forEach {
+            it.registerOnVm(receiver, context)
+        }
+    }
+
+    fun deregisterOnVm(receiver: MessageReceiver, context: Context) {
+        favourites.values.forEach {
+            it.deregisterOnVm(receiver, context)
+        }
+    }
+
+    operator fun get(name: String): Favourite? {
+        return favourites[name]
+    }
+
+    operator fun get(position: Int): Favourite? {
+        return favourites.entries.sortedBy { it.key }[position].value
+    }
+
+    fun indexOf(name: String): Int {
+        val favourite = favourites[name]
+        return favourites.values.sortedBy { it.name }.indexOf(favourite)
+    }
+
+    val size
+        get() = favourites.size
 }
\ No newline at end of file




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 9ebfe9ef032d96f9637965f1e344239cae16643d..f9b95488b5e02ea346ef58f9646dbaf09101c512 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/FavouritesAdapter.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/FavouritesAdapter.kt
@@ -2,56 +2,60 @@ package ml.adamsprogs.bimba.models
 
 import android.app.Activity
 import android.content.Context
-import android.content.Intent
-import android.support.design.widget.AppBarLayout
 import android.support.v4.content.res.ResourcesCompat
 import android.support.v7.widget.*
 import android.support.v7.widget.PopupMenu
+import android.util.SparseBooleanArray
 import android.view.*
 import android.widget.*
 import ml.adamsprogs.bimba.R
 import android.view.LayoutInflater
 import java.util.*
 import kotlin.concurrent.thread
-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) :
+class FavouritesAdapter(val context: Context, var favourites: FavouriteStorage,
+                        private val onMenuItemClickListener: OnMenuItemClickListener,
+                        private val onClickListener: ViewHolder.OnClickListener) :
         RecyclerView.Adapter<FavouritesAdapter.ViewHolder>() {
 
-    private val isSelecting: Boolean
-        get() {
-            return selected.any { it }
+    private val selectedItems = SparseBooleanArray()
+
+    fun isSelected(position: Int) = getSelectedItems().contains(position)
+
+    fun toggleSelection(position: Int) {
+        if (selectedItems.get(position, false)) {
+            selectedItems.delete(position)
+        } else {
+            selectedItems.put(position, true)
         }
-    private val selected = ArrayList<Boolean>()
-    val selectedNames: ArrayList<String>
-        get() {
-            val l = ArrayList<String>()
-            for ((i, it) in selected.withIndex()) {
-                if (it)
-                    l.add(favourites[i].name)
-            }
-            return l
-        }
+        notifyItemChanged(position)
+    }
 
-    init {
-        favourites.forEach {
-            selected.add(false)
+    fun clearSelection() {
+        val selection = getSelectedItems()
+        selectedItems.clear()
+        for (i in selection) {
+            notifyItemChanged(i)
         }
     }
 
-    override fun getItemCount(): Int {
-        return favourites.size
+    fun getSelectedItemCount() = selectedItems.size()
+
+    fun getSelectedItems(): List<Int> {
+        val items = ArrayList<Int>(selectedItems.size())
+        (0 until selectedItems.size()).mapTo(items) { selectedItems.keyAt(it) }
+        return items
     }
 
+    override fun getItemCount() = favourites.size
+
     override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
+        holder?.selectedOverlay?.visibility = if (isSelected(position)) View.VISIBLE else View.INVISIBLE
 
         thread {
-            val favourite = favourites[position]
+            val favourite = favourites[position]!!
             val nextDeparture: Departure?
             try {
                 nextDeparture = favourite.nextDeparture()
@@ -71,27 +75,16 @@                 nextDepartureText = context.getString(R.string.no_next_departure)
                 nextDepartureLineText = ""
             }
             (context as Activity).runOnUiThread {
-                holder?.root?.setOnLongClickListener {
-                    toggleSelected(it as CardView, position)
-                    true
-                }
-                holder?.root?.setOnClickListener {
-                    val intent = Intent(context, StopActivity::class.java)
-                    intent.putExtra(StopActivity.SOURCE_TYPE, StopActivity.SOURCE_TYPE_FAV)
-                    intent.putExtra(StopActivity.EXTRA_FAVOURITE, favourite)
-                    context.startActivity(intent)
-                }
                 holder?.nameTextView?.text = favourite.name
                 holder?.timeTextView?.text = nextDepartureText
                 holder?.lineTextView?.text = nextDepartureLineText
-                if(nextDeparture!=null) {
+                if (nextDeparture != null) {
                     if (nextDeparture.vm)
                         holder?.typeIcon?.setImageDrawable(ResourcesCompat.getDrawable(context.resources, R.drawable.ic_departure_vm, context.theme))
                     else
                         holder?.typeIcon?.setImageDrawable(ResourcesCompat.getDrawable(context.resources, R.drawable.ic_departure_timetable, context.theme))
                 }
                 holder?.moreButton?.setOnClickListener {
-                    unSelect(holder.root, position)
                     val popup = PopupMenu(context, it)
                     val inflater = popup.menuInflater
                     popup.setOnMenuItemClickListener {
@@ -108,74 +101,39 @@             }
         }
     }
 
-    private fun toggleSelected(view: CardView, position: Int) {
-        growSelected(position)
-
-        if (selected[position])
-            unSelect(view, position)
-        else
-            select(view, position)
-    }
+    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
+        val context = parent?.context
+        val inflater = LayoutInflater.from(context)
 
-    private fun growSelected(position: Int) {
-        while (position >= selected.size)
-            selected.add(false)
+        val rowView = inflater.inflate(R.layout.row_favourite, parent, false)
+        return ViewHolder(rowView, onClickListener)
     }
 
-    private fun select(view: CardView, position: Int) {
-        growSelected(position)
+    class ViewHolder(itemView: View, private val listener: OnClickListener) : RecyclerView.ViewHolder(itemView), View.OnClickListener, View.OnLongClickListener {
+        override fun onLongClick(v: View?): Boolean {
+            return listener.onItemLongClicked(adapterPosition)
+        }
 
-        view.setCardBackgroundColor(getColour(R.color.colorAccent, context))
-        selected[position] = true
-        setSelecting()
-    }
+        override fun onClick(v: View?) {
+            listener.onItemClicked(adapterPosition)
+        }
 
-    private fun unSelect(view: CardView, position: Int) {
-        growSelected(position)
+        val selectedOverlay: View = itemView.findViewById(R.id.selected_overlay)
+        val nameTextView: TextView = itemView.findViewById(R.id.favourite_name)
+        val timeTextView: TextView = itemView.findViewById(R.id.favourite_time)
+        val lineTextView: TextView = itemView.findViewById(R.id.favourite_line)
+        val moreButton: ImageView = itemView.findViewById(R.id.favourite_more_button)
+        val typeIcon: ImageView = itemView.findViewById(R.id.departureTypeIcon)
 
-        val colour = TypedValue()
-        context.theme.resolveAttribute(R.attr.cardBackgroundColor, colour, true)
-        view.setCardBackgroundColor(colour.data)
-        selected[position] = false
-        setSelecting()
-    }
-
-    private fun setSelecting() {
-        context as Activity
-        if (isSelecting) {
-            context.findViewById<FloatingSearchView>(R.id.search_view).visibility = View.INVISIBLE
-            context.findViewById<AppBarLayout>(R.id.appbar).visibility = View.VISIBLE
-        } else {
-            context.findViewById<FloatingSearchView>(R.id.search_view).visibility = View.VISIBLE
-            context.findViewById<AppBarLayout>(R.id.appbar).visibility = View.INVISIBLE
+        init {
+            itemView.setOnClickListener(this)
+            itemView.setOnLongClickListener(this)
         }
-    }
-
-    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
-        val context = parent?.context
-        val inflater = LayoutInflater.from(context)
-
-        val rowView = inflater.inflate(R.layout.row_favourite, parent, false)
-        return ViewHolder(rowView)
-    }
 
-    fun stopSelecting(name: String) {
-        selected.clear()
-        favourites.forEach {
-            if (it.name == name)
-                selected.add(true)
-            else
-                selected.add(false)
+        interface OnClickListener {
+            fun onItemClicked(position: Int)
+            fun onItemLongClicked(position: Int): Boolean
         }
-    }
-
-    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
-        val root:CardView = itemView.findViewById(R.id.favourite_card)
-        val nameTextView:TextView = itemView.findViewById(R.id.favourite_name)
-        val timeTextView:TextView = itemView.findViewById(R.id.favourite_time)
-        val lineTextView:TextView = itemView.findViewById(R.id.favourite_line)
-        val moreButton:ImageView = itemView.findViewById(R.id.favourite_more_button)
-        val typeIcon:ImageView = itemView.findViewById(R.id.departureTypeIcon)
     }
 
     interface OnMenuItemClickListener {




diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/StopSegment.kt b/app/src/main/java/ml/adamsprogs/bimba/models/StopSegment.kt
index c982aa67348bc8e1eab7f62988ea1eba4e1f2088..2448eecc8384ea7219b5ff3b990dd3351c9f5d1f 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/StopSegment.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/StopSegment.kt
@@ -61,4 +61,11 @@         if (plates == null)
             return false
         return plates!!.contains(plateId)
     }
+
+    fun remove(plateId: Plate.ID) {
+        (plates as HashSet).remove(plateId)
+    }
+
+    val size: Int
+        get() = plates?.size ?: 0
 }
\ No newline at end of file




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 954d66484500ef5cf339dffa8f35cdd02d24df41..bb6cee18a3fd8ac8df8975091e80741b91692334 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt
@@ -126,11 +126,12 @@                 }
             }
         }
 
-        val stopsFile = File(filesDir, "gtfs_files/trips.txt")
-        parser.parseAll(stopsFile).forEach {
-            tripsCache[it[2]] = it
-            routes[it[2]] = Pair(it[0], it[3])
+        if (tripsCache.isEmpty())
+            createTripCache()
+        tripsCache.forEach {
+            routes[it.key] = Pair(it.value[0], it.value[3])
         }
+
 
         trips.forEach {
             val headsign = HashSet<String>()
@@ -152,6 +153,17 @@         4586 -> (AWF73, {10 → Franowo, 29 → Franowo, 6 → Miłostowo, 5 → Stomil, 18 → Franowo, 15 → Franowo, 12 → Starołęka, 74 → Os. Orła Białego})
         */
     }
 
+    private fun createTripCache() {
+        val settings = CsvParserSettings()
+        settings.format.setLineSeparator("\r\n")
+        settings.format.quote = '"'
+        val parser = CsvParser(settings)
+        val stopsFile = File(filesDir, "gtfs_files/trips.txt")
+        parser.parseAll(stopsFile).forEach {
+            tripsCache[it[2]] = it
+        }
+    }
+
     fun getStopName(stopId: AgencyAndId): String {
         val file = File(filesDir, "gtfs_files/stops.txt")
         val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE)
@@ -211,6 +223,8 @@         segment.fillPlates()
         return getStopDeparturesBySegment(segment, trips)
     }
 
+    fun getStopDeparturesBySegment(segment: StopSegment) = getStopDeparturesBySegment(segment, getTripsForStop(segment.stop))
+
     private fun getStopDeparturesBySegment(segment: StopSegment, trips: Map<String, Trip>): HashMap<AgencyAndId, List<Departure>> {
         println("getStopDeparturesBySegment: ${JCalendar.getInstance().timeInMillis}")
         val departures = HashMap<AgencyAndId, ArrayList<Departure>>()
@@ -249,55 +263,41 @@         println(": ${JCalendar.getInstance().timeInMillis}")
         return sortedDepartures
     }
 
-    fun getStopDepartures(plates: Set<Plate>): Map<AgencyAndId, ArrayList<Departure>> {
-        val trips = HashMap<String, Trip>()
-
-        tripsCache.forEach { trips[it.key] = tripFromCache(it.key) }
-        return Plate.join(getStopDeparturesByPlates(plates, trips))
-    }
-
-    private fun getStopDeparturesByPlates(plates: Set<Plate>, trips: Map<String, Trip>): Set<Plate> {
-        if (plates.isEmpty())
-            return emptySet()
-
-        return plates.map { getStopDeparturesByPlate(it, trips) }.toSet()
-    }
-
-    private fun getStopDeparturesByPlate(plate: Plate, trips: Map<String, Trip>): Plate { //fixme<c:optimisation> takes too long --- cache
-        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")
-        val mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE)
-        val header = mapReader.getHeader(true)
-
-        var stopTimesRow: Map<String, Any>? = null
-        val processors = Array<CellProcessor?>(header.size, { null })
-        while ({ stopTimesRow = mapReader.read(header, processors); stopTimesRow }() != null) {
-            val tripId = stopTimesRow!!["trip_id"] as String
-            stopTimes[tripId] = stopTimesRow!!
-        }
-        mapReader.close()
-
-        trips.forEach {
-            if (it.value.routeId == plate.id.line &&
-                    it.value.headsign == plate.id.headsign) {
-                val time = parseTime(stopTimes[it.key]!!["departure_time"] as String)
-                val serviceId = it.value.serviceId
-                val mode = calendarToMode(serviceId)
-                val lowFloor = it.value.wheelchairAccessible
-                val mod = explainModification(it.value,
-                        Integer.parseInt(stopTimes[it.key]!!["stop_sequence"] as String))
-
-                val dep = Departure(plate.id.line, mode, time, lowFloor, mod, plate.id.headsign)
-                if (resultPlate.departures!![serviceId] == null)
-                    resultPlate.departures[serviceId] = HashSet()
-                resultPlate.departures[serviceId]!!.add(dep)
-            }
-        }
-        println("</>: ${JCalendar.getInstance().timeInMillis}")
-        return resultPlate
-    }
+//    private fun getStopDeparturesByPlate(plate: Plate, trips: Map<String, Trip>): Plate { //fixme<c:optimisation> takes too long --- cache
+//        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")
+//        val mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE)
+//        val header = mapReader.getHeader(true)
+//
+//        var stopTimesRow: Map<String, Any>? = null
+//        val processors = Array<CellProcessor?>(header.size, { null })
+//        while ({ stopTimesRow = mapReader.read(header, processors); stopTimesRow }() != null) {
+//            val tripId = stopTimesRow!!["trip_id"] as String
+//            stopTimes[tripId] = stopTimesRow!!
+//        }
+//        mapReader.close()
+//
+//        trips.forEach {
+//            if (it.value.routeId == plate.id.line &&
+//                    it.value.headsign == plate.id.headsign) {
+//                val time = parseTime(stopTimes[it.key]!!["departure_time"] as String)
+//                val serviceId = it.value.serviceId
+//                val mode = calendarToMode(serviceId)
+//                val lowFloor = it.value.wheelchairAccessible
+//                val mod = explainModification(it.value,
+//                        Integer.parseInt(stopTimes[it.key]!!["stop_sequence"] as String))
+//
+//                val dep = Departure(plate.id.line, mode, time, lowFloor, mod, plate.id.headsign)
+//                if (resultPlate.departures!![serviceId] == null)
+//                    resultPlate.departures[serviceId] = HashSet()
+//                resultPlate.departures[serviceId]!!.add(dep)
+//            }
+//        }
+//        println("</>: ${JCalendar.getInstance().timeInMillis}")
+//        return resultPlate
+//    }
 
     private fun parseTime(time: String): Int {
         val cal = JCalendar.getInstance()
@@ -355,6 +355,8 @@         return explanations
     }
 
     private fun getRouteForTrip(trip: Trip): Route {
+        if (tripsCache.isEmpty())
+            createTripCache()
         val routeId = tripsCache[trip.id.rawId]!![0]
 
         val tripsFile = File(filesDir, "gtfs_files/routes.txt")
@@ -437,6 +439,8 @@         return filteredTrips
     }
 
     private fun tripFromCache(id: String): Trip {
+        if (tripsCache.isEmpty())
+            createTripCache()
         return Trip(AgencyAndId(tripsCache[id]!![0]),
                 AgencyAndId(tripsCache[id]!![1]), createTripId(tripsCache[id]!![2]),
                 tripsCache[id]!![3], Integer.parseInt(tripsCache[id]!![4]),
@@ -511,26 +515,21 @@         val tomorrowDoW = tomorrow.get(JCalendar.DAY_OF_WEEK)
         return getServiceFor(tomorrowDoW)
     }
 
-    private fun getServiceFor(day: Int): AgencyAndId {
-        val dow = arrayOf("sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday")
-
-        return getServiceFor(dow[day - 1])
-    }
-
-    fun getServiceFor(day: String): AgencyAndId {
+    fun getServiceFor(day: Int): AgencyAndId {
+        val dayColumn = ((day + 5) % 7) + 1
         val file = File(filesDir, "gtfs_files/calendar.txt")
-        val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE)
-        val header = mapReader.getHeader(true)
 
-        var row: Map<String, Any>? = null
-        val processors = Array<CellProcessor?>(header.size, { null })
-        while ({ row = mapReader.read(header, processors); row }() != null) {
-            if ((row!![day] as String) == "1") {
-                mapReader.close()
-                return AgencyAndId(row!!["service_id"] as String)
+        val settings = CsvParserSettings()
+        settings.format.quote = '"'
+        settings.format.setLineSeparator("\r\n")
+        settings.isHeaderExtractionEnabled = true
+        val parser = CsvParser(settings)
+
+        parser.parseAll(file).forEach {
+            if ((it[dayColumn] as String) == "1") {
+                return AgencyAndId(it[0] as String)
             }
         }
-        mapReader.close()
         throw IllegalArgumentException("Day $day not in calendar")
 
     }
@@ -575,6 +574,8 @@         return filteredPlates
     }
 
     fun getTripGraphs(id: AgencyAndId): List<Map<Int, List<Int>>> {
+        if(tripsCache.isEmpty())
+            createTripCache()
         tripsCache.forEach {
             if (it.value[0] == id.id) {
                 //todo needs stop_times.txt indexed by trips




diff --git a/app/src/main/res/layout/row_favourite.xml b/app/src/main/res/layout/row_favourite.xml
index 76733ef1eb085c419ae1f120e5d09cc15585cd62..509a5cbe3111eb1ad5746fc948150efcb30b0bb9 100644
--- a/app/src/main/res/layout/row_favourite.xml
+++ b/app/src/main/res/layout/row_favourite.xml
@@ -6,8 +6,16 @@     android:id="@+id/favourite_card"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_margin="4dp"
+    android:foreground="?android:attr/selectableItemBackground"
     tools:layout_editor_absoluteX="8dp"
     tools:layout_editor_absoluteY="128dp">
+
+    <View
+        android:id="@+id/selected_overlay"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/colorAccent"
+        android:visibility="invisible" />
 
     <android.support.constraint.ConstraintLayout
         android:layout_width="match_parent"
@@ -73,4 +81,5 @@             app:srcCompat="@drawable/ic_more"
             tools:layout_editor_absoluteX="328dp"
             tools:layout_editor_absoluteY="16dp" />
     </android.support.constraint.ConstraintLayout>
+
 </android.support.v7.widget.CardView>
\ No newline at end of file




diff --git a/app/src/main/res/layout/row_favourite_edit.xml b/app/src/main/res/layout/row_favourite_edit.xml
index 34d681af9dedd9f4c0ab42612d3ac9416b971d53..7de8a6a53aa15b716770b036dba2906bd855bfe1 100644
--- a/app/src/main/res/layout/row_favourite_edit.xml
+++ b/app/src/main/res/layout/row_favourite_edit.xml
@@ -19,7 +19,7 @@         android:text=""
         android:textAlignment="viewStart"
         android:textAppearance="@style/TextAppearance.AppCompat"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/favourite_edit_split"
+        app:layout_constraintEnd_toStartOf="@+id/favourite_edit_delete"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent" />
 
@@ -31,25 +31,25 @@         android:layout_marginBottom="16dp"
         android:layout_marginStart="9dp"
         android:layout_marginTop="16dp"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toEndOf="@+id/favourite_edit_split"
+        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
         app:srcCompat="@drawable/ic_delete"
         tools:layout_editor_absoluteX="335dp"
         tools:layout_editor_absoluteY="16dp"
         android:contentDescription="@string/favourite_element_delete_button" />
 
-    <ImageView
-        android:id="@+id/favourite_edit_split"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="16dp"
-        android:layout_marginEnd="58dp"
-        android:layout_marginTop="16dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:srcCompat="@drawable/ic_split"
-        tools:layout_editor_absoluteX="302dp"
-        tools:layout_editor_absoluteY="16dp"
-        android:contentDescription="@string/favourite_element_split_button" />
+    <!--<ImageView-->
+        <!--android:id="@+id/favourite_edit_split"-->
+        <!--android:layout_width="wrap_content"-->
+        <!--android:layout_height="wrap_content"-->
+        <!--android:layout_marginBottom="16dp"-->
+        <!--android:layout_marginEnd="58dp"-->
+        <!--android:layout_marginTop="16dp"-->
+        <!--app:layout_constraintBottom_toBottomOf="parent"-->
+        <!--app:layout_constraintEnd_toEndOf="parent"-->
+        <!--app:layout_constraintTop_toTopOf="parent"-->
+        <!--app:srcCompat="@drawable/ic_split"-->
+        <!--tools:layout_editor_absoluteX="302dp"-->
+        <!--tools:layout_editor_absoluteY="16dp"-->
+        <!--android:contentDescription="@string/favourite_element_split_button" />-->
 </android.support.constraint.ConstraintLayout>
\ No newline at end of file