package net.osmand.plus.download;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import net.osmand.AndroidUtils;
import net.osmand.IndexConstants;
import net.osmand.PlatformUtil;
import net.osmand.osm.io.NetworkUtils;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandSettings;
import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetManager;
import android.provider.Settings.Secure;
public class DownloadOsmandIndexesHelper {
private final static Log log = PlatformUtil.getLog(DownloadOsmandIndexesHelper.class);
public static class IndexFileList implements Serializable {
private static final long serialVersionUID = 1L;
private boolean downloadedFromInternet = false;
IndexItem basemap;
ArrayList<IndexItem> indexFiles = new ArrayList<IndexItem>();
private String mapversion;
private Comparator<IndexItem> comparator = new Comparator<IndexItem>(){
@Override
public int compare(IndexItem o1, IndexItem o2) {
String object1 = o1.getFileName();
String object2 = o2.getFileName();
if(object1.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)){
if(object2.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)){
return object1.compareTo(object2);
} else {
return -1;
}
} else if(object2.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)){
return 1;
}
return object1.compareTo(object2);
}
};
public void setDownloadedFromInternet(boolean downloadedFromInternet) {
this.downloadedFromInternet = downloadedFromInternet;
}
public boolean isDownloadedFromInternet() {
return downloadedFromInternet;
}
public void setMapVersion(String mapversion) {
this.mapversion = mapversion;
}
@SuppressLint("DefaultLocale")
public void add(IndexItem indexItem) {
indexFiles.add(indexItem);
if(indexItem.getFileName().toLowerCase().startsWith("world_basemap")) {
basemap = indexItem;
}
}
public void sort(){
Collections.sort(indexFiles, comparator);
}
public boolean isAcceptable() {
return (indexFiles != null && !indexFiles.isEmpty()) || (mapversion != null);
}
public List<IndexItem> getIndexFiles() {
return indexFiles;
}
public IndexItem getBasemap() {
return basemap;
}
public boolean isIncreasedMapVersion() {
try {
int mapVersionInList = Integer.parseInt(mapversion);
return IndexConstants.BINARY_MAP_VERSION < mapVersionInList;
} catch (NumberFormatException e) {
//ignore this...
}
return false;
}
}
public static IndexFileList getIndexesList(OsmandApplication app) {
PackageManager pm = app.getPackageManager();
AssetManager amanager = app.getAssets();
IndexFileList result = downloadIndexesListFromInternet(app);
if (result == null) {
result = new IndexFileList();
} else {
result.setDownloadedFromInternet(true);
}
// add all tts files from assets
listVoiceAssets(result, amanager, pm, app.getSettings());
return result;
}
private static Map<String, String> assetMapping(AssetManager assetManager) throws XmlPullParserException, IOException {
XmlPullParser xmlParser = XmlPullParserFactory.newInstance().newPullParser();
InputStream isBundledAssetsXml = assetManager.open("bundled_assets.xml");
xmlParser.setInput(isBundledAssetsXml, "UTF-8");
Map<String, String> assets = new HashMap<String, String>();
int next = 0;
while ((next = xmlParser.next()) != XmlPullParser.END_DOCUMENT) {
if (next == XmlPullParser.START_TAG && xmlParser.getName().equals("asset")) {
final String source = xmlParser.getAttributeValue(null, "source");
final String destination = xmlParser.getAttributeValue(null, "destination");
assets.put(source, destination);
}
}
isBundledAssetsXml.close();
return assets;
}
private static void listVoiceAssets(IndexFileList result, AssetManager amanager, PackageManager pm,
OsmandSettings settings) {
try {
String ext = DownloadActivityType.addVersionToExt(IndexConstants.TTSVOICE_INDEX_EXT_ZIP, IndexConstants.TTSVOICE_VERSION);
File voicePath = settings.getContext().getAppPath(IndexConstants.VOICE_INDEX_DIR);
// list = amanager.list("voice");
String date = "";
long dateModified = System.currentTimeMillis();
try {
ApplicationInfo appInfo = pm.getApplicationInfo(OsmandApplication.class.getPackage().getName(), 0);
dateModified = new File(appInfo.sourceDir).lastModified();
date = AndroidUtils.formatDate((Context) settings.getContext(), dateModified);
} catch (NameNotFoundException e) {
//do nothing...
}
Map<String, String> mapping = assetMapping(amanager);
for (String key : mapping.keySet()) {
String target = mapping.get(key);
if (target.endsWith("-tts/_ttsconfig.p") && target.startsWith("voice/")) {
String voice = target.substring("voice/".length(), target.length() - "/_ttsconfig.p".length());
File destFile = new File(voicePath, voice + File.separatorChar + "_ttsconfig.p");
result.add(new AssetIndexItem(voice + ext, "voice", date, dateModified, "0.1", destFile.length(), key,
destFile.getPath(), DownloadActivityType.VOICE_FILE));
}
}
result.sort();
} catch (IOException e) {
log.error("Error while loading tts files from assets", e); //$NON-NLS-1$
} catch (XmlPullParserException e) {
log.error("Error while loading tts files from assets", e); //$NON-NLS-1$
}
}
private static IndexFileList downloadIndexesListFromInternet(OsmandApplication ctx){
try {
IndexFileList result = new IndexFileList();
log.debug("Start loading list of index files"); //$NON-NLS-1$
try {
String strUrl = ctx.getAppCustomization().getIndexesUrl();
long nd = ctx.getAppInitializer().getFirstInstalledDays();
if(nd > 0) {
strUrl += "&nd=" + nd;
}
strUrl += "&ns=" + ctx.getAppInitializer().getNumberOfStarts();
try {
strUrl += "&aid=" + Secure.getString(ctx.getContentResolver(), Secure.ANDROID_ID);
} catch (Exception e) {
e.printStackTrace();
}
log.info(strUrl);
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
URLConnection connection = NetworkUtils.getHttpURLConnection(strUrl);
InputStream in = connection.getInputStream();
GZIPInputStream gzin = new GZIPInputStream(in);
parser.setInput(gzin, "UTF-8"); //$NON-NLS-1$
int next;
while((next = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (next == XmlPullParser.START_TAG) {
DownloadActivityType tp = DownloadActivityType.getIndexType(parser.getAttributeValue(null, "type"));
if (tp != null) {
IndexItem it = tp.parseIndexItem(ctx, parser);
if(it != null && !it.getFileName().contains("_wiki")) {
result.add(it);
}
} else if ("osmand_regions".equals(parser.getName())) {
String mapversion = parser.getAttributeValue(null, "mapversion");
result.setMapVersion(mapversion);
}
}
}
result.sort();
gzin.close();
in.close();
} catch (IOException e) {
log.error("Error while loading indexes from repository", e); //$NON-NLS-1$
return null;
} catch (XmlPullParserException e) {
log.error("Error while loading indexes from repository", e); //$NON-NLS-1$
return null;
}
if (result.isAcceptable()) {
return result;
} else {
return null;
}
} catch (RuntimeException e) {
log.error("Error while loading indexes from repository", e); //$NON-NLS-1$
return null;
}
}
public static class AssetIndexItem extends IndexItem {
private final String assetName;
private final String destFile;
private final long dateModified;
public AssetIndexItem(String fileName, String description, String date,
long dateModified, String size, long sizeL, String assetName, String destFile, DownloadActivityType type) {
super(fileName, description, dateModified, size, sizeL, sizeL, type);
this.dateModified = dateModified;
this.assetName = assetName;
this.destFile = destFile;
}
public long getDateModified() {
return dateModified;
}
@Override
public DownloadEntry createDownloadEntry(OsmandApplication ctx) {
return new DownloadEntry(assetName, destFile, dateModified);
}
public String getDestFile(){
return destFile;
}
}
}