package cgeo.geocaching.maps.mapsforge;
import cgeo.geocaching.CgeoApplication;
import cgeo.geocaching.R;
import cgeo.geocaching.maps.AbstractMapProvider;
import cgeo.geocaching.maps.MapProviderFactory;
import cgeo.geocaching.maps.interfaces.MapItemFactory;
import cgeo.geocaching.maps.interfaces.MapProvider;
import cgeo.geocaching.maps.interfaces.MapSource;
import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.utils.Log;
import cgeo.geocaching.utils.TextUtils;
import android.app.Activity;
import android.content.res.Resources;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.mapsforge.v3.android.maps.mapgenerator.MapGeneratorInternal;
import org.mapsforge.v3.map.reader.MapDatabase;
import org.mapsforge.v3.map.reader.header.FileOpenResult;
public final class MapsforgeMapProvider extends AbstractMapProvider {
public static final String MAPSFORGE_CYCLEMAP_ID = "MAPSFORGE_CYCLEMAP";
public static final String MAPSFORGE_MAPNIK_ID = "MAPSFORGE_MAPNIK";
private MapItemFactory mapItemFactory = new MapsforgeMapItemFactory();
private MapsforgeMapProvider() {
final Resources resources = CgeoApplication.getInstance().getResources();
registerMapSource(new MapsforgeMapSource(MAPSFORGE_MAPNIK_ID, this, resources.getString(R.string.map_source_osm_mapnik), MapGeneratorInternal.MAPNIK));
registerMapSource(new MapsforgeMapSource(MAPSFORGE_CYCLEMAP_ID, this, resources.getString(R.string.map_source_osm_cyclemap), MapGeneratorInternal.THUNDERFOREST));
updateOfflineMaps();
}
private static final class Holder {
private static final MapsforgeMapProvider INSTANCE = new MapsforgeMapProvider();
}
public static MapsforgeMapProvider getInstance() {
return Holder.INSTANCE;
}
public static List<String> getOfflineMaps() {
final String directoryPath = Settings.getMapFileDirectory();
if (StringUtils.isBlank(directoryPath)) {
return Collections.emptyList();
}
final File directory = new File(directoryPath);
if (directory.isDirectory()) {
try {
final List<String> mapFileList = new ArrayList<>();
final File[] files = directory.listFiles();
if (ArrayUtils.isNotEmpty(files)) {
for (final File file : files) {
if (file.getName().endsWith(".map") && isValidMapFile(file.getAbsolutePath())) {
mapFileList.add(file.getAbsolutePath());
}
}
Collections.sort(mapFileList, TextUtils.COLLATOR);
}
return mapFileList;
} catch (final Exception e) {
Log.e("MapsforgeMapProvider.getOfflineMaps: ", e);
}
}
return Collections.emptyList();
}
public static boolean isValidMapFile(final String mapFileIn) {
if (StringUtils.isEmpty(mapFileIn)) {
return false;
}
final MapDatabase mapDB = new MapDatabase();
final FileOpenResult result = mapDB.openFile(new File(mapFileIn));
mapDB.closeFile();
return result.isSuccess();
}
@Override
public boolean isSameActivity(final MapSource source1, final MapSource source2) {
return source1.getNumericalId() == source2.getNumericalId() || (!(source1 instanceof OfflineMapSource) && !(source2 instanceof OfflineMapSource));
}
@Override
public Class<? extends Activity> getMapClass() {
mapItemFactory = new MapsforgeMapItemFactory();
return MapsforgeMapActivity.class;
}
@Override
public int getMapViewId() {
return R.id.mfmap;
}
@Override
public int getMapLayoutId() {
return R.layout.map_mapsforge;
}
@Override
public MapItemFactory getMapItemFactory() {
return mapItemFactory;
}
/**
* Offline maps use the hash of the filename as ID. That way changed files can easily be detected. Also we do no
* longer need to differentiate between internal map sources and offline map sources, as they all just have an
* numerical ID (based on the hash code).
*/
public static final class OfflineMapSource extends MapsforgeMapSource {
private final String fileName;
public OfflineMapSource(final String fileName, final MapProvider mapProvider, final String name, final MapGeneratorInternal generator) {
super(fileName, mapProvider, name, generator);
this.fileName = fileName;
}
@Override
public boolean isAvailable() {
return isValidMapFile(fileName);
}
public String getFileName() {
return fileName;
}
}
public void updateOfflineMaps() {
MapProviderFactory.deleteOfflineMapSources();
final Resources resources = CgeoApplication.getInstance().getResources();
final List<String> offlineMaps = getOfflineMaps();
for (final String mapFile : offlineMaps) {
final String mapName = StringUtils.capitalize(StringUtils.substringBeforeLast(new File(mapFile).getName(), "."));
registerMapSource(new OfflineMapSource(mapFile, this, mapName + " (" + resources.getString(R.string.map_source_osm_offline) + ")", MapGeneratorInternal.DATABASE_RENDERER));
}
}
}