Bimba.git

commit a3e2b0460b07602472e1c3b6e515576b8f205547

Author: Adam Pioterek <adam.pioterek@protonmail.ch>

server error handling

 app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt | 5 
 app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt | 14 
 app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt | 5 
 app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt | 5 
 app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt | 28 +
 app/src/main/java/ml/adamsprogs/bimba/datasources/VmService.kt | 15 
 app/src/main/java/ml/adamsprogs/bimba/extensions.kt | 12 
 app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt | 4 
 app/src/main/res/values-de/strings.xml | 4 
 app/src/main/res/values-it/strings.xml | 4 
 app/src/main/res/values-nl/strings.xml | 4 
 app/src/main/res/values-pl/strings.xml | 4 
 app/src/main/res/values/strings.xml | 4 
 build.gradle | 2 


diff --git a/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt b/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt
index c40abec7cc2d1edd1e714a8a4f24dbe6afb6d577..faedcc757de71be595d9cd42158d306e3d44964d 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/MessageReceiver.kt
@@ -32,8 +32,9 @@         if (intent?.action == VmService.ACTION_READY) {
             val departures = intent.getStringArrayListExtra(VmService.EXTRA_DEPARTURES)?.map { Departure.fromString(it) }?.toSet()
             val plateId = intent.getSerializableExtra(VmService.EXTRA_PLATE_ID) as Plate.ID?
             val stopCode = intent.getSerializableExtra(VmService.EXTRA_STOP_CODE) as String
+            val code = intent.getIntExtra(VmService.EXTRA_CODE, 0)
             for (listener in onVmListeners) {
-                listener.onVm(departures, plateId, stopCode)
+                listener.onVm(departures, plateId, stopCode, code)
             }
         }
     }
@@ -59,6 +60,6 @@         fun onTimetableDownload(result: String?)
     }
 
     interface OnVmListener {
-        fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID?, stopCode: String)
+        fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID?, stopCode: String, code: Int)
     }
 }
\ No newline at end of file




diff --git a/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt b/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt
index 32dddcd358b1801b29be9440b9a0c0322cdb8460..45a60fc90e45adb61e3dd09c5a5ed12bbe9b7059 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/ProviderProxy.kt
@@ -205,7 +205,7 @@         return timetable.getServiceFirstDay(service)
     }
 
     interface OnDeparturesReadyListener {
-        fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?)
+        fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?, code: Int)
     }
 
     inner class Request(private val listener: OnDeparturesReadyListener, private val segments: Set<StopSegment>) : MessageReceiver.OnVmListener {
@@ -221,20 +221,24 @@                 cache = constructSegmentDepartures(segments)
             }
         }
 
