Bimba.git

commit 9e68a8d6728eb35555554ef680f0ad8192962a65

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) {