Bimba.git

commit 8e82fc5bc0f528a732118c80565687f02b2d7c8e

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

better caching

 app/src/main/java/ml/adamsprogs/bimba/CacheManager.kt | 38 ++++++--
 app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt | 36 +++----


diff --git a/app/src/main/java/ml/adamsprogs/bimba/CacheManager.kt b/app/src/main/java/ml/adamsprogs/bimba/CacheManager.kt
index 15832219c47408de967b6edf2b94b02992de65ae..a4271b84969e54773e0debddae5e6354dd73bb02 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/CacheManager.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/CacheManager.kt
@@ -7,13 +7,15 @@
 class CacheManager private constructor(context: Context) {
     companion object {
         private var manager: CacheManager? = null
-        fun getCacheManager(context: Context) : CacheManager {
+        fun getCacheManager(context: Context): CacheManager {
             return if (manager == null) {
                 manager = CacheManager(context)
                 manager!!
             } else
                 manager!!
         }
+
+        val MAX_SIZE = 40
     }
 
     private var cachePreferences: SharedPreferences = context.getSharedPreferences("ml.adamsprogs.bimba.cachePreferences.cache", Context.MODE_PRIVATE)
@@ -22,6 +24,10 @@
     private var cache: HashMap<String, Plate> = HashMap()
     private var cacheHits: HashMap<String, Int> = HashMap()
 
+    fun keys(): List<Plate> {
+        return cache.map { Plate(it.key.split("@")[0], it.key.split("@")[1], null) }
+    }
+
     fun hasAll(plates: HashSet<Plate>): Boolean {
         plates
                 .filterNot { has(it) }
@@ -37,31 +43,40 @@         return false
     }
 
     fun has(plate: Plate): Boolean {
-        val key = "${plate.line}@${plate.stop}"
-        return cache.containsKey(key)
+        return cache.containsKey(key(plate))
     }
 
     fun push(plates: HashSet<Plate>) {
+        val removeNumber = cache.size + plates.size - MAX_SIZE
         val editor = cachePreferences.edit()
+        val editorCacheHits = cacheHitsPreferences.edit()
+        cacheHits.map { "${it.value}|${it.key}" }.sortedBy { it }.slice(0 until removeNumber).forEach {
+            val key = it.split("|")[1]
+            cache.remove(key)
+            editor.remove(key)
+        }
         for (plate in plates) {
-            val key = "${plate.line}@${plate.stop}"
+            val key = key(plate)
             cache[key] = plate
+            cacheHits[key] = 0
             editor.putString(key, cache[key].toString())
+            editorCacheHits.putInt(key, 0)
         }
         editor.apply()
+        editorCacheHits.apply()
     }
 
     fun push(plate: Plate) {
         val editorCache = cachePreferences.edit()
         val editorCacheHits = cacheHitsPreferences.edit()
-        if (cacheHits.size == 40) { //todo size?
+        if (cacheHits.size == MAX_SIZE) {
             val key = cacheHits.minBy { it.value }?.key
             cache.remove(key)
             editorCache.remove(key)
             cacheHits.remove(key)
             editorCacheHits.remove(key)
         }
-        val key = "${plate.line}@${plate.stop}"
+        val key = key(plate)
         cache[key] = plate
         cacheHits[key] = 0
         editorCache.putString(key, plate.toString())
@@ -77,7 +92,7 @@             val value = get(plate)
             if (value == null)
                 result.add(plate)
             else
-                result.add(get(plate)!!)
+                result.add(value)
         }
         return result
     }
@@ -85,19 +100,20 @@
     fun get(plate: Plate): Plate? {
         if (!has(plate))
             return null
-        val key = "${plate.line}@${plate.stop}"
+        val key = key(plate)
         val hits = cacheHits[key]
         if (hits != null)
             cacheHits[key] = hits + 1
         return cache[key]
     }
 
-    fun recreate() {
-        TODO()
+    fun recreate(stopDeparturesByPlates: HashSet<Plate>) {
+        stopDeparturesByPlates.forEach { cache[key(it)] = it }
     }
 
     init {
         cache = cacheFromString(cachePreferences.all)
+        @Suppress("UNCHECKED_CAST")
         cacheHits = cacheHitsPreferences.all as HashMap<String, Int>
     }
 
@@ -108,4 +124,6 @@             result[key] = Plate.fromString(value as String)
         }
         return result
     }
+
+    private fun key(plate: Plate) = "${plate.line}@${plate.stop}"
 }