-        override fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID?, stopCode: String) {
+        override fun onVm(vmDepartures: Set<Departure>?, plateId: Plate.ID?, stopCode: String, code: Int) {
             launch(UI) {
+                if ((plateId == null || vmDepartures == null) and (timetable.isEmpty())) {
+                    listener.onDeparturesReady(emptyList(), null, code)
+                    return@launch
+                }
                 if (plateId == null) {
-                    listener.onDeparturesReady(filterDepartures(cache!!.await()), null)
+                    listener.onDeparturesReady(filterDepartures(cache!!.await()), null, code)
                 } else {
                     if (segments.any { plateId in it }) {
                         if (vmDepartures != null) {
-                            listener.onDeparturesReady(vmDepartures.toList(), plateId)
+                            listener.onDeparturesReady(vmDepartures.toList(), plateId, code)
                             if (plateId !in receivedPlates)
                                 receivedPlates.add(plateId)
                         } else {
                             receivedPlates.remove(plateId)
                             if (receivedPlates.isEmpty()) {
-                                listener.onDeparturesReady(filterDepartures(cache!!.await()), null)
+                                listener.onDeparturesReady(filterDepartures(cache!!.await()), null, code)
                             }
                         }
                     }




diff --git a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt
index 9320e22aef6596e5757970c7ba44d98b0c0afcf8..43f017d67c36885dd9a1accfb6cf4d083773eb7c 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/activities/DashActivity.kt
@@ -251,8 +251,9 @@         favouritesList.itemAnimator = DefaultItemAnimator()
         favouritesList.layoutManager = layoutManager
     }
 
-    override fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?) {
+    override fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?, code: Int) {
         favouritesList.adapter.notifyDataSetChanged()
+        showError(drawer_layout, code, this)
     }
 
     private fun getSuggestions() {
@@ -298,7 +299,7 @@     }
 
     override fun onTimetableDownload(result: String?) {
         val message: String = when (result) {
-            TimetableDownloader.RESULT_NO_CONNECTIVITY -> getString(R.string.no_connectivity)
+            TimetableDownloader.RESULT_NO_CONNECTIVITY -> getString(R.string.no_connectivity_cant_update)
             TimetableDownloader.RESULT_UP_TO_DATE -> getString(R.string.timetable_up_to_date)
             TimetableDownloader.RESULT_FINISHED -> getString(R.string.timetable_downloaded)
             else -> getString(R.string.error_try_later)




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 b6dbb3eeab4dbf3ca1b8ef2bd3077c7276377fd9..8aeed16739529be5f2f5f927660fa12f76fd659f 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/activities/StopActivity.kt
@@ -137,7 +137,8 @@         } else
             favourite!!.subscribeForDepartures(this, context)
     }
 
-    override fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?) {
+    override fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?, code: Int) {
+        showError(stop_layout, code, this)
         if (plateId == null) {
             this.departures.clear()
             this.departures[Plate.ID.dummy] = departures
@@ -164,7 +165,7 @@     }
 
     override fun onTimetableDownload(result: String?) {
         val message: String = when (result) {
-            TimetableDownloader.RESULT_NO_CONNECTIVITY -> getString(R.string.no_connectivity)
+            TimetableDownloader.RESULT_NO_CONNECTIVITY -> getString(R.string.no_connectivity_cant_update)
             TimetableDownloader.RESULT_UP_TO_DATE -> getString(R.string.timetable_up_to_date)
             TimetableDownloader.RESULT_FINISHED -> getString(R.string.timetable_downloaded)
             else -> getString(R.string.error_try_later)




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 af652ab3dad6fec82a7bb93d85733aac2cc10f56..51f7ceb59d2c1d06b955e977950e04c463317fe8 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmClient.kt
@@ -24,7 +24,7 @@         }
     }
 
     suspend fun getSheds(name: String): Map<String, Set<String>> {
-        val response = makeRequest("getBollardsByStopPoint", """{"name": "$name"}""")
+        val (_, response) = makeRequest("getBollardsByStopPoint", """{"name": "$name"}""")
         if (!response.has("success"))
             return emptyMap()
         val rootObject = response["success"].asJsonObject["bollards"].asJsonArray
@@ -55,7 +55,7 @@         }.toSet()
     }*/
 
     suspend fun getStops(pattern: String): List<StopSuggestion> {
-        val response = withContext(DefaultDispatcher) {
+        val (_, response) = withContext(DefaultDispatcher) {
             makeRequest("getStopPoints", """{"pattern": "$pattern"}""")
         }
 
@@ -74,9 +74,9 @@
         return names.map { StopSuggestion(it, "", "") }
     }
 
-    suspend fun makeRequest(method: String, data: String): JsonObject {
+    suspend fun makeRequest(method: String, data: String): Pair<Int, JsonObject> {
         if (!NetworkStateReceiver.isNetworkAvailable())
-            return JsonObject()
+            return Pair(0, JsonObject())
 
         val client = OkHttpClient()
         val url = "http://www.peka.poznan.pl/vm/method.vm?ts=${Calendar.getInstance().timeInMillis}"
@@ -88,24 +88,28 @@                 .post(body)
                 .build()
 
 
-        val responseBody: String?
+        var responseBody: String? = null
+        var responseCode = 0
         try {
-            responseBody = withContext(CommonPool) {
-                client.newCall(request).execute().body()?.string()
+            withContext(CommonPool) {
+                client.newCall(request).execute().let {
+                    responseCode = it.code()
+                    responseBody = it.body()?.string()
+                }
             }
         } catch (e: IOException) {
-            return JsonObject()
+            return Pair(0, JsonObject())
         }
 
         return try {
-            Gson().fromJson(responseBody, JsonObject::class.java)
+            Pair(responseCode, Gson().fromJson(responseBody, JsonObject::class.java))
         } catch (e: JsonSyntaxException) {
-            JsonObject()
+            Pair(responseCode, JsonObject())
         }
     }
 
     suspend fun getName(symbol: String): String? {
-        val timesResponse = withContext(DefaultDispatcher) {
+        val (_, timesResponse) = withContext(DefaultDispatcher) {
             makeRequest("getTimes", """{"symbol": "$symbol"}""")
         }
         if (!timesResponse.has("success"))
@@ -116,7 +120,7 @@     }
 
     suspend fun getDirections(symbol: String): StopSegment? {
         val name = getName(symbol)
-        val directionsResponse = makeRequest("getBollardsByStopPoint", """{"name": "$name"}""")
+        val (_, directionsResponse) = makeRequest("getBollardsByStopPoint", """{"name": "$name"}""")
 
         if (!directionsResponse.has("success"))
             return null




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 710aa49f6b3ad7a7f661864ea4231306aa403f20..9dfcc25f261319fbca5cad8912d608614a00347b 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/datasources/VmService.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/datasources/VmService.kt
@@ -20,6 +20,7 @@         const val ACTION_READY = "ml.adamsprogs.bimba.action.vm.ready"
         const val EXTRA_DEPARTURES = "ml.adamsprogs.bimba.extra.vm.departures"
         const val EXTRA_PLATE_ID = "ml.adamsprogs.bimba.extra.vm.plate"
         const val EXTRA_STOP_CODE = "ml.adamsprogs.bimba.extra.vm.stop"
+        const val EXTRA_CODE = "ml.adamsprogs.bimba.extra.vm.code"
         const val TICK_6_ZINA_TIM = 12500L
         const val TICK_6_ZINA_TIM_WITH_MARGIN = TICK_6_ZINA_TIM * 3 / 4
     }
@@ -76,9 +77,10 @@         return START_STICKY
     }
 
     private fun cleanRequests() {
-        requests.forEach {
-            if (it.value <= 0)
-                requests.remove(it.key)
+        val newRequests = requests.filter { it.value > 0 }
+        requests.clear()
+        newRequests.forEach {
+            requests[it.key] = it.value
         }
     }
 
@@ -121,10 +123,10 @@             sendResult(stopCode, null, null)
             return
         }
 
-        val javaRootMapObject = VmClient.getVmClient().makeRequest("getTimes", """{"symbol": "$stopCode"}""")
+        val (code, javaRootMapObject) = VmClient.getVmClient().makeRequest("getTimes", """{"symbol": "$stopCode"}""")
 
         if (!javaRootMapObject.has("success")) {
-            sendResult(stopCode, null, null)
+            sendResult(stopCode, null, null, code)
             return
         }
 
@@ -173,12 +175,13 @@         }
 
     }
 
-    private fun sendResult(stopCode: String, plateId: Plate.ID?, departures: HashSet<Departure>?) {
+    private fun sendResult(stopCode: String, plateId: Plate.ID?, departures: HashSet<Departure>?, code: Int = 200) {
         val broadcastIntent = Intent()
         broadcastIntent.action = ACTION_READY
         broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT)
         if (departures != null)
             broadcastIntent.putStringArrayListExtra(EXTRA_DEPARTURES, departures.map { it.toString() } as ArrayList)
+        broadcastIntent.putExtra(EXTRA_CODE, code)
         broadcastIntent.putExtra(EXTRA_PLATE_ID, plateId)
         broadcastIntent.putExtra(EXTRA_STOP_CODE, stopCode)
         sendBroadcast(broadcastIntent)




diff --git a/app/src/main/java/ml/adamsprogs/bimba/extensions.kt b/app/src/main/java/ml/adamsprogs/bimba/extensions.kt
index a349dc85e76661e7745d7245c11cf93a3d450e75..9b51907278628c82c89e90b50b4f7b8c2bc241a9 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/extensions.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/extensions.kt
@@ -4,7 +4,9 @@ import android.annotation.SuppressLint
 import android.content.Context
 import android.graphics.drawable.Drawable
 import android.os.Build
+import android.support.design.widget.Snackbar
 import android.text.format.DateFormat
+import android.view.View
 import ml.adamsprogs.bimba.activities.StopActivity
 import java.io.*
 import java.text.SimpleDateFormat
@@ -126,4 +128,14 @@         val time = timeFormat.format(this.time)
         "$date, $time"
     } else
         date
+}
+
+fun showError(view: View, code: Int, context: Context) {
+    val message = when {
+        code == 0 -> context.getString(R.string.no_connectivity)
+        (code >= 500) and (code < 600) -> context.getString(R.string.server_error)
+        else -> ""
+    }
+    if (message != "")
+        Snackbar.make(view, message, Snackbar.LENGTH_LONG).show()
 }
\ No newline at end of file




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 8e2a99c5f289cabf1caacd451ff3bd91cb26d5d1..da24710bb4eb92c79f3cdb3c77e10394ffe2bf23 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/Favourite.kt
@@ -145,9 +145,9 @@         listenerId = providerProxy.subscribeForDepartures(segments, this, context)
         return listenerId
     }
 
