/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.osmdroid.tileprovider.modules;
import android.graphics.drawable.Drawable;
import org.mozilla.mozstumbler.service.core.logging.ClientLog;
import org.mozilla.mozstumbler.svclocator.services.log.LoggerUtil;
import org.mozilla.osmdroid.tileprovider.ExpirableBitmapDrawable;
import org.mozilla.osmdroid.tileprovider.MapTile;
import org.mozilla.osmdroid.tileprovider.MapTileRequestState;
import org.mozilla.osmdroid.tileprovider.constants.OSMConstants;
import java.util.Iterator;
/**
* Load the requested tile. An abstract internal class whose objects are used by worker threads
* to acquire tiles from servers. It processes tiles from the 'pending' set to the 'working' set
* as they become available. The key unimplemented method is 'loadTile'.
*/
public abstract class AbstractTileLoader implements Runnable {
private static final String LOG_TAG = LoggerUtil.makeLogTag(AbstractTileLoader.class);
private MapTileModuleProviderBase mapTileModuleProviderBase;
public AbstractTileLoader(MapTileModuleProviderBase mapTileModuleProviderBase) {
this.mapTileModuleProviderBase = mapTileModuleProviderBase;
}
/**
* Load the requested tile.
*
* @param pState
* @return the tile if it was loaded successfully, or null if failed to
* load and other tile providers need to be called
* @throws CantContinueException
*/
protected abstract Drawable loadTile(MapTileRequestState pState)
throws CantContinueException;
protected void onTileLoaderInit() {
// Do nothing by default
}
protected void onTileLoaderShutdown() {
// Do nothing by default
}
protected MapTileRequestState nextTile() {
synchronized (mapTileModuleProviderBase.mQueueLockObject) {
MapTile result = null;
// get the most recently accessed tile
// - the last item in the iterator that's not already being
// processed
Iterator<MapTile> iterator = mapTileModuleProviderBase.mPending.keySet().iterator();
// TODO this iterates the whole list, make this faster...
while (iterator.hasNext()) {
final MapTile tile = iterator.next();
if (!mapTileModuleProviderBase.mWorking.containsKey(tile)) {
if (OSMConstants.DEBUG_TILE_PROVIDERS) {
ClientLog.d(LOG_TAG, "TileLoader.nextTile() on provider: " + mapTileModuleProviderBase.getName()
+ " found tile in working queue: " + tile);
}
result = tile;
}
}
if (result != null) {
if (OSMConstants.DEBUG_TILE_PROVIDERS) {
ClientLog.d(LOG_TAG, "TileLoader.nextTile() on provider: " + mapTileModuleProviderBase.getName()
+ " adding tile to working queue: " + result);
}
mapTileModuleProviderBase.mWorking.put(result, mapTileModuleProviderBase.mPending.get(result));
}
return (result != null ? mapTileModuleProviderBase.mPending.get(result) : null);
}
}
/**
* A tile has loaded.
*/
protected void tileLoaded(final MapTileRequestState pState, final Drawable pDrawable) {
if (OSMConstants.DEBUG_TILE_PROVIDERS) {
ClientLog.d(LOG_TAG, "TileLoader.tileLoaded() on provider: " + mapTileModuleProviderBase.getName() + " with tile: "
+ pState.getMapTile());
}
mapTileModuleProviderBase.removeTileFromQueues(pState.getMapTile());
pState.getCallback().mapTileRequestCompleted(pState, pDrawable);
}
/**
* A tile has loaded but it's expired.
* Return it <b>and</b> send request to next provider.
*/
protected void tileLoadedExpired(final MapTileRequestState pState, final Drawable pDrawable) {
if (OSMConstants.DEBUG_TILE_PROVIDERS) {
ClientLog.d(LOG_TAG, "TileLoader.tileLoadedExpired() on provider: " + mapTileModuleProviderBase.getName()
+ " with tile: " + pState.getMapTile());
}
mapTileModuleProviderBase.removeTileFromQueues(pState.getMapTile());
pState.getCallback().mapTileRequestExpiredTile(pState, pDrawable);
}
protected void tileLoadedFailed(final MapTileRequestState pState) {
if (OSMConstants.DEBUG_TILE_PROVIDERS) {
ClientLog.d(LOG_TAG, "TileLoader.tileLoadedFailed() on provider: " + mapTileModuleProviderBase.getName()
+ " with tile: " + pState.getMapTile());
}
mapTileModuleProviderBase.removeTileFromQueues(pState.getMapTile());
pState.getCallback().mapTileRequestFailed(pState);
}
/**
* This is a functor class of type Runnable. The run method is the encapsulated function.
*/
@Override
final public void run() {
onTileLoaderInit();
MapTileRequestState state;
Drawable result = null;
while ((state = nextTile()) != null) {
if (OSMConstants.DEBUG_TILE_PROVIDERS) {
ClientLog.d(LOG_TAG, "TileLoader.run() processing next tile: " + state.getMapTile());
}
try {
result = null;
result = loadTile(state);
} catch (final CantContinueException e) {
ClientLog.e(LOG_TAG, "Tile loader can't continue: " + state.getMapTile(), e);
mapTileModuleProviderBase.clearQueue();
} catch (final Throwable e) {
ClientLog.e(LOG_TAG, "Error downloading tile: " + state.getMapTile(), e);
}
if (result == null) {
tileLoadedFailed(state);
} else if (ExpirableBitmapDrawable.isDrawableExpired(result)) {
tileLoadedExpired(state, result);
} else {
tileLoaded(state, result);
}
}
onTileLoaderShutdown();
}
}