diff --git a/app/build.gradle b/app/build.gradle --- a/app/build.gradle +++ b/app/build.gradle @@ -95,7 +95,7 @@ implementation 'com.google.protobuf:protobuf-java:3.17.2' // mqtt library implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5' - implementation 'com.github.hannesa2:paho.mqtt.android:3.5.3' + implementation 'com.github.hannesa2:paho.mqtt.android:4.1' // ViewModel diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ + 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 @@ -28,6 +28,7 @@ errorListener: Response.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")) 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 @@ -5,13 +5,10 @@ import androidx.lifecycle.LifecycleOwner import info.mqtt.android.service.Ack import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import org.eclipse.paho.client.mqttv3.* import info.mqtt.android.service.MqttAndroidClient import info.mqtt.android.service.QoS -import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence import org.json.JSONArray import org.json.JSONException import java.lang.ref.WeakReference @@ -34,10 +31,11 @@ private lateinit var lifecycle: LifecycleOwner private var context: Context?= null + private var connectionTrials = 0 private fun connect(context: Context, iMqttActionListener: IMqttActionListener?){ - val clientID = "mqttjs_${getRandomString(8)}" + val clientID = "mqtt-explorer-${getRandomString(8)}"//"mqttjs_${getRandomString(8)}" client = MqttAndroidClient(context,SERVER_ADDR,clientID,Ack.AUTO_ACK) @@ -49,6 +47,7 @@ headersPars.setProperty("Host","mapi.5t.torino.it") options.customWebSocketHeaders = headersPars + Log.d(DEBUG_TAG,"client name: $clientID") //actually connect client.connect(options,null, iMqttActionListener) isStarted = true @@ -63,28 +62,47 @@ Log.d(DEBUG_TAG, "Connected to server, reconnect: $reconnect") Log.d(DEBUG_TAG, "Have listeners: $respondersMap") } + private fun connectTopic(topic: String){ + if(context==null){ + Log.e(DEBUG_TAG, "Trying to connect but context is null") + return + } + connectionTrials += 1 + connect(context!!, object : IMqttActionListener{ + override fun onSuccess(asyncActionToken: IMqttToken?) { + val disconnectedBufferOptions = DisconnectedBufferOptions() + disconnectedBufferOptions.isBufferEnabled = true + disconnectedBufferOptions.bufferSize = 100 + disconnectedBufferOptions.isPersistBuffer = false + disconnectedBufferOptions.isDeleteOldestMessages = false + client.setBufferOpts(disconnectedBufferOptions) + client.subscribe(topic, QoS.AtMostOnce.value) + isStarted = true + } + + override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) { + Log.e(DEBUG_TAG, "FAILED To connect to the server",exception) + if (connectionTrials < 10) { + Log.d(DEBUG_TAG, "Reconnecting") + connectTopic(topic) + } + else { + //reset connection trials + connectionTrials = 0 + } + } + + }) + } fun startAndSubscribe(lineId: String, responder: MQTTMatoListener, context: Context): Boolean{ //start the client, and then subscribe to the topic val topic = mapTopic(lineId) + this.context = context.applicationContext synchronized(this) { if(!isStarted){ - connect(context, object : IMqttActionListener{ - override fun onSuccess(asyncActionToken: IMqttToken?) { - val disconnectedBufferOptions = DisconnectedBufferOptions() - disconnectedBufferOptions.isBufferEnabled = true - disconnectedBufferOptions.bufferSize = 100 - disconnectedBufferOptions.isPersistBuffer = false - disconnectedBufferOptions.isDeleteOldestMessages = false - client.setBufferOpts(disconnectedBufferOptions) - client.subscribe(topic, QoS.AtMostOnce.value) - } - override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) { - Log.e(DEBUG_TAG, "FAILED To connect to the server") - } - - }) + connectTopic(topic) //wait for connection } else { client.subscribe(topic, QoS.AtMostOnce.value) @@ -137,16 +155,22 @@ return currentPositions } - fun sendUpdateToResponders(responders: ArrayList>): Boolean{ - var sent = false - for (wrD in responders) - if (wrD.get() == null) + private fun sendUpdateToResponders(responders: ArrayList>): Int{ + //var sent = false + var count = 0 + for (wrD in responders) { + if (wrD.get() == null) { + Log.d(DEBUG_TAG, "Removing weak reference") responders.remove(wrD) + } else { wrD.get()!!.onUpdateReceived(currentPositions) - sent = true + //sent = true + count++ } - return sent + } + + return count } override fun connectionLost(cause: Throwable?) { @@ -185,7 +209,7 @@ override fun messageArrived(topic: String?, message: MqttMessage?) { if (topic==null || message==null) return - + //Log.d(DEBUG_TAG,"Arrived message on topic $topic, ${String(message.payload)}") parseMessageAndAddToList(topic, message) //GlobalScope.launch { } @@ -243,17 +267,20 @@ it[vehicleId] = posUpdate valid = true } - var sent = false + //sending + //Log.d(DEBUG_TAG, "Parsed update on topic $topic, line $lineId, responders $respondersMap") + var cc = 0 if (LINES_ALL in respondersMap.keys) { - sent = sendUpdateToResponders(respondersMap[LINES_ALL]!!) - - + val count = sendUpdateToResponders(respondersMap[LINES_ALL]!!) + cc +=count } + if(lineId in respondersMap.keys){ - sent = sendUpdateToResponders(respondersMap[lineId]!!) or sent + cc += sendUpdateToResponders(respondersMap[lineId]!!) } - if(!sent){ + //Log.d(DEBUG_TAG, "Sent to $cc responders, have $respondersMap") + if(cc==0){ Log.w(DEBUG_TAG, "We have received an update but apparently there is no one to send it") var emptyResp = true for(en in respondersMap.values){ @@ -270,8 +297,10 @@ } //Log.d(DEBUG_TAG, "We have update on line $lineId, vehicle $vehicleId") } catch (e: JSONException){ - Log.e(DEBUG_TAG,"Cannot decipher message on topic $topic, line $lineId, veh $vehicleId") - e.printStackTrace() + Log.w(DEBUG_TAG,"Cannot decipher message on topic $topic, line $lineId, veh $vehicleId (bad JSON)") + + } catch (e: Exception){ + Log.e(DEBUG_TAG, "Exception occurred", e) } } 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 @@ -57,9 +57,9 @@ for(trip in tripsList){ queriedMatoTrips.add(trip) matoRepository.requestTripUpdate(trip,{error-> - Log.e(DEBUG_TAG, "Cannot download Gtfs Trip $trip") - val stacktrace = error.stackTrace.take(5) - Log.w(DEBUG_TAG, "Stacktrace:\n$stacktrace") + Log.e(DEBUG_TAG, "Cannot download Gtfs Trip $trip", error) + //val stacktrace = error.stackTrace.take(5) + //Log.w(DEBUG_TAG, "Stacktrace:\n$stacktrace") failedMatoTripsDownload.add(trip) requestCountDown.countDown() }){ 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 @@ -540,7 +540,8 @@ val paint = Paint() paint.color = ContextCompat.getColor(requireContext(),R.color.line_drawn_poly) paint.isAntiAlias = true - paint.strokeWidth = 16f + paint.strokeWidth = 13f + paint.style = Paint.Style.FILL_AND_STROKE paint.strokeJoin = Paint.Join.ROUND paint.strokeCap = Paint.Cap.ROUND diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt --- a/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt +++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt @@ -20,6 +20,7 @@ import android.app.Application import android.util.Log import androidx.lifecycle.* +import com.android.volley.DefaultRetryPolicy import com.android.volley.Response import it.reyboz.bustorino.backend.NetworkVolleyManager import it.reyboz.bustorino.backend.gtfs.GtfsRtPositionsRequest @@ -70,7 +71,7 @@ } val time = System.currentTimeMillis() if(lastTimeReceived == (0.toLong()) || (time-lastTimeReceived)>500){ - updatesLiveData.value = (mupds) + updatesLiveData.postValue(mupds) lastTimeReceived = time } @@ -167,8 +168,6 @@ } } //Gtfs Real time - - private val gtfsPositionsReqListener = object: GtfsRtPositionsRequest.Companion.RequestListener{ override fun onResponse(response: ArrayList?) { Log.i(DEBUG_TI,"Got response from the GTFS RT server") @@ -189,13 +188,16 @@ } private val positionRequestErrorListener = Response.ErrorListener { - Log.e(DEBUG_TI, "Could not download the update, error:\n"+it.stackTrace) + Log.e(DEBUG_TI, "Could not download the update", it) gtfsRtRequestRunning.postValue(false) } fun requestGTFSUpdates(){ if(gtfsRtRequestRunning.value == null || !gtfsRtRequestRunning.value!!) { val request = GtfsRtPositionsRequest(positionRequestErrorListener, gtfsPositionsReqListener) + request.setRetryPolicy( + DefaultRetryPolicy(1000,10,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT) + ) netVolleyManager.requestQueue.add(request) Log.i(DEBUG_TI, "Requested GTFS realtime position updates") gtfsRtRequestRunning.value = true @@ -211,6 +213,6 @@ } companion object{ - private const val DEBUG_TI = "BusTO-MQTTLiveData" + private const val DEBUG_TI = "BusTO-LivePosViewModel" } } \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -168,9 +168,10 @@ - Canale unico delle notifiche - Database + Canale default delle notifiche + Operazioni sul database Informazioni sul database (aggiornamento) + Downloading trips from MaTO server diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -196,8 +196,9 @@ --> Default Default channel for notifications - Database - Notifications on the update of the database + Database operations + Updates of the app database + Downloading trips from MaTO server Asked for %1$s permission too many times