// Created by plusminus on 21:46:22 - 25.09.2008
package com.mapbox.mapboxsdk.tileprovider;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.mapbox.mapboxsdk.geometry.BoundingBox;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.tileprovider.constants.TileLayerConstants;
import com.mapbox.mapboxsdk.tileprovider.tilesource.ITileLayer;
import com.mapbox.mapboxsdk.util.BitmapUtils;
import uk.co.senab.bitmapcache.CacheableBitmapDrawable;
/**
* This is an abstract class. The tile provider is responsible for:
* <ul>
* <li>determining if a map tile is available,</li>
* <li>notifying the client, via a callback handler</li>
* </ul>
* see {@link MapTile} for an overview of how tiles are served by this provider.
*
* @author Marc Kurtz
* @author Nicolas Gramlich
*/
public abstract class MapTileLayerBase implements IMapTileProviderCallback, TileLayerConstants {
protected Context context;
protected final MapTileCache mTileCache;
private Handler mTileRequestCompleteHandler;
private boolean mUseDataConnection = true;
private ITileLayer mTileSource;
protected String mCacheKey = "";
/**
* Attempts to get a Drawable that represents a {@link MapTile}. If the tile is not immediately
* available this will return null and attempt to get the tile from known tile sources for
* subsequent future requests. Note that this may return a {@link CacheableBitmapDrawable} in
* which case you should follow proper handling procedures for using that Drawable or it may
* reused while you are working with it.
*
* @see CacheableBitmapDrawable
*/
public abstract Drawable getMapTile(MapTile pTile, boolean allowRemote);
public abstract void detach();
/**
* Gets the minimum zoom level this tile provider can provide
*
* @return the minimum zoom level
*/
public float getMinimumZoomLevel() {
return mTileSource.getMinimumZoomLevel();
}
/**
* Get the maximum zoom level this tile provider can provide.
*
* @return the maximum zoom level
*/
public float getMaximumZoomLevel() {
return mTileSource.getMaximumZoomLevel();
}
/**
* Get the tile size in pixels this tile provider provides.
*
* @return the tile size in pixels
*/
public int getTileSizePixels() {
return mTileSource.getTileSizePixels();
}
/**
* Get the tile provider bounding box.
*
* @return the tile source bounding box
*/
public BoundingBox getBoundingBox() {
return mTileSource.getBoundingBox();
}
/**
* Get the tile provider center.
*
* @return the tile source center
*/
public LatLng getCenterCoordinate() {
return mTileSource.getCenterCoordinate();
}
/**
* Get the tile provider suggested starting zoom.
*
* @return the tile suggested starting zoom
*/
public float getCenterZoom() {
return mTileSource.getCenterZoom();
}
/**
* Sets the tile source for this tile provider.
*
* @param pTileSource the tile source
*/
public void setTileSource(final ITileLayer pTileSource) {
if (mTileSource != null) {
mTileSource.detach();
}
mTileSource = pTileSource;
if (mTileSource != null) {
mCacheKey = mTileSource.getCacheKey();
}
}
/**
* Gets the tile source for this tile provider.
*
* @return the tile source
*/
public ITileLayer getTileSource() {
return mTileSource;
}
/**
* Gets the cache key for that layer
*
* @return the cache key
*/
public String getCacheKey() {
return mCacheKey;
}
/**
* Creates a {@link MapTileCache} to be used to cache tiles in memory.
*/
public MapTileCache createTileCache(final Context aContext) {
return new MapTileCache(aContext);
}
public MapTileLayerBase(final Context aContext, final ITileLayer pTileSource) {
this(aContext, pTileSource, null);
}
public MapTileLayerBase(final Context aContext, final ITileLayer pTileSource,
final Handler pDownloadFinishedListener) {
this.context = aContext;
mTileRequestCompleteHandler = pDownloadFinishedListener;
mTileSource = pTileSource;
mTileCache = this.createTileCache(aContext);
}
/**
* Called by implementation class methods indicating that they have completed the request as
* best it can. The tile is added to the cache, and a MAPTILE_SUCCESS_ID message is sent.
*
* @param pState the map tile request state object
* @param pDrawable the Drawable of the map tile
*/
@Override
public void mapTileRequestCompleted(final MapTileRequestState pState,
final Drawable pDrawable) {
// tell our caller we've finished and it should update its view
if (mTileRequestCompleteHandler != null) {
Message msg = new Message();
msg.obj = pState.getMapTile().getTileRect();
msg.what = MapTile.MAPTILE_SUCCESS_ID;
mTileRequestCompleteHandler.sendMessage(msg);
} else {
Log.e(TAG, "Failed to send map update request because mTileRequestCompleteHandler == null");
}
if (DEBUG_TILE_PROVIDERS) {
Log.d(TAG, "MapTileLayerBase.mapTileRequestCompleted(): " + pState.getMapTile());
}
}
/**
* Called by implementation class methods indicating that they have failed to retrieve the
* requested map tile. a MAPTILE_FAIL_ID message is sent.
*
* @param pState the map tile request state object
*/
@Override
public void mapTileRequestFailed(final MapTileRequestState pState) {
if (mTileRequestCompleteHandler != null) {
mTileRequestCompleteHandler.sendEmptyMessage(MapTile.MAPTILE_FAIL_ID);
}
if (DEBUG_TILE_PROVIDERS) {
Log.d(TAG, "MapTileLayerBase.mapTileRequestFailed(): " + pState.getMapTile());
}
}
/**
* Called by implementation class methods indicating that they have produced an expired result
* that can be used but better results may be delivered later. The tile is added to the cache,
* and a MAPTILE_SUCCESS_ID message is sent.
*
* @param pState the map tile request state object
* @param pDrawable the Drawable of the map tile
*/
@Override
public void mapTileRequestExpiredTile(MapTileRequestState pState,
CacheableBitmapDrawable pDrawable) {
// Put the expired tile into the cache
putExpiredTileIntoCache(pState.getMapTile(), pDrawable.getBitmap());
// tell our caller we've finished and it should update its view
if (mTileRequestCompleteHandler != null) {
mTileRequestCompleteHandler.sendEmptyMessage(MapTile.MAPTILE_SUCCESS_ID);
}
if (DEBUG_TILE_PROVIDERS) {
Log.i(TAG, "MapTileLayerBase.mapTileRequestExpiredTile(): " + pState.getMapTile());
}
}
private void putTileIntoCacheInternal(final MapTile pTile, final Drawable pDrawable) {
mTileCache.putTile(pTile, pDrawable);
}
private class CacheTask extends AsyncTask<Object, Void, Void> {
@Override
protected Void doInBackground(Object... params) {
putTileIntoCacheInternal((MapTile) params[0], (Drawable) params[1]);
return null;
}
}
private void putTileIntoCache(final MapTile pTile, final Drawable pDrawable) {
if (pDrawable != null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
(new CacheTask()).execute(pTile, pDrawable);
} else {
putTileIntoCacheInternal(pTile, pDrawable);
}
}
}
protected void putTileIntoCache(final MapTileRequestState pState, final Drawable pDrawable) {
putTileIntoCache(pState.getMapTile(), pDrawable);
}
protected void removeTileFromCache(final MapTileRequestState pState) {
mTileCache.removeTileFromMemory(pState.getMapTile());
}
public void putExpiredTileIntoCache(final MapTile pTile, final Bitmap bitmap) {
if (bitmap == null) {
return;
}
CacheableBitmapDrawable drawable = mTileCache.putTileInMemoryCache(pTile, bitmap);
BitmapUtils.setCacheDrawableExpired(drawable);
}
public void setTileRequestCompleteHandler(final Handler handler) {
mTileRequestCompleteHandler = handler;
}
public void clearTileMemoryCache() {
mTileCache.purgeMemoryCache();
}
public void memoryCacheNeedsMoreMemory(int numberOfTiles) {
mTileCache.getCache().resizeMemoryForTiles(numberOfTiles);
}
public void clearTileDiskCache() {
mTileCache.purgeDiskCache();
}
public void setDiskCacheEnabled(final boolean enabled) {
mTileCache.setDiskCacheEnabled(enabled);
}
/**
* Whether to use the network connection if it's available.
*/
@Override
public boolean useDataConnection() {
return mUseDataConnection;
}
/**
* Set whether to use the network connection if it's available.
*
* @param pMode if true use the network connection if it's available. if false don't use the
* network connection even if it's available.
*/
public void setUseDataConnection(final boolean pMode) {
mUseDataConnection = pMode;
}
public boolean hasNoSource() {
return mTileSource == null;
}
public CacheableBitmapDrawable getMapTileFromMemory(MapTile pTile) {
return (mTileCache != null) ? mTileCache.getMapTileFromMemory(pTile) : null;
}
public CacheableBitmapDrawable createCacheableBitmapDrawable(Bitmap bitmap, MapTile aTile) {
return (mTileCache != null) ? mTileCache.createCacheableBitmapDrawable(bitmap, aTile)
: null;
}
public Bitmap getBitmapFromRemoved(final int width, final int height) {
return (mTileCache != null) ? mTileCache.getBitmapFromRemoved(width, height) : null;
}
/**
* If a given MapTile is present in this cache, remove it from memory.
* @param aTile
*/
public void removeTileFromMemory(final MapTile aTile) {
if (mTileCache != null) {
mTileCache.removeTileFromMemory(aTile);
}
}
private static final String TAG = "MapTileLayerBase";
}