Author: Adam Evyčędo <git@apiote.xyz>
add view model to departures
%!v(PANIC=String method: strings: negative Repeat count)
diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/api/Api.kt b/app/src/main/java/xyz/apiote/bimba/czwek/api/Api.kt index 4a55f6332c6ae928d872e58573a3a3dca71aa9b3..b431e47864298918ed9ff8fa8539e2ef89e18b25 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/api/Api.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/api/Api.kt @@ -147,6 +147,22 @@ feedID ) } +fun mapHttpError(code: Int): Pair<Int, Int> { + return when (code) { + 400 -> Pair(R.string.error_400, R.drawable.error_app) + 401 -> Pair(R.string.error_401, R.drawable.error_sec) + 403 -> Pair(R.string.error_403, R.drawable.error_sec) + 404 -> Pair(R.string.error_404, R.drawable.error_search) + 406 -> Pair(R.string.error_406, R.drawable.error_accept) + 429 -> Pair(R.string.error_429, R.drawable.error_limit) + 500 -> Pair(R.string.error_50x, R.drawable.error_server) + 502 -> Pair(R.string.error_50x, R.drawable.error_server) + 503 -> Pair(R.string.error_50x, R.drawable.error_server) + 504 -> Pair(R.string.error_50x, R.drawable.error_server) + else -> Pair(R.string.error_unknown, R.drawable.error_other) + } +} + suspend fun rawRequest( url: URL, server: Server, cm: ConnectivityManager, responseVersion: Array<UInt> ): Result { @@ -163,19 +179,7 @@ try { if (c.responseCode == 200) { Result(c.inputStream, null) } else { - val (string, image) = when (c.responseCode) { - 400 -> Pair(R.string.error_400, R.drawable.error_app) - 401 -> Pair(R.string.error_401, R.drawable.error_sec) - 403 -> Pair(R.string.error_403, R.drawable.error_sec) - 404 -> Pair(R.string.error_404, R.drawable.error_search) - 406 -> Pair(R.string.error_406, R.drawable.error_accept) - 429 -> Pair(R.string.error_429, R.drawable.error_limit) - 500 -> Pair(R.string.error_50x, R.drawable.error_server) - 502 -> Pair(R.string.error_50x, R.drawable.error_server) - 503 -> Pair(R.string.error_50x, R.drawable.error_server) - 504 -> Pair(R.string.error_50x, R.drawable.error_server) - else -> Pair(R.string.error_unknown, R.drawable.error_other) - } + val (string, image) = mapHttpError(c.responseCode) Result(c.errorStream, Error(c.responseCode, string, image)) } } catch (e: IOException) { diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt b/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt index 9a17d965b1b7fdfe1eb3dc8669aff6e9ac3d7421..2a2ca160108611ed10c7ad1183722caff5f24c40 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/departures/Departures.kt @@ -29,7 +29,6 @@ import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.dpToPixelI import xyz.apiote.bimba.czwek.repo.Departure import xyz.apiote.bimba.czwek.repo.Vehicle -import java.time.Duration import java.time.ZoneId import java.time.ZonedDateTime import java.util.* @@ -93,10 +92,7 @@ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { val oldDeparture = oldDepartures[oldItemPosition] val newDeparture = newDepartures[newItemPosition] return oldDeparture.vehicle.Line == newDeparture.vehicle.Line && oldDeparture.vehicle.Headsign == newDeparture.vehicle.Headsign && - Duration.between(lastUpdate, oldDeparture.time.toDateTime()) == Duration.between( - ZonedDateTime.now(), - oldDeparture.time.toDateTime() - ) + oldDeparture.statusText(context) == newDeparture.statusText(context) } } @@ -139,6 +135,10 @@ this.departures = departures departuresPositions = newPositions lastUpdate = ZonedDateTime.now() diff.dispatchUpdatesTo(this) + } + + fun refreshItems() { + update(this.departures) } } diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt index 44ad949ddfbc159e7209c351605104ddb1cf45a3..3468de432ce398c849ec8e3657f9b25eb621f2e4 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesActivity.kt @@ -4,30 +4,24 @@ // SPDX-License-Identifier: GPL-3.0-or-later package xyz.apiote.bimba.czwek.departures -import android.content.Context import android.content.Intent -import android.net.ConnectivityManager import android.os.Bundle import android.os.Handler import android.os.Looper -import android.util.Log import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.res.ResourcesCompat import androidx.core.view.WindowCompat +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import kotlinx.coroutines.MainScope import kotlinx.coroutines.Runnable -import kotlinx.coroutines.launch import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.api.Error import xyz.apiote.bimba.czwek.databinding.ActivityDeparturesBinding import xyz.apiote.bimba.czwek.repo.Departure -import xyz.apiote.bimba.czwek.repo.OnlineRepository import xyz.apiote.bimba.czwek.repo.Stop -import xyz.apiote.bimba.czwek.repo.TrafficResponseException class DeparturesActivity : AppCompatActivity() { private var _binding: ActivityDeparturesBinding? = null @@ -40,14 +34,23 @@ private var runnable = Runnable {} private var openBottomSheet: DepartureBottomSheet? = null - private var requestedItemsNumber = 12 - private var allItemsRequested = false + private lateinit var viewModel: DeparturesViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) _binding = ActivityDeparturesBinding.inflate(layoutInflater) setContentView(binding.root) + viewModel = ViewModelProvider(this)[DeparturesViewModel::class.java] + + viewModel.departures.observe(this){stopDepartures -> + updateItems(stopDepartures.departures, stopDepartures.stop) + openBottomSheet?.departureID()?.let { adapter.get(it) }?.let { openBottomSheet?.update(it) } + } + viewModel.error.observe(this) { + showError(it) + } + binding.collapsingLayout.apply { title = getName() val tf = ResourcesCompat.getFont(this@DeparturesActivity, R.font.yellowcircle8) @@ -63,8 +66,8 @@ super.onScrolled(recyclerView, dx, dy) val llm = binding.departuresRecycler.layoutManager as LinearLayoutManager val dataLength = adapter.itemCount if (llm.findLastCompletelyVisibleItemPosition() == dataLength - 1) { - if (!allItemsRequested) { - requestedItemsNumber += 12 + if (!viewModel.allItemsRequested) { + viewModel.requestedItemsNumber += 12 getDepartures() } } @@ -119,7 +122,7 @@ private fun getFeedID(): String { return when (intent?.action) { Intent.ACTION_VIEW -> { - return when (intent?.data?.host) { + @Suppress("SpellCheckingInspection") return when (intent?.data?.host) { "www.peka.poznan.pl" -> "poznan_ztm" "rj.metropoliaztm.pl" -> "gzm_ztm" else -> "" @@ -141,27 +144,9 @@ else -> null } } - private fun getDepartures() { - val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - MainScope().launch { - try { - val repository = OnlineRepository() - val stopDepartures = - repository.getDepartures( - cm, - getFeedID(), - getCode(), - getLine(), - this@DeparturesActivity, - requestedItemsNumber - ) - updateItems(stopDepartures!!.departures, stopDepartures.stop) - openBottomSheet?.departureID()?.let { adapter.get(it) }?.let { openBottomSheet?.update(it) } - } catch (e: TrafficResponseException) { - showError(e.error) - Log.w("Departures", "$e") - } - } + fun getDepartures() { + adapter.refreshItems() + viewModel.getDepartures(this, getFeedID(), getCode(), getLine()) handler.removeCallbacks(runnable) runnable = Runnable { getDepartures() } handler.postDelayed(runnable, 30 * 1000) @@ -195,8 +180,8 @@ this, R.drawable.error_search ) ) } else { - if (departures.size < requestedItemsNumber) { - allItemsRequested = true + if (departures.size < viewModel.requestedItemsNumber) { + viewModel.allItemsRequested = true } binding.departuresOverlay.visibility = View.GONE binding.errorImage.visibility = View.GONE diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesViewModel.kt b/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..afa387ec253c29c9db692458ee413effdc55ed93 --- /dev/null +++ b/app/src/main/java/xyz/apiote/bimba/czwek/departures/DeparturesViewModel.kt @@ -0,0 +1,51 @@ +package xyz.apiote.bimba.czwek.departures + +import android.content.Context +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch +import xyz.apiote.bimba.czwek.api.Error +import xyz.apiote.bimba.czwek.api.mapHttpError +import xyz.apiote.bimba.czwek.repo.OnlineRepository +import xyz.apiote.bimba.czwek.repo.StopDepartures +import xyz.apiote.bimba.czwek.repo.TrafficResponseException + +class DeparturesViewModel: ViewModel() { + private val _departures = MutableLiveData<StopDepartures>() + val departures: LiveData<StopDepartures> = _departures + private val _error = MutableLiveData<Error>() + val error: LiveData<Error> = _error + var requestedItemsNumber = 12 + var allItemsRequested = false + + fun getDepartures(context: Context, feedID: String, code: String, line: String?) { + MainScope().launch { + try { + val repository = OnlineRepository() + val stopDepartures = + repository.getDepartures( + feedID, + code, + line, + context, + requestedItemsNumber + ) + stopDepartures?.let { + if (stopDepartures.departures.isEmpty()) { + val (string, image) = mapHttpError(404) + throw TrafficResponseException(404, "", Error(404, string, image)) + } + _departures.value = it + } + } catch (e: TrafficResponseException) { + if (!departures.isInitialized) { + _error.value = e.error + } + Log.w("Departures", "$e") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Interfaces.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Interfaces.kt index aa30c413ca65aed5e28f6f53f930acfbba20244e..4905c0f55b5ce6589d62fbaefd313b170a9fc639 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Interfaces.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Interfaces.kt @@ -23,7 +23,6 @@ context: Context ): Map<String, FeedInfo>? suspend fun getDepartures( - cm: ConnectivityManager, feedID: String, stop: String, line: String?, diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/OfflineRepository.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/OfflineRepository.kt index 9706dd10b196ae2355538c9f7bec053326eff9c4..9943189f8702bf06fd84fd25a1d919b8f3f181db 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/OfflineRepository.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/OfflineRepository.kt @@ -56,7 +56,6 @@ } } override suspend fun getDepartures( - cm: ConnectivityManager, feedID: String, stop: String, line: String?, diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/OnlineRepository.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/OnlineRepository.kt index 6d4493fe97f3795ef5709679f60f0f60122a345d..a84e433fe5cb2fc1ca004ebc8b5852c1a9216fd2 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/OnlineRepository.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/OnlineRepository.kt @@ -84,13 +84,13 @@ } } override suspend fun getDepartures( - cm: ConnectivityManager, feedID: String, stop: String, line: String?, context: Context, limit: Int? ): StopDepartures? { + val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val result = xyz.apiote.bimba.czwek.api.getDepartures(cm, Server.get(context), feedID, stop, line, limit) if (result.error != null) {