package org.osmdroid.tileprovider.modules; import java.io.File; import java.util.concurrent.atomic.AtomicReference; import org.osmdroid.config.Configuration; import org.osmdroid.tileprovider.ExpirableBitmapDrawable; import org.osmdroid.tileprovider.IRegisterReceiver; import org.osmdroid.tileprovider.MapTile; import org.osmdroid.tileprovider.MapTileRequestState; import org.osmdroid.tileprovider.tilesource.BitmapTileSourceBase.LowMemoryException; import org.osmdroid.tileprovider.tilesource.ITileSource; import org.osmdroid.tileprovider.tilesource.TileSourceFactory; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.util.Log; import org.osmdroid.api.IMapView; import org.osmdroid.tileprovider.constants.OpenStreetMapTileProviderConstants; import org.osmdroid.tileprovider.util.Counters; /** * Implements a file system cache and provides cached tiles. This functions as a tile provider by * serving cached tiles for the supplied tile source. * * @author Marc Kurtz * @author Nicolas Gramlich * */ public class MapTileFilesystemProvider extends MapTileFileStorageProviderBase { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== private final long mMaximumCachedFileAge; private final AtomicReference<ITileSource> mTileSource = new AtomicReference<ITileSource>(); // =========================================================== // Constructors // =========================================================== public MapTileFilesystemProvider(final IRegisterReceiver pRegisterReceiver) { this(pRegisterReceiver, TileSourceFactory.DEFAULT_TILE_SOURCE); } public MapTileFilesystemProvider(final IRegisterReceiver pRegisterReceiver, final ITileSource aTileSource) { this(pRegisterReceiver, aTileSource, Configuration.getInstance().getExpirationExtendedDuration() + OpenStreetMapTileProviderConstants.DEFAULT_MAXIMUM_CACHED_FILE_AGE); } public MapTileFilesystemProvider(final IRegisterReceiver pRegisterReceiver, final ITileSource pTileSource, final long pMaximumCachedFileAge) { this(pRegisterReceiver, pTileSource, pMaximumCachedFileAge, Configuration.getInstance().getTileFileSystemThreads(), Configuration.getInstance().getTileFileSystemMaxQueueSize()); } /** * Provides a file system based cache tile provider. Other providers can register and store data * in the cache. * * @param pRegisterReceiver */ public MapTileFilesystemProvider(final IRegisterReceiver pRegisterReceiver, final ITileSource pTileSource, final long pMaximumCachedFileAge, int pThreadPoolSize, int pPendingQueueSize) { super(pRegisterReceiver, pThreadPoolSize, pPendingQueueSize); setTileSource(pTileSource); mMaximumCachedFileAge = pMaximumCachedFileAge; } // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods from SuperClass/Interfaces // =========================================================== @Override public boolean getUsesDataConnection() { return false; } @Override protected String getName() { return "File System Cache Provider"; } @Override protected String getThreadGroupName() { return "filesystem"; } @Override protected Runnable getTileLoader() { return new TileLoader(); } @Override public int getMinimumZoomLevel() { ITileSource tileSource = mTileSource.get(); return tileSource != null ? tileSource.getMinimumZoomLevel() : OpenStreetMapTileProviderConstants.MINIMUM_ZOOMLEVEL; } @Override public int getMaximumZoomLevel() { ITileSource tileSource = mTileSource.get(); return tileSource != null ? tileSource.getMaximumZoomLevel() : microsoft.mappoint.TileSystem.getMaximumZoomLevel(); } @Override public void setTileSource(final ITileSource pTileSource) { mTileSource.set(pTileSource); } // =========================================================== // Inner and Anonymous Classes // =========================================================== protected class TileLoader extends MapTileModuleProviderBase.TileLoader { @Override public Drawable loadTile(final MapTileRequestState pState) throws CantContinueException { ITileSource tileSource = mTileSource.get(); if (tileSource == null) { return null; } final MapTile tile = pState.getMapTile(); // if there's no sdcard then don't do anything if (!isSdCardAvailable()) { if (Configuration.getInstance().isDebugMode()) { Log.d(IMapView.LOGTAG,"No sdcard - do nothing for tile: " + tile); } Counters.fileCacheMiss++; return null; } // Check the tile source to see if its file is available and if so, then render the // drawable and return the tile final File file = new File(Configuration.getInstance().getOsmdroidTileCache(), tileSource.getTileRelativeFilenameString(tile) + OpenStreetMapTileProviderConstants.TILE_PATH_EXTENSION); if (file.exists()) { try { final Drawable drawable = tileSource.getDrawable(file.getPath()); // Check to see if file has expired final long now = System.currentTimeMillis(); final long lastModified = file.lastModified(); final boolean fileExpired = lastModified < now - mMaximumCachedFileAge; if (fileExpired && drawable != null) { if (Configuration.getInstance().isDebugMode()) { Log.d(IMapView.LOGTAG,"Tile expired: " + tile); } ExpirableBitmapDrawable.setDrawableExpired(drawable); } Counters.fileCacheHit++; return drawable; } catch (final LowMemoryException e) { // low memory so empty the queue Log.w(IMapView.LOGTAG,"LowMemoryException downloading MapTile: " + tile + " : " + e); Counters.fileCacheOOM++; throw new CantContinueException(e); } } Counters.fileCacheMiss++; // If we get here then there is no file in the file cache return null; } } }