diff --git a/app/src/main/java/it/reyboz/bustorino/backend/LivePositionsServiceStatus.kt b/app/src/main/java/it/reyboz/bustorino/backend/LivePositionsServiceStatus.kt new file mode 100644 --- /dev/null +++ b/app/src/main/java/it/reyboz/bustorino/backend/LivePositionsServiceStatus.kt @@ -0,0 +1,5 @@ +package it.reyboz.bustorino.backend + +enum class LivePositionsServiceStatus { + OK,CONNECTING, NO_POSITIONS, ERROR_CONNECTION, ERROR_PARSING_RESPONSE, ERROR_NETWORK_RESPONSE +} \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsRtPositionsRequest.kt b/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsRtPositionsRequest.kt --- a/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsRtPositionsRequest.kt +++ b/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsRtPositionsRequest.kt @@ -24,19 +24,23 @@ import com.android.volley.toolbox.HttpHeaderParser import com.google.transit.realtime.GtfsRealtime +import it.reyboz.bustorino.backend.Fetcher class GtfsRtPositionsRequest( - errorListener: Response.ErrorListener?, + errorListener: ErrorListener, val listener: RequestListener) : Request>(Method.GET, URL_POSITION, errorListener) { override fun parseNetworkResponse(response: NetworkResponse?): Response> { if (response == null){ - return Response.error(VolleyError("Null response")) + return Response.error(RequestError(Fetcher.Result.PARSER_ERROR)) + } + if (response.statusCode == 404){ + return Response.error(RequestError(Fetcher.Result.SERVER_ERROR_404)) + } + else if (response.statusCode != 200){ + return Response.error(RequestError(Fetcher.Result.SERVER_ERROR)) } - - if (response.statusCode != 200) - return Response.error(VolleyError("Error code is ${response.statusCode}")) val gtfsreq = GtfsRealtime.FeedMessage.parseFrom(response.data) @@ -58,16 +62,22 @@ listener.onResponse(response) } + override fun parseNetworkError(volleyError: VolleyError?): VolleyError? { + return super.parseNetworkError(volleyError) + } + companion object{ const val URL_POSITION = "http://percorsieorari.gtt.to.it/das_gtfsrt/vehicle_position.aspx" const val URL_TRIP_UPDATES ="http://percorsieorari.gtt.to.it/das_gtfsrt/trip_update.aspx" const val URL_ALERTS = "http://percorsieorari.gtt.to.it/das_gtfsrt/alerts.aspx" - public interface RequestListener{ + interface RequestListener{ fun onResponse(response: ArrayList?) } + fun interface ErrorListener: Response.ErrorListener } + class RequestError(val result: Fetcher.Result): VolleyError() } \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt b/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt --- a/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt +++ b/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt @@ -10,6 +10,7 @@ import com.hivemq.client.mqtt.mqtt3.Mqtt3ClientBuilder import com.hivemq.client.mqtt.mqtt3.message.publish.Mqtt3Publish import it.reyboz.bustorino.BuildConfig +import it.reyboz.bustorino.backend.LivePositionsServiceStatus import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate import org.json.JSONArray import org.json.JSONException @@ -198,7 +199,9 @@ responders.remove(wrD) } else { - wrD.get()!!.onUpdateReceived(currentPositions) + val resp = wrD.get()!! + resp.onStatusUpdate(LivePositionsServiceStatus.OK) + resp.onUpdateReceived(currentPositions) //sent = true count++ } @@ -206,6 +209,22 @@ return count } + private fun sendStatusToResponders(status: LivePositionsServiceStatus){ + val responders = respondersMap + for (els in respondersMap.values) + for (wrD in els) { + + if (wrD.get() == null) { + Log.d(DEBUG_TAG, "Removing weak reference") + els.remove(wrD) + } + else { + wrD.get()!!.onStatusUpdate(status) + //sent = true + } + } + + } /*override fun connectionLost(cause: Throwable?) { var doReconnect = false @@ -280,6 +299,7 @@ if(jsonList.get(4)==null){ Log.d(DEBUG_TAG, "We have null tripId: line $lineId veh $vehicleId: $jsonList") + sendStatusToResponders(LivePositionsServiceStatus.NO_POSITIONS) return } val posUpdate = LivePositionUpdate( @@ -334,9 +354,12 @@ //Log.d(DEBUG_TAG, "We have update on line $lineId, vehicle $vehicleId") } catch (e: JSONException){ Log.w(DEBUG_TAG,"Cannot decipher message on topic $topic, line $lineId, veh $vehicleId (bad JSON)") + sendStatusToResponders(LivePositionsServiceStatus.ERROR_PARSING_RESPONSE) + } catch (e: Exception){ Log.e(DEBUG_TAG, "Exception occurred", e) + sendStatusToResponders(LivePositionsServiceStatus.ERROR_PARSING_RESPONSE) } } @@ -382,9 +405,11 @@ } - fun interface MQTTMatoListener{ + interface MQTTMatoListener{ //positionsMap is a dict with line -> vehicle -> Update fun onUpdateReceived(posUpdates: PositionsMap) + + fun onStatusUpdate(status: LivePositionsServiceStatus) } fun getHTTPHeaders(): HashMap { val headers = HashMap() diff --git a/app/src/main/java/it/reyboz/bustorino/data/MatoRepository.kt b/app/src/main/java/it/reyboz/bustorino/data/MatoRepository.kt --- a/app/src/main/java/it/reyboz/bustorino/data/MatoRepository.kt +++ b/app/src/main/java/it/reyboz/bustorino/data/MatoRepository.kt @@ -20,6 +20,7 @@ import android.content.Context import android.util.Log import com.android.volley.Response +import com.android.volley.toolbox.ClearCacheRequest import it.reyboz.bustorino.backend.NetworkVolleyManager import it.reyboz.bustorino.backend.Result import it.reyboz.bustorino.backend.mato.MatoQueries @@ -50,6 +51,13 @@ )) } + fun clearVolleyCache(){ + val clearReq = ClearCacheRequest(netVolleyManager.requestQueue.cache){ + Log.d(DEBUG_TAG, "Volley cache is cleared") + } + netVolleyManager.addToRequestQueue(clearReq) + } + fun interface Callback { fun onResultAvailable(result: Result) } diff --git a/app/src/main/java/it/reyboz/bustorino/data/MatoTripsDownloadWorker.kt b/app/src/main/java/it/reyboz/bustorino/data/MatoTripsDownloadWorker.kt --- a/app/src/main/java/it/reyboz/bustorino/data/MatoTripsDownloadWorker.kt +++ b/app/src/main/java/it/reyboz/bustorino/data/MatoTripsDownloadWorker.kt @@ -21,6 +21,7 @@ import android.content.Context import android.util.Log import androidx.work.* +import com.android.volley.toolbox.ClearCacheRequest import it.reyboz.bustorino.backend.Notifications import it.reyboz.bustorino.data.gtfs.GtfsTrip import java.util.concurrent.CountDownLatch @@ -30,19 +31,20 @@ override suspend fun doWork(): Result { - return downloadGtfsTrips() - } - - /** - * Download GTFS Trips from Mato - */ - private fun downloadGtfsTrips():Result{ val tripsList = inputData.getStringArray(TRIPS_KEYS) if (tripsList== null){ Log.e(DEBUG_TAG,"trips list given is null") return Result.failure() } + return downloadGtfsTrips(tripsList!!) + } + + /** + * Download GTFS Trips from Mato + */ + private fun downloadGtfsTrips(tripsList: Array):Result{ + val gtfsRepository = GtfsRepository(applicationContext) val matoRepository = MatoRepository(applicationContext) //clear the matoTrips @@ -52,7 +54,7 @@ val failedMatoTripsDownload = HashSet() - Log.i(DEBUG_TAG, "Requesting download for the trips") + Log.i(DEBUG_TAG, "Requesting download for ${tripsList.size} trips") val requestCountDown = CountDownLatch(tripsList.size); for(trip in tripsList){ queriedMatoTrips.add(trip) @@ -82,6 +84,8 @@ } requestCountDown.await() val tripsIDsCompleted = downloadedMatoTrips.map { trip-> trip.tripID } + //clear cache to avoid tripping memory + matoRepository.clearVolleyCache() if (tripsIDsCompleted.isEmpty()){ Log.d(DEBUG_TAG, "No trips have been downloaded, set work to fail") return Result.failure() diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt --- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt @@ -40,6 +40,7 @@ import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import androidx.core.view.ViewCompat +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager @@ -244,7 +245,7 @@ private val tripMarkersAnimators = HashMap() - private val liveBusViewModel: LivePositionsViewModel by viewModels() + private val liveBusViewModel: LivePositionsViewModel by activityViewModels() //extra items to use the LibreMap private lateinit var symbolManager : SymbolManager diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LivePositionsDialogFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LivePositionsDialogFragment.kt new file mode 100644 --- /dev/null +++ b/app/src/main/java/it/reyboz/bustorino/fragments/LivePositionsDialogFragment.kt @@ -0,0 +1,87 @@ +package it.reyboz.bustorino.fragments + +import it.reyboz.bustorino.R + +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.ImageButton +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import it.reyboz.bustorino.backend.LivePositionsServiceStatus +import it.reyboz.bustorino.viewmodels.LivePositionsViewModel + +class LivePositionsDialogFragment : DialogFragment() { + private val viewModel: LivePositionsViewModel by activityViewModels() + private lateinit var providerNameTextView: TextView + private lateinit var statusMessageTextView: TextView + + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + //return super.onCreateDialog(savedInstanceState) + val builder = AlertDialog.Builder(requireContext()) + val view = layoutInflater.inflate(R.layout.fragment_dialog_buspositions, null) + + providerNameTextView = view.findViewById(R.id.providerNameTextView) + statusMessageTextView = view.findViewById(R.id.statusMessageTextView) + val btnSwitch = view.findViewById