diff --git a/OsmAnd/src/net/osmand/plus/server/MetaTileFileSystemCache.java b/OsmAnd/src/net/osmand/plus/server/MetaTileFileSystemCache.java new file mode 100644 index 0000000000..a06fade868 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/server/MetaTileFileSystemCache.java @@ -0,0 +1,77 @@ +package net.osmand.plus.server; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import net.osmand.PlatformUtil; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.server.endpoints.TileEndpoint; +import org.apache.commons.logging.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class MetaTileFileSystemCache { + private static final Log LOG = PlatformUtil.getLog(TileEndpoint.class); + private static final Object TILES_FOLDER = "tiles"; + private static final int MAX_IN_MEMORY_CACHE_SIZE = 4; + private static final int MAX_CACHE_SIZE = 4; + private final ConcurrentLinkedQueue inMemoryCache = new ConcurrentLinkedQueue<>(); + private final File externalCacheDir; + public boolean inMemoryCacheEnabled = false; + + public MetaTileFileSystemCache(OsmandApplication application) { + externalCacheDir = new File( + application.getExternalCacheDir().getAbsoluteFile() + File.separator + TILES_FOLDER); + if (!externalCacheDir.exists()) { + externalCacheDir.mkdir(); + } + } + + public void put(TileEndpoint.MetaTileCache tile) { + while (inMemoryCache.size() > MAX_IN_MEMORY_CACHE_SIZE) { + inMemoryCache.poll(); + } + while (externalCacheDir.listFiles().length > MAX_CACHE_SIZE) { + //remove outdated files + for (int i = 0; i < externalCacheDir.listFiles().length - MAX_CACHE_SIZE; i++) { + externalCacheDir.listFiles()[i].delete(); + } + } + String fileName = tile.getTileId(); + File file = new File(externalCacheDir, fileName); + if (file.exists()) { + file.delete(); + } + try { + FileOutputStream out = new FileOutputStream(file); + tile.getBitmap().compress(Bitmap.CompressFormat.PNG, 100, out); + out.flush(); + out.close(); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + } + if (inMemoryCacheEnabled) { + inMemoryCache.add(tile); + } + } + + public TileEndpoint.MetaTileCache get(int zoom, int METATILE_SIZE, int x, int y) { + for (int tx = x - METATILE_SIZE + 1; tx < METATILE_SIZE + x - 1; tx++) { + for (int ty = y - METATILE_SIZE + 1; ty < METATILE_SIZE + y - 1; ty++) { + File file = new File(externalCacheDir, zoom + "_" + METATILE_SIZE + "_" + tx + "_" + ty); + if (file.exists()) { + TileEndpoint.MetaTileCache tile = new TileEndpoint.MetaTileCache( + BitmapFactory.decodeFile(file.getAbsolutePath()), + tx, ty, tx + METATILE_SIZE, ty + METATILE_SIZE, zoom + ); + if (inMemoryCacheEnabled) { + inMemoryCache.add(tile); + } + return tile; + } + } + } + return null; + } +} diff --git a/OsmAnd/src/net/osmand/plus/server/OsmAndHttpServer.java b/OsmAnd/src/net/osmand/plus/server/OsmAndHttpServer.java index ed15fe7172..a20472d589 100644 --- a/OsmAnd/src/net/osmand/plus/server/OsmAndHttpServer.java +++ b/OsmAnd/src/net/osmand/plus/server/OsmAndHttpServer.java @@ -62,6 +62,7 @@ public class OsmAndHttpServer extends NanoHTTPD { return e.getValue().process(session, uri); } catch (Exception exception) { LOG.error("SERVER ERROR: " + exception.getMessage()); + return ErrorResponses.response500; } } } diff --git a/OsmAnd/src/net/osmand/plus/server/endpoints/TileEndpoint.java b/OsmAnd/src/net/osmand/plus/server/endpoints/TileEndpoint.java index 467903bf9d..e081538d68 100644 --- a/OsmAnd/src/net/osmand/plus/server/endpoints/TileEndpoint.java +++ b/OsmAnd/src/net/osmand/plus/server/endpoints/TileEndpoint.java @@ -6,13 +6,13 @@ import net.osmand.PlatformUtil; import net.osmand.data.RotatedTileBox; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.resources.AsyncLoadingThread; +import net.osmand.plus.server.MetaTileFileSystemCache; import net.osmand.plus.server.OsmAndHttpServer; import net.osmand.util.MapUtils; import org.apache.commons.logging.Log; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.util.concurrent.ConcurrentLinkedQueue; import static fi.iki.elonen.NanoHTTPD.newFixedLengthResponse; @@ -22,14 +22,12 @@ public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint { private static final int TIMEOUT_STEP = 500; private static final int TIMEOUT = 5000; private static final int METATILE_SIZE = 2; - private static final int MAX_CACHE_SIZE = 4; private static final Log LOG = PlatformUtil.getLog(TileEndpoint.class); private final MapActivity mapActivity; - // TODO store on file system - private final ConcurrentLinkedQueue cache = new ConcurrentLinkedQueue<>(); + private final MetaTileFileSystemCache cache; - private static class MetaTileCache { + public static class MetaTileCache { Bitmap bmp; int sx; int sy; @@ -37,11 +35,28 @@ public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint { int ey; int zoom; + public MetaTileCache() { + + } + + public MetaTileCache(Bitmap bmp, int sx, int sy, int ex, int ey, int zoom) { + this.bmp = bmp; + this.sx = sx; + this.sy = sy; + this.ex = ex; + this.ey = ey; + this.zoom = zoom; + } + // to be used in file name public String getTileId() { return zoom + "_" + METATILE_SIZE + "_" + sx + "_" + sy; } + public Bitmap getBitmap() { + return bmp; + } + public Bitmap getSubtile(int x, int y) { return Bitmap.createBitmap(bmp, (x - sx) * TILE_SIZE_PX * TILE_DENSITY, @@ -52,13 +67,7 @@ public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint { public TileEndpoint(MapActivity mapActivity) { this.mapActivity = mapActivity; - } - - private void addToMemoryCache(MetaTileCache res) { - while (cache.size() > MAX_CACHE_SIZE) { - cache.poll(); - } - cache.add(res); + this.cache = new MetaTileFileSystemCache(mapActivity.getMyApplication()); } @Override @@ -78,13 +87,7 @@ public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint { int zoom = Integer.parseInt(prms[1]); int x = Integer.parseInt(prms[2]); int y = Integer.parseInt(prms[3]); - - MetaTileCache res = null; - for (MetaTileCache r : cache) { - if (r.zoom == zoom && r.ex >= x && r.ey >= y && r.sx <= x && r.sy <= y) { - res = r; - } - } + MetaTileCache res = cache.get(zoom, METATILE_SIZE, x, y); if (res == null) { res = requestMetatile(x, y, zoom); if (res == null) { @@ -137,7 +140,7 @@ public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint { res.zoom = zoom; Bitmap tempBmp = mapActivity.getMapView().getBufferBitmap(); res.bmp = tempBmp.copy(tempBmp.getConfig(), true); - addToMemoryCache(res); + cache.put(res); } return res; } catch (InterruptedException e) {