// Created by plusminus on 21:46:41 - 25.09.2008 package org.osmdroid.tileprovider.modules; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicReference; import org.osmdroid.config.Configuration; import org.osmdroid.tileprovider.IRegisterReceiver; import org.osmdroid.tileprovider.MapTile; import org.osmdroid.tileprovider.MapTileProviderBase; import org.osmdroid.tileprovider.MapTileRequestState; import org.osmdroid.tileprovider.tilesource.ITileSource; import org.osmdroid.tileprovider.util.StreamUtils; import android.graphics.drawable.Drawable; import android.util.Log; import org.osmdroid.api.IMapView; import org.osmdroid.tileprovider.constants.OpenStreetMapTileProviderConstants; /** * A tile provider that can serve tiles from an archive using the supplied tile source. The tile * provider will automatically find existing archives and use each one that it finds. * * @author Marc Kurtz * @author Nicolas Gramlich * */ public class MapTileFileArchiveProvider extends MapTileFileStorageProviderBase { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== private final ArrayList<IArchiveFile> mArchiveFiles = new ArrayList<IArchiveFile>(); private final AtomicReference<ITileSource> mTileSource = new AtomicReference<ITileSource>(); /** Disable the search of archives if specified in constructor */ private final boolean mSpecificArchivesProvided; // =========================================================== // Constructors // =========================================================== /** * The tiles may be found on several media. This one works with tiles stored on the file system. * It and its friends are typically created and controlled by {@link MapTileProviderBase}. */ public MapTileFileArchiveProvider(final IRegisterReceiver pRegisterReceiver, final ITileSource pTileSource, final IArchiveFile[] pArchives) { super(pRegisterReceiver, Configuration.getInstance().getTileFileSystemThreads(), Configuration.getInstance().getTileFileSystemMaxQueueSize()); setTileSource(pTileSource); if (pArchives == null) { mSpecificArchivesProvided = false; findArchiveFiles(); } else { mSpecificArchivesProvided = true; for (int i = pArchives.length - 1; i >= 0; i--) { mArchiveFiles.add(pArchives[i]); } } } public MapTileFileArchiveProvider(final IRegisterReceiver pRegisterReceiver, final ITileSource pTileSource) { this(pRegisterReceiver, pTileSource, null); } // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods from SuperClass/Interfaces // =========================================================== @Override public boolean getUsesDataConnection() { return false; } @Override protected String getName() { return "File Archive Provider"; } @Override protected String getThreadGroupName() { return "filearchive"; } @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 protected void onMediaMounted() { if (!mSpecificArchivesProvided) { findArchiveFiles(); } } @Override protected void onMediaUnmounted() { if (!mSpecificArchivesProvided) { findArchiveFiles(); } } @Override public void setTileSource(final ITileSource pTileSource) { mTileSource.set(pTileSource); } @Override public void detach() { while(!mArchiveFiles.isEmpty()) { IArchiveFile t = mArchiveFiles.get(0); if (t!=null) mArchiveFiles.get(0).close(); mArchiveFiles.remove(0); } super.detach(); } // =========================================================== // Methods // =========================================================== private void findArchiveFiles() { mArchiveFiles.clear(); if (!isSdCardAvailable()) { return; } // path should be optionally configurable File cachePaths = Configuration.getInstance().getOsmdroidBasePath(); final File[] files = cachePaths.listFiles(); if (files != null) { for (final File file : files) { final IArchiveFile archiveFile = ArchiveFileFactory.getArchiveFile(file); if (archiveFile != null) { mArchiveFiles.add(archiveFile); } } } } private synchronized InputStream getInputStream(final MapTile pTile, final ITileSource tileSource) { for (final IArchiveFile archiveFile : mArchiveFiles) { if (archiveFile!=null) { final InputStream in = archiveFile.getInputStream(tileSource, pTile); if (in != null) { if (Configuration.getInstance().isDebugMode()) { Log.d(IMapView.LOGTAG, "Found tile " + pTile + " in " + archiveFile); } return in; } } } return null; } // =========================================================== // Inner and Anonymous Classes // =========================================================== protected class TileLoader extends MapTileModuleProviderBase.TileLoader { @Override public Drawable loadTile(final MapTileRequestState pState) { ITileSource tileSource = mTileSource.get(); if (tileSource == null) { return null; } final MapTile pTile = 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: " + pTile); } return null; } InputStream inputStream = null; try { if (Configuration.getInstance().isDebugMode()) { Log.d(IMapView.LOGTAG,"Archives - Tile doesn't exist: " + pTile); } inputStream = getInputStream(pTile, tileSource); if (inputStream != null) { if (Configuration.getInstance().isDebugMode()) { Log.d(IMapView.LOGTAG,"Use tile from archive: " + pTile); } final Drawable drawable = tileSource.getDrawable(inputStream); return drawable; } } catch (final Throwable e) { Log.e(IMapView.LOGTAG,"Error loading tile", e); } finally { if (inputStream != null) { StreamUtils.closeStream(inputStream); } } return null; } } }