Author: Adam Pioterek <adam.pioterek@protonmail.ch>
fixed dynamic tabs
app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt | 1 app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt | 111 app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt | 3 app/src/main/java/ml/adamsprogs/bimba/gtfs/Trip.kt | 4 app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt | 5 app/src/main/java/ml/adamsprogs/bimba/models/Plate.kt | 1 app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt | 130
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 04da7ada55bdb13f87789f6fec5dccf0a11e118f..9731a065e2b2754d6f0d52cdb01b74f74bbd9e90 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/SplashActivity.kt @@ -19,6 +19,7 @@ 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 fb942717e129f320c45b662e851bbfff1dabf6cc..98728eaddb91d85a24cd3cdeb43b383b15a23182 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt @@ -20,6 +20,7 @@ import kotlinx.android.synthetic.main.activity_stop.* import ml.adamsprogs.bimba.datasources.TimetableDownloader import ml.adamsprogs.bimba.datasources.VmClient import ml.adamsprogs.bimba.gtfs.AgencyAndId +import android.support.v4.view.ViewPager class StopActivity : AppCompatActivity(), MessageReceiver.OnTimetableDownloadListener, MessageReceiver.OnVmListener, Favourite.OnVmPreparedListener { companion object { @@ -41,6 +42,7 @@ 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 @@ -96,11 +98,36 @@ } private fun getFavouriteDepartures() { thread { - sectionsPagerAdapter!!.departures = favourite!!.allDepartures() + refreshAdapter(favourite!!.allDepartures()) } - runOnUiThread { + } + + 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 { sectionsPagerAdapter?.notifyDataSetChanged() + } catch (e: Exception) { + runOnUiThread { + sectionsPagerAdapter?.notifyDataSetChanged() + } } + + selectTodayPage() } override fun onVmPrepared() { @@ -121,7 +148,7 @@ fab.setOnClickListener { if (!favourites.has(stopSymbol)) { val items = HashSet<Plate>() - timetable.getTripsForStop(stopSegment!!.stop).forEach { + timetable.getTripsForStop(stopSegment!!.stop).values.forEach { val o = Plate(Plate.ID(it.routeId, stopSegment!!.stop, it.headsign), null) items.add(o) } @@ -151,6 +178,9 @@ favourite!!.registerOnVm(receiver, context) } override fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID) { + if (vmDepartures == null && this.vmDepartures.isEmpty() && hasDepartures) { + return + } if (timetableType == "departure" && stopSegment!!.contains(plateId)) { if (vmDepartures != null) this.vmDepartures[plateId] = vmDepartures @@ -159,10 +189,11 @@ 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() } - sectionsPagerAdapter?.departures = departures - } else - sectionsPagerAdapter?.departures = Departure.createDepartures(stopSegment!!.stop) - sectionsPagerAdapter?.notifyDataSetChanged() + refreshAdapter(departures) + } else { + refreshAdapter(Departure.createDepartures(stopSegment!!.stop)) + hasDepartures = true + } } } @@ -174,11 +205,14 @@ TimetableDownloader.RESULT_UP_TO_DATE -> getString(R.string.timetable_up_to_date) TimetableDownloader.RESULT_FINISHED -> getString(R.string.timetable_downloaded) else -> getString(R.string.error_try_later) } - Snackbar.make(findViewById(R.id.drawer_layout), message, Snackbar.LENGTH_LONG).show() + try { + Snackbar.make(findViewById(R.id.drawer_layout), message, Snackbar.LENGTH_LONG).show() + } catch (e: IllegalArgumentException) { + } //todo refresh } - private fun selectTodayPage() { + private fun selectTodayPage() { //fixme does not work val today = (Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1) % 7 tabLayout.getTabAt(sectionsPagerAdapter!!.todayTab(today)) } @@ -195,21 +229,19 @@ if (id == R.id.action_change_type) { if (timetableType == "departure") { timetableType = "full" item.icon = (ResourcesCompat.getDrawable(resources, R.drawable.ic_timetable_departure, this.theme)) + sectionsPagerAdapter?.relativeTime = false if (sourceType == SOURCE_TYPE_STOP) - sectionsPagerAdapter?.departures = timetable.getStopDepartures(stopSegment!!.stop) + refreshAdapter(timetable.getStopDepartures(stopSegment!!.stop)) else - sectionsPagerAdapter?.departures = favourite!!.fullTimetable() - sectionsPagerAdapter?.relativeTime = false - sectionsPagerAdapter?.notifyDataSetChanged() + refreshAdapter(favourite!!.fullTimetable()) } else { timetableType = "departure" item.icon = (ResourcesCompat.getDrawable(resources, R.drawable.ic_timetable_full, this.theme)) + sectionsPagerAdapter?.relativeTime = true if (sourceType == SOURCE_TYPE_STOP) - sectionsPagerAdapter?.departures = Departure.createDepartures(stopSegment!!.stop) + refreshAdapter(Departure.createDepartures(stopSegment!!.stop)) else - sectionsPagerAdapter?.departures = favourite!!.allDepartures() - sectionsPagerAdapter?.relativeTime = true - sectionsPagerAdapter?.notifyDataSetChanged() + refreshAdapter(favourite!!.allDepartures()) } return true } @@ -255,6 +287,10 @@ 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()) { val d = ArrayList<String>() departures.mapTo(d) { it.toString() } @@ -268,17 +304,28 @@ } } } - inner class SectionsPagerAdapter(fm: FragmentManager, departures: Map<AgencyAndId, List<Departure>>) : FragmentStatePagerAdapter(fm) { + inner class SectionsPagerAdapter(fm: FragmentManager, departures: Map<AgencyAndId, List<Departure>>) : FragmentStatePagerAdapter(fm) { //todo swipe var departures: Map<AgencyAndId, List<Departure>> = departures set(value) { - refreshTabs() + 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 { - refreshTabs() + val tab = tabLayout.newTab() + tab.text = getString(R.string.today) + tabLayout.addTab(tab) + sectionsPagerAdapter?.notifyDataSetChanged() } var relativeTime = true @@ -291,30 +338,16 @@ timetable.calendarToMode(it).contains(today) }[0]) } - - private fun refreshTabs() { - tabLayout.removeAllTabs() - departures.keys.sortedBy { - timetable.calendarToMode(AgencyAndId(it.id)).sorted()[0] - }.mapTo(modes) { it } - modes.forEach { - val tab = tabLayout.newTab() - tab.text = timetable.calendarToMode(it) - .joinToString { resources.getStringArray(R.array.daysOfWeekShort)[it] } - tabLayout.addTab(tab) - } - if (modes.isEmpty()) { - val tab = tabLayout.newTab() - tab.text = getString(R.string.today) - tabLayout.addTab(tab) - } - } - 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 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 3e0201d46f297094460f835d3a3c68b052f21320..3bdb006976f4f1b60cf5e7c8797ca97445ed6526 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt @@ -27,6 +27,7 @@ const val ACTION_READY = "ml.adamsprogs.bimba.action.vm.ready" const val EXTRA_DEPARTURES = "ml.adamsprogs.bimba.extra.vm.departures" const val EXTRA_PLATE_ID = "ml.adamsprogs.bimba.extra.vm.plate" } + private var handler: Handler? = null private val tick6ZinaTim: Runnable = object : Runnable { override fun run() { @@ -217,7 +218,7 @@ val departuresForPlate = HashMap<AgencyAndId, HashSet<Departure>>() departuresForPlate[timetable.getServiceForToday()] = departures - val vm = vms[plateId.stop]!! + val vm = vms[plateId.stop] ?: HashSet() vm.remove(vm.filter { it.id == plateId }[0]) vm.add(Plate(plateId, departuresForPlate)) vms[plateId.stop] = vm diff --git a/app/src/main/java/ml/adamsprogs/bimba/gtfs/Trip.kt b/app/src/main/java/ml/adamsprogs/bimba/gtfs/Trip.kt index 6b37b64bbd71f5ce34029d20e1dcefa244495171..e7a45a9ebe4c3f90b1780bafd37b7da814d85441 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/gtfs/Trip.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/gtfs/Trip.kt @@ -1,6 +1,8 @@ package ml.adamsprogs.bimba.gtfs -data class Trip(val routeId: AgencyAndId, val serviceId: AgencyAndId, val id: ID, val headsign: String, val direction: Int, val shapeId: AgencyAndId) { +data class Trip(val routeId: AgencyAndId, val serviceId: AgencyAndId, val id: ID, + val headsign: String, val direction: Int, val shapeId: AgencyAndId, + val wheelchairAccessible: Boolean) { data class ID(val id: AgencyAndId, val modification: Set<Modification>, val isMain: Boolean) { data class Modification(val id: AgencyAndId, val stopRange: IntRange?) } 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 425df8f15ccc7fccf78d8e30a5100067a7462fe1..ba7a0ef79c1445d5c974c185c741ecec40f2c620 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Departure.kt @@ -64,6 +64,11 @@ moreDepartures[mode] as ArrayList) as ArrayList rolledDepartures[mode] = filterDepartures(rolledDepartures[mode]!!) } + println("rolled:") + rolledDepartures[AgencyAndId("4")]?.forEach { + println("${it.lineText} -> ${it.direction} @ ${it.time}") + } + return rolledDepartures } diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Plate.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Plate.kt index 2a661964d69a99829a85721de236790646fb761e..74de88e77e2cde41b56eb12ab07204a0b81a28a2 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Plate.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Plate.kt @@ -59,7 +59,6 @@ data class ID(val line: AgencyAndId, val stop: AgencyAndId, val headsign: String) : Serializable { companion object { fun fromString(string: String): ID { - println(string) val (line, stop, headsign) = string.split("|") return ID(AgencyAndId.convertFromString(line), AgencyAndId.convertFromString(stop), headsign) 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 0ef3fc05099e29112c9444407c4dcc9b2e84abb2..d6106c90f3bd0b2ec63a2fca13b377adbfc1986e 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt @@ -3,7 +3,6 @@ import android.content.Context import com.univocity.parsers.csv.CsvParser import com.univocity.parsers.csv.CsvParserSettings -import ml.adamsprogs.bimba.datasources.CacheManager import ml.adamsprogs.bimba.gtfs.AgencyAndId import ml.adamsprogs.bimba.gtfs.Route import ml.adamsprogs.bimba.gtfs.Trip @@ -28,23 +27,23 @@ return if (timetable == null || force) if (context != null) { timetable = Timetable() timetable!!.filesDir = context.filesDir - timetable!!.cacheManager = CacheManager.getCacheManager(context) - timetable as Timetable + //timetable!!.cacheManager = CacheManager.getCacheManager(context) + timetable!! } else throw IllegalArgumentException("new timetable requested and no context given") else - timetable as Timetable + timetable!! } } - private lateinit var cacheManager: CacheManager + //private lateinit var cacheManager: CacheManager private var _stops: List<StopSuggestion>? = null private lateinit var filesDir: File fun refresh() { - cacheManager.recreate(getStopDeparturesByPlates(cacheManager.keys().toSet())) + //cacheManager.recreate(getStopDeparturesByPlates(cacheManager.keys().toSet())) - getStops(true) + //getStops(true) } fun getStops(force: Boolean = false): List<StopSuggestion> { @@ -170,97 +169,67 @@ fun getStopDepartures(stopId: AgencyAndId): Map > { val plates = HashSet<Plate>() val toGet = HashSet<Plate>() - getTripsForStop(stopId) - .map { - Plate(Plate.ID(it.routeId, stopId, it.headsign), null) - } - .forEach { - if (cacheManager.has(it)) - plates.add(cacheManager.get(it)!!) - else { - toGet.add(it) - } + val trips = getTripsForStop(stopId) + trips.values.map { + Plate(Plate.ID(it.routeId, stopId, it.headsign), null) + }.forEach { + toGet.add(it) } - getStopDeparturesByPlates(toGet).forEach { cacheManager.push(it); plates.add(it) } + getStopDeparturesByPlates(toGet, trips).forEach { plates.add(it) } return Plate.join(plates) } fun getStopDepartures(plates: Set<Plate>): Map<AgencyAndId, ArrayList<Departure>> { - val result = HashSet<Plate>() - val toGet = HashSet<Plate>() - - for (plate in plates) { - if (cacheManager.has(plate)) - result.add(cacheManager.get(plate)!!) - else - toGet.add(plate) - } + val trips = HashMap<String, Trip>() + //todo get trips - getStopDeparturesByPlates(toGet).forEach { cacheManager.push(it); result.add(it) } - - return Plate.join(result) + return Plate.join(getStopDeparturesByPlates(plates, trips)) } - private fun getStopDeparturesByPlates(plates: Set<Plate>): Set<Plate> { + private fun getStopDeparturesByPlates(plates: Set<Plate>, trips: Map<String, Trip>): Set<Plate> { if (plates.isEmpty()) return emptySet() - return plates.map { getStopDeparturesByPlate(it) }.toSet() + return plates.map { getStopDeparturesByPlate(it, trips) }.toSet() } - private fun getStopDeparturesByPlate(plate: Plate): Plate { + private fun getStopDeparturesByPlate(plate: Plate, trips: Map<String, Trip>): Plate { 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") - 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 stopTimes[tripId] = stopTimesRow!! } 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 }) - - val trips = HashMap<String, Map<String, Any>>() - while ({ tripsRow = mapReader.read(header, processors); tripsRow }() != null) { - val tripId = tripsRow!!["trip_id"] as String - if (tripsRow!!["route_id"] as String == plate.id.line.id - && tripsRow!!["trip_headsign"] as String == plate.id.headsign) { //check if toLower is needed - trips[tripId] = tripsRow!! - } - } - mapReader.close() - trips.forEach { - val cal = JCalendar.getInstance() - val (h, m, s) = (stopTimes[it.key]!!["departure_time"] as String).split(":") - cal.set(JCalendar.HOUR_OF_DAY, h.toInt()) - cal.set(JCalendar.MINUTE, m.toInt()) - cal.set(JCalendar.SECOND, s.toInt()) - val time = cal.secondsAfterMidnight() - val serviceId = AgencyAndId(it.value["service_id"] as String) - val mode = calendarToMode(serviceId) - val lowFloor = it.value["wheelchair_accessible"] as String == "1" - val mod = explainModification(Trip(AgencyAndId(it.value["route_id"] as String), - serviceId, createTripId(it.value["trip_id"] as String), - it.value["trip_headsign"] as String, Integer.parseInt(it.value["direction_id"] as String), - AgencyAndId(it.value["shape_id"] as String)), Integer.parseInt(stopTimes[it.key]!!["stop_sequence"] as String)) + if (it.value.routeId == plate.id.line && + it.value.headsign == plate.id.headsign) { + val cal = JCalendar.getInstance() + val (h, m, s) = (stopTimes[it.key]!!["departure_time"] as String).split(":") + cal.set(JCalendar.HOUR_OF_DAY, h.toInt()) + cal.set(JCalendar.MINUTE, m.toInt()) + cal.set(JCalendar.SECOND, s.toInt()) + val time = cal.secondsAfterMidnight() + 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) + 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) + } } return resultPlate } @@ -295,6 +264,7 @@ throw IllegalArgumentException("Service $serviceId not in store") } private fun explainModification(trip: Trip, stopSequence: Int): List<String> { + return listOf("err") val definitions = getRouteForTrip(trip).modifications val explanations = ArrayList<String>() @@ -370,7 +340,7 @@ // store.allStopTimes.filter { it.stop.id == stopId }.forEach { lines.add(it.trip.route.id) } // return lines // } - fun getTripsForStop(stopId: AgencyAndId): Set<Trip> { + 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) @@ -399,14 +369,15 @@ 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)) + AgencyAndId(tripsRow!!["shape_id"] as String), + tripsRow!!["wheelchair_accessible"] as String == "1") } mapReader.close() - val filteredTrips = HashSet<Trip>() + val filteredTrips = HashMap<String, Trip>() tripIds.forEach { - filteredTrips.add(trips[it]!!) + filteredTrips[it] = trips[it]!! } return filteredTrips } @@ -433,16 +404,9 @@ } fun isEmpty(): Boolean { try { - val file = File(filesDir, "gtfs_files/feed_info.txt") - val mapReader = CsvMapReader(FileReader(file), CsvPreference.STANDARD_PREFERENCE) - val header = mapReader.getHeader(true) - - val processors = Array<CellProcessor?>(header.size, { null }) - if (mapReader.read(header, processors) == null) { - mapReader.close() + val file = File(filesDir, "gtfs_files/feed_info.txt").readText() + if (file == "") return true - } - mapReader.close() return false } catch (e: Exception) { return true