Author: Adam Pioterek <adam.pioterek@protonmail.ch>
edit favourites
%!v(PANIC=String method: strings: negative Repeat count)
diff --git a/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt b/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt index f45c01a976e60d588a49bce3749a06e20b9ee428..a484c43a07383038d0b9f3b60c967bc4b030e26e 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt @@ -11,7 +11,7 @@ import kotlin.collections.HashMap //todo make singleton class ProviderProxy(context: Context? = null) { - private val vmStopsClient = VmClient.getVmStopClient() + private val vmClient = VmClient.getVmClient() private var timetable: Timetable = Timetable.getTimetable(context) private var suggestions = emptyList<GtfsSuggestion>() private val requests = HashMap<String, Request>() @@ -35,7 +35,7 @@ } private suspend fun getStopSuggestions(query: String): List<StopSuggestion> { val vmSuggestions = withContext(DefaultDispatcher) { - vmStopsClient.getStops(query) + vmClient.getStops(query) } return if (vmSuggestions.isEmpty() and !timetable.isEmpty()) { @@ -67,7 +67,7 @@ fun getSheds(name: String, callback: (Map<String, Set<String>>) -> Unit) { launch(UI) { val sheds = withContext(DefaultDispatcher) { - val vmSheds = vmStopsClient.getSheds(name) + val vmSheds = vmClient.getSheds(name) if (vmSheds.isEmpty() and !timetable.isEmpty()) { timetable.getHeadlinesForStop(name) @@ -158,6 +158,39 @@ emptyMap() else timetable.getStopDeparturesBySegments(stopSegments) + } + + fun fillStopSegment(stopSegment: StopSegment, callback: (StopSegment?) -> Unit) { + launch(UI) { + withContext(DefaultDispatcher) { + callback(fillStopSegment(stopSegment)) + } + } + } + + suspend fun fillStopSegment(stopSegment: StopSegment): StopSegment? { + if (stopSegment.plates != null) + return stopSegment + + return if (timetable.isEmpty()) + vmClient.getDirections(stopSegment.stop) + else + timetable.getHeadlinesForStopCode(stopSegment.stop) + } + + fun getStopName(stopCode: String, callback: (String?) -> Unit) { + launch(UI) { + withContext(DefaultDispatcher) { + callback(getStopName(stopCode)) + } + } + } + + suspend fun getStopName(stopCode: String): String? { + return if (timetable.isEmpty()) + vmClient.getName(stopCode) + else + timetable.getStopName(stopCode) } interface OnDeparturesReadyListener { 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 047a1243509f73149635309d43770b932d9215b5..7bcd314cf54224f830474503d13805caf820e671 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt @@ -72,7 +72,7 @@ showFab() val layoutManager = LinearLayoutManager(this) departuresList.addItemDecoration(DividerItemDecoration(departuresList.context, layoutManager.orientation)) - departuresList.adapter = DeparturesAdapter(this, emptyList(), true) + departuresList.adapter = DeparturesAdapter(this, null, true) adapter = departuresList.adapter as DeparturesAdapter departuresList.layoutManager = layoutManager diff --git a/app/src/main/java/ml/adamsprogs/bimba/collections/FavouriteStorage.kt b/app/src/main/java/ml/adamsprogs/bimba/collections/FavouriteStorage.kt index 948b545155f4a33101d0b41d762abe2782467422..d937e039aa9cc926d197f7bebe913883845281a5 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/collections/FavouriteStorage.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/collections/FavouriteStorage.kt @@ -85,9 +85,11 @@ positionIndex.remove(name) serialize() } - fun delete(name: String, plate: Plate.ID) { - favourites[name]?.delete(plate) - serialize() + fun delete(name: String, plate: Plate.ID): Boolean { + return favourites[name]?.delete(plate).let { + serialize() + it + } ?: false } private fun serialize() { 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 a13d730482d3b2484e636b4c993d24fe1abc9f29..af652ab3dad6fec82a7bb93d85733aac2cc10f56 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt @@ -3,6 +3,8 @@ import com.google.gson.* import kotlinx.coroutines.experimental.* import ml.adamsprogs.bimba.NetworkStateReceiver +import ml.adamsprogs.bimba.models.Plate +import ml.adamsprogs.bimba.models.StopSegment import ml.adamsprogs.bimba.models.suggestions.* import okhttp3.* import java.io.IOException @@ -14,7 +16,7 @@ class VmClient { companion object { private var vmClient: VmClient? = null - fun getVmStopClient(): VmClient { + fun getVmClient(): VmClient { if (vmClient == null) vmClient = VmClient() return vmClient!! @@ -100,5 +102,32 @@ Gson().fromJson(responseBody, JsonObject::class.java) } catch (e: JsonSyntaxException) { JsonObject() } + } + + suspend fun getName(symbol: String): String? { + val timesResponse = withContext(DefaultDispatcher) { + makeRequest("getTimes", """{"symbol": "$symbol"}""") + } + if (!timesResponse.has("success")) + return null + + return timesResponse["success"].asJsonObject["bollard"].asJsonObject["name"].asString + } + + suspend fun getDirections(symbol: String): StopSegment? { + val name = getName(symbol) + val directionsResponse = makeRequest("getBollardsByStopPoint", """{"name": "$name"}""") + + if (!directionsResponse.has("success")) + return null + + return StopSegment(symbol, + directionsResponse["success"].asJsonObject["bollards"].asJsonArray.filter { + it.asJsonObject["bollard"].asJsonObject["tag"].asString == symbol + }[0].asJsonObject["directions"].asJsonArray.map { + it.asJsonObject.let { direction -> + Plate.ID(direction["lineName"].asString, symbol, direction["direction"].asString) + } + }.toSet()) } } diff --git a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmService.kt b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmService.kt index 06aa77db42b01d4c0e94ee094007eb064c51e9dd..710aa49f6b3ad7a7f661864ea4231306aa403f20 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmService.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmService.kt @@ -50,7 +50,9 @@ handler!!.postDelayed(tick6ZinaTim, TICK_6_ZINA_TIM) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - val stopCode = intent?.getStringExtra("stop")!! + if (intent == null) + return START_STICKY + val stopCode = intent.getStringExtra("stop")!! val action = intent.action val once = intent.getBooleanExtra("once", false) if (action == "request") { @@ -119,7 +121,7 @@ sendResult(stopCode, null, null) return } - val javaRootMapObject = VmClient.getVmStopClient().makeRequest("getTimes", """{"symbol": "$stopCode"}""") + val javaRootMapObject = VmClient.getVmClient().makeRequest("getTimes", """{"symbol": "$stopCode"}""") if (!javaRootMapObject.has("success")) { sendResult(stopCode, null, 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 a1e71f73cbed2a420f310de9ce9b85d03d31295c..8e2a99c5f289cabf1caacd451ff3bd91cb26d5d1 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt @@ -94,11 +94,13 @@ } mapFile.writeText(map) } - fun delete(plateId: Plate.ID) { + fun delete(plateId: Plate.ID): Boolean { segments.forEach { - it.remove(plateId) + if (!it.remove(plateId)) + return false } removeFromCache(plateId) + return true } fun rename(newName: String) { 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 22b8e0b9ef369a53affb2c3df27cdffc902f9372..50f2fb4860b39f77025541cb341a46a18ab93e2c 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/StopSegment.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/StopSegment.kt @@ -62,8 +62,10 @@ return plateId.stop == stop return plates!!.contains(plateId) } - fun remove(plateId: Plate.ID) { - (plates as HashSet).remove(plateId) + fun remove(plateId: Plate.ID): Boolean { + if (plates == null) + return false + return (plates as HashSet).remove(plateId) } override fun toString(): String { 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 f6b9dcfe6915fa4e06b2980cf7d5a341d0e0a96a..d2aad3409764302956a3650228b9fc792c495669 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt @@ -145,6 +145,29 @@ AWF73 -> {10 → Franowo, 29 → Franowo, 6 → Miłostowo, 5 → Stomil, 18 → Franowo, 15 → Franowo, 12 → Starołęka, 74 → Os. Orła Białego} */ } + fun getHeadlinesForStopCode(stop: String): StopSegment { + var cursor = db!!.rawQuery("select stop_id from stops where stop_code = ?", + arrayOf(stop)) + cursor.moveToFirst() + val stopId = cursor.getInt(0) + cursor.close() + + + cursor = db!!.rawQuery("select route_id, trip_headsign " + + "from stop_times natural join trips where stop_id = ? ", + arrayOf(stopId.toString())) + + val plates = HashSet<Plate.ID>() + + while (cursor.moveToNext()) { + val route = cursor.getString(0) + val headsign = cursor.getString(1) + plates.add(Plate.ID(route, stop, headsign)) + } + cursor.close() + return StopSegment(stop, plates) + } + fun getStopName(stopCode: String): String { val cursor = db!!.rawQuery("select stop_name from stops where stop_code = ?", arrayOf(stopCode)) diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/adapters/FavouriteEditRowAdapter.kt b/app/src/main/java/ml/adamsprogs/bimba/models/adapters/FavouriteEditRowAdapter.kt index 4b2a2eac4ee1f245a99982b9dc6cbfbd1940ed85..8532b027357f41a83507319536e2fd74de53f0ef 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/adapters/FavouriteEditRowAdapter.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/adapters/FavouriteEditRowAdapter.kt @@ -6,31 +6,61 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView +import kotlinx.coroutines.experimental.DefaultDispatcher +import kotlinx.coroutines.experimental.android.UI +import kotlinx.coroutines.experimental.launch +import kotlinx.coroutines.experimental.withContext +import ml.adamsprogs.bimba.ProviderProxy import ml.adamsprogs.bimba.R import ml.adamsprogs.bimba.collections.FavouriteStorage import ml.adamsprogs.bimba.models.Favourite import ml.adamsprogs.bimba.models.Plate -import ml.adamsprogs.bimba.models.Timetable +import ml.adamsprogs.bimba.models.StopSegment //todo when plates null -> get all plates from proxy class FavouriteEditRowAdapter(private var favourite: Favourite) : RecyclerView.Adapter<FavouriteEditRowAdapter.ViewHolder>() { + + private val segments = HashMap<String, StopSegment>() + private val providerProxy = ProviderProxy() + + init { + launch(UI) { + withContext(DefaultDispatcher) { + favourite.segments.forEach { + segments[it.stop] = providerProxy.fillStopSegment(it) ?: it + } + } + this@FavouriteEditRowAdapter.notifyDataSetChanged() + } + } + + override fun getItemCount(): Int { - return favourite.size + return segments.flatMap { it.value.plates ?: emptyList<Plate.ID>() }.size } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val timetable = Timetable.getTimetable() - val favourites = FavouriteStorage.getFavouriteStorage() - 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${plate.id.line} → ${plate.id.headsign}" - holder.rowTextView.text = favouriteElement - holder.deleteButton.setOnClickListener { - favourites.delete(favourite.name, id) - favourite = favourites.favourites[favourite.name]!! - notifyDataSetChanged() + launch(UI) { + val plates = segments.flatMap { it.value.plates ?: emptyList<Plate.ID>() } + val favourites = FavouriteStorage.getFavouriteStorage() + val id = plates.sortedBy { "${it.line}${it.stop}" }[position] + val favouriteElement = withContext(DefaultDispatcher) { + providerProxy.getStopName(id.stop).let { + "${it ?: ""} (${id.stop}):\n${id.line} → ${id.headsign}" + } + } + holder.rowTextView.text = favouriteElement + holder.deleteButton.setOnClickListener { + launch(UI) { + favourite.segments.clear() + favourite.segments.addAll(segments.map { it.value }) + favourites.delete(favourite.name, id) + favourite = favourites.favourites[favourite.name]!! + notifyDataSetChanged() + } + } } } @@ -43,7 +73,7 @@ return ViewHolder(rowView) } inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - val rowTextView:TextView = itemView.findViewById(R.id.favourite_edit_row) - val deleteButton:ImageView = itemView.findViewById(R.id.favourite_edit_delete) + val rowTextView: TextView = itemView.findViewById(R.id.favourite_edit_row) + val deleteButton: ImageView = itemView.findViewById(R.id.favourite_edit_delete) } } \ No newline at end of file