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;
}
}
}