Author: Adam Pioterek <adam.pioterek@protonmail.ch>
faster iterating over trips & dynamic tabs in StopActivity
%!v(PANIC=String method: strings: negative Repeat count)
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 27baedc722898d0b2ae894c661346fb75c100ee8..fb942717e129f320c45b662e851bbfff1dabf6cc 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt @@ -178,13 +178,9 @@ Snackbar.make(findViewById(R.id.drawer_layout), message, Snackbar.LENGTH_LONG).show() //todo refresh } - private fun selectTodayPage() { //todo Services - val today = Calendar.getInstance() - when (today.get(Calendar.DAY_OF_WEEK)) { - Calendar.SATURDAY -> tabLayout.getTabAt(1)?.select() - Calendar.SUNDAY -> tabLayout.getTabAt(2)?.select() - else -> tabLayout.getTabAt(0)?.select() - } + private fun selectTodayPage() { + val today = (Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1) % 7 + tabLayout.getTabAt(sectionsPagerAdapter!!.todayTab(today)) } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -272,13 +268,47 @@ } } } - inner class SectionsPagerAdapter(fm: FragmentManager, var departures: Map<AgencyAndId, List<Departure>>) : FragmentStatePagerAdapter(fm) { + inner class SectionsPagerAdapter(fm: FragmentManager, departures: Map<AgencyAndId, List<Departure>>) : FragmentStatePagerAdapter(fm) { - private val modes = departures.keys.sortedBy { - timetable.calendarToMode(AgencyAndId(it.id)).sorted()[0] + var departures: Map<AgencyAndId, List<Departure>> = departures + set(value) { + refreshTabs() + } + + private var modes = ArrayList<AgencyAndId>() + + init { + refreshTabs() } var relativeTime = true + + fun todayTab(today: Int): Int { + if (modes.isEmpty()) + return 0 + return modes.indexOf(modes.filter { + 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 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 d8cf258e3ae5b3ff20508948a1601ebf6f843770..ae47d0daee70f338262a9699d121ec57fe1411a0 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/StopSpecifyActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/StopSpecifyActivity.kt @@ -65,7 +65,7 @@ context.startActivity(intent) } holder?.stopCode?.text = values.values.sortedBy { it.first }[position].first holder?.stopHeadlines?.text = values.values.sortedBy { it.first }[position].second - .sortedBy { it } + .sortedBy { it } // fixme natural sort .joinToString() } 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 e5b0a6357e3d5cb9d5065d89bcf81cd94ef5ebe7..3e0201d46f297094460f835d3a3c68b052f21320 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt @@ -126,7 +126,9 @@ private fun isAlreadyRequested(stopSegment: StopSegment): Boolean { val platesIn = requests[stopSegment.stop]?.map { it.plate }?.toSet() val platesOut = stopSegment.plates - return (platesOut == platesIn || platesIn!!.containsAll(platesOut!!)) + if (platesIn == null || platesIn.isEmpty()) + return false + return (platesOut == platesIn || platesIn.containsAll(platesOut!!)) } @@ -229,7 +231,8 @@ private fun sendResult(plateId: Plate.ID, departures: HashSet?) { val broadcastIntent = Intent() broadcastIntent.action = ACTION_READY broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT) - broadcastIntent.putStringArrayListExtra(EXTRA_DEPARTURES, departures?.map { it.toString() } as java.util.ArrayList<String>) + if (departures != null) + broadcastIntent.putStringArrayListExtra(EXTRA_DEPARTURES, departures.map { it.toString() } as ArrayList) broadcastIntent.putExtra(EXTRA_PLATE_ID, plateId) sendBroadcast(broadcastIntent) } 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 8eaeca2bed5e0f54c6e8eaac1d7d70e2ea692020..2a661964d69a99829a85721de236790646fb761e 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Plate.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Plate.kt @@ -59,6 +59,7 @@ 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) @@ -72,7 +73,7 @@ return line == other.line && stop == other.stop && headsign.toLowerCase() == other.headsign.toLowerCase() } override fun toString(): String { - return "$line|$stop$headsign" + return "$line|$stop|$headsign" } override fun hashCode(): Int { 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 dfd3cbf72ad518ab0eedc9b4d241bec7400026fe..39d6ebd24628bd690314b6159ac0b46d8f580bb1 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/StopSegment.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/StopSegment.kt @@ -7,7 +7,7 @@ data class StopSegment(val stop: AgencyAndId, var plates: Set<Plate.ID>?) : Parcelable { constructor(parcel: Parcel) : this( parcel.readSerializable() as AgencyAndId, - parcel.readString().split("|").map { Plate.ID.fromString(it) }.toSet() + parcel.readString().split(";").map { Plate.ID.fromString(it) }.toSet() ) companion object CREATOR : Parcelable.Creator<StopSegment> { @@ -27,7 +27,7 @@ override fun writeToParcel(dest: Parcel?, flags: Int) { dest?.writeSerializable(stop) if (plates != null) - dest?.writeString(plates!!.joinToString("|") { it.toString() }) + dest?.writeString(plates!!.joinToString(";") { it.toString() }) } override fun describeContents(): Int { 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 d1351d8d56a399e1ef17aa0959a036024fde5196..0ef3fc05099e29112c9444407c4dcc9b2e84abb2 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt @@ -72,14 +72,12 @@ return _stops!! } fun getHeadlinesForStop(stops: Set<AgencyAndId>): Map<AgencyAndId, Pair<String, Set<String>>> { - println(JCalendar.getInstance().timeInMillis) val trips = HashMap<String, HashSet<String>>() val routes = HashMap<String, Pair<String, String>>() val headsigns = HashMap<AgencyAndId, Pair<String, HashSet<String>>>() val settings = CsvParserSettings() settings.format.setLineSeparator("\r\n") - settings.format.quote='"' - println(JCalendar.getInstance().timeInMillis) + settings.format.quote = '"' val parser = CsvParser(settings) stops.forEach { trips[it.id] = HashSet() @@ -91,18 +89,11 @@ trips[stop]!!.add(it[0]) } } } - println(JCalendar.getInstance().timeInMillis) - - //val allTrips = trips.flatMap { it.value } - println(JCalendar.getInstance().timeInMillis) val stopsFile = File(filesDir, "gtfs_files/trips.txt") parser.parseAll(stopsFile).forEach { - //if (it[2] in allTrips) routes[it[2]] = Pair(it[0], it[3]) } - println(JCalendar.getInstance().timeInMillis) - trips.forEach { val headsign = HashSet<String>() @@ -111,7 +102,6 @@ headsign.add("${routes[it]!!.first} → ${routes[it]!!.second}") } headsigns[AgencyAndId(it.key)] = Pair(getStopCode(AgencyAndId(it.key)), headsign) } - println(JCalendar.getInstance().timeInMillis) return headsigns /* @@ -222,18 +212,16 @@ } private fun getStopDeparturesByPlate(plate: Plate): Plate { val resultPlate = Plate(Plate.ID(plate.id), HashMap()) - val trips = HashMap<String, Map<String, Any>>() - val stopTimesFile = File(filesDir, "gtfs_files/stop_times.txt") //todo stop_times_$stopId + 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) var stopTimesRow: Map<String, Any>? = null var processors = Array<CellProcessor?>(header.size, { null }) while ({ stopTimesRow = mapReader.read(header, processors); stopTimesRow }() != null) { - if ((stopTimesRow!!["stop_id"] as String) == plate.id.stop.id) { - val tripId = stopTimesRow!!["trip_id"] as String - trips[tripId] = stopTimesRow!! - } + val tripId = stopTimesRow!!["trip_id"] as String + stopTimes[tripId] = stopTimesRow!! } mapReader.close() @@ -243,31 +231,37 @@ 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 (tripId in trips && tripsRow!!["route_id"] as String == plate.id.line.id + if (tripsRow!!["route_id"] as String == plate.id.line.id && tripsRow!!["trip_headsign"] as String == plate.id.headsign) { //check if toLower is needed - val cal = JCalendar.getInstance() - val (h, m, s) = (trips[tripId]!!["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(tripsRow!!["service_id"] as String) - val mode = calendarToMode(serviceId) - val lowFloor = trips[tripId]!!["wheelchair_accessible"] as String == "1" - val mod = explainModification(Trip(AgencyAndId(tripsRow!!["route_id"] as String), - serviceId, createTripId(tripsRow!!["trip_id"] as String), - tripsRow!!["trip_headsign"] as String, Integer.parseInt(tripsRow!!["direction_id"] as String), - AgencyAndId(tripsRow!!["shape_id"] as String)), Integer.parseInt(trips[tripId]!!["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) + 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)) + + 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 } @@ -309,7 +303,7 @@ if (it.stopRange != null) { if (stopSequence in it.stopRange) explanations.add(definitions[it.id.id]!!) } else { - explanations.add(definitions[it.id.id]!!) + explanations.add(definitions[it.id.id]!!) //fixme null } } @@ -317,7 +311,7 @@ return explanations } private fun getRouteForTrip(trip: Trip): Route { - val routesFile = File(filesDir, "gtfs_files/routes.txt") + val routesFile = File(filesDir, "gtfs_files/trips.txt") var mapReader = CsvMapReader(FileReader(routesFile), CsvPreference.STANDARD_PREFERENCE) var header = mapReader.getHeader(true) @@ -336,7 +330,7 @@ mapReader.close() throw IllegalArgumentException("Trip ${trip.rawId} not in store") } - val tripsFile = File(filesDir, "gtfs_files/trips.txt") + val tripsFile = File(filesDir, "gtfs_files/routes.txt") mapReader = CsvMapReader(FileReader(tripsFile), CsvPreference.STANDARD_PREFERENCE) header = mapReader.getHeader(true) @@ -378,22 +372,20 @@ // } fun getTripsForStop(stopId: AgencyAndId): Set<Trip> { val tripIds = HashSet<String>() - val stopTimesFile = File(filesDir, "gtfs_files/stop_times.txt") + val stopTimesFile = File(filesDir, "gtfs_files/stop_times_${stopId.id}.txt") var mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE) var header = mapReader.getHeader(true) var stopTimesRow: Map<String, Any>? = null var processors = Array<CellProcessor?>(header.size, { null }) while ({ stopTimesRow = mapReader.read(header, processors); stopTimesRow }() != null) { - if ((stopTimesRow!!["stop_id"] as String) == stopId.id) { - val tripId = stopTimesRow!!["trip_id"] as String - tripIds.add(tripId) - } + val tripId = stopTimesRow!!["trip_id"] as String + tripIds.add(tripId) } mapReader.close() - val trips = HashSet<Trip>() + val trips = HashMap<String, Trip>() val tripsFile = File(filesDir, "gtfs_files/trips.txt") mapReader = CsvMapReader(FileReader(tripsFile), CsvPreference.STANDARD_PREFERENCE) header = mapReader.getHeader(true) @@ -402,17 +394,21 @@ var tripsRow: Map ? = null processors = Array(header.size, { null }) while ({ tripsRow = mapReader.read(header, processors); tripsRow }() != null) { val tripId = tripsRow!!["trip_id"] as String - if (tripId in tripIds) { - trips.add(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))) - } + 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)) } mapReader.close() - return trips + + val filteredTrips = HashSet<Trip>() + + tripIds.forEach { + filteredTrips.add(trips[it]!!) + } + return filteredTrips } private fun createTripId(rawId: String): Trip.ID { @@ -523,19 +519,17 @@ throw IllegalArgumentException("Route $number not in store") } fun getPlatesForStop(stop: AgencyAndId): Set<Plate.ID> { - val plates = HashSet<Plate.ID>() + val plates = HashMap<String, Plate.ID>() val tripIds = HashSet<String>() - val stopTimesFile = File(filesDir, "gtfs_files/stop_times.txt") + val stopTimesFile = File(filesDir, "gtfs_files/stop_times_${stop.id}.txt") var mapReader = CsvMapReader(FileReader(stopTimesFile), CsvPreference.STANDARD_PREFERENCE) var header = mapReader.getHeader(true) var stopTimesRow: Map<String, Any>? = null var processors = Array<CellProcessor?>(header.size, { null }) while ({ stopTimesRow = mapReader.read(header, processors); stopTimesRow }() != null) { - if ((stopTimesRow!!["stop_id"] as String) == stop.id) { - val tripId = stopTimesRow!!["trip_id"] as String - tripIds.add(tripId) - } + val tripId = stopTimesRow!!["trip_id"] as String + tripIds.add(tripId) } mapReader.close() @@ -547,13 +541,18 @@ var tripsRow: Map<String, Any>? = null processors = Array(header.size, { null }) while ({ tripsRow = mapReader.read(header, processors); tripsRow }() != null) { - if (tripsRow!!["trip_id"] as String in tripIds) { - plates.add(Plate.ID(AgencyAndId(tripsRow!!["route_id"] as String), stop, tripsRow!!["trip_headsign"] as String)) - } + 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() - return plates + val filteredPlates = HashSet<Plate.ID>() + tripIds.forEach { + filteredPlates.add(plates[it]!!) + } + + return filteredPlates } } diff --git a/app/src/main/res/layout/activity_stop.xml b/app/src/main/res/layout/activity_stop.xml index a56ab1e2d284b956c2fb13106f8d74bd7d2eabcc..6de28177cef9c063f6323d9a2e1dd99572646e63 100644 --- a/app/src/main/res/layout/activity_stop.xml +++ b/app/src/main/res/layout/activity_stop.xml @@ -5,8 +5,8 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent" - android:fitsSystemWindows="true" android:animateLayoutChanges="true" + android:fitsSystemWindows="true" tools:context="ml.adamsprogs.bimba.activities.StopActivity"> <android.support.design.widget.AppBarLayout @@ -30,27 +30,7 @@ <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <android.support.design.widget.TabItem - android:id="@+id/tabItem" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/tab_workday_text" /> - - <android.support.design.widget.TabItem - android:id="@+id/tabItem2" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/tab_saturday_text" /> - - <android.support.design.widget.TabItem - android:id="@+id/tabItem3" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/tab_sunday_text" /> - - </android.support.design.widget.TabLayout> + android:layout_height="wrap_content" /> </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 f32ba834964104a849d4039d163c2bffae2c0ea1..cc3dac1c13fa07f11275b532af588802f03da62b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -83,7 +83,18 @@ - Saturday
<item>Sunday</item> </string-array> + <string-array name="daysOfWeekShort"> + <item>Mon</item> + <item>Tue</item> + <item>Wed</item> + <item>Thu</item> + <item>Fri</item> + <item>Sat</item> + <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> </resources> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 9519974ec391690efa8cab3744f5951f0ba5019f..0ac6eb65d8ffdca102eac78605f52d186780b659 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -68,5 +68,15 @@- Freitag
<item>Samstag</item> <item>Sontag</item> </string-array> + <string-array name="daysOfWeekShort"> + <item>Mon</item> + <item>Die</item> + <item>Mit</item> + <item>Don</item> + <item>Fre</item> + <item>Sam</item> + <item>Son</item> + </string-array> <string name="timetable_converting">Fahrplan wird umgewandelt…</string> + <string name="today">Heute</string> </resources> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 37743291599fe9d5d4b0a64fab051653776a0088..fd5f16720a42519165056818678b1c28c809b79b 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -66,5 +66,15 @@- Venerdi
<item>Sabato</item> <item>Domenica</item> </string-array> + <string-array name="daysOfWeekShort"> + <item>Lu</item> + <item>Ma</item> + <item>Me</item> + <item>Gi</item> + <item>Ve</item> + <item>Sa</item> + <item>Do</item> + </string-array> <string name="timetable_converting">L’orario e stando convertito</string> + <string name="today">Oggi</string> </resources> diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 7d0841a718752fca9fb3cdc3a2cb4f6f1095844e..6f77515832fe84b73f3c13be4b6486b09a819a54 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -68,5 +68,15 @@- Piątek
<item>Sobota</item> <item>Niedziela</item> </string-array> + <string-array name="daysOfWeekShort"> + <item>Pn</item> + <item>Wt</item> + <item>Śr</item> + <item>Czw</item> + <item>Pt</item> + <item>S</item> + <item>N</item> + </string-array> <string name="timetable_converting">Konwertowanie rozkładu</string> + <string name="today">Dzisiaj</string> </resources> \ No newline at end of file