\ No newline at end of file




diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt
index 2ca7bd58bf3a7055d735292963e1015965874a92..8c5b119273aedac36074beeadf1a4d0ec471328f 100644
--- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt
+++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt
@@ -5,7 +5,6 @@ import android.database.CursorIndexOutOfBoundsException
 import android.database.sqlite.SQLiteCantOpenDatabaseException
 import android.database.sqlite.SQLiteDatabase
 import android.database.sqlite.SQLiteDatabaseCorruptException
-import android.util.Log
 import ml.adamsprogs.bimba.CacheManager
 import java.io.File
 
@@ -61,7 +60,7 @@             throw SQLiteCantOpenDatabaseException("db corrupt")
         }
         this.db = db
 
-        //todo cacheManager.recreate()
+        cacheManager.recreate(getStopDeparturesByPlates(cacheManager.keys().toSet() as HashSet<Plate>)) //todo optimise
     }
 
     fun getStops(): ArrayList<StopSuggestion> {
@@ -108,28 +107,28 @@     }
 
     fun getStopDepartures(stopId: String, lineId: String? = null): HashMap<String, ArrayList<Departure>> {
         val plates = HashSet<Plate>()
+        val toGet = HashSet<Plate>()
 
         if (lineId == null) {
-            for (line in getLinesForStop(stopId)) {
-                val plate = Plate(line, stopId, null)
-                if (cacheManager.has(plate))
-                    plates.add(cacheManager.get(plate)!!)
-                else {
-                    val p = Plate(line, stopId, getStopDeparturesByLine(line, stopId)) //fixme to one query
-                    plates.add(p)
-                    cacheManager.push(p)
-                }
-            }
+            getLinesForStop(stopId)
+                    .map { Plate(it, stopId, null) }
+                    .forEach {
+                        if (cacheManager.has(it))
+                            plates.add(cacheManager.get(it)!!)
+                        else {
+                            toGet.add(it)
+                        }
+                    }
         } else {
             val plate = Plate(lineId, stopId, null)
             if (cacheManager.has(plate))
                 plates.add(cacheManager.get(plate)!!)
             else {
-                val p = Plate(lineId, stopId, getStopDeparturesByLine(lineId, stopId))
-                plates.add(p)
-                cacheManager.push(p)
+                toGet.add(plate)
             }
         }
+
+        getStopDeparturesByPlates(toGet).forEach {cacheManager.push(it); plates.add(it)}
 
         return Plate.join(plates)
     }
@@ -145,7 +144,7 @@             else
                 toGet.add(plate)
         }
 
-        result.addAll(getStopDeparturesByPlates(toGet))
+        getStopDeparturesByPlates(toGet).forEach {cacheManager.push(it); result.add(it)}
 
         return Plate.join(result)
     }
@@ -196,11 +195,6 @@         return lines
     }
 
     fun getFavouriteElement(plate: Plate): String {
-        val q = "select name || ' (' || stops.symbol || stops.number || '): \n' " +
-                "|| lines.number || ' → ' || headsign from timetables join stops on (stops.id = stop_id) " +
-                "join lines on(lines.id = line_id) join nodes on(nodes.symbol = stops.symbol) where " +
-                "stop_id = ${plate.stop} and line_id = ${plate.line}"
-
         val cursor = db.rawQuery("select name || ' (' || stops.symbol || stops.number || '): \n' " +
                 "|| lines.number || ' → ' || headsign from timetables join stops on (stops.id = stop_id) " +
                 "join lines on(lines.id = line_id) join nodes on(nodes.symbol = stops.symbol) where " +