From 731c10136b8d70110ceeec9bcbc15961d12e75c6 Mon Sep 17 00:00:00 2001 From: Dave Craig Date: Thu, 31 Oct 2024 11:05:26 +0000 Subject: [PATCH] Add caching to protomaps tile client This change moves the soundscape-backend specific parts of TileClient out into a sub-class so that a protomaps TileClient can share the caching code. It also removes the unused http caching from MainActivity - we no longer use the type of HTTP access that would use this cache. --- .../soundscape/MainActivity.kt | 9 ----- .../soundscape/geoengine/GeoEngine.kt | 17 ++++++--- .../soundscape/network/ITileDAO.kt | 17 +++++++++ .../soundscape/network/ProtomapsTileClient.kt | 38 +++++-------------- .../network/SoundscapeBackendTileClient.kt | 21 ++++++++++ .../soundscape/network/TileClient.kt | 28 ++++++-------- 6 files changed, 70 insertions(+), 60 deletions(-) create mode 100644 app/src/main/java/org/scottishtecharmy/soundscape/network/SoundscapeBackendTileClient.kt diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/MainActivity.kt b/app/src/main/java/org/scottishtecharmy/soundscape/MainActivity.kt index aa3dd675..5deb1837 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/MainActivity.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/MainActivity.kt @@ -117,15 +117,6 @@ class MainActivity : AppCompatActivity() { return } - // Install HTTP cache this caches all of the UI tiles (at least?) - try { - val httpCacheDir = File(applicationContext.cacheDir, "http") - val httpCacheSize = (100 * 1024 * 1024).toLong() // 100 MiB - HttpResponseCache.install(httpCacheDir, httpCacheSize) - } catch (e: IOException) { - Log.i("Injection", "HTTP response cache installation failed:$e") - } - checkAndRequestNotificationPermissions() soundscapeServiceConnection.tryToBindToServiceIfRunning(applicationContext) diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/GeoEngine.kt b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/GeoEngine.kt index f9ea18d0..c93e58b6 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/GeoEngine.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/geoengine/GeoEngine.kt @@ -38,6 +38,7 @@ import org.scottishtecharmy.soundscape.locationprovider.DirectionProvider import org.scottishtecharmy.soundscape.locationprovider.LocationProvider import org.scottishtecharmy.soundscape.network.ITileDAO import org.scottishtecharmy.soundscape.network.ProtomapsTileClient +import org.scottishtecharmy.soundscape.network.SoundscapeBackendTileClient import org.scottishtecharmy.soundscape.network.TileClient import org.scottishtecharmy.soundscape.utils.RelativeDirections import org.scottishtecharmy.soundscape.utils.TileGrid @@ -90,7 +91,7 @@ class GeoEngine { private val tilesDao : TilesDao = TilesDao(tileDataRealm) private val tilesRepository : TilesRepository = TilesRepository(tilesDao) - // HTTP connection to soundscape-backend tile server + // HTTP connection to soundscape-backend or protomaps tile server private lateinit var tileClient : TileClient private lateinit var locationProvider : LocationProvider @@ -111,7 +112,12 @@ class GeoEngine { sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application.applicationContext) - tileClient = TileClient(application) + tileClient = if(SOUNDSCAPE_TILE_BACKEND) { + SoundscapeBackendTileClient(application) + } else { + ProtomapsTileClient(application) + } + configLocale = getCurrentLocale() configuration = Configuration(application.applicationContext.resources.configuration) configuration.setLocale(configLocale) @@ -177,11 +183,12 @@ class GeoEngine { var ret = false withContext(Dispatchers.IO) { try { - val protomapsClient = ProtomapsTileClient() + val service = + tileClient.retrofitInstance?.create(ITileDAO::class.java) val tileReq = async { - protomapsClient.getClient().getMvtTileWithCache(x, y, ZOOM_LEVEL) + service?.getVectorTileWithCache(x, y, ZOOM_LEVEL) } - val result = tileReq.await().awaitResponse().body() + val result = tileReq.await()?.awaitResponse()?.body() if (result != null) { val tileFeatureCollection = vectorTileToGeoJson(x, y, result) val tileData = processTileFeatureCollection(tileFeatureCollection, quadkey) diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/network/ITileDAO.kt b/app/src/main/java/org/scottishtecharmy/soundscape/network/ITileDAO.kt index 2dde4bad..263d227b 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/network/ITileDAO.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/network/ITileDAO.kt @@ -3,8 +3,10 @@ package org.scottishtecharmy.soundscape.network import retrofit2.Call import retrofit2.http.GET import retrofit2.http.Path +import vector_tile.VectorTile interface ITileDAO { + // soundscape-backend functions @GET("tiles/16/{x}/{y}.json") fun getTile( @Path("x") x: Int, @@ -16,4 +18,19 @@ interface ITileDAO { @Path("x") x: Int, @Path("y") y: Int ): Call + + // protomaps server functions + @GET("protomaps/{z}/{x}/{y}.mvt") + fun getVectorTile( + @Path("x") x: Int, + @Path("y") y: Int, + @Path("z") z: Int + ): Call + + @GET("protomaps/{z}/{x}/{y}.mvt") + fun getVectorTileWithCache( + @Path("x") x: Int, + @Path("y") y: Int, + @Path("z") z: Int + ): Call } \ No newline at end of file diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/network/ProtomapsTileClient.kt b/app/src/main/java/org/scottishtecharmy/soundscape/network/ProtomapsTileClient.kt index 774d6ace..a89e639a 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/network/ProtomapsTileClient.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/network/ProtomapsTileClient.kt @@ -1,39 +1,19 @@ package org.scottishtecharmy.soundscape.network -import retrofit2.Call +import android.app.Application import retrofit2.Retrofit import retrofit2.converter.protobuf.ProtoConverterFactory -import retrofit2.http.GET -import retrofit2.http.Path -import vector_tile.VectorTile -interface IProtomapsTileDAO { - @GET("protomaps/{z}/{x}/{y}.mvt") - fun getMvtTileWithCache( - @Path("x") x: Int, - @Path("y") y: Int, - @Path("z") z: Int - ): Call -} -/** - * This is a retrofit client for getting tiles from our protomaps server and parsing the protobuf - * automatically as it goes - * @param x tile coordinate - * @param y tile coordinate - * @param z zoom level - */ -class ProtomapsTileClient { +class ProtomapsTileClient(application: Application) : TileClient(application) { - private var retrofitInstance : Retrofit? = null - fun getClient(): IProtomapsTileDAO { - if(retrofitInstance == null) { - retrofitInstance = Retrofit.Builder() - .baseUrl(BASE_URL) - .addConverterFactory(ProtoConverterFactory.create()) - .build() - } - return retrofitInstance!!.create(IProtomapsTileDAO::class.java) + override fun buildRetrofit() : Retrofit { + return Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(ProtoConverterFactory.create()) + .client(okHttpClient) + .build() } + companion object { private const val BASE_URL = "https://d1wzlzgah5gfol.cloudfront.net" } diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/network/SoundscapeBackendTileClient.kt b/app/src/main/java/org/scottishtecharmy/soundscape/network/SoundscapeBackendTileClient.kt new file mode 100644 index 00000000..aebc3003 --- /dev/null +++ b/app/src/main/java/org/scottishtecharmy/soundscape/network/SoundscapeBackendTileClient.kt @@ -0,0 +1,21 @@ +package org.scottishtecharmy.soundscape.network + +import android.app.Application +import retrofit2.Retrofit +import retrofit2.converter.scalars.ScalarsConverterFactory + +class SoundscapeBackendTileClient(application: Application) : TileClient(application) { + + override fun buildRetrofit() : Retrofit { + return Retrofit.Builder() + .baseUrl(BASE_URL) + // use it to output the string + .addConverterFactory(ScalarsConverterFactory.create()) + .client(okHttpClient) + .build() + } + + companion object { + private const val BASE_URL = "https://soundscape.scottishtecharmy.org" + } +} diff --git a/app/src/main/java/org/scottishtecharmy/soundscape/network/TileClient.kt b/app/src/main/java/org/scottishtecharmy/soundscape/network/TileClient.kt index 0a5d772f..93aa3659 100644 --- a/app/src/main/java/org/scottishtecharmy/soundscape/network/TileClient.kt +++ b/app/src/main/java/org/scottishtecharmy/soundscape/network/TileClient.kt @@ -7,9 +7,7 @@ import android.net.NetworkCapabilities import okhttp3.Cache import okhttp3.CacheControl import okhttp3.OkHttpClient -// import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit -import retrofit2.converter.scalars.ScalarsConverterFactory import java.util.concurrent.TimeUnit // TODO I want to get to the Cache class which is described in these articles: @@ -17,7 +15,7 @@ import java.util.concurrent.TimeUnit // https://stackoverflow.com/questions/70711512/context-getapplicationcontext-on-a-null-object-when-using-okhttp-cache //https://proandroiddev.com/increase-performance-of-your-app-by-caching-api-calls-using-okhttp-1384a621c51f // https://stackoverflow.com/questions/23429046/can-retrofit-with-okhttp-use-cache-data-when-offline?noredirect=1&lq=1 -class TileClient(val application: Application) { +abstract class TileClient(val application: Application) { private val connectivityManager: ConnectivityManager init { @@ -30,7 +28,7 @@ class TileClient(val application: Application) { private val cacheSize = (5 * 1024 * 1024).toLong() //5MB cache size private val myCache = Cache(application.applicationContext.cacheDir, cacheSize) - private val okHttpClient = OkHttpClient.Builder() + protected val okHttpClient = OkHttpClient.Builder() .cache(myCache) .addInterceptor { chain -> @@ -66,17 +64,20 @@ class TileClient(val application: Application) { })*/ .build() + /** + * buildRetrofit is called in the sub-class and is responsible for creating the correct type + * of Retrofit object. The SoundscapeBackendTiledClient returns a String type and the + * ProtomapsTileClient returns a VectorTile. That and the differing server URLs is taken care + * of in buildRetrofit. + */ + abstract fun buildRetrofit() : Retrofit + val retrofitInstance : Retrofit? get() { // has this object been created yet? if (retrofit == null) { // create it - retrofit = Retrofit.Builder() - .baseUrl(BASE_URL) - // use it to output the string - .addConverterFactory(ScalarsConverterFactory.create()) - .client(okHttpClient) - .build() + retrofit = buildRetrofit() } return retrofit } @@ -93,11 +94,4 @@ class TileClient(val application: Application) { else -> false } } - - companion object { - private const val BASE_URL = "https://soundscape.scottishtecharmy.org" - } - } - -