package net.osmand.plus.resources; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Stack; import net.osmand.PlatformUtil; import net.osmand.ResultMatcher; import net.osmand.data.RotatedTileBox; import net.osmand.map.ITileSource; import net.osmand.map.MapTileDownloader.DownloadRequest; import net.osmand.plus.SQLiteTileSource; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; /** * Thread to load map objects (POI, transport stops )async */ public class AsyncLoadingThread extends Thread { public static final int LIMIT_TRANSPORT = 200; private static final Log log = PlatformUtil.getLog(AsyncLoadingThread.class); Stack<Object> requests = new Stack<Object>(); private final ResourceManager resourceManger; public AsyncLoadingThread(ResourceManager resourceManger) { super("Loader map objects (synchronizer)"); //$NON-NLS-1$ this.resourceManger = resourceManger; } @Override public void run() { while (true) { try { boolean tileLoaded = false; boolean mapLoaded = false; while (!requests.isEmpty()) { Object req = requests.pop(); if (req instanceof TileLoadDownloadRequest) { TileLoadDownloadRequest r = (TileLoadDownloadRequest) req; tileLoaded |= resourceManger.getRequestedImageTile(r) != null; } else if (req instanceof MapLoadRequest) { if (!mapLoaded) { MapLoadRequest r = (MapLoadRequest) req; resourceManger.getRenderer().loadMap(r.tileBox, resourceManger.getMapTileDownloader()); mapLoaded = !resourceManger.getRenderer().wasInterrupted(); if (r.mapLoadedListener != null) { r.mapLoadedListener.onMapLoaded(!mapLoaded); } } } } if (tileLoaded || mapLoaded) { // use downloader callback resourceManger.getMapTileDownloader().fireLoadCallback(null); } sleep(750); } catch (InterruptedException e) { log.error(e, e); } catch (RuntimeException e) { log.error(e, e); } } } public void requestToLoadImage(TileLoadDownloadRequest req) { requests.push(req); } public void requestToLoadMap(MapLoadRequest req) { requests.push(req); } public boolean isFilePendingToDownload(File fileToSave) { return resourceManger.getMapTileDownloader().isFilePendingToDownload(fileToSave); } public boolean isFileCurrentlyDownloaded(File fileToSave) { return resourceManger.getMapTileDownloader().isFileCurrentlyDownloaded(fileToSave); } public void requestToDownload(TileLoadDownloadRequest req) { resourceManger.getMapTileDownloader().requestToDownload(req); } public static class TileLoadDownloadRequest extends DownloadRequest { public final String tileId; public final File dirWithTiles; public final ITileSource tileSource; public TileLoadDownloadRequest(File dirWithTiles, String url, File fileToSave, String tileId, ITileSource source, int tileX, int tileY, int zoom) { super(url, fileToSave, tileX, tileY, zoom); this.dirWithTiles = dirWithTiles; this.tileSource = source; this.tileId = tileId; } public TileLoadDownloadRequest(File dirWithTiles, String url, File fileToSave, String tileId, ITileSource source, int tileX, int tileY, int zoom, String referer) { super(url, fileToSave, tileX, tileY, zoom); this.dirWithTiles = dirWithTiles; this.tileSource = source; this.tileId = tileId; this.referer = referer; } public void saveTile(InputStream inputStream) throws IOException { if(tileSource instanceof SQLiteTileSource){ ByteArrayOutputStream stream = null; try { stream = new ByteArrayOutputStream(inputStream.available()); Algorithms.streamCopy(inputStream, stream); stream.flush(); try { ((SQLiteTileSource) tileSource).insertImage(xTile, yTile, zoom, stream.toByteArray()); } catch (IOException e) { log.warn("Tile x="+xTile +" y="+ yTile+" z="+ zoom+" couldn't be read", e); //$NON-NLS-1$//$NON-NLS-2$ } } finally { Algorithms.closeStream(inputStream); Algorithms.closeStream(stream); } } else { super.saveTile(inputStream); } } } protected class MapObjectLoadRequest<T> implements ResultMatcher<T> { protected double topLatitude; protected double bottomLatitude; protected double leftLongitude; protected double rightLongitude; protected boolean cancelled = false; protected volatile boolean running = false; public boolean isContains(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude) { boolean inside = this.topLatitude >= topLatitude && this.leftLongitude <= leftLongitude && this.rightLongitude >= rightLongitude && this.bottomLatitude <= bottomLatitude; return inside; } public void setBoundaries(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude) { this.topLatitude = topLatitude; this.bottomLatitude = bottomLatitude; this.leftLongitude = leftLongitude; this.rightLongitude = rightLongitude; } public boolean isRunning() { return running && !cancelled; } public void start() { running = true; } public void finish() { running = false; // use downloader callback resourceManger.getMapTileDownloader().fireLoadCallback(null); } @Override public boolean isCancelled() { return cancelled; } @Override public boolean publish(T object) { return true; } } public interface OnMapLoadedListener { void onMapLoaded(boolean interrupted); } protected static class MapLoadRequest { public final RotatedTileBox tileBox; public final OnMapLoadedListener mapLoadedListener; public MapLoadRequest(RotatedTileBox tileBox, OnMapLoadedListener mapLoadedListener) { super(); this.tileBox = tileBox; this.mapLoadedListener = mapLoadedListener; } } }