-    override fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?) {
+    override fun onDeparturesReady(departures: List<Departure>, plateId: Plate.ID?, code: Int) {
         cache = departures
-        listener.onDeparturesReady(departures, plateId)
+        listener.onDeparturesReady(departures, plateId, code)
     }
 
     fun unsubscribeFromDepartures(context: Context) {




diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b9385f070698ba94c13d54a19aa103dee644cf0b..513e4652283dcb5cd773feefafbc6ce810b49bcd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -18,7 +18,8 @@     Downloading timetable
     <string name="timetable_downloading_progress" translatable="false">%1$1.2f MiB/%2$1.2f MiB</string>
     <string name="timetable_decompressing">Decompressing timetable</string>
     <string name="search_placeholder">Stop…</string>
-    <string name="no_connectivity">No connectivity – can’t update timetable</string>
+    <string name="no_connectivity_cant_update">No connectivity – can’t update timetable</string>
+    <string name="no_connectivity">No connectivity</string>
     <string name="timetable_up_to_date">Timetable is up-to-date</string>
     <string name="validity_failed">Downloaded timetable is corrupted – can’t update</string>
     <string name="error_try_later">Error. Try again later</string>
@@ -89,4 +90,5 @@     Fri
     <string name="Sat">Sat</string>
     <string name="Sun">Sun</string>
     <string name="summary_timetable_automatic_update">Automatically check for and download timetable updates</string>
+    <string name="server_error">Server error</string>
 </resources>




diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 0d89712827a98db6c34e2a4b7faac7f0647f0956..094229af232761e69b00552d2a3022dca6161e84 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -17,7 +17,8 @@     Verbind mit dem Internet, um den Fahrplan herunterzuladen
     <string name="no_db_downloading">Fahrplan wird heruntergeladen…</string>
     <string name="timetable_downloading">Fahrplan wird heruntergeladen</string>
     <string name="search_placeholder">Haltestelle…</string>
-    <string name="no_connectivity">Kein Verbindung – kann nicht den Fahrplan aktualisieren</string>
+    <string name="no_connectivity_cant_update">Kein Verbindung – kann nicht den Fahrplan aktualisieren</string>
+    <string name="no_connectivity">Kein Verbindung</string>
     <string name="timetable_up_to_date">Fahrplan ist aktuell</string>
     <string name="validity_failed">Der heruntergeladene Fahrplan ist geschädigt – kann nicht aktualisieren</string>
     <string name="error_try_later">Fehler. Versuch später noch einmal</string>
@@ -70,4 +71,5 @@     Sa.
     <string name="Sun">So.</string>
     <string name="title_timetable_automatic_update">Automatische Updates</string>
     <string name="summary_timetable_automatic_update">Automatisch nach Fahrplanaktualisierungen suchen und diese herunterladen</string>
+    <string name="server_error">Serverfehler</string>
 </resources>




diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 16ed32fc1cb98d9bd718d396ff7d53ff47c4d766..2269942ded34a1ddcc980175b7536da1b0b10ede 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -20,7 +20,8 @@     Connetti a Internet per scaricare l’orario
     <string name="no_db_downloading">L’orario è stando scaricato</string>
     <string name="timetable_downloading">Scaricando l’orario</string>
     <string name="search_placeholder">Fermata…</string>
-    <string name="no_connectivity">Nessuna connettività – non si riesce aggiornare l’orario</string>
+    <string name="no_connectivity_cant_update">Nessuna connettività – non si riesce aggiornare l’orario</string>
+    <string name="no_connectivity">Nessuna connettività</string>
     <string name="timetable_up_to_date">L’orario sta aggiornato</string>
     <string name="validity_failed">L’orario scaricato sta corrotto – non si riesce aggiornare</string>
     <string name="error_try_later">Errore. Riprova più tardi</string>
@@ -69,4 +70,5 @@     sab
     <string name="Sun">dom</string>
     <string name="summary_timetable_automatic_update">Controlla e scarica automaticamente gli aggiornamenti dell’orario</string>
     <string name="title_timetable_automatic_update">Aggiornamenti automatici</string>
+    <string name="server_error">Errore del server</string>
 </resources>




diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index b221b994f403382f9f5d11fb7aeb7cd93a3dbf09..e3a00a950c096b82a64501d0a55b6649e1cccce8 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -12,7 +12,8 @@     Maak verbinding met internet om de dienstregeling te downloaden.
     <string name="no_db_downloading">De dienstregeling wordt gedownload…</string>
     <string name="timetable_downloading">Bezig met downloaden van dienstregeling</string>
     <string name="search_placeholder">Halte…</string>
-    <string name="no_connectivity">Geen internetverbinding – de dienstregeling kan niet worden bijgewerkt.</string>
+    <string name="no_connectivity_cant_update">Geen internetverbinding – de dienstregeling kan niet worden bijgewerkt.</string>
+    <string name="no_connectivity">Geen internetverbinding</string>
     <string name="timetable_up_to_date">De dienstregeling is volledig bijgewerkt.</string>
     <string name="validity_failed">De gedownloade dienstregeling bevat fouten – bijwerken is niet mogelijk.</string>
     <string name="error_try_later">Fout; probeer het later opnieuw.</string>
@@ -71,4 +72,5 @@     za
     <string name="Sun">zo</string>
     <string name="title_timetable_automatic_update">Automatische updates</string>
     <string name="summary_timetable_automatic_update">Automatisch controleren en download dienstregeling updates</string>
+    <string name="server_error">Serverfout</string>
 </resources>




diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index b7e12ec5d2b6da0c24c679dab4db23edda1e645b..d627a692c607c5efb7198a47e16ecda5d285f774 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -14,7 +14,8 @@     Pobieranie rozkładu
     <string name="search_placeholder">Przystanek…</string>
     <string name="timetable_up_to_date">Rozkład jest aktualny</string>
     <string name="validity_failed">Pobrany rozkład jest uszkodzony – nie można zaktualizować</string>
-    <string name="no_connectivity">Brak połączenia z Internetem – nie można zaktualizować rozkładu</string>
+    <string name="no_connectivity_cant_update">Brak połączenia z Internetem – nie można zaktualizować rozkładu</string>
+    <string name="no_connectivity">Brak połączenia z Internetem</string>
     <string name="error_try_later">Błąd. Spróbuj ponownie później</string>
     <string name="now">Teraz</string>
     <string name="stop_already_fav">Ten przystanek już jest pośród ulubionych</string>
@@ -70,4 +71,5 @@     sob.
     <string name="Sun">niedz.</string>
     <string name="summary_timetable_automatic_update">Automatycznie sprawdzaj i pobieraj aktualizacje rozkładu</string>
     <string name="title_timetable_automatic_update">Automatyczne aktualizacje</string>
+    <string name="server_error">Błąd servera</string>
 </resources>
\ No newline at end of file




diff --git a/build.gradle b/build.gradle
index 9dd962ea072e40838a705c23178e0f7c41df1161..46a60aa5ed0c08de693c8ed41f436fa47d145972 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
 // Top-level build file where you can add configuration options common to all sub-projects/modules.
 
 buildscript {
-    ext.kotlin_version = '1.2.61'
+    ext.kotlin_version = '1.2.70'
     repositories {
         jcenter()
         maven { url 'https://maven.google.com' }