Author: Adam Pioterek <adam.pioterek@protonmail.ch>
favourites
%!v(PANIC=String method: strings: negative Repeat count)
diff --git a/.idea/misc.xml b/.idea/misc.xml index 635999df1e86791ad3787e455b4524e4d8879b93..ba7052b8197ddf8ba8756022d905d03055c7ad60 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -24,7 +24,7 @@ </value> </option> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/build/classes" /> </component> <component name="ProjectType"> diff --git a/app/build.gradle b/app/build.gradle index ddb34020f421af5d25578b83df9a2e7b365efb62..32d9dd851397095766b302faa0bacf997d79bdc8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 27 @@ -30,21 +31,17 @@ implementation 'com.android.support:appcompat-v7:27.1.0' implementation 'com.android.support:cardview-v7:27.1.0' implementation 'com.android.support:design:27.1.0' implementation 'com.android.support:support-vector-drawable:27.1.0' - testImplementation 'junit:junit:4.12' implementation 'com.android.support.constraint:constraint-layout:1.0.2' implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'com.github.arimorty:floatingsearchview:2.1.1' implementation 'com.google.code.gson:gson:2.8.1' implementation 'com.squareup.okhttp3:okhttp:3.8.1' implementation 'com.github.ghost1372:Mzip-Android:0.4.0' - implementation 'com.univocity:univocity-parsers:2.5.9' - implementation 'net.sf.supercsv:super-csv:2.4.0' implementation 'io.requery:sqlite-android:3.22.0' + testImplementation 'junit:junit:4.12' } repositories { maven { url "https://maven.google.com" } maven { url 'https://jitpack.io' } mavenCentral() } - -apply plugin: 'kotlin-android-extensions' 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 b468c5c6a00ffbb89e7324e729f38ec0ffb1ac7b..af04bc1b7093bbfe79b6cc8dc371bffa983ef5c6 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt @@ -1,36 +1,29 @@ package ml.adamsprogs.bimba.activities import android.annotation.SuppressLint +import android.app.Activity import android.content.* import android.os.* -import android.support.design.widget.Snackbar -import android.support.v7.app.* -import android.text.Html -import com.arlib.floatingsearchview.FloatingSearchView -import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion -import ml.adamsprogs.bimba.models.* -import kotlin.concurrent.thread -import android.app.Activity -import android.support.design.widget.NavigationView +import android.support.design.widget.* import android.support.v4.widget.* import android.support.v7.widget.* +import android.support.v7.app.* +import android.text.Html import android.view.* import android.view.inputmethod.InputMethodManager -import ml.adamsprogs.bimba.* -import android.os.Bundle -import android.util.Log +import kotlin.concurrent.thread +import kotlin.collections.ArrayList import kotlinx.android.synthetic.main.activity_dash.* -import ml.adamsprogs.bimba.datasources.TimetableDownloader -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 import java.util.* -import kotlin.collections.ArrayList -//todo cards https://enoent.fr/blog/2015/01/18/recyclerview-basics/ +import ml.adamsprogs.bimba.models.* +import ml.adamsprogs.bimba.* +import ml.adamsprogs.bimba.datasources.* +import ml.adamsprogs.bimba.models.suggestions.* + +import com.arlib.floatingsearchview.FloatingSearchView +import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion + //todo searchView integration class DashActivity : AppCompatActivity(), MessageReceiver.OnTimetableDownloadListener, FavouritesAdapter.OnMenuItemClickListener, Favourite.OnVmPreparedListener, @@ -194,8 +187,8 @@ } } private fun notifyTimetableValidity(daysTillInvalid: Int) { - val message = when(daysTillInvalid) { - -1 -> getString (R.string.timetable_validity_finished) + val message = when (daysTillInvalid) { + -1 -> getString(R.string.timetable_validity_finished) 0 -> getString(R.string.timetable_validity_today) 1 -> getString(R.string.timetable_validity_tomorrow) else -> return @@ -210,6 +203,9 @@ } private fun prepareFavourites() { favourites = FavouriteStorage.getFavouriteStorage(context) + favourites.forEach { + it.addOnVmPreparedListener(this) + } val layoutManager = LinearLayoutManager(context) favouritesList = favourites_list adapter = FavouritesAdapter(context, favourites, this, this) @@ -219,12 +215,11 @@ favouritesList.layoutManager = layoutManager } override fun onVmPrepared() { - Log.i("VM", "DataSetChange") favouritesList.adapter.notifyDataSetChanged() } private fun getSuggestions() { - suggestions = (timetable.getStopSuggestions(context) + timetable.getLineSuggestions()).sorted() //todo<p:v+1> + bike stations, &c + suggestions = (timetable.getStopSuggestions(context) + timetable.getLineSuggestions()).sorted() //todo<p:v+1> + bike stations, train stations, &c } private fun prepareListeners() { @@ -263,7 +258,7 @@ favourites.deregisterOnVm(receiver, context) unregisterReceiver(receiver) } - fun deAccent(str: String): String { + private fun deAccent(str: String): String { var result = str.replace('ę', 'e') result = result.replace('ó', 'o') result = result.replace('ą', 'a') @@ -304,7 +299,7 @@ } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { if (requestCode == REQUEST_EDIT_FAVOURITE) { - if (resultCode == Activity.RESULT_OK) { + if (resultCode == Activity.RESULT_OK) { // todo change favourite content (shown) immediately val name = data.getStringExtra(EditFavouriteActivity.EXTRA_NEW_NAME) val positionBefore = data.getIntExtra(EditFavouriteActivity.EXTRA_POSITION_BEFORE, -1) //adapter.favourites = favourites.favouritesList @@ -335,9 +330,12 @@ override fun onItemClicked(position: Int) { if (actionMode != null) { toggleSelection(position) + } else { + val intent = Intent(context, StopActivity::class.java) + intent.putExtra(StopActivity.SOURCE_TYPE, StopActivity.SOURCE_TYPE_FAV) + intent.putExtra(StopActivity.EXTRA_FAVOURITE, favourites[position]) + startActivity(intent) } - - //todo else -> StopActivity } override fun onItemLongClicked(position: Int): Boolean { diff --git a/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt b/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt index 012c1a1a943fcd85a366c4d3cf6538c91bfaa6d2..cad776b9fd5704eaa69c0979307298bacd3f6942 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/TimetableDownloader.kt @@ -12,8 +12,6 @@ import android.os.Build import com.google.gson.Gson import com.google.gson.JsonArray import com.google.gson.JsonObject -import com.univocity.parsers.csv.CsvParser -import com.univocity.parsers.csv.CsvParserSettings import ir.mahdi.mzip.zip.ZipArchive import ml.adamsprogs.bimba.NetworkStateReceiver import ml.adamsprogs.bimba.NotificationChannels @@ -122,7 +120,7 @@ } } private fun createIndices() { - val settings = CsvParserSettings() + /*val settings = CsvParserSettings() settings.format.setLineSeparator("\r\n") settings.format.quote = '"' settings.isHeaderExtractionEnabled = true @@ -161,6 +159,7 @@ println(Calendar.getInstance().timeInMillis) serialiseIndex(stopsIndex, stopIndexFile) serialiseIndex(tripsIndex, tripIndexFile) + */ } private fun serialiseIndex(index: HashMap<String, List<Long>>, file: File) { 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 2cd8d9151394dc88ddc6fa01411f15edf001a85f..0c1acdd386bc2ab37ca55ab20d16371c1b5a5cc8 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt @@ -37,7 +37,7 @@ downloadVM() } } private val requests = HashMap<AgencyAndId, Set<Request>>() - private val vms = HashMap<AgencyAndId, HashSet<Plate>>() //HashSet<Departure>? + private val vms = HashMap<AgencyAndId, Set<Plate>>() //HashSet<Departure>? private val timetable = try { Timetable.getTimetable(this) } catch (e: NullPointerException) { @@ -154,7 +154,7 @@ } private fun downloadVM(stopSegment: StopSegment) { if (!NetworkStateReceiver.isNetworkAvailable(this)) { - vms[stopSegment.stop] = stopSegment.plates!!.map { Plate(it, null) }.toSet() as HashSet<Plate> + vms[stopSegment.stop] = HashSet(stopSegment.plates!!.map { Plate(it, null) }.toSet()) stopSegment.plates!!.forEach { sendResult(it, null) } @@ -221,10 +221,10 @@ val departuresForPlate = HashMap>() departuresForPlate[timetable.getServiceForToday()] = departures val vm = vms[plateId.stop] ?: HashSet() try { - vm.remove(vm.filter { it.id == plateId }[0]) + (vm as HashSet).remove(vm.filter { it.id == plateId }[0]) } catch (e: IndexOutOfBoundsException) { } - vm.add(Plate(plateId, departuresForPlate)) + (vm as HashSet).add(Plate(plateId, departuresForPlate)) vms[plateId.stop] = vm if (departures.isEmpty()) sendResult(plateId, null) 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 1381ac50abf19a581df0055d375aaeeb96ee03de..ddb83ca5ab2770ba195ebd500488fa3badb5f9eb 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,13 @@ class Favourite : Parcelable, MessageReceiver.OnVmListener { private var isRegisteredOnVmListener: Boolean = false var name: String private set - var timetables: HashSet<StopSegment> + var segments: HashSet<StopSegment> private set - private var vmDepartures = HashMap<Plate.ID, Set<Departure>>() + private var vmDepartures = HashMap<Plate.ID, List<Departure>>() val timetable = Timetable.getTimetable() val size - get() = timetables.sumBy { + get() = segments.sumBy { it.size } @@ -44,12 +44,12 @@ val array = parcel.readParcelableArray(StopSegment::class.java.classLoader) array.forEach { set.add(it as StopSegment) } - this.timetables = set + this.segments = set } constructor(name: String, timetables: HashSet<StopSegment>) { this.name = name - this.timetables = timetables + this.segments = timetables } @@ -59,20 +59,20 @@ } override fun writeToParcel(dest: Parcel?, flags: Int) { dest?.writeString(name) - val parcelableSegments = timetables.map { it }.toTypedArray() + val parcelableSegments = segments.map { it }.toTypedArray() dest?.writeParcelableArray(parcelableSegments, flags) } private fun filterVmDepartures() { this.vmDepartures.forEach { - val newSet = it.value - .filter { it.timeTill(true) >= 0 }.toSet() - this.vmDepartures[it.key] = newSet + val newVms = it.value + .filter { it.timeTill(true) >= 0 }.sortedBy { it.timeTill() } + this.vmDepartures[it.key] = newVms } } fun delete(plateId: Plate.ID) { - timetables.forEach { + segments.forEach { it.remove(plateId) } } @@ -83,7 +83,7 @@ receiver.addOnVmListener(this) isRegisteredOnVmListener = true - timetables.forEach { + segments.forEach { val intent = Intent(context, VmClient::class.java) intent.putExtra("stop", it) intent.action = "request" @@ -97,7 +97,7 @@ if (isRegisteredOnVmListener) { receiver.removeOnVmListener(this) isRegisteredOnVmListener = false - timetables.forEach { + segments.forEach { val intent = Intent(context, VmClient::class.java) intent.putExtra("stop", it) intent.action = "remove" @@ -122,7 +122,7 @@ } fun nextDeparture(): Departure? { filterVmDepartures() - if (timetables.isEmpty() && vmDepartures.isEmpty()) + if (segments.isEmpty() && vmDepartures.isEmpty()) return null if (vmDepartures.isNotEmpty()) { @@ -142,7 +142,7 @@ .filter { it.timeTill(true) >= 0 } .minBy { it.timeTill(true) } } - private fun nowDepartures(): ArrayList<Departure> { + private fun nowDepartures(): List<Departure> { val today = timetable.getServiceForToday() val tomorrowCal = Calendar.getInstance() tomorrowCal.add(Calendar.DAY_OF_MONTH, 1) @@ -154,18 +154,14 @@ } val departures = fullTimetable() - println(departures.keys.joinToString(",")) val todayDepartures = departures[today]!! - val tomorrowDepartures = ArrayList<Departure>() - val twoDayDepartures = ArrayList<Departure>() + val tomorrowDepartures = ArrayList<Departure>() /** todo as in {@link Departure.rollDeparture rollDeparture} **/ if (tomorrow != -1) { departures[tomorrow]!!.mapTo(tomorrowDepartures) { it.copy() } tomorrowDepartures.forEach { it.tomorrow = true } } - todayDepartures.forEach { twoDayDepartures.add(it) } - tomorrowDepartures.forEach { twoDayDepartures.add(it) } - return twoDayDepartures + return todayDepartures + tomorrowDepartures } fun allDepartures(): Map<AgencyAndId, List<Departure>> { @@ -180,27 +176,20 @@ val departures = fullTimetable() return Departure.rollDepartures(departures) } - fun fullTimetable(): Map<AgencyAndId, List<Departure>> { - 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 - } + fun fullTimetable() = timetable.getStopDeparturesBySegments(segments) override fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID) { - if (timetables.any { it.contains(plateId) }) { + if (segments.any { it.contains(plateId) }) { if (vmDepartures == null) this.vmDepartures.remove(plateId) else - this.vmDepartures[plateId] = vmDepartures + this.vmDepartures[plateId] = vmDepartures.sortedBy { it.timeTill() } } filterVmDepartures() + //todo<p:1> think about tick + onVmPreparedListeners.forEach { + it.onVmPrepared() + } } interface OnVmPreparedListener { 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 d05a401a423698f043129f70e99908351a55a3b5..7e78913947deebee44524e88984d9e3d0b71f4bc 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteEditRowAdapter.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteEditRowAdapter.kt @@ -17,9 +17,9 @@ override fun onBindViewHolder(holder: ViewHolder, position: Int) { val timetable = Timetable.getTimetable() val favourites = FavouriteStorage.getFavouriteStorage() - val id = favourite.timetables.flatMap { it.plates!! }.sortedBy { "${it.line}${it.stop}"}[position] + val id = favourite.segments.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}" + val favouriteElement = "${timetable.getStopName(plate.id.stop)} ( ${timetable.getStopCode(plate.id.stop)}):\n${plate.id.line} → ${plate.id.headsign}" holder.rowTextView.text = favouriteElement // holder?.splitButton?.setOnClickListener { // favourites.detach(favourite.name, id, favouriteElement) 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 0dbb60de3e15c7f9188a53bbc9f41de42ea939db..dd8b3a8b0b317c5234e6a68f30999ca0fbe39985 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteStorage.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/FavouriteStorage.kt @@ -80,7 +80,7 @@ private fun serialize() { val rootObject = JsonObject() for ((name, favourite) in favourites) { val timetables = JsonArray() - for (timetable in favourite.timetables) { + for (timetable in favourite.segments) { val segment = JsonObject() segment.addProperty("stop", timetable.stop.id) val plates = JsonArray() @@ -119,7 +119,7 @@ if (names.size < 2) return val newFavourite = Favourite(names[0], HashSet()) for (name in names) { - newFavourite.timetables.addAll(favourites[name]!!.timetables) + newFavourite.segments.addAll(favourites[name]!!.segments) favourites.remove(name) } favourites[names[0]] = newFavourite 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 b22b2f48b568c1569d7f6f920ef74ea0dddf115e..9bac859e4e93f8dd60b4cdd8e4017e9371238983 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt @@ -1,31 +1,14 @@ package ml.adamsprogs.bimba.models +import android.annotation.SuppressLint import android.content.Context -import android.database.Cursor -import android.database.CursorIndexOutOfBoundsException +import android.database.* import android.database.sqlite.SQLiteDatabase -import com.google.gson.Gson -import com.google.gson.JsonObject -import com.univocity.parsers.csv.CsvParser -import com.univocity.parsers.csv.CsvParserSettings -import ml.adamsprogs.bimba.R -import ml.adamsprogs.bimba.getColour -import ml.adamsprogs.bimba.getSecondaryExternalFilesDir -import ml.adamsprogs.bimba.models.gtfs.AgencyAndId -import ml.adamsprogs.bimba.models.gtfs.Route -import ml.adamsprogs.bimba.models.gtfs.Trip -import ml.adamsprogs.bimba.models.suggestions.LineSuggestion -import ml.adamsprogs.bimba.models.suggestions.StopSuggestion -import ml.adamsprogs.bimba.secondsAfterMidnight -import org.supercsv.cellprocessor.ift.CellProcessor -import org.supercsv.io.CsvMapReader -import org.supercsv.prefs.CsvPreference -import java.io.File -import java.io.FileReader -import kotlin.collections.ArrayList -import kotlin.collections.HashMap -import kotlin.collections.HashSet -import kotlin.system.measureTimeMillis +import ml.adamsprogs.bimba.* +import ml.adamsprogs.bimba.models.gtfs.* +import ml.adamsprogs.bimba.models.suggestions.* +import java.io.* +import kotlin.collections.* import java.util.Calendar as JCalendar class Timetable private constructor() { @@ -50,7 +33,6 @@ private lateinit var db: SQLiteDatabase private var _stops: List<StopSuggestion>? = null private lateinit var filesDir: File - private val tripsCache = HashMap<String, Array<String>>() fun refresh() { } @@ -144,61 +126,6 @@ 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 parseStopTimesWithStopIndex(stopIds: List<String>, process: (Array<String>) -> Unit) { - val lines = readIndex(stopIds, File(filesDir, "gtfs_files/stop_index.yml")) - parseStopTimesWithIndex(lines, process) - } - - private fun parseStopTimesWithTripIndex(tripIds: List<String>, process: (Array<String>) -> Unit) { - val lines = readIndex(tripIds, File(filesDir, "gtfs_files/trip_index.yml")) - parseStopTimesWithIndex(lines, process) - } - - private fun readIndex(ids: List<String>, indexFile: File): List<Long> { - val index = HashMap<String, List<Long>>() - - val reader = indexFile.bufferedReader() //fixme 5s - val json = Gson().fromJson(reader.readText(), JsonObject::class.java) - reader.close() - - json.entrySet().forEach { - //fixme 3s - index[it.key] = ArrayList() - it.value.asJsonArray.mapTo(index[it.key] as ArrayList) { it.asLong } - } - return index.filter { it.key in ids }.flatMap { it.value }.sorted() - } - - private fun parseStopTimesWithIndex(lines: List<Long>, process: (Array<String>) -> Unit) { - val settings = CsvParserSettings() - settings.format.setLineSeparator("\r\n") - settings.format.quote = '"' - settings.isHeaderExtractionEnabled = true - val parser = CsvParser(settings) - - parser.beginParsing(File(filesDir, "gtfs_files/stop_times.txt")) - lines.forEach { - //fixme 3s -// println("At line ${parser.context.currentLine()}, skipping ${lines[lineKey] - parser.context.currentLine() - 1} lines to line ${lines[lineKey]}") - parser.context.skipLines(it - parser.context.currentLine() - 1) - val line = parser.parseNext() - process(line) - } - - } - - private fun createTripCache() { - val settings = CsvParserSettings() - settings.format.setLineSeparator("\r\n") - settings.format.quote = '"' - settings.isHeaderExtractionEnabled = true - 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 cursor = db.rawQuery("select stop_name from stops where stop_id = ?", arrayOf(stopId.id)) @@ -219,118 +146,82 @@ return code } - fun getLineNumber(lineId: AgencyAndId): String { - val file = File(filesDir, "gtfs_files/routes.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!!["route_id"] as String) == lineId.id) { - mapReader.close() - return row!!["route_short_name"] as String - } - } - mapReader.close() - throw IllegalArgumentException("Route $lineId not in store") - } - fun getStopDepartures(stopId: AgencyAndId): Map<AgencyAndId, List<Departure>> { val map = HashMap<AgencyAndId, ArrayList<Departure>>() - val measure = measureTimeMillis { - - val cursor = db.rawQuery("select route_id, service_id, departure_time, " + - "wheelchair_accessible, stop_sequence, trip_id, trip_headsign, route_desc " + - "from stop_times natural join trips natural join routes where stop_id = ?", - arrayOf(stopId.id)) - - while (cursor.moveToNext()) { - val line = AgencyAndId(cursor.getString(0)) - val service = AgencyAndId(cursor.getInt(1).toString()) - val mode = calendarToMode(service) - val time = parseTime(cursor.getString(2)) - val lowFloor = cursor.getInt(3) == 1 - val stopSequence = cursor.getInt(4) - val tripId = createTripId(cursor.getString(5)) - val headsign = cursor.getString(6) - val desc = cursor.getString(7) + val cursor = db.rawQuery("select route_id, service_id, departure_time, " + + "wheelchair_accessible, stop_sequence, trip_id, trip_headsign, route_desc " + + "from stop_times natural join trips natural join routes where stop_id = ?", + arrayOf(stopId.id)) - val modifications = Route.createModifications(desc) + while (cursor.moveToNext()) { + val line = AgencyAndId(cursor.getString(0)) + val service = AgencyAndId(cursor.getInt(1).toString()) + val mode = calendarToMode(service) + val time = parseTime(cursor.getString(2)) + val lowFloor = cursor.getInt(3) == 1 + val stopSequence = cursor.getInt(4) + val tripId = createTripId(cursor.getString(5)) + val headsign = cursor.getString(6) + val desc = cursor.getString(7) - val modification = explainModification(tripId, stopSequence, modifications) - val departure = Departure(line, mode, time, lowFloor, modification, headsign) - if (map[service] == null) - map[service] = ArrayList() - map[service]!!.add(departure) - } + val modifications = Route.createModifications(desc) - cursor.close() - map.forEach { it.value.sortBy { it.time } } + val modification = explainModification(tripId, stopSequence, modifications) + val departure = Departure(line, mode, time, lowFloor, modification, headsign) + if (map[service] == null) + map[service] = ArrayList() + map[service]!!.add(departure) } - //println(measure) + cursor.close() + map.forEach { it.value.sortBy { it.time } } return map } - fun getTrip(id: String): Trip { - val cursor = db.rawQuery("select * from trips where trip_id = ?", arrayOf(id)) + fun getStopDeparturesBySegments(segments: HashSet<StopSegment>): Map<AgencyAndId, List<Departure>> { + val wheres = segments.flatMap { + it.plates?.map { + "(stop_id = ${it.stop} and route_id = '${it.line}' and trip_headsign = '${it.headsign}')" + } ?: listOf() + }.joinToString(" or ") - val trip = Trip( - AgencyAndId(cursor.getString(0)), - AgencyAndId(cursor.getInt(1).toString()), - createTripId(cursor.getString(2)), - cursor.getString(3), - cursor.getInt(4), - AgencyAndId(cursor.getInt(5).toString()), - cursor.getInt(6) == 1 - ) + val cursor = db.rawQuery("select route_id, service_id, departure_time, " + + "wheelchair_accessible, stop_sequence, trip_id, trip_headsign, route_desc " + + "from stop_times natural join trips natural join routes where $wheres", null) + val map = parseDeparturesCursor(cursor) cursor.close() - return trip + return map } - 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>>() + private fun parseDeparturesCursor(cursor: Cursor): Map<AgencyAndId, List<Departure>> { + val map = HashMap<AgencyAndId, ArrayList<Departure>>() - val tripsInStop = HashMap<String, Pair<Int, Int>>() + while (cursor.moveToNext()) { + val line = AgencyAndId(cursor.getString(0)) + val service = AgencyAndId(cursor.getInt(1).toString()) + val mode = calendarToMode(service) + val time = parseTime(cursor.getString(2)) + val lowFloor = cursor.getInt(3) == 1 + val stopSequence = cursor.getInt(4) + val tripId = createTripId(cursor.getString(5)) + val headsign = cursor.getString(6) + val desc = cursor.getString(7) - parseStopTimesWithStopIndex(listOf(segment.stop.id)) { - tripsInStop[it[0]] = Pair(parseTime(it[2]), it[4].toInt()) - } + val modifications = Route.createModifications(desc) - val file = File(filesDir, "gtfs_files/calendar.txt") - val settings = CsvParserSettings() - settings.format.setLineSeparator("\r\n") - settings.format.quote = '"' - settings.isHeaderExtractionEnabled = true - val parser = CsvParser(settings) - parser.parseAll(file).forEach { - departures[AgencyAndId(it[0])] = ArrayList() + val modification = explainModification(tripId, stopSequence, modifications) + val departure = Departure(line, mode, time, lowFloor, modification, headsign) + if (map[service] == null) + map[service] = ArrayList() + map[service]!!.add(departure) } - tripsInStop.forEach { - //fixme this part is long --- cache is the only option - departures[trips[it.key]!!.serviceId]!!.add(Departure(trips[it.key]!!.routeId, // fixme null? - calendarToMode(trips[it.key]!!.serviceId), - it.value.first, trips[it.key]!!.wheelchairAccessible, - explainModification(trips[it.key]!!, it.value.second), - trips[it.key]!!.headsign)) - } - println("getStopDeparturesBySegment: ${JCalendar.getInstance().timeInMillis}") - val sortedDepartures = HashMap<AgencyAndId, List<Departure>>() - departures.keys.forEach { - sortedDepartures[it] = departures[it]!!.sortedBy { it.time } - } - println("</>: ${JCalendar.getInstance().timeInMillis}") - println("</>: ${JCalendar.getInstance().timeInMillis}") - return sortedDepartures*/ - TODO("FIXME") + map.forEach { it.value.sortBy { it.time } } + return map } + private fun parseTime(time: String): Int { val cal = JCalendar.getInstance() @@ -367,16 +258,6 @@ } } return explanations - } - - private fun getRouteForTrip(trip: Trip): Route { - val cursor = db.rawQuery("select * from routes natural join trips where trip_id = ?", - arrayOf(trip.id.rawId)) - - cursor.moveToNext() - val route = createRouteFromCursorRow(cursor) - cursor.close() - return route } private fun createRouteFromCursorRow(cursor: Cursor): Route { @@ -392,31 +273,6 @@ return Route.create(routeId, agencyId, shortName, longName, desc, type, colour, textColour) } - fun getTripsForStop(stopId: AgencyAndId): HashMap<String, Trip> { - val tripIds = HashSet<String>() - - parseStopTimesWithStopIndex(listOf(stopId.id)) { - tripIds.add(it[0]) - } - - val filteredTrips = HashMap<String, Trip>() - - tripIds.forEach { - filteredTrips[it] = tripFromCache(it) - } - 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]), - AgencyAndId(tripsCache[id]!![5]), tripsCache[id]!![6] == "1") - - } - private fun createTripId(rawId: String): Trip.ID { if (rawId.contains('^')) { var modification = rawId.split("^")[1] @@ -439,14 +295,19 @@ } else return Trip.ID(rawId, AgencyAndId(rawId), HashSet(), false) } + @SuppressLint("Recycle") fun isEmpty(): Boolean { - return try { - File(filesDir, "timetable.db") - //todo check if not empty - false + var result: Boolean + var cursor: Cursor? = null + try { + cursor = db.rawQuery("select * from feed_info", null) + result = !cursor.moveToNext() } catch (e: Exception) { - true + result = true + } finally { + cursor!!.close() } + return result } fun getValidSince(): String { @@ -496,23 +357,6 @@ throw IllegalArgumentException() } } - fun getLineForNumber(number: String): AgencyAndId { - val file = File(filesDir, "gtfs_files/routes.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!!["route_short_name"] as String) == number) { - mapReader.close() - return AgencyAndId(row!!["route_id"] as String) - } - } - mapReader.close() - throw IllegalArgumentException("Route $number not in store") - } - fun getPlatesForStop(stop: AgencyAndId): Set<Plate.ID> { val plates = HashSet<Plate.ID>() val cursor = db.rawQuery("select route_id, trip_headsign " + @@ -530,28 +374,7 @@ return plates } fun getTripGraphs(id: AgencyAndId): List<Map<Int, List<Int>>> { - val tripsToDo = HashSet<String>() - if (tripsCache.isEmpty()) - createTripCache() - tripsCache.forEach { - if (it.value[0] == id.id) { - tripsToDo.add(it.key) //todo and direction {0,1} - } - } - parseStopTimesWithTripIndex(tripsToDo.toList()) { - //todo create graph - } - val map = ArrayList<HashMap<Int, List<Int>>>() - val map0 = HashMap<Int, List<Int>>() - map0[0] = listOf(1, 2) - map0[1] = listOf(3, 4, 5) - map.add(map0) - val map1 = HashMap<Int, List<Int>>() - map1[0] = listOf(1) - map1[1] = listOf(2, 3, 4) - map1[2] = listOf(4, 5) - map.add(map1) - return map + TODO("create graph") } }