private void scanTranslates(File file, Map<String, Set<TranslateEntity>> translates) throws XmlPullParserException, IOException { XmlPullParser parser = PlatformUtil.newXMLPullParser(); parser.setInput(new FileReader(file)); int tok; TranslateEntity te = null; while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { if (tok == XmlPullParser.START_TAG) { String name = parser.getName(); if (name.equals("way") || name.equals("node") || name.equals("relation")) { te = new TranslateEntity(name); } else if (name.equals("tag") && te != null) { Map<String, String> attrs = new LinkedHashMap<String, String>(); for (int i = 0; i < parser.getAttributeCount(); i++) { attrs.put(parser.getAttributeName(i), parser.getAttributeValue(i)); } te.tm.put(attrs.get("k"), attrs.get("v")); } } else if (tok == XmlPullParser.END_TAG) { String name = parser.getName(); if (name.equals("way") || name.equals("node") || name.equals("relation")) { if (!te.isEmpty()) { Iterator<Entry<String, String>> it = te.tm.entrySet().iterator(); addTranslate(translates, te, "entity=" + te.name); while (it.hasNext()) { Entry<String, String> e = it.next(); addTranslate( translates, te, e.getKey().toLowerCase() + "=" + e.getValue().toLowerCase()); } } te = null; } } } }
public CountryRegion parseRegionStructure(String repo) throws XmlPullParserException, IOException { String regionsXml = repo + "/resources/countries-info/regions.xml"; XmlPullParser parser = PlatformUtil.newXMLPullParser(); parser.setInput(new FileReader(regionsXml)); int tok; CountryRegion global = new CountryRegion(); List<CountryRegion> stack = new ArrayList<CountryOcbfGeneration.CountryRegion>(); stack.add(global); CountryRegion current = global; while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { if (tok == XmlPullParser.START_TAG) { String name = parser.getName(); if (name.equals("region")) { Map<String, String> attrs = new LinkedHashMap<String, String>(); for (int i = 0; i < parser.getAttributeCount(); i++) { attrs.put(parser.getAttributeName(i), parser.getAttributeValue(i)); } CountryRegion cr = createRegion(current, attrs); stack.add(cr); current = cr; } } else if (tok == XmlPullParser.END_TAG) { String name = parser.getName(); if (name.equals("region")) { stack.remove(stack.size() - 1); current = stack.get(stack.size() - 1); } } } return global; }
public static RoutingConfiguration.Builder parseFromInputStream(InputStream is) throws IOException, XmlPullParserException { XmlPullParser parser = PlatformUtil.newXMLPullParser(); final RoutingConfiguration.Builder config = new RoutingConfiguration.Builder(); GeneralRouter currentRouter = null; String previousKey = null; String previousTag = null; int tok; parser.setInput(is, "UTF-8"); while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { if (tok == XmlPullParser.START_TAG) { String name = parser.getName(); if ("osmand_routing_config".equals(name)) { config.defaultRouter = parser.getAttributeValue("", "defaultProfile"); } else if ("routingProfile".equals(name)) { currentRouter = parseRoutingProfile(parser, config); } else if ("attribute".equals(name)) { parseAttribute(parser, config, currentRouter); previousKey = parser.getAttributeValue("", "name"); previousTag = name; } else if ("specialization".equals(name)) { parseSpecialization(parser, currentRouter, previousKey, previousTag); } else { previousKey = parser.getAttributeValue("", "tag") + "$" + parser.getAttributeValue("", "value"); previousTag = name; if (parseCurrentRule(parser, currentRouter, previousKey, name)) { } else { } } } } return config; }
public class _R { private static final Log log = PlatformUtil.getLog(_R.class); public static byte[] getIconData(String data) { InputStream io = _R.class.getResourceAsStream("h_" + data + ".png"); if (io == null) { io = _R.class.getResourceAsStream("mm_" + data + ".png"); } if (io != null) { try { byte[] buf = new byte[1024]; int cnt; ByteArrayOutputStream ous = new ByteArrayOutputStream(); while ((cnt = io.read(buf)) != -1) { ous.write(buf, 0, cnt); } return ous.toByteArray(); } catch (IOException e) { log.error(e.getMessage(), e); } } return null; } }
public class Reshaper { private static final Log LOG = PlatformUtil.getLog(Reshaper.class); public static String reshape(String s) { // if(true) { // return s; // } try { ArabicShaping as = new ArabicShaping(ArabicShaping.LETTERS_SHAPE | ArabicShaping.LENGTH_GROW_SHRINK); try { s = as.shape(s); } catch (ArabicShapingException e) { LOG.error(e.getMessage(), e); } Bidi line = new Bidi(s.length(), s.length()); line.setPara(s, Bidi.LEVEL_DEFAULT_LTR, null); byte direction = line.getDirection(); if (direction != Bidi.MIXED) { // unidirectional if (line.isLeftToRight()) { return s; } else { char[] chs = new char[s.length()]; for (int i = 0; i < chs.length; i++) { chs[i] = s.charAt(chs.length - i - 1); } return new String(chs); } } else { // // mixed-directional int count = line.countRuns(); // if (styleRunCount <= 1) { // int style = styleRuns[0].style; // // iterate over directional runs // for (i = 0; i < count; ++i) { // run = line.getVisualRun(i); // renderRun(text, run.getStart(), run.getLimit(), // run.getDirection(), style); // } // } StringBuilder res = new StringBuilder(); // iterate over both directional and style runs for (int i = 0; i < count; ++i) { BidiRun run = line.getVisualRun(i); int st = run.getStart(); int e = run.getLimit(); int j = run.getDirection() == Bidi.LTR ? st : e - 1; int l = run.getDirection() == Bidi.LTR ? e : st - 1; boolean plus = run.getDirection() == Bidi.LTR; while (j != l) { res.append(s.charAt(j)); if (plus) { j++; } else { j--; } } } return res.toString(); } } catch (RuntimeException e) { LOG.error(e.getMessage(), e); return s; } } /* public static void main(String[] args) { // char[] c = new char[] {'א', 'ד','ם', ' ', '1', '2'} ; // String reshape = "אדם"; char[] c = new char[] {'א', 'ד','ם'} ; String reshape = reshape(new String(c)); for(int i=0; i < reshape.length(); i++) { System.out.println(reshape.charAt(i)); } }*/ }
/** Created by Barsik on 07.07.2014. */ public class FileNameTranslationHelper { private static final Log LOG = PlatformUtil.getLog(FileNameTranslationHelper.class); public static final String WIKI_NAME = "_wiki"; public static final String HILL_SHADE = "Hillshade_"; public static String getFileName(Context ctx, OsmandRegions regions, String fileName) { String basename = getBasename(fileName); if (basename.endsWith(WIKI_NAME)) { // wiki files return getWikiName(ctx, basename); } else if (fileName.endsWith("tts")) { // tts files return getVoiceName(ctx, fileName); } else if (fileName.startsWith(HILL_SHADE)) { return getHillShadeName(ctx, regions, basename); } else if (fileName.length() == 2) { // voice recorded files try { Field f = R.string.class.getField("lang_" + fileName); if (f != null) { Integer in = (Integer) f.get(null); return ctx.getString(in); } } catch (Exception e) { System.err.println(e.getMessage()); } } // if nothing else String lc = basename.toLowerCase(); String std = getStandardMapName(ctx, lc); if (std != null) { return std; } if (regions != null) { return regions.getLocaleName(basename); } return null; } public static String getHillShadeName(Context ctx, OsmandRegions regions, String basename) { String hillsh = ctx.getString(R.string.download_hillshade_item) + " "; String locName = regions.getLocaleName(basename.trim()); return hillsh + locName; } public static String getWikiName(Context ctx, String basename) { String cutted = basename.substring(0, basename.indexOf("_wiki")); String wikiName = getStandardLangName(ctx, cutted); if (wikiName == null) { wikiName = cutted; } String wikiWord = ctx.getString(R.string.amenity_type_osmwiki); int index = wikiWord.indexOf("("); if (index >= 0) { // removing word in "()" from recourse file return wikiName + " " + wikiWord.substring(0, index).trim(); } return wikiName + " " + ctx.getString(R.string.amenity_type_osmwiki); } public static String getVoiceName(Context ctx, String basename) { try { String nm = basename.replace('-', '_').replace(' ', '_'); if (nm.endsWith("_tts") || nm.endsWith("-tts")) { nm = nm.substring(0, nm.length() - 4); } Field f = R.string.class.getField("lang_" + nm); if (f != null) { Integer in = (Integer) f.get(null); return ctx.getString(in); } } catch (Exception e) { System.err.println(e.getMessage()); } return basename; } private static String getBasename(String fileName) { if (fileName.endsWith(IndexConstants.EXTRA_ZIP_EXT)) { return fileName.substring(0, fileName.length() - IndexConstants.EXTRA_ZIP_EXT.length()); } if (fileName.endsWith(IndexConstants.SQLITE_EXT)) { return fileName .substring(0, fileName.length() - IndexConstants.SQLITE_EXT.length()) .replace('_', ' '); } int ls = fileName.lastIndexOf("-roads"); if (ls >= 0) { return fileName.substring(0, ls); } else { ls = fileName.indexOf("."); if (ls >= 0) { return fileName.substring(0, ls); } } return fileName; } private static String getStandardLangName(Context ctx, String filename) { if (filename.equalsIgnoreCase("Croatian")) { return ctx.getString(R.string.lang_hr); } else if (filename.equalsIgnoreCase("Chinese")) { return ctx.getString(R.string.lang_zh); } else if (filename.equalsIgnoreCase("Portuguese")) { return ctx.getString(R.string.lang_pt_br); } else if (filename.equalsIgnoreCase("English")) { return ctx.getString(R.string.lang_en); } else if (filename.equalsIgnoreCase("Afrikaans") || filename.equalsIgnoreCase("Africaans")) { return ctx.getString(R.string.lang_af); } else if (filename.equalsIgnoreCase("Armenian")) { return ctx.getString(R.string.lang_hy); } else if (filename.equalsIgnoreCase("Basque")) { return ctx.getString(R.string.lang_eu); } else if (filename.equalsIgnoreCase("Belarusian")) { return ctx.getString(R.string.lang_be); } else if (filename.equalsIgnoreCase("Bosnian")) { return ctx.getString(R.string.lang_bs); } else if (filename.equalsIgnoreCase("Bulgarian")) { return ctx.getString(R.string.lang_bg); } else if (filename.equalsIgnoreCase("Catalan")) { return ctx.getString(R.string.lang_ca); } else if (filename.equalsIgnoreCase("Czech")) { return ctx.getString(R.string.lang_cs); } else if (filename.equalsIgnoreCase("Danish")) { return ctx.getString(R.string.lang_da); } else if (filename.equalsIgnoreCase("Dutch")) { return ctx.getString(R.string.lang_nl); } else if (filename.equalsIgnoreCase("Finnish")) { return ctx.getString(R.string.lang_fi); } else if (filename.equalsIgnoreCase("French")) { return ctx.getString(R.string.lang_fr); } else if (filename.equalsIgnoreCase("Georgian")) { return ctx.getString(R.string.lang_ka); } else if (filename.equalsIgnoreCase("German")) { return ctx.getString(R.string.lang_de); } else if (filename.equalsIgnoreCase("Greek")) { return ctx.getString(R.string.lang_el); } else if (filename.equalsIgnoreCase("Hebrew")) { return ctx.getString(R.string.lang_iw); } else if (filename.equalsIgnoreCase("Hindi")) { return ctx.getString(R.string.lang_hi); } else if (filename.equalsIgnoreCase("Hungarian")) { return ctx.getString(R.string.lang_hu); } else if (filename.equalsIgnoreCase("Indonesian")) { return ctx.getString(R.string.lang_id); } else if (filename.equalsIgnoreCase("Italian")) { return ctx.getString(R.string.lang_it); } else if (filename.equalsIgnoreCase("Japanese")) { return ctx.getString(R.string.lang_ja); } else if (filename.equalsIgnoreCase("Korean")) { return ctx.getString(R.string.lang_ko); } else if (filename.equalsIgnoreCase("Latvian")) { return ctx.getString(R.string.lang_lv); } else if (filename.equalsIgnoreCase("Lithuanian")) { return ctx.getString(R.string.lang_lt); } else if (filename.equalsIgnoreCase("Marathi")) { return ctx.getString(R.string.lang_mr); } else if (filename.equalsIgnoreCase("Norwegian")) { return ctx.getString(R.string.lang_no); } else if (filename.equalsIgnoreCase("Polish")) { return ctx.getString(R.string.lang_pl); } else if (filename.equalsIgnoreCase("Portuguese")) { return ctx.getString(R.string.lang_pt); } else if (filename.equalsIgnoreCase("Romanian")) { return ctx.getString(R.string.lang_ro); } else if (filename.equalsIgnoreCase("Russian")) { return ctx.getString(R.string.lang_ru); } else if (filename.equalsIgnoreCase("Slovak")) { return ctx.getString(R.string.lang_sk); } else if (filename.equalsIgnoreCase("Slovenian")) { return ctx.getString(R.string.lang_sl); } else if (filename.equalsIgnoreCase("Spanish")) { return ctx.getString(R.string.lang_es); } else if (filename.equalsIgnoreCase("Swedish")) { return ctx.getString(R.string.lang_sv); } else if (filename.equalsIgnoreCase("Turkish")) { return ctx.getString(R.string.lang_tr); } else if (filename.equalsIgnoreCase("Ukrainian")) { return ctx.getString(R.string.lang_uk); } else if (filename.equalsIgnoreCase("Vietnamese")) { return ctx.getString(R.string.lang_vi); } else if (filename.equalsIgnoreCase("Welsh")) { return ctx.getString(R.string.lang_cy); } return null; } public static String getStandardMapName(Context ctx, String basename) { if (basename.equals("world-ski")) { return ctx.getString(R.string.index_item_world_ski); } else if (basename.equals("world_altitude_correction_ww15mgh")) { return ctx.getString(R.string.index_item_world_altitude_correction); } else if (basename.equals("world_basemap")) { return ctx.getString(R.string.index_item_world_basemap); } else if (basename.equals("world_bitcoin_payments")) { return ctx.getString(R.string.index_item_world_bitcoin_payments); } else if (basename.equals("world_seamarks_basemap")) { return ctx.getString(R.string.index_item_world_seamarks); } return null; } }
public class WorldRegion { public static final String AFRICA_REGION_ID = "africa"; public static final String ASIA_REGION_ID = "asia"; public static final String AUSTRALIA_AND_OCEANIA_REGION_ID = "australia-oceania"; public static final String CENTRAL_AMERICA_REGION_ID = "centralamerica"; public static final String EUROPE_REGION_ID = "europe"; public static final String NORTH_AMERICA_REGION_ID = "northamerica"; public static final String RUSSIA_REGION_ID = "russia"; public static final String SOUTH_AMERICA_REGION_ID = "southamerica"; private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(WorldRegion.class); // Region data private String regionId; private String downloadsIdPrefix; private String name; private Set<DownloadActivityType> resourceTypes; // Hierarchy private WorldRegion superregion; private List<WorldRegion> subregions; private List<WorldRegion> flattenedSubregions; private boolean purchased; private boolean isInPurchasedArea; private MapState mapState = MapState.NOT_DOWNLOADED; public String getRegionId() { return regionId; } public String getDownloadsIdPrefix() { return downloadsIdPrefix; } public String getName() { return name; } public Set<DownloadActivityType> getResourceTypes() { return resourceTypes; } public void setResourceTypes(Set<DownloadActivityType> resourceTypes) { this.resourceTypes = resourceTypes; } public WorldRegion getSuperregion() { return superregion; } public List<WorldRegion> getSubregions() { return subregions; } public List<WorldRegion> getFlattenedSubregions() { return flattenedSubregions; } public boolean isPurchased() { return purchased; } public boolean isInPurchasedArea() { return isInPurchasedArea; } public MapState getMapState() { return mapState; } public void processNewMapState(MapState mapState) { switch (this.mapState) { case NOT_DOWNLOADED: this.mapState = mapState; break; case DOWNLOADED: if (mapState == MapState.OUTDATED) this.mapState = mapState; break; case OUTDATED: break; } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; WorldRegion that = (WorldRegion) o; return !(name != null ? !name.toLowerCase().equals(that.name.toLowerCase()) : that.name != null); } @Override public int hashCode() { return name != null ? name.hashCode() : 0; } public WorldRegion() { superregion = null; subregions = new LinkedList<>(); flattenedSubregions = new LinkedList<>(); } public void initWorld() { regionId = ""; downloadsIdPrefix = "world_"; name = ""; superregion = null; } private WorldRegion init(String regionId, OsmandRegions osmandRegions, String name) { this.regionId = regionId; String downloadName = osmandRegions.getDownloadName(regionId); if (downloadName != null) { downloadsIdPrefix = downloadName.toLowerCase() + "."; } else { this.downloadsIdPrefix = regionId.toLowerCase() + "."; } if (name != null) { this.name = name; } else { this.name = osmandRegions.getLocaleNameByFullName(regionId); if (this.name == null) { this.name = capitalize(regionId.replace('_', ' ')); } } return this; } private WorldRegion init(String regionId, OsmandRegions osmandRegions) { this.regionId = regionId; String downloadName = osmandRegions.getDownloadName(regionId); if (downloadName != null) { downloadsIdPrefix = downloadName.toLowerCase() + "."; } else { this.downloadsIdPrefix = regionId.toLowerCase() + "."; } this.name = osmandRegions.getLocaleNameByFullName(regionId); if (this.name == null) { this.name = capitalize(regionId.replace('_', ' ')); } return this; } private WorldRegion init(String regionId, String name) { this.regionId = regionId; this.downloadsIdPrefix = regionId.toLowerCase() + "."; this.name = name; return this; } private void addSubregion(WorldRegion subregion) { subregion.superregion = this; subregions.add(subregion); propagateSubregionToFlattenedHierarchy(subregion); } private void propagateSubregionToFlattenedHierarchy(WorldRegion subregion) { flattenedSubregions.add(subregion); if (superregion != null) { superregion.propagateSubregionToFlattenedHierarchy(subregion); } } public void loadWorldRegions(OsmandApplication app) { OsmandRegions osmandRegions = app.getRegions(); Map<String, String> loadedItems = osmandRegions.getFullNamesToLowercaseCopy(); if (loadedItems.size() == 0) { return; } HashMap<String, WorldRegion> regionsLookupTable = new HashMap<>(loadedItems.size()); // Create main regions Resources res = app.getResources(); WorldRegion africaRegion = createRegionAs( AFRICA_REGION_ID, loadedItems, osmandRegions, res.getString(R.string.index_name_africa)); addSubregion(africaRegion); regionsLookupTable.put(africaRegion.regionId, africaRegion); WorldRegion asiaRegion = createRegionAs( ASIA_REGION_ID, loadedItems, osmandRegions, res.getString(R.string.index_name_asia)); addSubregion(asiaRegion); regionsLookupTable.put(asiaRegion.regionId, asiaRegion); WorldRegion australiaAndOceaniaRegion = createRegionAs( AUSTRALIA_AND_OCEANIA_REGION_ID, loadedItems, osmandRegions, res.getString(R.string.index_name_oceania)); addSubregion(australiaAndOceaniaRegion); regionsLookupTable.put(australiaAndOceaniaRegion.regionId, australiaAndOceaniaRegion); WorldRegion centralAmericaRegion = createRegionAs( CENTRAL_AMERICA_REGION_ID, loadedItems, osmandRegions, res.getString(R.string.index_name_central_america)); addSubregion(centralAmericaRegion); regionsLookupTable.put(centralAmericaRegion.regionId, centralAmericaRegion); WorldRegion europeRegion = createRegionAs( EUROPE_REGION_ID, loadedItems, osmandRegions, res.getString(R.string.index_name_europe)); addSubregion(europeRegion); regionsLookupTable.put(europeRegion.regionId, europeRegion); WorldRegion northAmericaRegion = createRegionAs( NORTH_AMERICA_REGION_ID, loadedItems, osmandRegions, res.getString(R.string.index_name_north_america)); addSubregion(northAmericaRegion); regionsLookupTable.put(northAmericaRegion.regionId, northAmericaRegion); WorldRegion russiaRegion = createRegionAs( RUSSIA_REGION_ID, loadedItems, osmandRegions, res.getString(R.string.index_name_russia)); addSubregion(russiaRegion); regionsLookupTable.put(russiaRegion.regionId, russiaRegion); WorldRegion southAmericaRegion = createRegionAs( SOUTH_AMERICA_REGION_ID, loadedItems, osmandRegions, res.getString(R.string.index_name_south_america)); addSubregion(southAmericaRegion); regionsLookupTable.put(southAmericaRegion.regionId, southAmericaRegion); // Process remaining regions for (; ; ) { int processedRegions = 0; Iterator<Entry<String, String>> iterator = loadedItems.entrySet().iterator(); while (iterator.hasNext()) { String regionId = iterator.next().getKey(); String parentRegionId = osmandRegions.getParentFullName(regionId); if (parentRegionId == null) { continue; } // Try to find parent of this region WorldRegion parentRegion = regionsLookupTable.get(parentRegionId); if (parentRegion == null) { continue; } WorldRegion newRegion = new WorldRegion().init(regionId, osmandRegions); parentRegion.addSubregion(newRegion); regionsLookupTable.put(newRegion.regionId, newRegion); // Remove processedRegions++; iterator.remove(); } // If all remaining are orphans, that's all if (processedRegions == 0) break; } LOG.warn("Found orphaned regions: " + loadedItems.size()); for (String regionId : loadedItems.keySet()) { LOG.warn("FullName = " + regionId + " parent=" + osmandRegions.getParentFullName(regionId)); } } private static WorldRegion createRegionAs( String regionId, Map<String, String> loadedItems, OsmandRegions osmandRegions, String localizedName) { WorldRegion worldRegion; boolean hasRegion = loadedItems.containsKey(regionId); if (hasRegion) { worldRegion = new WorldRegion().init(regionId, osmandRegions, localizedName); loadedItems.remove(regionId); } else { worldRegion = new WorldRegion().init(regionId, localizedName); } return worldRegion; } private String capitalize(String s) { String[] words = s.split(" "); if (words[0].length() > 0) { StringBuilder sb = new StringBuilder(); sb.append(Character.toUpperCase(words[0].charAt(0))) .append(words[0].subSequence(1, words[0].length()).toString().toLowerCase()); for (int i = 1; i < words.length; i++) { sb.append(" "); sb.append(Character.toUpperCase(words[i].charAt(0))) .append(words[i].subSequence(1, words[i].length()).toString().toLowerCase()); } return sb.toString(); } else { return s; } } public WorldRegion getRegionById(String regionId) { if (regionId.length() == 0) { return this; } else { for (WorldRegion region : flattenedSubregions) { if (region != null && region.getRegionId().equals(regionId)) { return region; } } } return null; } public enum MapState { NOT_DOWNLOADED, DOWNLOADED, OUTDATED } }
public class OpenstreetmapRemoteUtil extends AbstractOpenstreetmapUtil { // private final static String SITE_API = "http://api06.dev.openstreetmap.org/"; private static final String SITE_API = "http://api.openstreetmap.org/"; // $NON-NLS-1$ private static final long NO_CHANGESET_ID = -1; private final OsmandApplication ctx; private final View view; private EntityInfo entityInfo; // reuse changeset private long changeSetId = NO_CHANGESET_ID; private long changeSetTimeStamp = NO_CHANGESET_ID; public static final Log log = PlatformUtil.getLog(OpenstreetmapRemoteUtil.class); private OsmandSettings settings; public OpenstreetmapRemoteUtil(Context uiContext, View view) { this.ctx = ((OsmandApplication) uiContext.getApplicationContext()); this.view = view; settings = ctx.getSettings(); } @Override public EntityInfo getEntityInfo() { return entityInfo; } private static final String URL_TO_UPLOAD_GPX = " http://api.openstreetmap.org/api/0.6/gpx/create"; public String uploadGPXFile(String tagstring, String description, String visibility, File f) { String url = URL_TO_UPLOAD_GPX; Map<String, String> additionalData = new LinkedHashMap<String, String>(); additionalData.put("description", description); additionalData.put("tags", tagstring); additionalData.put("visibility", visibility); return NetworkUtils.uploadFile( url, f, settings.USER_NAME.get() + ":" + settings.USER_PASSWORD.get(), "file", true, additionalData); } protected String sendRequsetThroughHttpClient( String url, String requestMethod, String requestBody, String userOperation, boolean doAuthenticate) { StringBuilder responseBody = new StringBuilder(); try { HttpParams params = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(params, 15000); DefaultHttpClient httpclient = new DefaultHttpClient(params); if (doAuthenticate) { UsernamePasswordCredentials credentials = new UsernamePasswordCredentials( settings.USER_NAME.get() + ":" //$NON-NLS-1$ + settings.USER_PASSWORD.get()); httpclient .getCredentialsProvider() .setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), credentials); } HttpRequestBase method = null; if (requestMethod.equals("GET")) { // $NON-NLS-1$ method = new HttpGet(url); } else if (requestMethod.equals("POST")) { // $NON-NLS-1$ method = new HttpPost(url); } else if (requestMethod.equals("PUT")) { // $NON-NLS-1$ method = new HttpPut(url); } else if (requestMethod.equals("DELETE")) { // $NON-NLS-1$ method = new HttpDelete(url); } else { throw new IllegalArgumentException(requestMethod + " is invalid method"); // $NON-NLS-1$ } if (requestMethod.equals("PUT") || requestMethod.equals("POST") || requestMethod.equals("DELETE")) { // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ // TODO add when needed // connection.setDoOutput(true); // connection.setRequestProperty("Content-type", "text/xml"); // OutputStream out = connection.getOutputStream(); // if (requestBody != null) { // BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")); // bwr.write(requestBody); // bwr.flush(); // } // out.close(); } HttpResponse response = httpclient.execute(method); if (response.getStatusLine() == null || response.getStatusLine().getStatusCode() != 200) { String msg; if (response.getStatusLine() != null) { msg = userOperation + " " + ctx.getString(R.string.failed_op); // $NON-NLS-1$ } else { msg = userOperation + " " + ctx.getString(R.string.failed_op) + response.getStatusLine().getStatusCode() + " : " + //$NON-NLS-1$//$NON-NLS-2$ response.getStatusLine().getReasonPhrase(); } log.error(msg); showWarning(msg); } else { InputStream is = response.getEntity().getContent(); if (is != null) { BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8")); // $NON-NLS-1$ String s; while ((s = in.readLine()) != null) { responseBody.append(s); responseBody.append("\n"); // $NON-NLS-1$ } is.close(); } httpclient.getConnectionManager().shutdown(); return responseBody.toString(); } } catch (MalformedURLException e) { log.error(userOperation + " failed", e); // $NON-NLS-1$ showWarning( MessageFormat.format( ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); } catch (IOException e) { log.error(userOperation + " failed", e); // $NON-NLS-1$ showWarning( MessageFormat.format( ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); } return null; } private String sendRequest( String url, String requestMethod, String requestBody, String userOperation, boolean doAuthenticate) { log.info("Sending request " + url); // $NON-NLS-1$ // if(true){ // return sendRequsetThroughHttpClient(url, requestMethod, requestBody, userOperation, // doAuthenticate); // } try { HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); connection.setConnectTimeout(15000); connection.setRequestMethod(requestMethod); StringBuilder responseBody = new StringBuilder(); if (doAuthenticate) { String token = settings.USER_NAME.get() + ":" + settings.USER_PASSWORD.get(); // $NON-NLS-1$ connection.addRequestProperty( "Authorization", "Basic " + Base64.encode( token.getBytes("UTF-8"))); // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } connection.setDoInput(true); if (requestMethod.equals("PUT") || requestMethod.equals("POST") || requestMethod.equals("DELETE")) { // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ connection.setDoOutput(true); connection.setRequestProperty("Content-type", "text/xml"); // $NON-NLS-1$ //$NON-NLS-2$ OutputStream out = connection.getOutputStream(); if (requestBody != null) { BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"), 1024); // $NON-NLS-1$ bwr.write(requestBody); bwr.flush(); } out.close(); } connection.connect(); if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { String msg = userOperation + " " + ctx.getString(R.string.failed_op) + " : " + connection.getResponseMessage(); // $NON-NLS-1$//$NON-NLS-2$ log.error(msg); showWarning(msg); } else { log.info("Response : " + connection.getResponseMessage()); // $NON-NLS-1$ // populate return fields. responseBody.setLength(0); InputStream i = connection.getInputStream(); if (i != null) { BufferedReader in = new BufferedReader(new InputStreamReader(i, "UTF-8"), 256); // $NON-NLS-1$ String s; boolean f = true; while ((s = in.readLine()) != null) { if (!f) { responseBody.append("\n"); // $NON-NLS-1$ } else { f = false; } responseBody.append(s); } } return responseBody.toString(); } } catch (NullPointerException e) { // that's tricky case why NPE is thrown to fix that problem httpClient could be used String msg = ctx.getString(R.string.auth_failed); log.error(msg, e); showWarning(msg); } catch (MalformedURLException e) { log.error(userOperation + " " + ctx.getString(R.string.failed_op), e); // $NON-NLS-1$ showWarning( MessageFormat.format( ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); } catch (IOException e) { log.error(userOperation + " " + ctx.getString(R.string.failed_op), e); // $NON-NLS-1$ showWarning( MessageFormat.format( ctx.getResources().getString(R.string.poi_error_io_error_template), userOperation)); } return null; } public long openChangeSet(String comment) { long id = -1; StringWriter writer = new StringWriter(256); XmlSerializer ser = Xml.newSerializer(); try { ser.setOutput(writer); ser.startDocument("UTF-8", true); // $NON-NLS-1$ ser.startTag(null, "osm"); // $NON-NLS-1$ ser.startTag(null, "changeset"); // $NON-NLS-1$ ser.startTag(null, "tag"); // $NON-NLS-1$ ser.attribute(null, "k", "comment"); // $NON-NLS-1$ //$NON-NLS-2$ ser.attribute(null, "v", comment); // $NON-NLS-1$ ser.endTag(null, "tag"); // $NON-NLS-1$ ser.startTag(null, "tag"); // $NON-NLS-1$ ser.attribute(null, "k", "created_by"); // $NON-NLS-1$ //$NON-NLS-2$ ser.attribute(null, "v", Version.getFullVersion(ctx)); // $NON-NLS-1$ ser.endTag(null, "tag"); // $NON-NLS-1$ ser.endTag(null, "changeset"); // $NON-NLS-1$ ser.endTag(null, "osm"); // $NON-NLS-1$ ser.endDocument(); writer.close(); } catch (IOException e) { log.error("Unhandled exception", e); // $NON-NLS-1$ } String response = sendRequest( SITE_API + "api/0.6/changeset/create/", "PUT", writer.getBuffer().toString(), ctx.getString(R.string.opening_changeset), true); //$NON-NLS-1$ //$NON-NLS-2$ if (response != null && response.length() > 0) { id = Long.parseLong(response); } return id; } private void writeNode(Node n, EntityInfo i, XmlSerializer ser, long changeSetId, String user) throws IllegalArgumentException, IllegalStateException, IOException { ser.startTag(null, "node"); // $NON-NLS-1$ ser.attribute(null, "id", n.getId() + ""); // $NON-NLS-1$ //$NON-NLS-2$ ser.attribute(null, "lat", n.getLatitude() + ""); // $NON-NLS-1$ //$NON-NLS-2$ ser.attribute(null, "lon", n.getLongitude() + ""); // $NON-NLS-1$ //$NON-NLS-2$ if (i != null) { // ser.attribute(null, "timestamp", i.getETimestamp()); // ser.attribute(null, "uid", i.getUid()); // ser.attribute(null, "user", i.getUser()); ser.attribute(null, "visible", i.getVisible()); // $NON-NLS-1$ ser.attribute(null, "version", i.getVersion()); // $NON-NLS-1$ } ser.attribute(null, "changeset", changeSetId + ""); // $NON-NLS-1$ //$NON-NLS-2$ for (String k : n.getTagKeySet()) { String val = n.getTag(k); if (val.length() == 0) continue; ser.startTag(null, "tag"); // $NON-NLS-1$ ser.attribute(null, "k", k); // $NON-NLS-1$ ser.attribute(null, "v", val); // $NON-NLS-1$ ser.endTag(null, "tag"); // $NON-NLS-1$ } ser.endTag(null, "node"); // $NON-NLS-1$ } private boolean isNewChangesetRequired() { // first commit if (changeSetId == NO_CHANGESET_ID) { return true; } long now = System.currentTimeMillis(); // changeset is idle for more than 30 minutes (1 hour according specification) if (now - changeSetTimeStamp > 30 * 60 * 1000) { return true; } return false; } @Override public Node commitNodeImpl( OsmPoint.Action action, final Node n, EntityInfo info, String comment, boolean closeChangeSet) { if (isNewChangesetRequired()) { changeSetId = openChangeSet(comment); changeSetTimeStamp = System.currentTimeMillis(); } if (changeSetId < 0) { return null; } try { Node newN = n; StringWriter writer = new StringWriter(256); XmlSerializer ser = Xml.newSerializer(); try { ser.setOutput(writer); ser.startDocument("UTF-8", true); // $NON-NLS-1$ ser.startTag(null, "osmChange"); // $NON-NLS-1$ ser.attribute(null, "version", "0.6"); // $NON-NLS-1$ //$NON-NLS-2$ ser.attribute(null, "generator", Version.getAppName(ctx)); // $NON-NLS-1$ ser.startTag(null, OsmPoint.stringAction.get(action)); ser.attribute(null, "version", "0.6"); // $NON-NLS-1$ //$NON-NLS-2$ ser.attribute(null, "generator", Version.getAppName(ctx)); // $NON-NLS-1$ writeNode(n, info, ser, changeSetId, settings.USER_NAME.get()); ser.endTag(null, OsmPoint.stringAction.get(action)); ser.endTag(null, "osmChange"); // $NON-NLS-1$ ser.endDocument(); } catch (IOException e) { log.error("Unhandled exception", e); // $NON-NLS-1$ } String res = sendRequest( SITE_API + "api/0.6/changeset/" + changeSetId + "/upload", "POST", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ writer.getBuffer().toString(), ctx.getString(R.string.commiting_node), true); log.debug(res + ""); // $NON-NLS-1$ if (res != null) { if (OsmPoint.Action.CREATE == action) { long newId = n.getId(); int i = res.indexOf("new_id=\""); // $NON-NLS-1$ if (i > 0) { i = i + "new_id=\"".length(); // $NON-NLS-1$ int end = res.indexOf("\"", i); // $NON-NLS-1$ if (end > 0) { newId = Long.parseLong(res.substring(i, end)); // << 1; newN = new Node(n, newId); } } } changeSetTimeStamp = System.currentTimeMillis(); return newN; } return null; } finally { if (closeChangeSet) { closeChangeSet(); } } } @Override public void closeChangeSet() { if (changeSetId != NO_CHANGESET_ID) { String response = sendRequest( SITE_API + "api/0.6/changeset/" + changeSetId + "/close", "PUT", "", ctx.getString(R.string.closing_changeset), true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ log.info("Response : " + response); // $NON-NLS-1$ changeSetId = NO_CHANGESET_ID; } } public EntityInfo loadNode(Node n) { long nodeId = n.getId(); // >> 1; try { String res = sendRequest( SITE_API + "api/0.6/node/" + nodeId, "GET", null, ctx.getString(R.string.loading_poi_obj) + nodeId, false); //$NON-NLS-1$ //$NON-NLS-2$ if (res != null) { OsmBaseStorage st = new OsmBaseStorage(); st.parseOSM( new ByteArrayInputStream(res.getBytes("UTF-8")), null, null, true); // $NON-NLS-1$ EntityId id = new Entity.EntityId(EntityType.NODE, nodeId); // Node entity = (Node) st.getRegisteredEntities().get(id); entityInfo = st.getRegisteredEntityInfo().get(id); return entityInfo; } } catch (IOException e) { log.error("Loading node failed " + nodeId, e); // $NON-NLS-1$ AccessibleToast.makeText( ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG) .show(); } catch (SAXException e) { log.error("Loading node failed " + nodeId, e); // $NON-NLS-1$ AccessibleToast.makeText( ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG) .show(); } return null; } @Override public Node loadNode(Amenity n) { if (n.getId() % 2 == 1) { // that's way id return null; } long nodeId = n.getId() >> 1; try { String res = sendRequest( SITE_API + "api/0.6/node/" + nodeId, "GET", null, ctx.getString(R.string.loading_poi_obj) + nodeId, false); //$NON-NLS-1$ //$NON-NLS-2$ if (res != null) { OsmBaseStorage st = new OsmBaseStorage(); st.parseOSM( new ByteArrayInputStream(res.getBytes("UTF-8")), null, null, true); // $NON-NLS-1$ EntityId id = new Entity.EntityId(EntityType.NODE, nodeId); Node entity = (Node) st.getRegisteredEntities().get(id); entityInfo = st.getRegisteredEntityInfo().get(id); // check whether this is node (because id of node could be the same as relation) if (entity != null && MapUtils.getDistance(entity.getLatLon(), n.getLocation()) < 50) { return entity; } return null; } } catch (IOException e) { log.error("Loading node failed " + nodeId, e); // $NON-NLS-1$ AccessibleToast.makeText( ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG) .show(); } catch (SAXException e) { log.error("Loading node failed " + nodeId, e); // $NON-NLS-1$ AccessibleToast.makeText( ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG) .show(); } return null; } private void showWarning(final String msg) { view.post( new Runnable() { @Override public void run() { AccessibleToast.makeText(ctx, msg, Toast.LENGTH_LONG).show(); } }); } }
public class MultiTouchSupport { private static final Log log = PlatformUtil.getLog(MultiTouchSupport.class); public static final int ACTION_MASK = 255; public static final int ACTION_POINTER_ID_SHIFT = 8; public static final int ACTION_POINTER_DOWN = 5; public static final int ACTION_POINTER_UP = 6; private float angleStarted; private float angleRelative; public interface MultiTouchZoomListener { public void onZoomStarted(PointF centerPoint); public void onZoomingOrRotating(double relativeToStart, float angle); public void onZoomEnded(double relativeToStart, float angleRelative); public void onGestureInit(float x1, float y1, float x2, float y2); } private boolean multiTouchAPISupported = false; private final MultiTouchZoomListener listener; protected final Context ctx; protected Method getPointerCount; protected Method getX; protected Method getY; protected Method getPointerId; public MultiTouchSupport(Context ctx, MultiTouchZoomListener listener) { this.ctx = ctx; this.listener = listener; initMethods(); } public boolean isMultiTouchSupported() { return multiTouchAPISupported; } public boolean isInZoomMode() { return inZoomMode; } private void initMethods() { try { getPointerCount = MotionEvent.class.getMethod("getPointerCount"); // $NON-NLS-1$ getPointerId = MotionEvent.class.getMethod("getPointerId", Integer.TYPE); // $NON-NLS-1$ getX = MotionEvent.class.getMethod("getX", Integer.TYPE); // $NON-NLS-1$ getY = MotionEvent.class.getMethod("getY", Integer.TYPE); // $NON-NLS-1$ multiTouchAPISupported = true; } catch (Exception e) { multiTouchAPISupported = false; log.info("Multi touch not supported", e); // $NON-NLS-1$ } } private boolean inZoomMode = false; private double zoomStartedDistance = 100; private double zoomRelative = 1; private PointF centerPoint = new PointF(); public boolean onTouchEvent(MotionEvent event) { if (!isMultiTouchSupported()) { return false; } int actionCode = event.getAction() & ACTION_MASK; try { Integer pointCount = (Integer) getPointerCount.invoke(event); if (pointCount < 2) { if (inZoomMode) { listener.onZoomEnded(zoomRelative, angleRelative); } return false; } Float x1 = (Float) getX.invoke(event, 0); Float x2 = (Float) getX.invoke(event, 1); Float y1 = (Float) getY.invoke(event, 0); Float y2 = (Float) getY.invoke(event, 1); float distance = (float) Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); float angle = 0; boolean angleDefined = false; if (x1 != x2 || y1 != y2) { angleDefined = true; angle = (float) (Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI); } if (actionCode == ACTION_POINTER_DOWN) { centerPoint = new PointF((x1 + x2) / 2, (y1 + y2) / 2); listener.onGestureInit(x1, y1, x2, y2); listener.onZoomStarted(centerPoint); zoomStartedDistance = distance; angleStarted = angle; inZoomMode = true; return true; } else if (actionCode == ACTION_POINTER_UP) { if (inZoomMode) { listener.onZoomEnded(zoomRelative, angleRelative); inZoomMode = false; } return true; } else if (inZoomMode && actionCode == MotionEvent.ACTION_MOVE) { if (angleDefined) { angleRelative = MapUtils.unifyRotationTo360(angle - angleStarted); } zoomRelative = distance / zoomStartedDistance; listener.onZoomingOrRotating(zoomRelative, angleRelative); return true; } } catch (Exception e) { log.debug("Multi touch exception", e); // $NON-NLS-1$ } return false; } public PointF getCenterPoint() { return centerPoint; } }
public class OsmBugsLayer extends OsmandMapLayer implements IContextMenuProvider, DialogProvider { private static final Log log = PlatformUtil.getLog(OsmBugsLayer.class); private static final int startZoom = 8; private final OsmEditingPlugin plugin; private OsmandMapTileView view; private Paint pointClosedUI; private Paint pointOpenedUI; private Paint pointNotSubmitedUI; private final MapActivity activity; private static final String KEY_AUTHOR = "author"; private static final String KEY_MESSAGE = "message"; protected static final String KEY_LATITUDE = "latitude"; protected static final String KEY_LONGITUDE = "longitude"; protected static final String KEY_BUG = "bug"; private static final int DIALOG_COMMENT_BUG = 301; private static final int DIALOG_CLOSE_BUG = 302; private static Bundle dialogBundle = new Bundle(); private OsmBugsLocalUtil local; private OsmBugsRemoteUtil remote; private MapLayerData<List<OpenStreetNote>> data; public OsmBugsLayer(MapActivity activity, OsmEditingPlugin plugin) { this.activity = activity; this.plugin = plugin; local = new OsmBugsLocalUtil(activity, plugin.getDBBug()); remote = new OsmBugsRemoteUtil(activity.getMyApplication()); } public OsmBugsUtil getOsmbugsUtil(OpenStreetNote bug) { OsmandSettings settings = ((OsmandApplication) activity.getApplication()).getSettings(); if ((bug != null && bug.isLocal()) || settings.OFFLINE_EDITION.get() || !settings.isInternetConnectionAvailable(true)) { return local; } else { return remote; } } @Override public void initLayer(OsmandMapTileView view) { this.view = view; pointOpenedUI = new Paint(); pointOpenedUI.setColor(activity.getResources().getColor(R.color.osmbug_opened)); pointOpenedUI.setAntiAlias(true); pointNotSubmitedUI = new Paint(); pointNotSubmitedUI.setColor(activity.getResources().getColor(R.color.osmbug_not_submitted)); pointNotSubmitedUI.setAntiAlias(true); pointClosedUI = new Paint(); pointClosedUI.setColor(activity.getResources().getColor(R.color.osmbug_closed)); pointClosedUI.setAntiAlias(true); data = new OsmandMapLayer.MapLayerData<List<OpenStreetNote>>() { { ZOOM_THRESHOLD = 1; } @Override protected List<OpenStreetNote> calculateResult(RotatedTileBox tileBox) { QuadRect bounds = tileBox.getLatLonBounds(); return loadingBugs(bounds.top, bounds.left, bounds.bottom, bounds.right); } }; } @Override public void destroyLayer() {} @Override public boolean drawInScreenPixels() { return false; } @Override public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) { if (tileBox.getZoom() >= startZoom) { // request to load data.queryNewData(tileBox); List<OpenStreetNote> objects = data.getResults(); if (objects != null) { for (OpenStreetNote o : objects) { int x = tileBox.getPixXFromLonNoRot(o.getLongitude()); int y = tileBox.getPixYFromLatNoRot(o.getLatitude()); canvas.drawCircle( x, y, getRadiusBug(tileBox), o.isLocal() ? pointNotSubmitedUI : (o.isOpened() ? pointOpenedUI : pointClosedUI)); } } } } @Override public void onDraw(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) {} public int getRadiusBug(RotatedTileBox tb) { int z; final double zoom = tb.getZoom(); if (zoom < startZoom) { z = 0; } else if (zoom <= 12) { z = 8; } else if (zoom <= 15) { z = 10; } else if (zoom == 16) { z = 13; } else if (zoom == 17) { z = 15; } else { z = 16; } return (int) (z * tb.getDensity()); } @Override public boolean onLongPressEvent(PointF point, RotatedTileBox tileBox) { return false; } public void getBugFromPoint(RotatedTileBox tb, PointF point, List<? super OpenStreetNote> res) { List<OpenStreetNote> objects = data.getResults(); if (objects != null && view != null) { int ex = (int) point.x; int ey = (int) point.y; final int rad = getRadiusBug(tb); int radius = rad * 3 / 2; int small = rad * 3 / 4; try { for (int i = 0; i < objects.size(); i++) { OpenStreetNote n = objects.get(i); int x = (int) tb.getPixXFromLatLon(n.getLatitude(), n.getLongitude()); int y = (int) tb.getPixYFromLatLon(n.getLatitude(), n.getLongitude()); if (Math.abs(x - ex) <= radius && Math.abs(y - ey) <= radius) { radius = small; res.add(n); } } } catch (IndexOutOfBoundsException e) { // that's really rare case, but is much efficient than introduce synchronized block } } } @Override public boolean onSingleTap(PointF point, RotatedTileBox tileBox) { ArrayList<OpenStreetNote> list = new ArrayList<OpenStreetNote>(); getBugFromPoint(tileBox, point, list); if (!list.isEmpty()) { StringBuilder res = new StringBuilder(); int i = 0; for (OpenStreetNote o : list) { if (i++ > 0) { res.append("\n\n"); } res.append( activity.getString(R.string.osb_bug_name) + " : " + o.getCommentDescription()); // $NON-NLS-1$ } AccessibleToast.makeText(activity, res.toString(), Toast.LENGTH_LONG).show(); return true; } return false; } public void clearCache() { if (data != null) { data.clearCache(); } } private static String readText(XmlPullParser parser, String key) throws XmlPullParserException, IOException { int tok; String text = ""; while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { if (tok == XmlPullParser.END_TAG && parser.getName().equals(key)) { break; } else if (tok == XmlPullParser.TEXT) { text += parser.getText(); } } return text; } protected List<OpenStreetNote> loadingBugs( double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude) { final int deviceApiVersion = android.os.Build.VERSION.SDK_INT; String SITE_API; if (deviceApiVersion >= android.os.Build.VERSION_CODES.GINGERBREAD) { SITE_API = "https://api.openstreetmap.org/"; } else { SITE_API = "http://api.openstreetmap.org/"; } List<OpenStreetNote> bugs = new ArrayList<OpenStreetNote>(); StringBuilder b = new StringBuilder(); b.append(SITE_API + "api/0.6/notes?bbox="); // $NON-NLS-1$ b.append(leftLongitude); // $NON-NLS-1$ b.append(",").append(bottomLatitude); // $NON-NLS-1$ b.append(",").append(rightLongitude); // $NON-NLS-1$ b.append(",").append(topLatitude); // $NON-NLS-1$ try { log.info("Loading bugs " + b); // $NON-NLS-1$ URLConnection connection = NetworkUtils.getHttpURLConnection(b.toString()); BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); XmlPullParser parser = Xml.newPullParser(); parser.setInput(reader); int tok; OpenStreetNote current = null; int commentIndex = 0; while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { if (tok == XmlPullParser.START_TAG) { if (parser.getName().equals("note")) { current = new OpenStreetNote(); commentIndex = -1; current.setLongitude(Double.parseDouble(parser.getAttributeValue("", "lon"))); current.setLatitude(Double.parseDouble(parser.getAttributeValue("", "lat"))); current.setOpened(true); bugs.add(current); } else if (parser.getName().equals("status") && current != null) { current.setOpened("open".equals(readText(parser, "status"))); } else if (parser.getName().equals("id") && current != null) { current.id = Long.parseLong(readText(parser, "id")); } else if (parser.getName().equals("comment")) { commentIndex++; } else if (parser.getName().equals("user") && current != null) { if (commentIndex == current.users.size()) { current.users.add(readText(parser, "user")); } } else if (parser.getName().equals("date") && current != null) { if (commentIndex == current.dates.size()) { current.dates.add(readText(parser, "date")); } } else if (parser.getName().equals("text") && current != null) { if (commentIndex == current.comments.size()) { current.comments.add(readText(parser, "text")); } } } } reader.close(); } catch (IOException e) { log.warn("Error loading bugs", e); // $NON-NLS-1$ } catch (NumberFormatException e) { log.warn("Error loading bugs", e); // $NON-NLS-1$ } catch (RuntimeException e) { log.warn("Error loading bugs", e); // $NON-NLS-1$ } catch (XmlPullParserException e) { log.warn("Error loading bugs", e); // $NON-NLS-1$ } for (OsmNotesPoint p : local.getOsmbugsPoints()) { if (p.getId() < 0) { OpenStreetNote bug = new OpenStreetNote(); bug.setId(p.getId()); bug.setLongitude(p.getLongitude()); bug.setLatitude(p.getLatitude()); bug.dates.add(""); bug.users.add(activity.getMyApplication().getSettings().USER_NAME.get()); bug.comments.add(p.getText()); bug.setOpened(p.getAction() == Action.CREATE || p.getAction() == Action.MODIFY); bug.setLocal(true); bugs.add(bug); } } return bugs; } private void openBugAlertDialog(final double latitude, final double longitude, String message) { dialogBundle.putDouble(KEY_LATITUDE, latitude); dialogBundle.putDouble(KEY_LONGITUDE, longitude); dialogBundle.putString(KEY_MESSAGE, message); OsmandSettings settings = activity.getMyApplication().getSettings(); dialogBundle.putString(KEY_AUTHOR, settings.USER_NAME.get()); createOpenBugDialog(dialogBundle).show(); } private void prepareOpenBugDialog(Dialog dlg, Bundle args) { ((EditText) dlg.findViewById(R.id.messageEditText)).setText(args.getString(KEY_MESSAGE)); ((EditText) dlg.findViewById(R.id.userNameEditText)).setText(args.getString(KEY_AUTHOR)); } private Dialog createOpenBugDialog(final Bundle args) { final View openBug = activity.getLayoutInflater().inflate(R.layout.open_bug, null); Builder builder = new AlertDialog.Builder(activity); builder.setTitle(R.string.osb_add_dialog_title); builder.setView(openBug); builder.setNegativeButton(R.string.shared_string_cancel, null); ((EditText) openBug.findViewById(R.id.passwordEditText)) .setText(((OsmandApplication) activity.getApplication()).getSettings().USER_PASSWORD.get()); ((EditText) openBug.findViewById(R.id.userNameEditText)).setText(getUserName()); AndroidUtils.softKeyboardDelayed((EditText) openBug.findViewById(R.id.messageEditText)); builder.setPositiveButton( R.string.shared_string_add, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { final double latitude = args.getDouble(KEY_LATITUDE); final double longitude = args.getDouble(KEY_LONGITUDE); final String text = getTextAndUpdateUserPwd(openBug); createNewBugAsync(latitude, longitude, text, getUserName()); } }); return builder.create(); } private void createNewBugAsync( final double latitude, final double longitude, final String text, final String author) { AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() { private OsmBugsUtil osmbugsUtil; @Override protected String doInBackground(Void... params) { osmbugsUtil = getOsmbugsUtil(null); return osmbugsUtil.createNewBug(latitude, longitude, text, author); } protected void onPostExecute(String result) { if (result == null) { if (local == osmbugsUtil) { AccessibleToast.makeText( activity, R.string.osm_changes_added_to_local_edits, Toast.LENGTH_LONG) .show(); } else { AccessibleToast.makeText( activity, R.string.osb_add_dialog_success, Toast.LENGTH_LONG) .show(); } refreshMap(); } else { AccessibleToast.makeText( activity, activity.getResources().getString(R.string.osb_add_dialog_error) + "\n" + result, Toast.LENGTH_LONG) .show(); openBugAlertDialog(latitude, longitude, text); } }; }; executeTaskInBackground(task); } private void addingCommentAsync( final OpenStreetNote bug, final String text, final String author) { AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() { private OsmBugsUtil osmbugsUtil; @Override protected String doInBackground(Void... params) { osmbugsUtil = getOsmbugsUtil(bug); return osmbugsUtil.addingComment(bug.getId(), text, author); } protected void onPostExecute(String warn) { if (warn == null) { if (local == osmbugsUtil) { AccessibleToast.makeText( activity, R.string.osm_changes_added_to_local_edits, Toast.LENGTH_LONG) .show(); } else { AccessibleToast.makeText( activity, R.string.osb_comment_dialog_success, Toast.LENGTH_LONG) .show(); } clearCache(); } else { AccessibleToast.makeText( activity, activity.getResources().getString(R.string.osb_comment_dialog_error) + "\n" + warn, Toast.LENGTH_LONG) .show(); } }; }; executeTaskInBackground(task); } public void openBug(final double latitude, final double longitude) { openBugAlertDialog(latitude, longitude, ""); } public void commentBug(final OpenStreetNote bug) { dialogBundle.putSerializable(KEY_BUG, bug); activity.showDialog(DIALOG_COMMENT_BUG); } private Dialog createCommentBugDialog(final Bundle args) { Builder builder = new AlertDialog.Builder(activity); builder.setTitle(R.string.osb_comment_dialog_title); final View view = activity.getLayoutInflater().inflate(R.layout.open_bug, null); builder.setView(view); ((EditText) view.findViewById(R.id.userNameEditText)).setText(getUserName()); ((EditText) view.findViewById(R.id.passwordEditText)) .setText(((OsmandApplication) activity.getApplication()).getSettings().USER_PASSWORD.get()); AndroidUtils.softKeyboardDelayed((EditText) view.findViewById(R.id.messageEditText)); builder.setNegativeButton(R.string.shared_string_cancel, null); builder.setPositiveButton( R.string.osb_comment_dialog_add_button, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { OpenStreetNote bug = (OpenStreetNote) args.getSerializable(KEY_BUG); String text = getTextAndUpdateUserPwd(view); addingCommentAsync(bug, text, getUserName()); } }); return builder.create(); } private String getUserName() { return ((OsmandApplication) activity.getApplication()).getSettings().USER_NAME.get(); } private String getTextAndUpdateUserPwd(final View view) { String text = ((EditText) view.findViewById(R.id.messageEditText)).getText().toString(); String author = ((EditText) view.findViewById(R.id.userNameEditText)).getText().toString(); String pwd = ((EditText) view.findViewById(R.id.passwordEditText)).getText().toString(); ((OsmandApplication) OsmBugsLayer.this.activity.getApplication()) .getSettings() .USER_NAME .set(author); ((OsmandApplication) OsmBugsLayer.this.activity.getApplication()) .getSettings() .USER_PASSWORD .set(pwd); return text; } public void refreshMap() { if (view != null && view.getLayers().contains(OsmBugsLayer.this)) { view.refreshMap(); } } public void closeBug(final OpenStreetNote bug) { dialogBundle.putSerializable(KEY_BUG, bug); activity.showDialog(DIALOG_CLOSE_BUG); } private Dialog createCloseBugDialog(final Bundle args) { Builder builder = new AlertDialog.Builder(activity); builder.setTitle(R.string.osb_close_dialog_title); builder.setNegativeButton(R.string.shared_string_cancel, null); builder.setPositiveButton( R.string.osb_close_dialog_close_button, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { OpenStreetNote bug = (OpenStreetNote) args.getSerializable(KEY_BUG); String us = activity.getMyApplication().getSettings().USER_NAME.get(); String pwd = activity.getMyApplication().getSettings().USER_PASSWORD.get(); if (us.length() == 0 || pwd.length() == 0) { AccessibleToast.makeText( activity, activity.getString(R.string.osb_author_or_password_not_specified), Toast.LENGTH_SHORT) .show(); } closingAsync(bug, ""); } }); return builder.create(); } private void closingAsync(final OpenStreetNote bug, final String text) { AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() { private OsmBugsUtil osmbugsUtil; @Override protected String doInBackground(Void... params) { osmbugsUtil = getOsmbugsUtil(bug); return osmbugsUtil.closingBug(bug.getId(), "", getUserName()); } protected void onPostExecute(String closed) { if (closed == null) { if (local == osmbugsUtil) { AccessibleToast.makeText( activity, R.string.osm_changes_added_to_local_edits, Toast.LENGTH_LONG) .show(); } else { AccessibleToast.makeText( activity, R.string.osb_close_dialog_success, Toast.LENGTH_LONG) .show(); } clearCache(); refreshMap(); } else { AccessibleToast.makeText( activity, activity.getString(R.string.osb_close_dialog_error) + "\n" + closed, Toast.LENGTH_LONG) .show(); } }; }; executeTaskInBackground(task); } @Override public void populateObjectContextMenu(Object o, ContextMenuAdapter adapter) { if (o instanceof OpenStreetNote) { final OpenStreetNote bug = (OpenStreetNote) o; OnContextMenuClick listener = new OnContextMenuClick() { @Override public boolean onContextMenuClick( ArrayAdapter<?> adapter, int itemId, int pos, boolean isChecked) { if (itemId == R.string.osb_comment_menu_item) { commentBug(bug); } else if (itemId == R.string.osb_close_menu_item) { closeBug(bug); } return true; } }; adapter .item(R.string.osb_comment_menu_item) .iconColor(R.drawable.ic_action_note_dark) .listen(listener) .reg(); adapter .item(R.string.osb_close_menu_item) .iconColor(R.drawable.ic_action_remove_dark) .listen(listener) .reg(); } } @Override public String getObjectDescription(Object o) { if (o instanceof OpenStreetNote) { return activity.getString(R.string.osb_bug_name) + " : " + ((OpenStreetNote) o).getCommentDescription(); // $NON-NLS-1$ } return null; } @Override public PointDescription getObjectName(Object o) { if (o instanceof OpenStreetNote) { return new PointDescription( PointDescription.POINT_TYPE_OSM_NOTE, ((OpenStreetNote) o).getCommentDescription()); } return null; } @Override public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List<Object> res) { getBugFromPoint(tileBox, point, res); } @Override public LatLon getObjectLocation(Object o) { if (o instanceof OpenStreetNote) { return new LatLon(((OpenStreetNote) o).getLatitude(), ((OpenStreetNote) o).getLongitude()); } return null; } @Override public Dialog onCreateDialog(int id) { Bundle args = dialogBundle; switch (id) { case DIALOG_COMMENT_BUG: return createCommentBugDialog(args); case DIALOG_CLOSE_BUG: return createCloseBugDialog(args); } return null; } @Override public void onPrepareDialog(int id, Dialog dialog) { switch (id) { case DIALOG_COMMENT_BUG: ((EditText) dialog.findViewById(R.id.messageEditText)).setText(""); break; } } public static class OpenStreetNote implements Serializable { private boolean local; private static final long serialVersionUID = -7848941747811172615L; private double latitude; private double longitude; private String name; private List<String> dates = new ArrayList<String>(); private List<String> comments = new ArrayList<String>(); private List<String> users = new ArrayList<String>(); private long id; private boolean opened; public double getLatitude() { return latitude; } public void setLatitude(double latitude) { this.latitude = latitude; } public double getLongitude() { return longitude; } public void setLongitude(double longitude) { this.longitude = longitude; } public String getCommentDescription() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < comments.size(); i++) { if (i < dates.size()) { sb.append(dates.get(i)).append(" "); } if (i < users.size()) { sb.append(users.get(i)).append(" : "); } sb.append(comments.get(i)).append("\n"); } return sb.toString(); } public long getId() { return id; } public void setId(long id) { this.id = id; } public boolean isOpened() { return opened; } public void setOpened(boolean opened) { this.opened = opened; } public boolean isLocal() { return local; } public void setLocal(boolean local) { this.local = local; } } }
public class DownloadIndexesThread { private DownloadIndexActivity uiActivity = null; private IndexFileList indexFiles = null; private List<SrtmIndexItem> cachedSRTMFiles; private Map<IndexItem, List<DownloadEntry>> entriesToDownload = new ConcurrentHashMap<IndexItem, List<DownloadEntry>>(); private Set<DownloadEntry> currentDownloads = new HashSet<DownloadEntry>(); private final Context ctx; private OsmandApplication app; private static final Log log = PlatformUtil.getLog(DownloadIndexesThread.class); private DownloadFileHelper downloadFileHelper; private List<BasicProgressAsyncTask<?, ?, ?>> currentRunningTask = Collections.synchronizedList(new ArrayList<BasicProgressAsyncTask<?, ?, ?>>()); private Map<String, String> indexFileNames = new LinkedHashMap<String, String>(); private Map<String, String> indexActivatedFileNames = new LinkedHashMap<String, String>(); public DownloadIndexesThread(Context ctx) { this.ctx = ctx; app = (OsmandApplication) ctx.getApplicationContext(); downloadFileHelper = new DownloadFileHelper(app); } public void setUiActivity(DownloadIndexActivity uiActivity) { this.uiActivity = uiActivity; } public List<DownloadEntry> flattenDownloadEntries() { List<DownloadEntry> res = new ArrayList<DownloadEntry>(); for (List<DownloadEntry> ens : getEntriesToDownload().values()) { if (ens != null) { res.addAll(ens); } } return res; } public List<IndexItem> getCachedIndexFiles() { return indexFiles != null ? indexFiles.getIndexFiles() : null; } public void updateLoadedFiles() { Map<String, String> indexActivatedFileNames = app.getResourceManager().getIndexFileNames(); DownloadIndexActivity.listWithAlternatives( app, app.getAppPath(""), IndexConstants.EXTRA_EXT, indexActivatedFileNames); Map<String, String> indexFileNames = app.getResourceManager().getIndexFileNames(); DownloadIndexActivity.listWithAlternatives( app, app.getAppPath(""), IndexConstants.EXTRA_EXT, indexFileNames); DownloadIndexActivity.listWithAlternatives( app, app.getAppPath(IndexConstants.TILES_INDEX_DIR), IndexConstants.SQLITE_EXT, indexFileNames); app.getResourceManager().getBackupIndexes(indexFileNames); this.indexFileNames = indexFileNames; this.indexActivatedFileNames = indexActivatedFileNames; } public boolean isDownloadedFromInternet() { return indexFiles != null && indexFiles.isDownloadedFromInternet(); } public class DownloadIndexesAsyncTask extends BasicProgressAsyncTask<IndexItem, Object, String> implements DownloadFileShowWarning { private OsmandPreference<Integer> downloads; public DownloadIndexesAsyncTask(Context ctx) { super(ctx); downloads = app.getSettings().NUMBER_OF_FREE_DOWNLOADS; } @Override public void setInterrupted(boolean interrupted) { super.setInterrupted(interrupted); if (interrupted) { downloadFileHelper.setInterruptDownloading(true); } } @Override protected void onProgressUpdate(Object... values) { for (Object o : values) { if (o instanceof DownloadEntry) { if (uiActivity != null) { ((DownloadIndexAdapter) uiActivity.getExpandableListAdapter()) .notifyDataSetInvalidated(); uiActivity.updateDownloadButton(false); } } else if (o instanceof IndexItem) { entriesToDownload.remove(o); if (uiActivity != null) { ((DownloadIndexAdapter) uiActivity.getExpandableListAdapter()) .notifyDataSetInvalidated(); uiActivity.updateDownloadButton(false); } } else if (o instanceof String) { AccessibleToast.makeText(ctx, (String) o, Toast.LENGTH_LONG).show(); } } super.onProgressUpdate(values); } @Override protected void onPreExecute() { currentRunningTask.add(this); super.onPreExecute(); if (uiActivity != null) { downloadFileHelper.setInterruptDownloading(false); View mainView = uiActivity.findViewById(R.id.MainLayout); if (mainView != null) { mainView.setKeepScreenOn(true); } startTask(ctx.getString(R.string.downloading), -1); } } @Override protected void onPostExecute(String result) { if (result != null && result.length() > 0) { AccessibleToast.makeText(ctx, result, Toast.LENGTH_LONG).show(); } currentDownloads.clear(); if (uiActivity != null) { View mainView = uiActivity.findViewById(R.id.MainLayout); if (mainView != null) { mainView.setKeepScreenOn(false); } DownloadIndexAdapter adapter = ((DownloadIndexAdapter) uiActivity.getExpandableListAdapter()); if (adapter != null) { adapter.setLoadedFiles(indexActivatedFileNames, indexFileNames); } } currentRunningTask.remove(this); if (uiActivity != null) { uiActivity.updateProgress(false); } } @Override protected String doInBackground(IndexItem... filesToDownload) { try { List<File> filesToReindex = new ArrayList<File>(); boolean forceWifi = downloadFileHelper.isWifiConnected(); currentDownloads = new HashSet<DownloadEntry>(); String breakDownloadMessage = null; downloadCycle: while (!entriesToDownload.isEmpty()) { Iterator<Entry<IndexItem, List<DownloadEntry>>> it = entriesToDownload.entrySet().iterator(); IndexItem file = null; List<DownloadEntry> list = null; while (it.hasNext()) { Entry<IndexItem, List<DownloadEntry>> n = it.next(); if (!currentDownloads.containsAll(n.getValue())) { file = n.getKey(); list = n.getValue(); break; } } if (file == null) { break downloadCycle; } if (list != null) { boolean success = false; for (DownloadEntry entry : list) { if (currentDownloads.contains(entry)) { continue; } currentDownloads.add(entry); double asz = getAvailableSpace(); // validate interrupted if (downloadFileHelper.isInterruptDownloading()) { break downloadCycle; } // validate enough space if (asz != -1 && entry.sizeMB > asz) { breakDownloadMessage = app.getString(R.string.download_files_not_enough_space, entry.sizeMB, asz); break downloadCycle; } if (exceedsFreelimit(entry)) { breakDownloadMessage = app.getString( R.string.free_version_message, DownloadIndexActivity.MAXIMUM_AVAILABLE_FREE_DOWNLOADS + ""); break downloadCycle; } boolean result = downloadFile(entry, filesToReindex, forceWifi); success = result || success; if (result) { if (DownloadActivityType.isCountedInDownloads(entry.type)) { downloads.set(downloads.get() + 1); } if (entry.existingBackupFile != null) { Algorithms.removeAllFiles(entry.existingBackupFile); } trackEvent(entry); publishProgress(entry); } } if (success) { entriesToDownload.remove(file); } } } String warn = reindexFiles(filesToReindex); if (breakDownloadMessage != null) { if (warn != null) { warn = breakDownloadMessage + "\n" + warn; } else { warn = breakDownloadMessage; } } updateLoadedFiles(); return warn; } catch (InterruptedException e) { log.info("Download Interrupted"); // do not dismiss dialog } return null; } private boolean exceedsFreelimit(DownloadEntry entry) { return Version.isFreeVersion(app) && DownloadActivityType.isCountedInDownloads(entry.type) && downloads.get() >= DownloadIndexActivity.MAXIMUM_AVAILABLE_FREE_DOWNLOADS; } private String reindexFiles(List<File> filesToReindex) { boolean vectorMapsToReindex = false; for (File f : filesToReindex) { if (f.getName().endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { vectorMapsToReindex = true; break; } } // reindex vector maps all at one time ResourceManager manager = app.getResourceManager(); manager.indexVoiceFiles(this); List<String> warnings = new ArrayList<String>(); if (vectorMapsToReindex) { warnings = manager.indexingMaps(this); } if (cachedSRTMFiles != null) { for (SrtmIndexItem i : cachedSRTMFiles) { ((SrtmIndexItem) i).updateExistingTiles(app.getResourceManager().getIndexFileNames()); } } if (!warnings.isEmpty()) { return warnings.get(0); } return null; } private void trackEvent(DownloadEntry entry) { String v = Version.getAppName(app); if (Version.isProductionVersion(app)) { v = Version.getFullVersion(app); } else { v += " test"; } new DownloadTracker() .trackEvent( app, v, Version.getAppName(app), entry.baseName, 1, app.getString(R.string.ga_api_key)); } @Override public void showWarning(String warning) { publishProgress(warning); } public boolean downloadFile(DownloadEntry de, List<File> filesToReindex, boolean forceWifi) throws InterruptedException { boolean res = false; if (de.isAsset) { try { if (uiActivity != null) { ResourceManager.copyAssets(uiActivity.getAssets(), de.assetName, de.targetFile); boolean changedDate = de.targetFile.setLastModified(de.dateModified); if (!changedDate) { log.error("Set last timestamp is not supported"); } res = true; } } catch (IOException e) { log.error("Copy exception", e); } } else { res = downloadFileHelper.downloadFile(de, this, filesToReindex, this, forceWifi); } if (res && de.attachedEntry != null) { return downloadFile(de.attachedEntry, filesToReindex, forceWifi); } return res; } @Override protected void updateProgress(boolean updateOnlyProgress) { if (uiActivity != null) { uiActivity.updateProgress(updateOnlyProgress); } } } private boolean checkRunning() { if (getCurrentRunningTask() != null) { AccessibleToast.makeText(app, R.string.wait_current_task_finished, Toast.LENGTH_SHORT).show(); return true; } return false; } public void runReloadIndexFiles() { checkRunning(); final BasicProgressAsyncTask<Void, Void, IndexFileList> inst = new BasicProgressAsyncTask<Void, Void, IndexFileList>(ctx) { @Override protected IndexFileList doInBackground(Void... params) { return DownloadOsmandIndexesHelper.getIndexesList(ctx); }; @Override protected void onPreExecute() { currentRunningTask.add(this); super.onPreExecute(); this.message = ctx.getString(R.string.downloading_list_indexes); } protected void onPostExecute(IndexFileList result) { indexFiles = result; if (indexFiles != null && uiActivity != null) { boolean basemapExists = uiActivity.getMyApplication().getResourceManager().containsBasemap(); IndexItem basemap = indexFiles.getBasemap(); if (!basemapExists && basemap != null) { List<DownloadEntry> downloadEntry = basemap.createDownloadEntry( uiActivity.getClientContext(), uiActivity.getType(), new ArrayList<DownloadEntry>()); uiActivity.getEntriesToDownload().put(basemap, downloadEntry); AccessibleToast.makeText( uiActivity, R.string.basemap_was_selected_to_download, Toast.LENGTH_LONG) .show(); uiActivity.findViewById(R.id.DownloadButton).setVisibility(View.VISIBLE); } if (indexFiles.isIncreasedMapVersion()) { showWarnDialog(); } } else { AccessibleToast.makeText( ctx, R.string.list_index_files_was_not_loaded, Toast.LENGTH_LONG) .show(); } currentRunningTask.remove(this); if (uiActivity != null) { uiActivity.updateProgress(false); runCategorization(uiActivity.getType()); } } private void showWarnDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(ctx); builder.setMessage(R.string.map_version_changed_info); builder.setPositiveButton( R.string.button_upgrade_osmandplus, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:net.osmand.plus")); try { ctx.startActivity(intent); } catch (ActivityNotFoundException e) { } } }); builder.setNegativeButton( R.string.default_buttons_cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); builder.show(); } @Override protected void updateProgress(boolean updateOnlyProgress) { if (uiActivity != null) { uiActivity.updateProgress(updateOnlyProgress); } }; }; execute(inst, new Void[0]); } public void runDownloadFiles() { if (checkRunning()) { return; } DownloadIndexesAsyncTask task = new DownloadIndexesAsyncTask(ctx); execute(task, new IndexItem[0]); } private <P> void execute(BasicProgressAsyncTask<P, ?, ?> task, P... indexItems) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // TODO check task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, indexItems); } else { task.execute(indexItems); } } public Map<IndexItem, List<DownloadEntry>> getEntriesToDownload() { return entriesToDownload; } public void runCategorization(final DownloadActivityType type) { final BasicProgressAsyncTask<Void, Void, List<IndexItem>> inst = new BasicProgressAsyncTask<Void, Void, List<IndexItem>>(ctx) { private List<IndexItemCategory> cats; @Override protected void onPreExecute() { super.onPreExecute(); currentRunningTask.add(this); this.message = ctx.getString(R.string.downloading_list_indexes); if (uiActivity != null) { uiActivity.updateProgress(false); } } @Override protected List<IndexItem> doInBackground(Void... params) { final List<IndexItem> filtered = getFilteredByType(); cats = IndexItemCategory.categorizeIndexItems(app, filtered); updateLoadedFiles(); return filtered; }; public List<IndexItem> getFilteredByType() { final List<IndexItem> filtered = new ArrayList<IndexItem>(); if (type == DownloadActivityType.SRTM_FILE) { Map<String, String> indexFileNames = app.getResourceManager().getIndexFileNames(); if (cachedSRTMFiles == null) { cachedSRTMFiles = new ArrayList<SrtmIndexItem>(); synchronized (cachedSRTMFiles) { List<RegionCountry> countries = RegionRegistry.getRegionRegistry().getCountries(); for (RegionCountry rc : countries) { if (rc.getTileSize() > 35 && rc.getSubRegions().size() > 0) { for (RegionCountry ch : rc.getSubRegions()) { cachedSRTMFiles.add(new SrtmIndexItem(ch, indexFileNames)); } } else { cachedSRTMFiles.add(new SrtmIndexItem(rc, indexFileNames)); } } filtered.addAll(cachedSRTMFiles); } } else { synchronized (cachedSRTMFiles) { for (SrtmIndexItem s : cachedSRTMFiles) { s.updateExistingTiles(indexFileNames); filtered.add(s); } } } } List<IndexItem> cachedIndexFiles = getCachedIndexFiles(); if (cachedIndexFiles != null) { for (IndexItem file : cachedIndexFiles) { if (file.getType() == type) { filtered.add(file); } } } return filtered; } @Override protected void onPostExecute(List<IndexItem> filtered) { if (uiActivity != null) { DownloadIndexAdapter a = ((DownloadIndexAdapter) uiActivity.getExpandableListAdapter()); a.setLoadedFiles(indexActivatedFileNames, indexFileNames); a.setIndexFiles(filtered, cats); a.notifyDataSetChanged(); a.getFilter().filter(uiActivity.getFilterText()); if (type == DownloadActivityType.SRTM_FILE && OsmandPlugin.getEnabledPlugin(SRTMPlugin.class) instanceof SRTMPlugin && !OsmandPlugin.getEnabledPlugin(SRTMPlugin.class).isPaid()) { Builder msg = new AlertDialog.Builder(uiActivity); msg.setTitle(R.string.srtm_paid_version_title); msg.setMessage(R.string.srtm_paid_version_msg); msg.setNegativeButton( R.string.button_upgrade_osmandplus, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:net.osmand.srtmPlugin.paid")); try { ctx.startActivity(intent); } catch (ActivityNotFoundException e) { } } }); msg.setPositiveButton(R.string.default_buttons_ok, null); msg.show(); } } currentRunningTask.remove(this); if (uiActivity != null) { uiActivity.updateProgress(false); } } @Override protected void updateProgress(boolean updateOnlyProgress) { if (uiActivity != null) { uiActivity.updateProgress(updateOnlyProgress); } }; }; execute(inst, new Void[0]); } public boolean isDownloadRunning() { for (int i = 0; i < currentRunningTask.size(); i++) { if (currentRunningTask.get(i) instanceof DownloadIndexesAsyncTask) { return true; } } return false; } public BasicProgressAsyncTask<?, ?, ?> getCurrentRunningTask() { for (int i = 0; i < currentRunningTask.size(); ) { if (currentRunningTask.get(i).getStatus() == Status.FINISHED) { currentRunningTask.remove(i); } else { i++; } } if (currentRunningTask.size() > 0) { return currentRunningTask.get(0); } return null; } public double getAvailableSpace() { File dir = app.getAppPath("").getParentFile(); double asz = -1; if (dir.canRead()) { StatFs fs = new StatFs(dir.getAbsolutePath()); asz = (((long) fs.getAvailableBlocks()) * fs.getBlockSize()) / (1 << 20); } return asz; } public int getDownloads() { int i = 0; Collection<List<DownloadEntry>> vs = getEntriesToDownload().values(); for (List<DownloadEntry> v : vs) { for (DownloadEntry e : v) { if (!currentDownloads.contains(e)) { i++; } } } if (!currentDownloads.isEmpty()) { i++; } return i; } }
public abstract class OsmandPlugin { private static List<OsmandPlugin> allPlugins = new ArrayList<OsmandPlugin>(); private static final Log LOG = PlatformUtil.getLog(OsmandPlugin.class); private static final String SRTM_PLUGIN_COMPONENT_PAID = "net.osmand.srtmPlugin.paid"; //$NON-NLS-1$ private static final String SRTM_PLUGIN_COMPONENT = "net.osmand.srtmPlugin"; private boolean active; private String installURL = null; public abstract String getId(); public abstract String getDescription(); public abstract String getName(); public abstract int getAssetResourceName(); public int getLogoResourceId() { return R.drawable.ic_extension_dark; } public abstract Class<? extends Activity> getSettingsActivity(); public String getVersion() { return ""; } /** Initialize plugin runs just after creation */ public boolean init(OsmandApplication app, Activity activity) { return true; } public void setActive(boolean active) { this.active = active; } public boolean isActive() { return active; } public boolean needsInstallation() { return installURL != null; } public void setInstallURL(String installURL) { this.installURL = installURL; } public String getInstallURL() { return installURL; } public void disable(OsmandApplication app) {} public String getHelpFileName() { return null; } public static void initPlugins(OsmandApplication app) { OsmandSettings settings = app.getSettings(); Set<String> enabledPlugins = settings.getEnabledPlugins(); allPlugins.add(new OsmandRasterMapsPlugin(app)); allPlugins.add(new OsmandMonitoringPlugin(app)); allPlugins.add(new OsMoPlugin(app)); checkMarketPlugin( app, new SRTMPlugin(app), true, SRTM_PLUGIN_COMPONENT_PAID, SRTM_PLUGIN_COMPONENT); // ? questionable - definitely not market plugin // checkMarketPlugin(app, new TouringViewPlugin(app), false, TouringViewPlugin.COMPONENT, // null); checkMarketPlugin(app, new NauticalMapsPlugin(app), false, NauticalMapsPlugin.COMPONENT, null); checkMarketPlugin(app, new SkiMapsPlugin(app), false, SkiMapsPlugin.COMPONENT, null); // checkMarketPlugin(app, new RoutePointsPlugin(app), false /*FIXME*/, // RoutePointsPlugin.ROUTE_POINTS_PLUGIN_COMPONENT, null); allPlugins.add(new AudioVideoNotesPlugin(app)); checkMarketPlugin( app, new ParkingPositionPlugin(app), false, ParkingPositionPlugin.PARKING_PLUGIN_COMPONENT, null); allPlugins.add(new DistanceCalculatorPlugin(app)); allPlugins.add(new AccessibilityPlugin(app)); allPlugins.add(new OsmEditingPlugin(app)); allPlugins.add(new OsmandDevelopmentPlugin(app)); if (android.os.Build.VERSION.SDK_INT >= 19) { allPlugins.add(new net.osmand.plus.smartnaviwatch.SmartNaviWatchPlugin(app)); } activatePlugins(app, enabledPlugins); } private static void activatePlugins(OsmandApplication app, Set<String> enabledPlugins) { for (OsmandPlugin plugin : allPlugins) { if (enabledPlugins.contains(plugin.getId()) || plugin.isActive()) { try { if (plugin.init(app, null)) { plugin.setActive(true); } } catch (Exception e) { LOG.error("Plugin initialization failed " + plugin.getId(), e); } } } } private static void checkMarketPlugin( OsmandApplication app, OsmandPlugin srtm, boolean paid, String id, String id2) { boolean marketEnabled = Version.isMarketEnabled(app); boolean pckg = isPackageInstalled(id, app) || isPackageInstalled(id2, app); if ((Version.isDeveloperVersion(app) || !Version.isProductionVersion(app)) && !paid) { // for test reasons marketEnabled = false; } if (pckg || (!marketEnabled && !paid)) { if (pckg && !app.getSettings().getPlugins().contains("-" + srtm.getId())) { srtm.setActive(true); } allPlugins.add(srtm); } else { if (marketEnabled) { srtm.setInstallURL(Version.marketPrefix(app) + id); allPlugins.add(srtm); } } } public static boolean enablePlugin( Activity activity, OsmandApplication app, OsmandPlugin plugin, boolean enable) { if (enable) { if (!plugin.init(app, activity)) { plugin.setActive(false); return false; } else { plugin.setActive(true); } } else { plugin.disable(app); plugin.setActive(false); } app.getSettings().enablePlugin(plugin.getId(), enable); if (activity instanceof MapActivity) { final MapActivity mapActivity = (MapActivity) activity; plugin.updateLayers(mapActivity.getMapView(), mapActivity); mapActivity.getDashboard().refreshDashboardFragments(); if (!enable && plugin.getCardFragment() != null) { Fragment fragment = mapActivity.getSupportFragmentManager().findFragmentByTag(plugin.getCardFragment().tag); LOG.debug("fragment=" + fragment); mapActivity.getSupportFragmentManager().beginTransaction().remove(fragment).commit(); } } return true; } public void updateLayers(OsmandMapTileView mapView, MapActivity activity) {}; /** * Register layers calls when activity is created and before @mapActivityCreate * * @param activity */ public void registerLayers(MapActivity activity) {} public void mapActivityCreate(MapActivity activity) {} public void mapActivityResume(MapActivity activity) {} public void mapActivityPause(MapActivity activity) {} public void mapActivityDestroy(MapActivity activity) {} public void mapActivityScreenOff(MapActivity activity) {} public boolean destinationReached() { return true; } public void registerLayerContextMenuActions( OsmandMapTileView mapView, ContextMenuAdapter adapter, MapActivity mapActivity) {} public void registerMapContextMenuActions( MapActivity mapActivity, double latitude, double longitude, ContextMenuAdapter adapter, Object selectedObj) {} public void registerOptionsMenuItems(MapActivity mapActivity, ContextMenuAdapter helper) {} public DashFragmentData getCardFragment() { return null; } public void updateLocation(Location location) {} public void addMyPlacesTab( FavoritesActivity favoritesActivity, List<TabItem> mTabs, Intent intent) {} public void contextMenuFragment( Activity activity, Fragment fragment, Object info, ContextMenuAdapter adapter) {} public void optionsMenuFragment( Activity activity, Fragment fragment, ContextMenuAdapter optionsMenuAdapter) {} public List<String> indexingFiles(IProgress progress) { return null; } public boolean mapActivityKeyUp(MapActivity mapActivity, int keyCode) { return false; } public void onMapActivityExternalResult(int requestCode, int resultCode, Intent data) {} public static void refreshLayers(OsmandMapTileView mapView, MapActivity activity) { for (OsmandPlugin plugin : getAvailablePlugins()) { plugin.updateLayers(mapView, activity); } } public static List<OsmandPlugin> getAvailablePlugins() { return allPlugins; } public static List<OsmandPlugin> getEnabledPlugins() { ArrayList<OsmandPlugin> lst = new ArrayList<OsmandPlugin>(allPlugins.size()); for (OsmandPlugin p : allPlugins) { if (p.isActive()) { lst.add(p); } } return lst; } public static List<OsmandPlugin> getNotEnabledPlugins() { ArrayList<OsmandPlugin> lst = new ArrayList<OsmandPlugin>(allPlugins.size()); for (OsmandPlugin p : allPlugins) { if (!p.isActive()) { lst.add(p); } } return lst; } @SuppressWarnings("unchecked") public static <T extends OsmandPlugin> T getEnabledPlugin(Class<T> clz) { for (OsmandPlugin lr : getEnabledPlugins()) { if (clz.isInstance(lr)) { return (T) lr; } } return null; } @SuppressWarnings("unchecked") public static <T extends OsmandPlugin> T getPlugin(Class<T> clz) { for (OsmandPlugin lr : getAvailablePlugins()) { if (clz.isInstance(lr)) { return (T) lr; } } return null; } public static List<String> onIndexingFiles(IProgress progress) { List<String> l = new ArrayList<String>(); for (OsmandPlugin plugin : getEnabledPlugins()) { List<String> ls = plugin.indexingFiles(progress); if (ls != null && ls.size() > 0) { l.addAll(ls); } } return l; } public static void onMapActivityCreate(MapActivity activity) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.mapActivityCreate(activity); } } public static void onMapActivityResume(MapActivity activity) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.mapActivityResume(activity); } } public static void onMapActivityPause(MapActivity activity) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.mapActivityPause(activity); } } public static void onMapActivityDestroy(MapActivity activity) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.mapActivityDestroy(activity); } } public static void onMapActivityResult(int requestCode, int resultCode, Intent data) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.onMapActivityExternalResult(requestCode, resultCode, data); } } public static void onMapActivityScreenOff(MapActivity activity) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.mapActivityScreenOff(activity); } } public static boolean onDestinationReached() { boolean b = true; for (OsmandPlugin plugin : getEnabledPlugins()) { if (!plugin.destinationReached()) { b = false; } } return b; } public static void createLayers(OsmandMapTileView mapView, MapActivity activity) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.registerLayers(activity); } } public static void registerMapContextMenu( MapActivity map, double latitude, double longitude, ContextMenuAdapter adapter, Object selectedObj) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.registerMapContextMenuActions(map, latitude, longitude, adapter, selectedObj); } } public static void registerLayerContextMenu( OsmandMapTileView mapView, ContextMenuAdapter adapter, MapActivity mapActivity) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.registerLayerContextMenuActions(mapView, adapter, mapActivity); } } public static void registerOptionsMenu(MapActivity map, ContextMenuAdapter helper) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.registerOptionsMenuItems(map, helper); } } public static void onContextMenuActivity( Activity activity, Fragment fragment, Object info, ContextMenuAdapter adapter) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.contextMenuFragment(activity, fragment, info, adapter); } } public static void onOptionsMenuActivity( Activity activity, Fragment fragment, ContextMenuAdapter optionsMenuAdapter) { for (OsmandPlugin plugin : getEnabledPlugins()) { plugin.optionsMenuFragment(activity, fragment, optionsMenuAdapter); } } public static Collection<DashFragmentData> getPluginsCardsList() { HashSet<DashFragmentData> collection = new HashSet<>(); for (OsmandPlugin plugin : getEnabledPlugins()) { final DashFragmentData fragmentData = plugin.getCardFragment(); if (fragmentData != null) collection.add(fragmentData); } return collection; } private static boolean isPackageInstalled(String packageInfo, OsmandApplication app) { if (packageInfo == null) { return false; } boolean installed = false; try { installed = app.getPackageManager().getPackageInfo(packageInfo, 0) != null; } catch (NameNotFoundException e) { } return installed; } public static boolean onMapActivityKeyUp(MapActivity mapActivity, int keyCode) { for (OsmandPlugin p : getEnabledPlugins()) { if (p.mapActivityKeyUp(mapActivity, keyCode)) return true; } return false; } public static void updateLocationPlugins(net.osmand.Location location) { for (OsmandPlugin p : getEnabledPlugins()) { p.updateLocation(location); } } public static boolean isDevelopment() { return getEnabledPlugin(OsmandDevelopmentPlugin.class) != null; } public static void addMyPlacesTabPlugins( FavoritesActivity favoritesActivity, List<TabItem> mTabs, Intent intent) { for (OsmandPlugin p : getEnabledPlugins()) { p.addMyPlacesTab(favoritesActivity, mTabs, intent); } } }
public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCallback, Callback { private static final int MAP_REFRESH_MESSAGE = OsmAndConstants.UI_HANDLER_MAP_VIEW + 4; private static final int BASE_REFRESH_MESSAGE = OsmAndConstants.UI_HANDLER_MAP_VIEW + 3; protected static final int LOWEST_ZOOM_TO_ROTATE = 9; private boolean MEASURE_FPS = false; private FPSMeasurement main = new FPSMeasurement(); private FPSMeasurement additional = new FPSMeasurement(); private class FPSMeasurement { int fpsMeasureCount = 0; int fpsMeasureMs = 0; long fpsFirstMeasurement = 0; float fps; void calculateFPS(long start, long end) { fpsMeasureMs += end - start; fpsMeasureCount++; if (fpsMeasureCount > 10 || (start - fpsFirstMeasurement) > 400) { fpsFirstMeasurement = start; fps = (1000f * fpsMeasureCount / fpsMeasureMs); fpsMeasureCount = 0; fpsMeasureMs = 0; } } } protected static final int emptyTileDivisor = 16; public interface OnTrackBallListener { public boolean onTrackBallEvent(MotionEvent e); } public interface OnLongClickListener { public boolean onLongPressEvent(PointF point); } public interface OnClickListener { public boolean onPressEvent(PointF point); } protected static final Log log = PlatformUtil.getLog(OsmandMapTileView.class); private RotatedTileBox currentViewport; private float rotate; // accumulate private int mapPosition; private boolean showMapPosition = true; private IMapLocationListener locationListener; private OnLongClickListener onLongClickListener; private OnClickListener onClickListener; private OnTrackBallListener trackBallDelegate; private AccessibilityActionsProvider accessibilityActions; private List<OsmandMapLayer> layers = new ArrayList<OsmandMapLayer>(); private BaseMapLayer mainLayer; private Map<OsmandMapLayer, Float> zOrders = new HashMap<OsmandMapLayer, Float>(); // UI Part // handler to refresh map (in ui thread - ui thread is not necessary, but msg queue is required). protected Handler handler; private Handler baseHandler; private AnimateDraggingMapThread animatedDraggingThread; private GestureDetector gestureDetector; private MultiTouchSupport multiTouchSupport; Paint paintGrayFill; Paint paintBlackFill; Paint paintWhiteFill; Paint paintCenter; private DisplayMetrics dm; private final OsmandApplication application; protected OsmandSettings settings = null; private Bitmap bufferBitmap; private RotatedTileBox bufferImgLoc; private Bitmap bufferBitmapTmp; private Paint paintImg; public OsmandMapTileView(Context context, AttributeSet attrs) { super(context, attrs); application = (OsmandApplication) context.getApplicationContext(); initView(); } public OsmandMapTileView(Context context) { super(context); application = (OsmandApplication) context.getApplicationContext(); initView(); } // ///////////////////////////// INITIALIZING UI PART /////////////////////////////////// public void initView() { paintGrayFill = new Paint(); paintGrayFill.setColor(Color.GRAY); paintGrayFill.setStyle(Style.FILL); // when map rotate paintGrayFill.setAntiAlias(true); paintBlackFill = new Paint(); paintBlackFill.setColor(Color.BLACK); paintBlackFill.setStyle(Style.FILL); // when map rotate paintBlackFill.setAntiAlias(true); paintWhiteFill = new Paint(); paintWhiteFill.setColor(Color.WHITE); paintWhiteFill.setStyle(Style.FILL); // when map rotate paintWhiteFill.setAntiAlias(true); paintCenter = new Paint(); paintCenter.setStyle(Style.STROKE); paintCenter.setColor(Color.rgb(60, 60, 60)); paintCenter.setStrokeWidth(2); paintCenter.setAntiAlias(true); paintImg = new Paint(); // paintImg.setFilterBitmap(true); // paintImg.setDither(true); setClickable(true); setLongClickable(true); setFocusable(true); handler = new Handler(); baseHandler = new Handler(application.getResourceManager().getRenderingBufferImageThread().getLooper()); getHolder().addCallback(this); animatedDraggingThread = new AnimateDraggingMapThread(this); gestureDetector = new GestureDetector( getContext(), new MapExplorer(this, new MapTileViewOnGestureListener())); multiTouchSupport = new MultiTouchSupport(getContext(), new MapTileViewMultiTouchZoomListener()); gestureDetector.setOnDoubleTapListener(new MapTileViewOnDoubleTapListener()); WindowManager mgr = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); dm = new DisplayMetrics(); mgr.getDefaultDisplay().getMetrics(dm); currentViewport = new RotatedTileBox.RotatedTileBoxBuilder() .setLocation(0, 0) .setZoomAndScale(3, 0) .setPixelDimensions(getWidth(), getHeight()) .build(); currentViewport.setDensity(dm.density); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { refreshMap(); } @Override public void surfaceCreated(SurfaceHolder holder) { refreshMap(); } @Override public void surfaceDestroyed(SurfaceHolder holder) {} @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return application.getInternalAPI().accessibilityEnabled() ? false : super.onKeyDown(keyCode, event); } public synchronized void addLayer(OsmandMapLayer layer, float zOrder) { int i = 0; for (i = 0; i < layers.size(); i++) { if (zOrders.get(layers.get(i)) > zOrder) { break; } } layer.initLayer(this); layers.add(i, layer); zOrders.put(layer, zOrder); } public synchronized void removeLayer(OsmandMapLayer layer) { while (layers.remove(layer)) ; zOrders.remove(layer); layer.destroyLayer(); } public List<OsmandMapLayer> getLayers() { return layers; } @SuppressWarnings("unchecked") public <T extends OsmandMapLayer> T getLayerByClass(Class<T> cl) { for (OsmandMapLayer lr : layers) { if (cl.isInstance(lr)) { return (T) lr; } } return null; } public OsmandApplication getApplication() { return application; } // ///////////////////////// NON UI PART (could be extracted in common) // ///////////////////////////// public void setIntZoom(int zoom) { if (mainLayer != null && zoom <= mainLayer.getMaximumShownMapZoom() && zoom >= mainLayer.getMinimumShownMapZoom()) { animatedDraggingThread.stopAnimating(); currentViewport.setZoomAndAnimation(zoom, 0); currentViewport.setRotate(zoom > LOWEST_ZOOM_TO_ROTATE ? rotate : 0); refreshMap(); } } public void setComplexZoom(int zoom, float scale) { if (mainLayer != null && zoom <= mainLayer.getMaximumShownMapZoom() && zoom >= mainLayer.getMinimumShownMapZoom()) { animatedDraggingThread.stopAnimating(); currentViewport.setZoom(zoom, scale, 0); currentViewport.setRotate(zoom > LOWEST_ZOOM_TO_ROTATE ? rotate : 0); refreshMap(); } } public boolean isMapRotateEnabled() { return getZoom() > LOWEST_ZOOM_TO_ROTATE; } public void setRotate(float rotate) { if (isMapRotateEnabled()) { float diff = MapUtils.unifyRotationDiff(rotate, getRotate()); if (Math.abs(diff) > 5) { // check smallest rotation animatedDraggingThread.startRotate(rotate); } } } public boolean isShowMapPosition() { return showMapPosition; } public void setShowMapPosition(boolean showMapPosition) { this.showMapPosition = showMapPosition; } public float getRotate() { return currentViewport.getRotate(); } public void setLatLon(double latitude, double longitude) { animatedDraggingThread.stopAnimating(); currentViewport.setLatLonCenter(latitude, longitude); refreshMap(); } public double getLatitude() { return currentViewport.getLatitude(); } public double getLongitude() { return currentViewport.getLongitude(); } public int getZoom() { return currentViewport.getZoom(); } public float getSettingsZoomScale() { return settings.getSettingsZoomScale(getDensity()); } public float getZoomScale() { return currentViewport.getZoomScale(); } public boolean isZooming() { return currentViewport.isZoomAnimated(); } public void setMapLocationListener(IMapLocationListener l) { locationListener = l; } /** Adds listener to control when map is dragging */ public IMapLocationListener setMapLocationListener() { return locationListener; } // ////////////////////////////// DRAWING MAP PART ///////////////////////////////////////////// public BaseMapLayer getMainLayer() { return mainLayer; } public void setMainLayer(BaseMapLayer mainLayer) { this.mainLayer = mainLayer; int zoom = currentViewport.getZoom(); if (mainLayer.getMaximumShownMapZoom() < zoom) { zoom = mainLayer.getMaximumShownMapZoom(); } if (mainLayer.getMinimumShownMapZoom() > zoom) { zoom = mainLayer.getMinimumShownMapZoom(); } currentViewport.setZoomAndAnimation(zoom, 0); refreshMap(); } public void setMapPosition(int type) { this.mapPosition = type; } public OsmandSettings getSettings() { if (settings == null) { settings = getApplication().getSettings(); } return settings; } private void drawBasemap(Canvas canvas) { if (bufferImgLoc != null) { float rot = -bufferImgLoc.getRotate(); canvas.rotate(rot, currentViewport.getCenterPixelX(), currentViewport.getCenterPixelY()); final RotatedTileBox calc = currentViewport.copy(); calc.setRotate(bufferImgLoc.getRotate()); int cz = getZoom(); QuadPoint lt = bufferImgLoc.getLeftTopTile(cz); QuadPoint rb = bufferImgLoc.getRightBottomTile(cz); final float x1 = calc.getPixXFromTile(lt.x, lt.y, cz); final float x2 = calc.getPixXFromTile(rb.x, rb.y, cz); final float y1 = calc.getPixYFromTile(lt.x, lt.y, cz); final float y2 = calc.getPixYFromTile(rb.x, rb.y, cz); if (!bufferBitmap.isRecycled()) { canvas.drawBitmap(bufferBitmap, null, new RectF(x1, y1, x2, y2), paintImg); } canvas.rotate(-rot, currentViewport.getCenterPixelX(), currentViewport.getCenterPixelY()); } } private void refreshBaseMapInternal(RotatedTileBox tileBox, DrawSettings drawSettings) { if (tileBox.getPixHeight() == 0 || tileBox.getPixWidth() == 0) { return; } if (bufferBitmapTmp == null || tileBox.getPixHeight() != bufferBitmapTmp.getHeight() || tileBox.getPixWidth() != bufferBitmapTmp.getWidth()) { bufferBitmapTmp = Bitmap.createBitmap(tileBox.getPixWidth(), tileBox.getPixHeight(), Config.RGB_565); } long start = SystemClock.elapsedRealtime(); final QuadPoint c = tileBox.getCenterPixelPoint(); Canvas canvas = new Canvas(bufferBitmapTmp); fillCanvas(canvas, drawSettings); for (int i = 0; i < layers.size(); i++) { try { OsmandMapLayer layer = layers.get(i); canvas.save(); // rotate if needed if (!layer.drawInScreenPixels()) { canvas.rotate(tileBox.getRotate(), c.x, c.y); } layer.onPrepareBufferImage(canvas, tileBox, drawSettings); canvas.restore(); } catch (IndexOutOfBoundsException e) { // skip it } } Bitmap t = bufferBitmap; synchronized (this) { bufferImgLoc = tileBox; bufferBitmap = bufferBitmapTmp; bufferBitmapTmp = t; } long end = SystemClock.elapsedRealtime(); additional.calculateFPS(start, end); } private void refreshMapInternal(DrawSettings drawSettings) { handler.removeMessages(MAP_REFRESH_MESSAGE); SurfaceHolder holder = getHolder(); long ms = SystemClock.elapsedRealtime(); synchronized (holder) { Canvas canvas = holder.lockCanvas(); if (canvas != null) { try { final float ratioy = mapPosition == OsmandSettings.BOTTOM_CONSTANT ? 0.8f : 0.5f; final int cy = (int) (ratioy * getHeight()); if (currentViewport.getPixWidth() != getWidth() || currentViewport.getPixHeight() != getHeight() || currentViewport.getCenterPixelY() != cy) { currentViewport.setPixelDimensions(getWidth(), getHeight(), 0.5f, ratioy); refreshBufferImage(drawSettings); } // make copy to avoid concurrency RotatedTileBox viewportToDraw = currentViewport.copy(); fillCanvas(canvas, drawSettings); drawOverMap(canvas, viewportToDraw, drawSettings); } finally { holder.unlockCanvasAndPost(canvas); } } if (MEASURE_FPS) { main.calculateFPS(ms, SystemClock.elapsedRealtime()); } } } private void fillCanvas(Canvas canvas, DrawSettings drawSettings) { if (drawSettings.isNightMode()) { canvas.drawARGB(255, 100, 100, 100); } else { canvas.drawARGB(255, 225, 225, 225); } } public boolean isMeasureFPS() { return MEASURE_FPS; } public void setMeasureFPS(boolean measureFPS) { MEASURE_FPS = measureFPS; } public float getFPS() { return main.fps; } public float getSecondaryFPS() { return additional.fps; } private void drawOverMap(Canvas canvas, RotatedTileBox tileBox, DrawSettings drawSettings) { final QuadPoint c = tileBox.getCenterPixelPoint(); synchronized (this) { if (bufferBitmap != null && !bufferBitmap.isRecycled()) { canvas.save(); canvas.rotate(tileBox.getRotate(), c.x, c.y); drawBasemap(canvas); canvas.restore(); } } for (int i = 0; i < layers.size(); i++) { try { OsmandMapLayer layer = layers.get(i); canvas.save(); // rotate if needed if (!layer.drawInScreenPixels()) { canvas.rotate(tileBox.getRotate(), c.x, c.y); } layer.onDraw(canvas, tileBox, drawSettings); canvas.restore(); } catch (IndexOutOfBoundsException e) { // skip it } } if (showMapPosition) { canvas.drawCircle(c.x, c.y, 3 * dm.density, paintCenter); canvas.drawCircle(c.x, c.y, 7 * dm.density, paintCenter); } } public boolean mapIsRefreshing() { return handler.hasMessages(MAP_REFRESH_MESSAGE); } private void refreshBufferImage(final DrawSettings drawSettings) { if (!baseHandler.hasMessages(BASE_REFRESH_MESSAGE) || drawSettings.isUpdateVectorRendering()) { Message msg = Message.obtain( baseHandler, new Runnable() { @Override public void run() { baseHandler.removeMessages(BASE_REFRESH_MESSAGE); try { refreshBaseMapInternal(currentViewport.copy(), drawSettings); sendRefreshMapMsg(drawSettings, 0); } catch (Exception e) { log.error(e.getMessage(), e); } } }); msg.what = BASE_REFRESH_MESSAGE; // baseHandler.sendMessageDelayed(msg, 0); baseHandler.sendMessage(msg); } } // this method could be called in non UI thread public void refreshMap() { refreshMap(false); } // this method could be called in non UI thread public void refreshMap(final boolean updateVectorRendering) { if (isShown()) { boolean nightMode = application.getDaynightHelper().isNightMode(); DrawSettings drawSettings = new DrawSettings(nightMode, updateVectorRendering); sendRefreshMapMsg(drawSettings, 20); refreshBufferImage(drawSettings); } } private void sendRefreshMapMsg(final DrawSettings drawSettings, int delay) { if (!handler.hasMessages(MAP_REFRESH_MESSAGE) || drawSettings.isUpdateVectorRendering()) { Message msg = Message.obtain( handler, new Runnable() { @Override public void run() { handler.removeMessages(MAP_REFRESH_MESSAGE); refreshMapInternal(drawSettings); } }); msg.what = MAP_REFRESH_MESSAGE; if (delay > 0) { handler.sendMessageDelayed(msg, delay); } else { handler.sendMessage(msg); } } } @Override public void tileDownloaded(DownloadRequest request) { // force to refresh map because image can be loaded from different threads // and threads can block each other especially for sqlite images when they // are inserting into db they block main thread refreshMap(); } // ///////////////////////////////// DRAGGING PART /////////////////////////////////////// public net.osmand.data.RotatedTileBox getCurrentRotatedTileBox() { return currentViewport; } public float getDensity() { return currentViewport.getDensity(); } public float getScaleCoefficient() { float scaleCoefficient = getDensity(); if (Math.min(dm.widthPixels / (dm.density * 160), dm.heightPixels / (dm.density * 160)) > 2.5f) { // large screen scaleCoefficient *= 1.5f; } return scaleCoefficient; } /** These methods do not consider rotating */ protected void dragToAnimate(float fromX, float fromY, float toX, float toY, boolean notify) { float dx = (fromX - toX); float dy = (fromY - toY); moveTo(dx, dy); if (locationListener != null && notify) { locationListener.locationChanged(getLatitude(), getLongitude(), this); } } protected void rotateToAnimate(float rotate) { if (isMapRotateEnabled()) { this.rotate = MapUtils.unifyRotationTo360(rotate); currentViewport.setRotate(this.rotate); refreshMap(); } } protected void setLatLonAnimate(double latitude, double longitude, boolean notify) { currentViewport.setLatLonCenter(latitude, longitude); refreshMap(); if (locationListener != null && notify) { locationListener.locationChanged(latitude, longitude, this); } } protected void setZoomAnimate(int zoom, float zoomScale, boolean notify) { currentViewport.setZoom(zoom, zoomScale, 0); refreshMap(); if (locationListener != null && notify) { locationListener.locationChanged(getLatitude(), getLongitude(), this); } } // for internal usage protected void zoomToAnimate(float tzoom, boolean notify) { int zoom = getZoom(); float zoomToAnimate = tzoom - zoom - getZoomScale(); if (zoomToAnimate >= 1) { zoom += (int) zoomToAnimate; zoomToAnimate -= (int) zoomToAnimate; } while (zoomToAnimate < 0) { zoom--; zoomToAnimate += 1; } if (mainLayer != null && mainLayer.getMaximumShownMapZoom() >= zoom && mainLayer.getMinimumShownMapZoom() <= zoom) { currentViewport.setZoomAndAnimation(zoom, zoomToAnimate); currentViewport.setRotate(zoom > LOWEST_ZOOM_TO_ROTATE ? rotate : 0); refreshMap(); if (notify && locationListener != null) { locationListener.locationChanged(getLatitude(), getLongitude(), this); } } } public void moveTo(float dx, float dy) { final QuadPoint cp = currentViewport.getCenterPixelPoint(); final LatLon latlon = currentViewport.getLatLonFromPixel(cp.x + dx, cp.y + dy); currentViewport.setLatLonCenter(latlon.getLatitude(), latlon.getLongitude()); refreshMap(); // do not notify here listener } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { animatedDraggingThread.stopAnimating(); } for (int i = layers.size() - 1; i >= 0; i--) { if (layers.get(i).onTouchEvent(event, getCurrentRotatedTileBox())) { return true; } } if (!multiTouchSupport.onTouchEvent(event)) { /* return */ gestureDetector.onTouchEvent(event); } return true; } @Override public boolean onTrackballEvent(MotionEvent event) { if (trackBallDelegate != null) { trackBallDelegate.onTrackBallEvent(event); } return super.onTrackballEvent(event); } public void setTrackBallDelegate(OnTrackBallListener trackBallDelegate) { this.trackBallDelegate = trackBallDelegate; } public void setOnLongClickListener(OnLongClickListener l) { this.onLongClickListener = l; } public void setOnClickListener(OnClickListener l) { this.onClickListener = l; } public void setAccessibilityActions(AccessibilityActionsProvider actions) { accessibilityActions = actions; } public AnimateDraggingMapThread getAnimatedDraggingThread() { return animatedDraggingThread; } public void showMessage(final String msg) { handler.post( new Runnable() { @Override public void run() { AccessibleToast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show(); // $NON-NLS-1$ } }); } private class MapTileViewMultiTouchZoomListener implements MultiTouchZoomListener { private PointF initialMultiTouchCenterPoint; private RotatedTileBox initialViewport; private float x1; private float y1; private float x2; private float y2; private LatLon initialCenterLatLon; private boolean startRotating = false; private static final float ANGLE_THRESHOLD = 18; @Override public void onZoomEnded(double relativeToStart, float angleRelative) { // 1.5 works better even on dm.density=1 devices float dz = (float) (Math.log(relativeToStart) / Math.log(2)) * 1.5f; setIntZoom(Math.round(dz) + initialViewport.getZoom()); if (Math.abs(angleRelative) < ANGLE_THRESHOLD) { angleRelative = 0; } rotateToAnimate(initialViewport.getRotate() + angleRelative); final int newZoom = getZoom(); if (application.getInternalAPI().accessibilityEnabled()) { if (newZoom != initialViewport.getZoom()) { showMessage(getContext().getString(R.string.zoomIs) + " " + newZoom); // $NON-NLS-1$ } else { final LatLon p1 = initialViewport.getLatLonFromPixel(x1, y1); final LatLon p2 = initialViewport.getLatLonFromPixel(x2, y2); showMessage( OsmAndFormatter.getFormattedDistance( (float) MapUtils.getDistance( p1.getLatitude(), p1.getLongitude(), p2.getLatitude(), p2.getLongitude()), application)); } } } @Override public void onGestureInit(float x1, float y1, float x2, float y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } @Override public void onZoomStarted(PointF centerPoint) { initialMultiTouchCenterPoint = centerPoint; initialViewport = getCurrentRotatedTileBox().copy(); initialCenterLatLon = initialViewport.getLatLonFromPixel( initialMultiTouchCenterPoint.x, initialMultiTouchCenterPoint.y); startRotating = false; } @Override public void onZoomingOrRotating(double relativeToStart, float relAngle) { double dz = (Math.log(relativeToStart) / Math.log(2)) * 1.5; if (Math.abs(dz) <= 0.1) { // keep only rotating dz = 0; } if (Math.abs(relAngle) < ANGLE_THRESHOLD && !startRotating) { relAngle = 0; } else { startRotating = true; } if (dz != 0 || relAngle != 0) { changeZoomPosition((float) dz, relAngle); } } private void changeZoomPosition(float dz, float angle) { final QuadPoint cp = initialViewport.getCenterPixelPoint(); float dx = cp.x - initialMultiTouchCenterPoint.x; float dy = cp.y - initialMultiTouchCenterPoint.y; final RotatedTileBox calc = initialViewport.copy(); calc.setLatLonCenter(initialCenterLatLon.getLatitude(), initialCenterLatLon.getLongitude()); float calcZoom = initialViewport.getZoom() + dz + initialViewport.getZoomScale(); float calcRotate = calc.getRotate() + angle; calc.setRotate(calcRotate); calc.setZoomAnimation(dz); final LatLon r = calc.getLatLonFromPixel(cp.x + dx, cp.y + dy); setLatLon(r.getLatitude(), r.getLongitude()); zoomToAnimate(calcZoom, true); rotateToAnimate(calcRotate); } } private class MapTileViewOnGestureListener implements OnGestureListener { @Override public boolean onDown(MotionEvent e) { return false; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { animatedDraggingThread.startDragging( velocityX, velocityY, e1.getX(), e1.getY(), e2.getX(), e2.getY(), true); return true; } @Override public void onLongPress(MotionEvent e) { if (multiTouchSupport.isInZoomMode()) { return; } if (log.isDebugEnabled()) { log.debug("On long click event " + e.getX() + " " + e.getY()); // $NON-NLS-1$ //$NON-NLS-2$ } PointF point = new PointF(e.getX(), e.getY()); if ((accessibilityActions != null) && accessibilityActions.onLongClick(point, getCurrentRotatedTileBox())) { return; } for (int i = layers.size() - 1; i >= 0; i--) { if (layers.get(i).onLongPressEvent(point, getCurrentRotatedTileBox())) { return; } } if (onLongClickListener != null && onLongClickListener.onLongPressEvent(point)) { return; } } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { dragToAnimate(e2.getX() + distanceX, e2.getY() + distanceY, e2.getX(), e2.getY(), true); return true; } @Override public void onShowPress(MotionEvent e) {} @Override public boolean onSingleTapUp(MotionEvent e) { PointF point = new PointF(e.getX(), e.getY()); if (log.isDebugEnabled()) { log.debug("On click event " + point.x + " " + point.y); // $NON-NLS-1$ //$NON-NLS-2$ } if ((accessibilityActions != null) && accessibilityActions.onClick(point, getCurrentRotatedTileBox())) { return true; } for (int i = layers.size() - 1; i >= 0; i--) { if (layers.get(i).onSingleTap(point, getCurrentRotatedTileBox())) { return true; } } if (onClickListener != null && onClickListener.onPressEvent(point)) { return true; } return false; } } private class MapTileViewOnDoubleTapListener implements OnDoubleTapListener { @Override public boolean onDoubleTap(MotionEvent e) { final RotatedTileBox tb = getCurrentRotatedTileBox(); final double lat = tb.getLatFromPixel(e.getX(), e.getY()); final double lon = tb.getLonFromPixel(e.getX(), e.getY()); getAnimatedDraggingThread().startMoving(lat, lon, getZoom() + 1, true); return true; } @Override public boolean onDoubleTapEvent(MotionEvent e) { return false; } @Override public boolean onSingleTapConfirmed(MotionEvent e) { return false; } } }
/** * Resource manager is responsible to work with all resources that could consume memory (especially * with file resources). Such as indexes, tiles. Also it is responsible to create cache for that * resources if they can't be loaded fully into memory & clear them on request. */ public class ResourceManager { public static final String VECTOR_MAP = "#vector_map"; // $NON-NLS-1$ private static final String INDEXES_CACHE = "ind.cache"; private static final Log log = PlatformUtil.getLog(ResourceManager.class); protected static ResourceManager manager = null; // it is not good investigated but no more than 64 (satellite images) // Only 8 MB (from 16 Mb whole mem) available for images : image 64K * 128 = 8 MB (8 bit), 64 - 16 // bit, 32 - 32 bit // at least 3*9? protected int maxImgCacheSize = 28; protected Map<String, Bitmap> cacheOfImages = new LinkedHashMap<String, Bitmap>(); protected Map<String, Boolean> imagesOnFS = new LinkedHashMap<String, Boolean>(); protected File dirWithTiles; private final OsmandApplication context; private BusyIndicator busyIndicator; public interface ResourceWatcher { public boolean indexResource(File f); public List<String> getWatchWorkspaceFolder(); } // Indexes private final Map<String, RegionAddressRepository> addressMap = new ConcurrentHashMap<String, RegionAddressRepository>(); protected final Map<String, AmenityIndexRepository> amenityRepositories = new ConcurrentHashMap<String, AmenityIndexRepository>(); protected final Map<String, String> indexFileNames = new ConcurrentHashMap<String, String>(); protected final Map<String, String> basemapFileNames = new ConcurrentHashMap<String, String>(); protected final Map<String, BinaryMapIndexReader> routingMapFiles = new ConcurrentHashMap<String, BinaryMapIndexReader>(); protected final Map<String, TransportIndexRepository> transportRepositories = new ConcurrentHashMap<String, TransportIndexRepository>(); protected final IncrementalChangesManager changesManager = new IncrementalChangesManager(this); protected final MapRenderRepositories renderer; protected final MapTileDownloader tileDownloader; public final AsyncLoadingThread asyncLoadingThread = new AsyncLoadingThread(this); private HandlerThread renderingBufferImageThread; protected boolean internetIsNotAccessible = false; private java.text.DateFormat dateFormat; public ResourceManager(OsmandApplication context) { this.context = context; this.renderer = new MapRenderRepositories(context); asyncLoadingThread.start(); renderingBufferImageThread = new HandlerThread("RenderingBaseImage"); renderingBufferImageThread.start(); tileDownloader = MapTileDownloader.getInstance(Version.getFullVersion(context)); dateFormat = DateFormat.getDateFormat(context); resetStoreDirectory(); WindowManager mgr = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); mgr.getDefaultDisplay().getMetrics(dm); // Only 8 MB (from 16 Mb whole mem) available for images : image 64K * 128 = 8 MB (8 bit), 64 - // 16 bit, 32 - 32 bit // at least 3*9? float tiles = (dm.widthPixels / 256 + 2) * (dm.heightPixels / 256 + 2) * 3; log.info("Tiles to load in memory : " + tiles); maxImgCacheSize = (int) (tiles); } public MapTileDownloader getMapTileDownloader() { return tileDownloader; } public HandlerThread getRenderingBufferImageThread() { return renderingBufferImageThread; } public void resetStoreDirectory() { dirWithTiles = context.getAppPath(IndexConstants.TILES_INDEX_DIR); dirWithTiles.mkdirs(); // ".nomedia" indicates there are no pictures and no music to list in this dir for the Gallery // app try { context.getAppPath(".nomedia").createNewFile(); // $NON-NLS-1$ } catch (Exception e) { } } public java.text.DateFormat getDateFormat() { return dateFormat; } public OsmandApplication getContext() { return context; } ////////////////////////////////////////////// Working with tiles // //////////////////////////////////////////////// public Bitmap getTileImageForMapAsync( String file, ITileSource map, int x, int y, int zoom, boolean loadFromInternetIfNeeded) { return getTileImageForMap(file, map, x, y, zoom, loadFromInternetIfNeeded, false, true); } public synchronized Bitmap getTileImageFromCache(String file) { return cacheOfImages.get(file); } public synchronized void putTileInTheCache(String file, Bitmap bmp) { cacheOfImages.put(file, bmp); } public Bitmap getTileImageForMapSync( String file, ITileSource map, int x, int y, int zoom, boolean loadFromInternetIfNeeded) { return getTileImageForMap(file, map, x, y, zoom, loadFromInternetIfNeeded, true, true); } public synchronized void tileDownloaded(DownloadRequest request) { if (request instanceof TileLoadDownloadRequest) { TileLoadDownloadRequest req = ((TileLoadDownloadRequest) request); imagesOnFS.put(req.tileId, Boolean.TRUE); /* if(req.fileToSave != null && req.tileSource instanceof SQLiteTileSource){ try { ((SQLiteTileSource) req.tileSource).insertImage(req.xTile, req.yTile, req.zoom, req.fileToSave); } catch (IOException e) { log.warn("File "+req.fileToSave.getName() + " couldn't be read", e); //$NON-NLS-1$//$NON-NLS-2$ } req.fileToSave.delete(); String[] l = req.fileToSave.getParentFile().list(); if(l == null || l.length == 0){ req.fileToSave.getParentFile().delete(); l = req.fileToSave.getParentFile().getParentFile().list(); if(l == null || l.length == 0){ req.fileToSave.getParentFile().getParentFile().delete(); } } }*/ } } public synchronized boolean tileExistOnFileSystem( String file, ITileSource map, int x, int y, int zoom) { if (!imagesOnFS.containsKey(file)) { boolean ex = false; if (map instanceof SQLiteTileSource) { if (((SQLiteTileSource) map).isLocked()) { return false; } ex = ((SQLiteTileSource) map).exists(x, y, zoom); } else { if (file == null) { file = calculateTileId(map, x, y, zoom); } ex = new File(dirWithTiles, file).exists(); } if (ex) { imagesOnFS.put(file, Boolean.TRUE); } else { imagesOnFS.put(file, null); } } return imagesOnFS.get(file) != null || cacheOfImages.get(file) != null; } public void clearTileImageForMap(String file, ITileSource map, int x, int y, int zoom) { getTileImageForMap(file, map, x, y, zoom, true, false, true, true); } /** @param file - null could be passed if you do not call very often with that param */ protected Bitmap getTileImageForMap( String file, ITileSource map, int x, int y, int zoom, boolean loadFromInternetIfNeeded, boolean sync, boolean loadFromFs) { return getTileImageForMap( file, map, x, y, zoom, loadFromInternetIfNeeded, sync, loadFromFs, false); } // introduce cache in order save memory protected StringBuilder builder = new StringBuilder(40); protected char[] tileId = new char[120]; private GeoidAltitudeCorrection geoidAltitudeCorrection; private boolean searchAmenitiesInProgress; public synchronized String calculateTileId(ITileSource map, int x, int y, int zoom) { builder.setLength(0); if (map == null) { builder.append(IndexConstants.TEMP_SOURCE_TO_LOAD); } else { builder.append(map.getName()); } if (map instanceof SQLiteTileSource) { builder.append('@'); } else { builder.append('/'); } builder .append(zoom) .append('/') .append(x) .append('/') .append(y) .append(map == null ? ".jpg" : map.getTileFormat()) .append(".tile"); // $NON-NLS-1$ //$NON-NLS-2$ return builder.toString(); } protected synchronized Bitmap getTileImageForMap( String tileId, ITileSource map, int x, int y, int zoom, boolean loadFromInternetIfNeeded, boolean sync, boolean loadFromFs, boolean deleteBefore) { if (tileId == null) { tileId = calculateTileId(map, x, y, zoom); if (tileId == null) { return null; } } if (deleteBefore) { cacheOfImages.remove(tileId); if (map instanceof SQLiteTileSource) { ((SQLiteTileSource) map).deleteImage(x, y, zoom); } else { File f = new File(dirWithTiles, tileId); if (f.exists()) { f.delete(); } } imagesOnFS.put(tileId, null); } if (loadFromFs && cacheOfImages.get(tileId) == null && map != null) { boolean locked = map instanceof SQLiteTileSource && ((SQLiteTileSource) map).isLocked(); if (!loadFromInternetIfNeeded && !locked && !tileExistOnFileSystem(tileId, map, x, y, zoom)) { return null; } String url = loadFromInternetIfNeeded ? map.getUrlToLoad(x, y, zoom) : null; File toSave = null; if (url != null) { if (map instanceof SQLiteTileSource) { toSave = new File( dirWithTiles, calculateTileId(((SQLiteTileSource) map).getBase(), x, y, zoom)); } else { toSave = new File(dirWithTiles, tileId); } } TileLoadDownloadRequest req = new TileLoadDownloadRequest(dirWithTiles, url, toSave, tileId, map, x, y, zoom); if (sync) { return getRequestedImageTile(req); } else { asyncLoadingThread.requestToLoadImage(req); } } return cacheOfImages.get(tileId); } protected Bitmap getRequestedImageTile(TileLoadDownloadRequest req) { if (req.tileId == null || req.dirWithTiles == null) { return null; } Bitmap cacheBmp = cacheOfImages.get(req.tileId); if (cacheBmp != null) { return cacheBmp; } if (cacheOfImages.size() > maxImgCacheSize) { clearTiles(); } if (req.dirWithTiles.canRead() && !asyncLoadingThread.isFileCurrentlyDownloaded(req.fileToSave) && !asyncLoadingThread.isFilePendingToDownload(req.fileToSave)) { long time = System.currentTimeMillis(); if (log.isDebugEnabled()) { log.debug( "Start loaded file : " + req.tileId + " " + Thread.currentThread().getName()); // $NON-NLS-1$ //$NON-NLS-2$ } Bitmap bmp = null; if (req.tileSource instanceof SQLiteTileSource) { try { long[] tm = new long[1]; bmp = ((SQLiteTileSource) req.tileSource).getImage(req.xTile, req.yTile, req.zoom, tm); if (tm[0] != 0) { int ts = req.tileSource.getExpirationTimeMillis(); if (ts != -1 && req.url != null && time - tm[0] > ts) { asyncLoadingThread.requestToDownload(req); } } } catch (OutOfMemoryError e) { log.error("Out of memory error", e); // $NON-NLS-1$ clearTiles(); } } else { File en = new File(req.dirWithTiles, req.tileId); if (en.exists()) { try { bmp = BitmapFactory.decodeFile(en.getAbsolutePath()); int ts = req.tileSource.getExpirationTimeMillis(); if (ts != -1 && req.url != null && time - en.lastModified() > ts) { asyncLoadingThread.requestToDownload(req); } } catch (OutOfMemoryError e) { log.error("Out of memory error", e); // $NON-NLS-1$ clearTiles(); } } } if (bmp != null) { cacheOfImages.put(req.tileId, bmp); if (log.isDebugEnabled()) { log.debug( "Loaded file : " + req.tileId + " " + -(time - System.currentTimeMillis()) + " ms " + cacheOfImages.size()); // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } if (cacheOfImages.get(req.tileId) == null && req.url != null) { asyncLoadingThread.requestToDownload(req); } } return cacheOfImages.get(req.tileId); } ////////////////////////////////////////////// Working with indexes // //////////////////////////////////////////////// public List<String> reloadIndexesOnStart(AppInitializer progress, List<String> warnings) { close(); // check we have some assets to copy to sdcard warnings.addAll(checkAssets(progress)); progress.notifyEvent(InitEvents.ASSETS_COPIED); reloadIndexes(progress, warnings); progress.notifyEvent(InitEvents.MAPS_INITIALIZED); return warnings; } public List<String> reloadIndexes(IProgress progress, List<String> warnings) { geoidAltitudeCorrection = new GeoidAltitudeCorrection(context.getAppPath(null)); // do it lazy // indexingImageTiles(progress); warnings.addAll(indexingMaps(progress)); warnings.addAll(indexVoiceFiles(progress)); warnings.addAll(OsmandPlugin.onIndexingFiles(progress)); warnings.addAll(indexAdditionalMaps(progress)); return warnings; } public List<String> indexAdditionalMaps(IProgress progress) { return context.getAppCustomization().onIndexingFiles(progress, indexFileNames); } public List<String> indexVoiceFiles(IProgress progress) { File file = context.getAppPath(IndexConstants.VOICE_INDEX_DIR); file.mkdirs(); List<String> warnings = new ArrayList<String>(); if (file.exists() && file.canRead()) { File[] lf = file.listFiles(); if (lf != null) { for (File f : lf) { if (f.isDirectory()) { File conf = new File(f, "_config.p"); if (!conf.exists()) { conf = new File(f, "_ttsconfig.p"); } if (conf.exists()) { indexFileNames.put( f.getName(), dateFormat.format(conf.lastModified())); // $NON-NLS-1$ } } } } } return warnings; } private List<String> checkAssets(IProgress progress) { String fv = Version.getFullVersion(context); if (!fv.equalsIgnoreCase(context.getSettings().PREVIOUS_INSTALLED_VERSION.get())) { File applicationDataDir = context.getAppPath(null); applicationDataDir.mkdirs(); if (applicationDataDir.canWrite()) { try { progress.startTask(context.getString(R.string.installing_new_resources), -1); AssetManager assetManager = context.getAssets(); boolean isFirstInstall = context.getSettings().PREVIOUS_INSTALLED_VERSION.get().equals(""); unpackBundledAssets(assetManager, applicationDataDir, progress, isFirstInstall); context.getSettings().PREVIOUS_INSTALLED_VERSION.set(fv); copyRegionsBoundaries(); copyPoiTypes(); for (String internalStyle : context.getRendererRegistry().getInternalRenderers().keySet()) { File fl = context.getRendererRegistry().getFileForInternalStyle(internalStyle); if (fl.exists()) { context.getRendererRegistry().copyFileForInternalStyle(internalStyle); } } } catch (SQLiteException e) { log.error(e.getMessage(), e); } catch (IOException e) { log.error(e.getMessage(), e); } catch (XmlPullParserException e) { log.error(e.getMessage(), e); } } } return Collections.emptyList(); } private void copyRegionsBoundaries() { try { File file = context.getAppPath("regions.ocbf"); if (file != null) { FileOutputStream fout = new FileOutputStream(file); Algorithms.streamCopy(OsmandRegions.class.getResourceAsStream("regions.ocbf"), fout); fout.close(); } } catch (Exception e) { log.error(e.getMessage(), e); } } private void copyPoiTypes() { try { File file = context.getAppPath("poi_types.xml"); if (file != null) { FileOutputStream fout = new FileOutputStream(file); Algorithms.streamCopy(MapPoiTypes.class.getResourceAsStream("poi_types.xml"), fout); fout.close(); } } catch (Exception e) { log.error(e.getMessage(), e); } } private static final String ASSET_INSTALL_MODE__alwaysCopyOnFirstInstall = "alwaysCopyOnFirstInstall"; private static final String ASSET_COPY_MODE__overwriteOnlyIfExists = "overwriteOnlyIfExists"; private static final String ASSET_COPY_MODE__alwaysOverwriteOrCopy = "alwaysOverwriteOrCopy"; private static final String ASSET_COPY_MODE__copyOnlyIfDoesNotExist = "copyOnlyIfDoesNotExist"; private void unpackBundledAssets( AssetManager assetManager, File appDataDir, IProgress progress, boolean isFirstInstall) throws IOException, XmlPullParserException { XmlPullParser xmlParser = XmlPullParserFactory.newInstance().newPullParser(); InputStream isBundledAssetsXml = assetManager.open("bundled_assets.xml"); xmlParser.setInput(isBundledAssetsXml, "UTF-8"); 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"); final String combinedMode = xmlParser.getAttributeValue(null, "mode"); final String[] modes = combinedMode.split("\\|"); if (modes.length == 0) { log.error("Mode '" + combinedMode + "' is not valid"); continue; } String installMode = null; String copyMode = null; for (String mode : modes) { if (ASSET_INSTALL_MODE__alwaysCopyOnFirstInstall.equals(mode)) installMode = mode; else if (ASSET_COPY_MODE__overwriteOnlyIfExists.equals(mode) || ASSET_COPY_MODE__alwaysOverwriteOrCopy.equals(mode) || ASSET_COPY_MODE__copyOnlyIfDoesNotExist.equals(mode)) copyMode = mode; else log.error("Mode '" + mode + "' is unknown"); } final File destinationFile = new File(appDataDir, destination); boolean unconditional = false; if (installMode != null) unconditional = unconditional || (ASSET_INSTALL_MODE__alwaysCopyOnFirstInstall.equals(installMode) && isFirstInstall); if (copyMode == null) log.error("No copy mode was defined for " + source); unconditional = unconditional || ASSET_COPY_MODE__alwaysOverwriteOrCopy.equals(copyMode); boolean shouldCopy = unconditional; shouldCopy = shouldCopy || (ASSET_COPY_MODE__overwriteOnlyIfExists.equals(copyMode) && destinationFile.exists()); shouldCopy = shouldCopy || (ASSET_COPY_MODE__copyOnlyIfDoesNotExist.equals(copyMode) && !destinationFile.exists()); if (shouldCopy) copyAssets(assetManager, source, destinationFile); } } isBundledAssetsXml.close(); } public static void copyAssets(AssetManager assetManager, String assetName, File file) throws IOException { if (file.exists()) { Algorithms.removeAllFiles(file); } file.getParentFile().mkdirs(); InputStream is = assetManager.open(assetName, AssetManager.ACCESS_STREAMING); FileOutputStream out = new FileOutputStream(file); Algorithms.streamCopy(is, out); Algorithms.closeStream(out); Algorithms.closeStream(is); } private List<File> collectFiles(File dir, String ext, List<File> files) { if (dir.exists() && dir.canRead()) { File[] lf = dir.listFiles(); if (lf == null || lf.length == 0) { return files; } for (File f : lf) { if (f.getName().endsWith(ext)) { files.add(f); } } } return files; } private void renameRoadsFiles(ArrayList<File> files, File roadsPath) { Iterator<File> it = files.iterator(); while (it.hasNext()) { File f = it.next(); if (f.getName().endsWith("-roads" + IndexConstants.BINARY_MAP_INDEX_EXT)) { f.renameTo( new File( roadsPath, f.getName() .replace( "-roads" + IndexConstants.BINARY_MAP_INDEX_EXT, IndexConstants.BINARY_ROAD_MAP_INDEX_EXT))); } else if (f.getName().endsWith(IndexConstants.BINARY_ROAD_MAP_INDEX_EXT)) { f.renameTo(new File(roadsPath, f.getName())); } } } public List<String> indexingMaps(final IProgress progress) { long val = System.currentTimeMillis(); ArrayList<File> files = new ArrayList<File>(); File appPath = context.getAppPath(null); File roadsPath = context.getAppPath(IndexConstants.ROADS_INDEX_DIR); roadsPath.mkdirs(); collectFiles(appPath, IndexConstants.BINARY_MAP_INDEX_EXT, files); renameRoadsFiles(files, roadsPath); collectFiles(roadsPath, IndexConstants.BINARY_MAP_INDEX_EXT, files); if (!Version.isFreeVersion(context)) { collectFiles( context.getAppPath(IndexConstants.WIKI_INDEX_DIR), IndexConstants.BINARY_MAP_INDEX_EXT, files); } if (OsmandPlugin.getEnabledPlugin(SRTMPlugin.class) != null) { collectFiles( context.getAppPath(IndexConstants.SRTM_INDEX_DIR), IndexConstants.BINARY_MAP_INDEX_EXT, files); } if (context.getSettings().BETA_TESTING_LIVE_UPDATES.get()) { changesManager.collectChangesFiles( context.getAppPath(IndexConstants.LIVE_INDEX_DIR), IndexConstants.BINARY_MAP_INDEX_EXT, files); } Collections.sort(files, Algorithms.getFileVersionComparator()); List<String> warnings = new ArrayList<String>(); renderer.clearAllResources(); CachedOsmandIndexes cachedOsmandIndexes = new CachedOsmandIndexes(); File indCache = context.getAppPath(INDEXES_CACHE); if (indCache.exists()) { try { cachedOsmandIndexes.readFromFile(indCache, CachedOsmandIndexes.VERSION); } catch (Exception e) { log.error(e.getMessage(), e); } } File liveDir = context.getAppPath(IndexConstants.LIVE_INDEX_DIR); for (File f : files) { progress.startTask( context.getString(R.string.indexing_map) + " " + f.getName(), -1); // $NON-NLS-1$ try { BinaryMapIndexReader mapReader = null; try { mapReader = cachedOsmandIndexes.getReader(f); if (mapReader.getVersion() != IndexConstants.BINARY_MAP_VERSION) { mapReader = null; } if (mapReader != null) { renderer.initializeNewResource(progress, f, mapReader); } } catch (IOException e) { log.error(String.format("File %s could not be read", f.getName()), e); } if (mapReader == null || (Version.isFreeVersion(context) && (f.getName().contains("_wiki") || f.getName().contains(".wiki")))) { warnings.add( MessageFormat.format( context.getString(R.string.version_index_is_not_supported), f.getName())); // $NON-NLS-1$ } else { if (mapReader.isBasemap()) { basemapFileNames.put(f.getName(), f.getName()); } long dateCreated = mapReader.getDateCreated(); if (dateCreated == 0) { dateCreated = f.lastModified(); } if (f.getParentFile().getName().equals(liveDir.getName())) { boolean toUse = changesManager.index(f, dateCreated, mapReader); if (!toUse) { try { mapReader.close(); } catch (IOException e) { log.error(e.getMessage(), e); } continue; } } else { changesManager.indexMainMap(f, dateCreated); indexFileNames.put(f.getName(), dateFormat.format(dateCreated)); // $NON-NLS-1$ } if (!mapReader.getRegionNames().isEmpty()) { try { RandomAccessFile raf = new RandomAccessFile(f, "r"); // $NON-NLS-1$ RegionAddressRepositoryBinary rarb = new RegionAddressRepositoryBinary( this, new BinaryMapIndexReader(raf, mapReader), f.getName()); addressMap.put(f.getName(), rarb); } catch (IOException e) { log.error("Exception reading " + f.getAbsolutePath(), e); // $NON-NLS-1$ warnings.add( MessageFormat.format( context.getString(R.string.version_index_is_not_supported), f.getName())); // $NON-NLS-1$ } } if (mapReader.hasTransportData()) { try { RandomAccessFile raf = new RandomAccessFile(f, "r"); // $NON-NLS-1$ transportRepositories.put( f.getName(), new TransportIndexRepositoryBinary(new BinaryMapIndexReader(raf, mapReader))); } catch (IOException e) { log.error("Exception reading " + f.getAbsolutePath(), e); // $NON-NLS-1$ warnings.add( MessageFormat.format( context.getString(R.string.version_index_is_not_supported), f.getName())); // $NON-NLS-1$ } } if (mapReader.containsRouteData()) { try { RandomAccessFile raf = new RandomAccessFile(f, "r"); // $NON-NLS-1$ routingMapFiles.put(f.getName(), new BinaryMapIndexReader(raf, mapReader)); } catch (IOException e) { log.error("Exception reading " + f.getAbsolutePath(), e); // $NON-NLS-1$ warnings.add( MessageFormat.format( context.getString(R.string.version_index_is_not_supported), f.getName())); // $NON-NLS-1$ } } if (mapReader.containsPoiData()) { try { RandomAccessFile raf = new RandomAccessFile(f, "r"); // $NON-NLS-1$ amenityRepositories.put( f.getName(), new AmenityIndexRepositoryBinary(new BinaryMapIndexReader(raf, mapReader))); } catch (IOException e) { log.error("Exception reading " + f.getAbsolutePath(), e); // $NON-NLS-1$ warnings.add( MessageFormat.format( context.getString(R.string.version_index_is_not_supported), f.getName())); // $NON-NLS-1$ } } } } catch (SQLiteException e) { log.error("Exception reading " + f.getAbsolutePath(), e); // $NON-NLS-1$ warnings.add( MessageFormat.format( context.getString(R.string.version_index_is_not_supported), f.getName())); // $NON-NLS-1$ } catch (OutOfMemoryError oome) { log.error("Exception reading " + f.getAbsolutePath(), oome); // $NON-NLS-1$ warnings.add( MessageFormat.format( context.getString(R.string.version_index_is_big_for_memory), f.getName())); } } log.debug("All map files initialized " + (System.currentTimeMillis() - val) + " ms"); if (files.size() > 0 && (!indCache.exists() || indCache.canWrite())) { try { cachedOsmandIndexes.writeToFile(indCache); } catch (Exception e) { log.error("Index file could not be written", e); } } return warnings; } public void initMapBoundariesCacheNative() { File indCache = context.getAppPath(INDEXES_CACHE); if (indCache.exists()) { NativeOsmandLibrary nativeLib = NativeOsmandLibrary.getLoadedLibrary(); if (nativeLib != null) { nativeLib.initCacheMapFile(indCache.getAbsolutePath()); } } } ////////////////////////////////////////////// Working with amenities // //////////////////////////////////////////////// public List<Amenity> searchAmenities( SearchPoiTypeFilter filter, double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, final ResultMatcher<Amenity> matcher) { final List<Amenity> amenities = new ArrayList<Amenity>(); searchAmenitiesInProgress = true; try { if (!filter.isEmpty()) { for (AmenityIndexRepository index : amenityRepositories.values()) { if (index.checkContains(topLatitude, leftLongitude, bottomLatitude, rightLongitude)) { List<Amenity> r = index.searchAmenities( MapUtils.get31TileNumberY(topLatitude), MapUtils.get31TileNumberX(leftLongitude), MapUtils.get31TileNumberY(bottomLatitude), MapUtils.get31TileNumberX(rightLongitude), zoom, filter, matcher); if (r != null) { amenities.addAll(r); } } } } } finally { searchAmenitiesInProgress = false; } return amenities; } public List<Amenity> searchAmenitiesOnThePath( List<Location> locations, double radius, SearchPoiTypeFilter filter, ResultMatcher<Amenity> matcher) { searchAmenitiesInProgress = true; final List<Amenity> amenities = new ArrayList<Amenity>(); try { if (locations != null && locations.size() > 0) { List<AmenityIndexRepository> repos = new ArrayList<AmenityIndexRepository>(); double topLatitude = locations.get(0).getLatitude(); double bottomLatitude = locations.get(0).getLatitude(); double leftLongitude = locations.get(0).getLongitude(); double rightLongitude = locations.get(0).getLongitude(); for (Location l : locations) { topLatitude = Math.max(topLatitude, l.getLatitude()); bottomLatitude = Math.min(bottomLatitude, l.getLatitude()); leftLongitude = Math.min(leftLongitude, l.getLongitude()); rightLongitude = Math.max(rightLongitude, l.getLongitude()); } if (!filter.isEmpty()) { for (AmenityIndexRepository index : amenityRepositories.values()) { if (index.checkContains(topLatitude, leftLongitude, bottomLatitude, rightLongitude)) { repos.add(index); } } if (!repos.isEmpty()) { for (AmenityIndexRepository r : repos) { List<Amenity> res = r.searchAmenitiesOnThePath(locations, radius, filter, matcher); if (res != null) { amenities.addAll(res); } } } } } } finally { searchAmenitiesInProgress = false; } return amenities; } public boolean containsAmenityRepositoryToSearch(boolean searchByName) { for (AmenityIndexRepository index : amenityRepositories.values()) { if (searchByName) { if (index instanceof AmenityIndexRepositoryBinary) { return true; } } else { return true; } } return false; } public List<Amenity> searchAmenitiesByName( String searchQuery, double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, double lat, double lon, ResultMatcher<Amenity> matcher) { List<Amenity> amenities = new ArrayList<Amenity>(); List<AmenityIndexRepositoryBinary> list = new ArrayList<AmenityIndexRepositoryBinary>(); for (AmenityIndexRepository index : amenityRepositories.values()) { if (index instanceof AmenityIndexRepositoryBinary) { if (index.checkContains(topLatitude, leftLongitude, bottomLatitude, rightLongitude)) { if (index.checkContains(lat, lon)) { list.add(0, (AmenityIndexRepositoryBinary) index); } else { list.add((AmenityIndexRepositoryBinary) index); } } } } // int left = MapUtils.get31TileNumberX(leftLongitude); // int top = MapUtils.get31TileNumberY(topLatitude); // int right = MapUtils.get31TileNumberX(rightLongitude); // int bottom = MapUtils.get31TileNumberY(bottomLatitude); int left = 0; int top = 0; int right = Integer.MAX_VALUE; int bottom = Integer.MAX_VALUE; for (AmenityIndexRepositoryBinary index : list) { if (matcher != null && matcher.isCancelled()) { break; } List<Amenity> result = index.searchAmenitiesByName( MapUtils.get31TileNumberX(lon), MapUtils.get31TileNumberY(lat), left, top, right, bottom, searchQuery, matcher); amenities.addAll(result); } return amenities; } public Map<PoiCategory, List<String>> searchAmenityCategoriesByName( String searchQuery, double lat, double lon) { Map<PoiCategory, List<String>> map = new LinkedHashMap<PoiCategory, List<String>>(); for (AmenityIndexRepository index : amenityRepositories.values()) { if (index instanceof AmenityIndexRepositoryBinary) { if (index.checkContains(lat, lon)) { ((AmenityIndexRepositoryBinary) index).searchAmenityCategoriesByName(searchQuery, map); } } } return map; } ////////////////////////////////////////////// Working with address // /////////////////////////////////////////// public RegionAddressRepository getRegionRepository(String name) { return addressMap.get(name); } public Collection<RegionAddressRepository> getAddressRepositories() { return addressMap.values(); } ////////////////////////////////////////////// Working with transport // //////////////////////////////////////////////// public List<TransportIndexRepository> searchTransportRepositories( double latitude, double longitude) { List<TransportIndexRepository> repos = new ArrayList<TransportIndexRepository>(); for (TransportIndexRepository index : transportRepositories.values()) { if (index.checkContains(latitude, longitude)) { repos.add(index); } } return repos; } public void searchTransportAsync( double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, List<TransportStop> toFill) { List<TransportIndexRepository> repos = new ArrayList<TransportIndexRepository>(); for (TransportIndexRepository index : transportRepositories.values()) { if (index.checkContains(topLatitude, leftLongitude, bottomLatitude, rightLongitude)) { if (!index.checkCachedObjects( topLatitude, leftLongitude, bottomLatitude, rightLongitude, zoom, toFill, true)) { repos.add(index); } } } if (!repos.isEmpty()) { TransportLoadRequest req = asyncLoadingThread.new TransportLoadRequest(repos, zoom); req.setBoundaries(topLatitude, leftLongitude, bottomLatitude, rightLongitude); asyncLoadingThread.requestToLoadTransport(req); } } ////////////////////////////////////////////// Working with map // //////////////////////////////////////////////// public boolean updateRenderedMapNeeded(RotatedTileBox rotatedTileBox, DrawSettings drawSettings) { return renderer.updateMapIsNeeded(rotatedTileBox, drawSettings); } public void updateRendererMap(RotatedTileBox rotatedTileBox) { renderer.interruptLoadingMap(); asyncLoadingThread.requestToLoadMap(new MapLoadRequest(rotatedTileBox)); } public void interruptRendering() { renderer.interruptLoadingMap(); } public boolean isSearchAmenitiesInProgress() { return searchAmenitiesInProgress; } public MapRenderRepositories getRenderer() { return renderer; } ////////////////////////////////////////////// Closing methods // //////////////////////////////////////////////// public void closeFile(String fileName) { AmenityIndexRepository rep = amenityRepositories.remove(fileName); if (rep != null) { rep.close(); } RegionAddressRepository rar = addressMap.remove(fileName); if (rar != null) { rar.close(); } TransportIndexRepository tir = transportRepositories.remove(fileName); if (tir != null) { tir.close(); } BinaryMapIndexReader rmp = routingMapFiles.remove(fileName); if (rmp != null) { try { rmp.close(); } catch (IOException e) { log.error(e, e); } } renderer.closeConnection(fileName); } public void closeAmenities() { for (AmenityIndexRepository r : amenityRepositories.values()) { r.close(); } amenityRepositories.clear(); } public void closeAddresses() { for (RegionAddressRepository r : addressMap.values()) { r.close(); } addressMap.clear(); } public void closeTransport() { for (TransportIndexRepository r : transportRepositories.values()) { r.close(); } transportRepositories.clear(); } public BusyIndicator getBusyIndicator() { return busyIndicator; } public synchronized void setBusyIndicator(BusyIndicator busyIndicator) { this.busyIndicator = busyIndicator; } public synchronized void close() { imagesOnFS.clear(); indexFileNames.clear(); basemapFileNames.clear(); renderer.clearAllResources(); closeAmenities(); closeRouteFiles(); closeAddresses(); closeTransport(); } public BinaryMapIndexReader[] getRoutingMapFiles() { return routingMapFiles.values().toArray(new BinaryMapIndexReader[routingMapFiles.size()]); } public void closeRouteFiles() { List<String> map = new ArrayList<String>(routingMapFiles.keySet()); for (String m : map) { try { BinaryMapIndexReader ind = routingMapFiles.remove(m); if (ind != null) { ind.getRaf().close(); } } catch (IOException e) { log.error("Error closing resource " + m, e); } } } public Map<String, String> getIndexFileNames() { return new LinkedHashMap<String, String>(indexFileNames); } public boolean containsBasemap() { return !basemapFileNames.isEmpty(); } public Map<String, String> getBackupIndexes(Map<String, String> map) { File file = context.getAppPath(IndexConstants.BACKUP_INDEX_DIR); if (file != null && file.isDirectory()) { File[] lf = file.listFiles(); if (lf != null) { for (File f : lf) { if (f != null && f.getName().endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { map.put( f.getName(), AndroidUtils.formatDate(context, f.lastModified())); // $NON-NLS-1$ } } } } return map; } public synchronized void reloadTilesFromFS() { imagesOnFS.clear(); } /// On low memory method /// public void onLowMemory() { log.info("On low memory : cleaning tiles - size = " + cacheOfImages.size()); // $NON-NLS-1$ clearTiles(); for (RegionAddressRepository r : addressMap.values()) { r.clearCache(); } renderer.clearCache(); System.gc(); } public GeoidAltitudeCorrection getGeoidAltitudeCorrection() { return geoidAltitudeCorrection; } public OsmandRegions getOsmandRegions() { return context.getRegions(); } protected synchronized void clearTiles() { log.info("Cleaning tiles - size = " + cacheOfImages.size()); // $NON-NLS-1$ ArrayList<String> list = new ArrayList<String>(cacheOfImages.keySet()); // remove first images (as we think they are older) for (int i = 0; i < list.size() / 2; i++) { cacheOfImages.remove(list.get(i)); } } public IncrementalChangesManager getChangesManager() { return changesManager; } }
public class OsmandApplication extends Application implements ClientContext { public static final String EXCEPTION_PATH = "exception.log"; // $NON-NLS-1$ private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(OsmandApplication.class); ResourceManager manager = null; PoiFiltersHelper poiFilters = null; RoutingHelper routingHelper = null; FavouritesDbHelper favorites = null; CommandPlayer player = null; OsmandSettings osmandSettings = null; DayNightHelper daynightHelper; NavigationService navigationService; RendererRegistry rendererRegistry; OsmAndLocationProvider locationProvider; OsmAndTaskManager taskManager; // start variables private ProgressDialogImplementation startDialog; private List<String> startingWarnings; private Handler uiHandler; private GPXFile gpxFileToDisplay; private SavingTrackHelper savingTrackHelper; private LiveMonitoringHelper liveMonitoringHelper; private TargetPointsHelper targetPointsHelper; private boolean applicationInitializing = false; private Locale prefferedLocale = null; SettingsAPI settingsAPI; ExternalServiceAPI externalServiceAPI; InternalToDoAPI internalToDoAPI; InternalOsmAndAPI internalOsmAndAPI; SQLiteAPI sqliteAPI; @Override public void onCreate() { long timeToStart = System.currentTimeMillis(); super.onCreate(); settingsAPI = new net.osmand.plus.api.SettingsAPIImpl(this); externalServiceAPI = new net.osmand.plus.api.ExternalServiceAPIImpl(this); internalToDoAPI = new net.osmand.plus.api.InternalToDoAPIImpl(this); internalOsmAndAPI = new net.osmand.plus.api.InternalOsmAndAPIImpl(this); sqliteAPI = new SQLiteAPIImpl(this); // settings used everywhere so they need to be created first osmandSettings = createOsmandSettingsInstance(); // always update application mode to default osmandSettings.APPLICATION_MODE.set(osmandSettings.DEFAULT_APPLICATION_MODE.get()); routingHelper = new RoutingHelper(this, player); taskManager = new OsmAndTaskManager(this); manager = new ResourceManager(this); daynightHelper = new DayNightHelper(this); locationProvider = new OsmAndLocationProvider(this); savingTrackHelper = new SavingTrackHelper(this); liveMonitoringHelper = new LiveMonitoringHelper(this); uiHandler = new Handler(); rendererRegistry = new RendererRegistry(); targetPointsHelper = new TargetPointsHelper(this); checkPrefferedLocale(); startApplication(); if (LOG.isDebugEnabled()) { LOG.debug( "Time to start application " + (System.currentTimeMillis() - timeToStart) + " ms. Should be less < 800 ms"); } timeToStart = System.currentTimeMillis(); OsmandPlugin.initPlugins(this); if (LOG.isDebugEnabled()) { LOG.debug( "Time to init plugins " + (System.currentTimeMillis() - timeToStart) + " ms. Should be less < 800 ms"); } } @Override public void onTerminate() { super.onTerminate(); if (routingHelper != null) { routingHelper.getVoiceRouter().onApplicationTerminate(this); } } public RendererRegistry getRendererRegistry() { return rendererRegistry; } public OsmAndTaskManager getTaskManager() { return taskManager; } /** * Creates instance of OsmandSettings * * @return Reference to instance of OsmandSettings */ protected OsmandSettings createOsmandSettingsInstance() { return new OsmandSettings(this); } public OsmAndLocationProvider getLocationProvider() { return locationProvider; } /** * Application settings * * @return Reference to instance of OsmandSettings */ public OsmandSettings getSettings() { if (osmandSettings == null) { LOG.error("Trying to access settings before they were created"); } return osmandSettings; } public SavingTrackHelper getSavingTrackHelper() { return savingTrackHelper; } public LiveMonitoringHelper getLiveMonitoringHelper() { return liveMonitoringHelper; } public PoiFiltersHelper getPoiFilters() { if (poiFilters == null) { poiFilters = new PoiFiltersHelper(this); } return poiFilters; } public void setGpxFileToDisplay(GPXFile gpxFileToDisplay, boolean showCurrentGpxFile) { this.gpxFileToDisplay = gpxFileToDisplay; osmandSettings.SHOW_CURRENT_GPX_TRACK.set(showCurrentGpxFile); if (gpxFileToDisplay == null) { getFavorites().setFavoritePointsFromGPXFile(null); } else { List<FavouritePoint> pts = new ArrayList<FavouritePoint>(); for (WptPt p : gpxFileToDisplay.points) { FavouritePoint pt = new FavouritePoint(); pt.setLatitude(p.lat); pt.setLongitude(p.lon); if (p.name == null) { p.name = ""; } pt.setName(p.name); pts.add(pt); } gpxFileToDisplay.proccessPoints(); getFavorites().setFavoritePointsFromGPXFile(pts); } } public GPXFile getGpxFileToDisplay() { return gpxFileToDisplay; } public FavouritesDbHelper getFavorites() { if (favorites == null) { favorites = new FavouritesDbHelper(this); } return favorites; } public ResourceManager getResourceManager() { return manager; } public DayNightHelper getDaynightHelper() { return daynightHelper; } @Override public void onLowMemory() { super.onLowMemory(); manager.onLowMemory(); } @Override public void onConfigurationChanged(Configuration newConfig) { if (prefferedLocale != null && !newConfig.locale.getLanguage().equals(prefferedLocale.getLanguage())) { super.onConfigurationChanged(newConfig); // ugly fix ! On devices after 4.0 screen is blinking when you rotate device! if (Build.VERSION.SDK_INT < 14) { newConfig.locale = prefferedLocale; } getBaseContext() .getResources() .updateConfiguration(newConfig, getBaseContext().getResources().getDisplayMetrics()); Locale.setDefault(prefferedLocale); } else { super.onConfigurationChanged(newConfig); } } public void checkPrefferedLocale() { Configuration config = getBaseContext().getResources().getConfiguration(); String lang = osmandSettings.PREFERRED_LOCALE.get(); if (!"".equals(lang) && !config.locale.getLanguage().equals(lang)) { prefferedLocale = new Locale(lang); Locale.setDefault(prefferedLocale); config.locale = prefferedLocale; getBaseContext() .getResources() .updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); } } public static final int PROGRESS_DIALOG = 5; /** * @param activity that supports onCreateDialog({@link #PROGRESS_DIALOG}) and returns @param * progressdialog * @param progressDialog - it should be exactly the same as onCreateDialog * @return */ public void checkApplicationIsBeingInitialized(Activity activity, ProgressDialog progressDialog) { // start application if it was previously closed startApplication(); synchronized (OsmandApplication.this) { if (startDialog != null) { try { SpecialPhrases.setLanguage(this, osmandSettings); } catch (IOException e) { LOG.error("I/O exception", e); Toast error = Toast.makeText( this, "Error while reading the special phrases. Restart OsmAnd if possible", Toast.LENGTH_LONG); error.show(); } progressDialog.setTitle(getString(R.string.loading_data)); progressDialog.setMessage(getString(R.string.reading_indexes)); activity.showDialog(PROGRESS_DIALOG); startDialog.setDialog(progressDialog); } else if (startingWarnings != null) { showWarnings(startingWarnings, activity); } } } public boolean isApplicationInitializing() { return startDialog != null; } public RoutingHelper getRoutingHelper() { return routingHelper; } public CommandPlayer getPlayer() { return player; } public void showDialogInitializingCommandPlayer(final Activity uiContext) { showDialogInitializingCommandPlayer(uiContext, true); } public void showDialogInitializingCommandPlayer( final Activity uiContext, boolean warningNoneProvider) { showDialogInitializingCommandPlayer(uiContext, warningNoneProvider, null); } public void showDialogInitializingCommandPlayer( final Activity uiContext, boolean warningNoneProvider, Runnable run) { String voiceProvider = osmandSettings.VOICE_PROVIDER.get(); if (voiceProvider == null || OsmandSettings.VOICE_PROVIDER_NOT_USE.equals(voiceProvider)) { if (warningNoneProvider && voiceProvider == null) { Builder builder = new AlertDialog.Builder(uiContext); builder.setCancelable(true); builder.setNegativeButton(R.string.default_buttons_cancel, null); builder.setPositiveButton( R.string.default_buttons_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(uiContext, SettingsActivity.class); intent.putExtra( SettingsActivity.INTENT_KEY_SETTINGS_SCREEN, SettingsActivity.SCREEN_NAVIGATION_SETTINGS); uiContext.startActivity(intent); } }); builder.setTitle(R.string.voice_is_not_available_title); builder.setMessage(R.string.voice_is_not_available_msg); builder.show(); } } else { if (player == null || !Algorithms.objectEquals(voiceProvider, player.getCurrentVoice())) { initVoiceDataInDifferentThread(uiContext, voiceProvider, run); } } } private void initVoiceDataInDifferentThread( final Activity uiContext, final String voiceProvider, final Runnable run) { final ProgressDialog dlg = ProgressDialog.show( uiContext, getString(R.string.loading_data), getString(R.string.voice_data_initializing)); new Thread( new Runnable() { @Override public void run() { try { if (player != null) { player.clear(); } player = CommandPlayerFactory.createCommandPlayer( voiceProvider, OsmandApplication.this, uiContext); routingHelper.getVoiceRouter().setPlayer(player); dlg.dismiss(); if (run != null && uiContext != null) { uiContext.runOnUiThread(run); } } catch (CommandPlayerException e) { dlg.dismiss(); showWarning(uiContext, e.getError()); } } }) .start(); } public NavigationService getNavigationService() { return navigationService; } public void setNavigationService(NavigationService navigationService) { this.navigationService = navigationService; } private void fullExit() { // http://stackoverflow.com/questions/2092951/how-to-close-android-application System.runFinalizersOnExit(true); System.exit(0); } public synchronized void closeApplication(final Activity activity) { if (getNavigationService() != null) { Builder bld = new AlertDialog.Builder(activity); bld.setMessage(R.string.background_service_is_enabled_question); bld.setPositiveButton( R.string.default_buttons_yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { closeApplicationAnyway(activity, true); } }); bld.setNegativeButton( R.string.default_buttons_no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { closeApplicationAnyway(activity, false); } }); bld.show(); } else { closeApplicationAnyway(activity, true); } } private void closeApplicationAnyway(final Activity activity, boolean disableService) { if (applicationInitializing) { manager.close(); } applicationInitializing = false; activity.finish(); if (getNavigationService() == null) { fullExit(); } else if (disableService) { final Intent serviceIntent = new Intent(this, NavigationService.class); stopService(serviceIntent); new Thread( new Runnable() { public void run() { // wait until the service has fully stopped while (getNavigationService() != null) { try { Thread.sleep(100); } catch (InterruptedException e) { } } fullExit(); } }) .start(); } } public synchronized void startApplication() { if (applicationInitializing) { return; } applicationInitializing = true; startDialog = new ProgressDialogImplementation(this, null, false); startDialog.setRunnable( "Initializing app", new Runnable() { //$NON-NLS-1$ @Override public void run() { startApplicationBackground(); } }); startDialog.run(); Thread.setDefaultUncaughtExceptionHandler(new DefaultExceptionHandler()); } public String exportFavorites(File f) { GPXFile gpx = new GPXFile(); for (FavouritePoint p : getFavorites().getFavouritePoints()) { if (p.isStored()) { WptPt pt = new WptPt(); pt.lat = p.getLatitude(); pt.lon = p.getLongitude(); pt.name = p.getName() + "_" + p.getCategory(); gpx.points.add(pt); } } return GPXUtilities.writeGpxFile(f, gpx, this); } private void startApplicationBackground() { List<String> warnings = new ArrayList<String>(); try { if (!Version.isBlackberry(this)) { if (osmandSettings.NATIVE_RENDERING_FAILED.get()) { osmandSettings.SAFE_MODE.set(true); osmandSettings.NATIVE_RENDERING_FAILED.set(false); warnings.add(getString(R.string.native_library_not_supported)); } else if (!osmandSettings.SAFE_MODE.get()) { osmandSettings.NATIVE_RENDERING_FAILED.set(true); startDialog.startTask(getString(R.string.init_native_library), -1); RenderingRulesStorage storage = rendererRegistry.getCurrentSelectedRenderer(); boolean initialized = NativeOsmandLibrary.getLibrary(storage, this) != null; osmandSettings.NATIVE_RENDERING_FAILED.set(false); if (!initialized) { LOG.info("Native library could not be loaded!"); } } else { warnings.add(getString(R.string.native_library_not_running)); } } warnings.addAll(manager.reloadIndexes(startDialog)); player = null; if (savingTrackHelper.hasDataToSave()) { startDialog.startTask(getString(R.string.saving_gpx_tracks), -1); warnings.addAll(savingTrackHelper.saveDataToGpx()); } // restore backuped favorites to normal file final File appDir = getAppPath(null); File save = new File(appDir, FavouritesDbHelper.FILE_TO_SAVE); File bak = new File(appDir, FavouritesDbHelper.FILE_TO_BACKUP); if (bak.exists() && (!save.exists() || bak.lastModified() > save.lastModified())) { if (save.exists()) { save.delete(); } bak.renameTo(save); } } finally { synchronized (OsmandApplication.this) { final ProgressDialog toDismiss; if (startDialog != null) { toDismiss = startDialog.getDialog(); } else { toDismiss = null; } startDialog = null; if (toDismiss != null) { uiHandler.post( new Runnable() { @Override public void run() { if (toDismiss != null) { // TODO handling this dialog is bad, we need a better standard way toDismiss.dismiss(); // toDismiss.getOwnerActivity().dismissDialog(PROGRESS_DIALOG); } } }); showWarnings(warnings, toDismiss.getContext()); } else { startingWarnings = warnings; } } } } protected void showWarnings(List<String> warnings, final Context uiContext) { if (warnings != null && !warnings.isEmpty()) { final StringBuilder b = new StringBuilder(); boolean f = true; for (String w : warnings) { if (f) { f = false; } else { b.append('\n'); } b.append(w); } showWarning(uiContext, b.toString()); } } private void showWarning(final Context uiContext, final String b) { uiHandler.post( new Runnable() { @Override public void run() { AccessibleToast.makeText(uiContext, b.toString(), Toast.LENGTH_LONG).show(); } }); } private class DefaultExceptionHandler implements UncaughtExceptionHandler { private UncaughtExceptionHandler defaultHandler; private PendingIntent intent; public DefaultExceptionHandler() { defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); intent = PendingIntent.getActivity( OsmandApplication.this.getBaseContext(), 0, new Intent( OsmandApplication.this.getBaseContext(), OsmandIntents.getMainMenuActivity()), 0); } @Override public void uncaughtException(final Thread thread, final Throwable ex) { File file = getAppPath(EXCEPTION_PATH); try { ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream printStream = new PrintStream(out); ex.printStackTrace(printStream); StringBuilder msg = new StringBuilder(); msg.append("Version " + Version.getFullVersion(OsmandApplication.this) + "\n") . //$NON-NLS-1$ append(DateFormat.format("dd.MM.yyyy h:mm:ss", System.currentTimeMillis())); try { PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0); if (info != null) { msg.append("\nApk Version : ") .append(info.versionName) .append(" ") .append(info.versionCode); // $NON-NLS-1$ //$NON-NLS-2$ } } catch (Throwable e) { } msg.append("\n") . //$NON-NLS-1$//$NON-NLS-2$ append("Exception occured in thread " + thread.toString() + " : \n") . //$NON-NLS-1$ //$NON-NLS-2$ append(new String(out.toByteArray())); if (file.getParentFile().canWrite()) { BufferedWriter writer = new BufferedWriter(new FileWriter(file, true)); writer.write(msg.toString()); writer.close(); } if (routingHelper.isFollowingMode()) { AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 2000, intent); System.exit(2); } defaultHandler.uncaughtException(thread, ex); } catch (Exception e) { // swallow all exceptions android.util.Log.e( PlatformUtil.TAG, "Exception while handle other exception", e); // $NON-NLS-1$ } } } public TargetPointsHelper getTargetPointsHelper() { return targetPointsHelper; } @Override public void showShortToastMessage(int msgId, Object... args) { AccessibleToast.makeText(this, getString(msgId, args), Toast.LENGTH_SHORT).show(); } @Override public void showToastMessage(int msgId, Object... args) { AccessibleToast.makeText(this, getString(msgId, args), Toast.LENGTH_LONG).show(); } @Override public void showToastMessage(String msg) { AccessibleToast.makeText(this, msg, Toast.LENGTH_LONG).show(); } @Override public SettingsAPI getSettingsAPI() { return settingsAPI; } @Override public ExternalServiceAPI getExternalServiceAPI() { return externalServiceAPI; } @Override public InternalToDoAPI getTodoAPI() { return internalToDoAPI; } @Override public InternalOsmAndAPI getInternalAPI() { return internalOsmAndAPI; } @Override public SQLiteAPI getSQLiteAPI() { return sqliteAPI; } @Override public void runInUIThread(Runnable run) { uiHandler.post(run); } @Override public void runInUIThread(Runnable run, long delay) { uiHandler.postDelayed(run, delay); } public void runMessageInUIThreadAndCancelPrevious( final int messageId, final Runnable run, long delay) { Message msg = Message.obtain( uiHandler, new Runnable() { @Override public void run() { if (!uiHandler.hasMessages(messageId)) { run.run(); } } }); msg.what = messageId; uiHandler.removeMessages(messageId); uiHandler.sendMessageDelayed(msg, delay); } @Override public File getAppPath(String path) { if (path == null) { path = ""; } return new File(getSettings().getExternalStorageDirectory(), IndexConstants.APP_DIR + path); } @Override public Location getLastKnownLocation() { return locationProvider.getLastKnownLocation(); } }
public class RoutingHelper { private static final org.apache.commons.logging.Log log = PlatformUtil.getLog(RoutingHelper.class); public static interface IRouteInformationListener { public void newRouteIsCalculated(boolean newRoute); public void routeWasCancelled(); } private static final float POSITION_TOLERANCE = 60; private List<IRouteInformationListener> listeners = new ArrayList<IRouteInformationListener>(); private OsmandApplication app; private boolean isFollowingMode = false; private boolean isRoutePlanningMode = false; private GPXRouteParamsBuilder currentGPXRoute = null; private RouteCalculationResult route = new RouteCalculationResult(""); private LatLon finalLocation; private List<LatLon> intermediatePoints; private Location lastProjection; private Location lastFixedLocation; private Thread currentRunningJob; private long lastTimeEvaluatedRoute = 0; private int evalWaitInterval = 3000; private ApplicationMode mode; private OsmandSettings settings; private RouteProvider provider = new RouteProvider(); private VoiceRouter voiceRouter; private boolean makeUturnWhenPossible = false; private long makeUTwpDetected = 0; // private long wrongMovementDetected = 0; private RouteCalculationProgressCallback progressRoute; // private ProgressBar progress; // private Handler progressHandler; public boolean makeUturnWhenPossible() { return makeUturnWhenPossible; } public RoutingHelper(OsmandApplication context, CommandPlayer player) { this.app = context; settings = context.getSettings(); voiceRouter = new VoiceRouter(this, settings, player); } public boolean isFollowingMode() { return isFollowingMode; } public void setFollowingMode(boolean follow) { isFollowingMode = follow; if (!follow) { if (app.getNavigationService() != null) { app.getNavigationService().stopIfNeeded(app, NavigationService.USED_BY_NAVIGATION); } } else { app.startNavigationService(NavigationService.USED_BY_NAVIGATION); } } public boolean isRoutePlanningMode() { return isRoutePlanningMode; } public void setRoutePlanningMode(boolean isRoutePlanningMode) { this.isRoutePlanningMode = isRoutePlanningMode; } public synchronized void setFinalAndCurrentLocation( LatLon finalLocation, List<LatLon> intermediatePoints, Location currentLocation) { RouteCalculationResult previousRoute = route; clearCurrentRoute(finalLocation, intermediatePoints); // to update route setCurrentLocation(currentLocation, false, previousRoute); } public synchronized void clearCurrentRoute( LatLon newFinalLocation, List<LatLon> newIntermediatePoints) { route = new RouteCalculationResult(""); makeUturnWhenPossible = false; evalWaitInterval = 3000; app.runInUIThread( new Runnable() { @Override public void run() { for (IRouteInformationListener l : listeners) { l.routeWasCancelled(); } } }); this.finalLocation = newFinalLocation; this.intermediatePoints = newIntermediatePoints; if (currentRunningJob instanceof RouteRecalculationThread) { ((RouteRecalculationThread) currentRunningJob).stopCalculation(); } if (newFinalLocation == null) { settings.FOLLOW_THE_ROUTE.set(false); settings.FOLLOW_THE_GPX_ROUTE.set(null); // clear last fixed location this.lastProjection = null; setFollowingMode(false); } } public GPXRouteParamsBuilder getCurrentGPXRoute() { return currentGPXRoute; } public void setGpxParams(GPXRouteParamsBuilder params) { currentGPXRoute = params; } public List<Location> getCurrentCalculatedRoute() { return route.getImmutableAllLocations(); } public void setAppMode(ApplicationMode mode) { this.mode = mode; voiceRouter.updateAppMode(); } public ApplicationMode getAppMode() { return mode; } public LatLon getFinalLocation() { return finalLocation; } public List<LatLon> getIntermediatePoints() { return intermediatePoints; } public boolean isRouteCalculated() { return route.isCalculated(); } public VoiceRouter getVoiceRouter() { return voiceRouter; } public Location getLastProjection() { return lastProjection; } public void addListener(IRouteInformationListener l) { listeners.add(l); } public boolean removeListener(IRouteInformationListener l) { return listeners.remove(l); } public void updateLocation(Location currentLocation) { if (isFollowingMode() || (settings.getPointToStart() == null && isRoutePlanningMode) || app.getLocationProvider().getLocationSimulation().isRouteAnimating()) { setCurrentLocation(currentLocation, false); } } public Location setCurrentLocation(Location currentLocation, boolean returnUpdatedLocation) { return setCurrentLocation(currentLocation, returnUpdatedLocation, route); } private Location setCurrentLocation( Location currentLocation, boolean returnUpdatedLocation, RouteCalculationResult previousRoute) { Location locationProjection = currentLocation; if (finalLocation == null || currentLocation == null) { makeUturnWhenPossible = false; return locationProjection; } float posTolerance = POSITION_TOLERANCE; if (currentLocation.hasAccuracy()) { posTolerance = POSITION_TOLERANCE / 2 + currentLocation.getAccuracy(); } boolean calculateRoute = false; synchronized (this) { // 0. Route empty or needs to be extended? Then re-calculate route. if (route.isEmpty()) { calculateRoute = true; } else { // 1. Update current route position status according to latest received location boolean finished = updateCurrentRouteStatus(currentLocation, posTolerance); if (finished) { return null; } List<Location> routeNodes = route.getImmutableAllLocations(); int currentRoute = route.currentRoute; // 2. Analyze if we need to recalculate route // >100m off current route (sideways) if (currentRoute > 0) { double dist = getOrthogonalDistance( currentLocation, routeNodes.get(currentRoute - 1), routeNodes.get(currentRoute)); if (dist > 1.7 * posTolerance) { log.info("Recalculate route, because correlation : " + dist); // $NON-NLS-1$ calculateRoute = true; } if (dist > 350) { voiceRouter.announceOffRoute(dist); } } // 3. Identify wrong movement direction Location next = route.getNextRouteLocation(); boolean wrongMovementDirection = checkWrongMovementDirection(currentLocation, next); if (wrongMovementDirection && currentLocation.distanceTo(routeNodes.get(currentRoute)) > 2 * posTolerance) { log.info( "Recalculate route, because wrong movement direction: " + currentLocation.distanceTo(routeNodes.get(currentRoute))); // $NON-NLS-1$ calculateRoute = true; } // 4. Identify if UTurn is needed boolean uTurnIsNeeded = identifyUTurnIsNeeded(currentLocation, posTolerance); // 5. Update Voice router if (isFollowingMode) { // don't update in route planing mode announceGpxWaypoints(currentLocation); boolean inRecalc = calculateRoute || isRouteBeingCalculated(); if (!inRecalc && !uTurnIsNeeded && !wrongMovementDirection) { voiceRouter.updateStatus(currentLocation, false); } else if (uTurnIsNeeded) { voiceRouter.makeUTStatus(); } } // calculate projection of current location if (currentRoute > 0) { locationProjection = new Location(currentLocation); Location nextLocation = routeNodes.get(currentRoute); LatLon project = getProject( currentLocation, routeNodes.get(currentRoute - 1), routeNodes.get(currentRoute)); locationProjection.setLatitude(project.getLatitude()); locationProjection.setLongitude(project.getLongitude()); // we need to update bearing too float bearingTo = locationProjection.bearingTo(nextLocation); locationProjection.setBearing(bearingTo); } } lastFixedLocation = currentLocation; lastProjection = locationProjection; } if (calculateRoute) { recalculateRouteInBackground( false, currentLocation, finalLocation, intermediatePoints, currentGPXRoute, previousRoute.isCalculated() ? previousRoute : null); } else if (currentRunningJob != null && currentRunningJob instanceof RouteRecalculationThread) { RouteRecalculationThread thread = (RouteRecalculationThread) currentRunningJob; thread.stopCalculation(); } double projectDist = mode != null && mode.hasFastSpeed() ? posTolerance : posTolerance / 2; if (returnUpdatedLocation && locationProjection != null && currentLocation.distanceTo(locationProjection) < projectDist) { return locationProjection; } else { return currentLocation; } } private void announceGpxWaypoints(Location currentLocation) { if (currentLocation != null) { List<WptPt> wpt = route.getWaypointsToAnnounce(currentLocation); if (wpt.size() > 0) { String s = ""; for (WptPt w : wpt) { if (!Algorithms.isEmpty(w.name)) { s = w.name + ","; } } if (!Algorithms.isEmpty(s)) { voiceRouter.announceWaypoint(s); } } } } private static double getOrthogonalDistance(Location loc, Location from, Location to) { return MapUtils.getOrthogonalDistance( loc.getLatitude(), loc.getLongitude(), from.getLatitude(), from.getLongitude(), to.getLatitude(), to.getLongitude()); } private static LatLon getProject(Location loc, Location from, Location to) { return MapUtils.getProjection( loc.getLatitude(), loc.getLongitude(), from.getLatitude(), from.getLongitude(), to.getLatitude(), to.getLongitude()); } private static int lookAheadFindMinOrthogonalDistance( Location currentLocation, List<Location> routeNodes, int currentRoute, int iterations) { double newDist; double dist = Double.POSITIVE_INFINITY; int index = currentRoute; while (iterations > 0 && currentRoute + 1 < routeNodes.size()) { newDist = getOrthogonalDistance( currentLocation, routeNodes.get(currentRoute), routeNodes.get(currentRoute + 1)); if (newDist < dist) { index = currentRoute; dist = newDist; } currentRoute++; iterations--; } return index; } private boolean updateCurrentRouteStatus(Location currentLocation, float posTolerance) { List<Location> routeNodes = route.getImmutableAllLocations(); int currentRoute = route.currentRoute; // 1. Try to proceed to next point using orthogonal distance (finding minimum orthogonal dist) while (currentRoute + 1 < routeNodes.size()) { double dist = currentLocation.distanceTo(routeNodes.get(currentRoute)); if (currentRoute > 0) { dist = getOrthogonalDistance( currentLocation, routeNodes.get(currentRoute - 1), routeNodes.get(currentRoute)); } boolean processed = false; // if we are still too far try to proceed many points // if not then look ahead only 3 in order to catch sharp turns boolean longDistance = dist >= 250; int newCurrentRoute = lookAheadFindMinOrthogonalDistance( currentLocation, routeNodes, currentRoute, longDistance ? 15 : 8); double newDist = getOrthogonalDistance( currentLocation, routeNodes.get(newCurrentRoute), routeNodes.get(newCurrentRoute + 1)); if (longDistance) { if (newDist < dist) { if (log.isDebugEnabled()) { log.debug( "Processed by distance : (new) " + newDist + " (old) " + dist); //$NON-NLS-1$//$NON-NLS-2$ } processed = true; } } else if (newDist < dist || newDist < 10) { // newDist < 10 (avoid distance 0 till next turn) if (dist > posTolerance) { processed = true; if (log.isDebugEnabled()) { log.debug( "Processed by distance : " + newDist + " " + dist); // $NON-NLS-1$//$NON-NLS-2$ } } else { // case if you are getting close to the next point after turn // but you have not yet turned (could be checked bearing) if (currentLocation.hasBearing() || lastFixedLocation != null) { float bearingToRoute = currentLocation.bearingTo(routeNodes.get(currentRoute)); float bearingRouteNext = routeNodes.get(newCurrentRoute).bearingTo(routeNodes.get(newCurrentRoute + 1)); float bearingMotion = currentLocation.hasBearing() ? currentLocation.getBearing() : lastFixedLocation.bearingTo(currentLocation); double diff = Math.abs(MapUtils.degreesDiff(bearingMotion, bearingToRoute)); double diffToNext = Math.abs(MapUtils.degreesDiff(bearingMotion, bearingRouteNext)); if (diff > diffToNext) { if (log.isDebugEnabled()) { log.debug("Processed point bearing deltas : " + diff + " " + diffToNext); } processed = true; } } } } if (processed) { // that node already passed route.updateCurrentRoute(newCurrentRoute + 1); currentRoute = newCurrentRoute + 1; } else { break; } } // 2. check if intermediate found if (route.getIntermediatePointsToPass() > 0 && route.getDistanceToNextIntermediate(lastFixedLocation) < POSITION_TOLERANCE * 2) { showMessage(app.getString(R.string.arrived_at_intermediate_point)); route.passIntermediatePoint(); TargetPointsHelper targets = app.getTargetPointsHelper(); List<String> ns = targets.getIntermediatePointNames(); int toDel = targets.getIntermediatePoints().size() - route.getIntermediatePointsToPass(); int currentIndex = toDel - 1; String name = currentIndex < 0 || currentIndex >= ns.size() || ns.get(currentIndex) == null ? "" : ns.get(currentIndex); if (isFollowingMode) { voiceRouter.arrivedIntermediatePoint(name); } while (toDel > 0) { targets.removeWayPoint(false, 0); toDel--; } while (intermediatePoints != null && route.getIntermediatePointsToPass() < intermediatePoints.size()) { intermediatePoints.remove(0); } } // 3. check if destination found Location lastPoint = routeNodes.get(routeNodes.size() - 1); if (currentRoute > routeNodes.size() - 3 && currentLocation.distanceTo(lastPoint) < (((float) settings.getApplicationMode().getArrivalDistance()) * settings.ARRIVAL_DISTANCE_FACTOR.get())) { showMessage(app.getString(R.string.arrived_at_destination)); TargetPointsHelper targets = app.getTargetPointsHelper(); String description = targets.getPointNavigateDescription(); if (isFollowingMode) { voiceRouter.arrivedDestinationPoint(description); } if (OsmandPlugin.onDestinationReached()) { clearCurrentRoute(null, null); setRoutePlanningMode(false); app.runInUIThread( new Runnable() { @Override public void run() { settings.APPLICATION_MODE.set(settings.DEFAULT_APPLICATION_MODE.get()); } }); // targets.clearPointToNavigate(false); return true; } } return false; } public boolean identifyUTurnIsNeeded(Location currentLocation, float posTolerance) { if (finalLocation == null || currentLocation == null || !route.isCalculated()) { this.makeUturnWhenPossible = false; return makeUturnWhenPossible; } boolean makeUturnWhenPossible = false; if (currentLocation.hasBearing()) { float bearingMotion = currentLocation.getBearing(); Location nextRoutePosition = route.getNextRouteLocation(); float bearingToRoute = currentLocation.bearingTo(nextRoutePosition); double diff = MapUtils.degreesDiff(bearingMotion, bearingToRoute); // 7. Check if you left the route and an unscheduled U-turn would bring you back (also Issue // 863) // This prompt is an interim advice and does only sound if a new route in forward direction // could not be found in x seconds if (Math.abs(diff) > 135f) { float d = currentLocation.distanceTo(nextRoutePosition); // 60m tolerance to allow for GPS inaccuracy if (d > posTolerance) { // require x sec continuous since first detection if (makeUTwpDetected == 0) { makeUTwpDetected = System.currentTimeMillis(); } else if ((System.currentTimeMillis() - makeUTwpDetected > 10000)) { makeUturnWhenPossible = true; // log.info("bearingMotion is opposite to bearingRoute"); //$NON-NLS-1$ } } } else { makeUTwpDetected = 0; } } this.makeUturnWhenPossible = makeUturnWhenPossible; return makeUturnWhenPossible; } /** * Wrong movement direction is considered when between current location bearing (determines by 2 * last fixed position or provided) and bearing from currentLocation to next (current) point the * difference is more than 60 degrees */ public boolean checkWrongMovementDirection(Location currentLocation, Location nextRouteLocation) { // measuring without bearing could be really error prone (with last fixed location) // this code has an effect on route recalculation which should be detected without mistakes if (currentLocation.hasBearing() && nextRouteLocation != null) { float bearingMotion = currentLocation.getBearing(); float bearingToRoute = currentLocation.bearingTo(nextRouteLocation); double diff = MapUtils.degreesDiff(bearingMotion, bearingToRoute); if (Math.abs(diff) > 60f) { // require delay interval since first detection, to avoid false positive // but leave out for now, as late detection is worse than false positive (it may reset voice // router then cause bogus turn and u-turn prompting) // if (wrongMovementDetected == 0) { // wrongMovementDetected = System.currentTimeMillis(); // } else if ((System.currentTimeMillis() - wrongMovementDetected > 500)) { return true; // } } else { // wrongMovementDetected = 0; return false; } } // wrongMovementDetected = 0; return false; } private synchronized void setNewRoute(RouteCalculationResult res, Location start) { final boolean newRoute = !this.route.isCalculated(); route = res; if (isFollowingMode) { if (lastFixedLocation != null) { start = lastFixedLocation; } // try remove false route-recalculated prompts by checking direction to second route node boolean wrongMovementDirection = false; List<Location> routeNodes = res.getImmutableAllLocations(); if (routeNodes != null && !routeNodes.isEmpty()) { int newCurrentRoute = lookAheadFindMinOrthogonalDistance(start, routeNodes, res.currentRoute, 15); if (newCurrentRoute + 1 < routeNodes.size()) { // This check is valid for Online/GPX services (offline routing is aware of route // direction) wrongMovementDirection = checkWrongMovementDirection(start, routeNodes.get(newCurrentRoute + 1)); // set/reset evalWaitInterval only if new route is in forward direction if (!wrongMovementDirection) { evalWaitInterval = 3000; } else { evalWaitInterval = evalWaitInterval * 3 / 2; evalWaitInterval = Math.min(evalWaitInterval, 120000); } } } // trigger voice prompt only if new route is in forward direction // If route is in wrong direction after one more setLocation it will be recalculated if (!wrongMovementDirection || newRoute) { voiceRouter.newRouteIsCalculated(newRoute); } } app.runInUIThread( new Runnable() { @Override public void run() { for (IRouteInformationListener l : listeners) { l.newRouteIsCalculated(newRoute); } } }); } public synchronized int getLeftDistance() { return route.getDistanceToFinish(lastFixedLocation); } public synchronized int getLeftDistanceNextIntermediate() { return route.getDistanceToNextIntermediate(lastFixedLocation); } public synchronized int getLeftTime() { return route.getLeftTime(lastFixedLocation); } public OsmandSettings getSettings() { return settings; } public String getGeneralRouteInformation() { int dist = getLeftDistance(); int hours = getLeftTime() / (60 * 60); int minutes = (getLeftTime() / 60) % 60; return app.getString( R.string.route_general_information, OsmAndFormatter.getFormattedDistance(dist, app), hours, minutes); } public Location getLocationFromRouteDirection(RouteDirectionInfo i) { return route.getLocationFromRouteDirection(i); } public synchronized NextDirectionInfo getNextRouteDirectionInfo( NextDirectionInfo info, boolean toSpeak) { NextDirectionInfo i = route.getNextRouteDirectionInfo(info, lastProjection, toSpeak); if (i != null) { i.imminent = voiceRouter.calculateImminent(i.distanceTo, lastProjection); } return i; } public synchronized float getCurrentMaxSpeed() { return route.getCurrentMaxSpeed(); } public synchronized AlarmInfo getMostImportantAlarm(MetricsConstants mc, boolean showCameras) { float mxspeed = route.getCurrentMaxSpeed(); AlarmInfo speedAlarm = createSpeedAlarm(mc, mxspeed, lastProjection); AlarmInfo alarm = route.getMostImportantAlarm(lastProjection, speedAlarm, showCameras); if (alarm != null) { voiceRouter.announceAlarm(alarm); } return alarm; } public AlarmInfo calculateMostImportantAlarm( RouteDataObject ro, Location loc, MetricsConstants mc, boolean showCameras) { float mxspeed = ro.getMaximumSpeed(); AlarmInfo speedAlarm = createSpeedAlarm(mc, mxspeed, loc); if (speedAlarm != null) { voiceRouter.announceAlarm(speedAlarm); return speedAlarm; } for (int i = 0; i < ro.getPointsLength(); i++) { int[] pointTypes = ro.getPointTypes(i); RouteRegion reg = ro.region; if (pointTypes != null) { for (int r = 0; r < pointTypes.length; r++) { RouteTypeRule typeRule = reg.quickGetEncodingRule(pointTypes[r]); AlarmInfo info = AlarmInfo.createAlarmInfo(typeRule, 0); if (info != null) { if (info.getType() != AlarmInfoType.SPEED_CAMERA || showCameras) { voiceRouter.announceAlarm(info); return info; } } } } } return null; } private static AlarmInfo createSpeedAlarm(MetricsConstants mc, float mxspeed, Location loc) { AlarmInfo speedAlarm = null; if (mxspeed != 0 && loc != null && loc.hasSpeed() && mxspeed != RouteDataObject.NONE_MAX_SPEED) { float delta = 5f / 3.6f; if (loc.getSpeed() > mxspeed + delta) { int speed; if (mc == MetricsConstants.KILOMETERS_AND_METERS) { speed = Math.round(mxspeed * 3.6f); } else { speed = Math.round(mxspeed * 3.6f / 1.6f); } speedAlarm = AlarmInfo.createSpeedLimit(speed); } } return speedAlarm; } public static String formatStreetName(String name, String ref, String destination) { if (destination != null && destination.length() > 0) { if (ref != null && ref.length() > 0) { destination = ref + " " + destination; } return destination; } else if (name != null && name.length() > 0) { if (ref != null && ref.length() > 0) { name = ref + " " + name; } return name; } else { if (ref == null) { return ""; } return ref; } } protected boolean isDistanceLess( float currentSpeed, double dist, double etalon, double defSpeed) { if (dist < etalon || ((dist / currentSpeed) < (etalon / defSpeed))) { return true; } return false; } public synchronized String getCurrentName() { NextDirectionInfo n = getNextRouteDirectionInfo(new NextDirectionInfo(), false); Location l = lastFixedLocation; float speed = 0; if (l != null && l.hasSpeed()) { speed = l.getSpeed(); } if (n.distanceTo > 0 && n.directionInfo != null && !n.directionInfo.getTurnType().isSkipToSpeak() && voiceRouter.isDistanceLess(speed, n.distanceTo, voiceRouter.PREPARE_DISTANCE * 0.75f)) { String nm = n.directionInfo.getStreetName(); String rf = n.directionInfo.getRef(); String dn = n.directionInfo.getDestinationName(); return "\u2566 " + formatStreetName(nm, rf, dn); } RouteSegmentResult rs = getCurrentSegmentResult(); if (rs != null) { String nm = rs.getObject().getName(); String rf = rs.getObject().getRef(); String dn = rs.getObject().getDestinationName(); return "\u21E7 " + formatStreetName(nm, rf, dn); } return null; } public RouteSegmentResult getCurrentSegmentResult() { return route.getCurrentSegmentResult(); } public List<RouteSegmentResult> getUpcomingTunnel(float distToStart) { return route.getUpcomingTunnel(distToStart); } public synchronized NextDirectionInfo getNextRouteDirectionInfoAfter( NextDirectionInfo previous, NextDirectionInfo to, boolean toSpeak) { NextDirectionInfo i = route.getNextRouteDirectionInfoAfter(previous, to, toSpeak); if (i != null) { i.imminent = voiceRouter.calculateImminent(i.distanceTo, null); } return i; } public List<RouteDirectionInfo> getRouteDirections() { return route.getRouteDirections(); } private class RouteRecalculationThread extends Thread { private final RouteCalculationParams params; public RouteRecalculationThread(String name, RouteCalculationParams params) { super(name); this.params = params; if (params.calculationProgress == null) { params.calculationProgress = new RouteCalculationProgress(); } } public void stopCalculation() { params.calculationProgress.isCancelled = true; } @Override public void run() { RouteCalculationResult res = provider.calculateRouteImpl(params); if (params.calculationProgress.isCancelled) { currentRunningJob = null; return; } final boolean onlineSourceWithoutInternet = !res.isCalculated() && params.type.isOnline() && !settings.isInternetConnectionAvailable(); if (onlineSourceWithoutInternet && settings.GPX_ROUTE_CALC_OSMAND_PARTS.get()) { if (params.previousToRecalculate != null && params.previousToRecalculate.isCalculated()) { res = provider.recalculatePartOfflineRoute(res, params); } } synchronized (RoutingHelper.this) { if (res.isCalculated()) { setNewRoute(res, params.start); } else { evalWaitInterval = evalWaitInterval * 3 / 2; evalWaitInterval = Math.min(evalWaitInterval, 120000); } currentRunningJob = null; } if (res.isCalculated()) { String msg = app.getString(R.string.new_route_calculated_dist) + ": " + OsmAndFormatter.getFormattedDistance(res.getWholeDistance(), app); if (res.getRoutingTime() != 0f) { msg += " (" + Algorithms.formatDuration((int) res.getRoutingTime()) + ")"; } showMessage(msg); } else if (onlineSourceWithoutInternet) { showMessage( app.getString(R.string.error_calculating_route) + ":\n" + app.getString( R.string.internet_connection_required_for_online_route)); // $NON-NLS-1$ } else { if (res.getErrorMessage() != null) { showMessage( app.getString(R.string.error_calculating_route) + ":\n" + res.getErrorMessage()); // $NON-NLS-1$ } else { showMessage(app.getString(R.string.empty_route_calculated)); } } lastTimeEvaluatedRoute = System.currentTimeMillis(); } } public void recalculateRouteDueToSettingsChange() { recalculateRouteInBackground( true, lastFixedLocation, finalLocation, intermediatePoints, currentGPXRoute, route); } private void recalculateRouteInBackground( boolean force, final Location start, final LatLon end, final List<LatLon> intermediates, final GPXRouteParamsBuilder gpxRoute, final RouteCalculationResult previousRoute) { if (start == null || end == null) { return; } if (currentRunningJob == null) { // do not evaluate very often if (force || System.currentTimeMillis() - lastTimeEvaluatedRoute > evalWaitInterval) { RouteCalculationParams params = new RouteCalculationParams(); params.start = start; params.end = end; params.intermediates = intermediates; params.gpxRoute = gpxRoute == null ? null : gpxRoute.build(start, settings); params.previousToRecalculate = previousRoute; params.leftSide = settings.DRIVING_REGION.get().leftHandDriving; params.fast = settings.FAST_ROUTE_MODE.getModeValue(mode); params.type = settings.ROUTER_SERVICE.getModeValue(mode); params.mode = mode; params.ctx = app; if (previousRoute == null && params.type == RouteService.OSMAND) { params.calculationProgress = new RouteCalculationProgress(); updateProgress(params.calculationProgress); } synchronized (this) { currentRunningJob = new RouteRecalculationThread("Calculating route", params); // $NON-NLS-1$ currentRunningJob.start(); } } } } public Thread startTaskInRouteThreadIfPossible(final Runnable r) { if (currentRunningJob == null) { synchronized (this) { currentRunningJob = new Thread( new Runnable() { @Override public void run() { try { r.run(); } finally { synchronized (RoutingHelper.this) { currentRunningJob = null; } } } }, "Calculating position"); //$NON-NLS-1$ currentRunningJob.start(); } } return currentRunningJob; } private void updateProgress(final RouteCalculationProgress calculationProgress) { if (progressRoute != null) { app.runInUIThread( new Runnable() { @Override public void run() { if (isRouteBeingCalculated()) { float p = calculationProgress.distanceFromBegin + calculationProgress.distanceFromEnd; float all = calculationProgress.totalEstimatedDistance * 1.5f; if (all > 0) { int t = (int) Math.min(p * p / (all * all) * 100f, 99); progressRoute.updateProgress(t); } updateProgress(calculationProgress); } else { progressRoute.finish(); } } }, 300); } } public void setProgressBar(RouteCalculationProgressCallback progressRoute) { this.progressRoute = progressRoute; } public interface RouteCalculationProgressCallback { // set visibility public void updateProgress(int progress); public void finish(); } public boolean isRouteBeingCalculated() { return currentRunningJob instanceof RouteRecalculationThread; } private void showMessage(final String msg) { app.runInUIThread( new Runnable() { @Override public void run() { app.showToastMessage(msg); } }); } // NEVER returns null public RouteCalculationResult getRoute() { return route; } public GPXFile generateGPXFileWithRoute() { return provider.createOsmandRouterGPX(route, app); } public void notifyIfRouteIsCalculated() { if (route.isCalculated()) { voiceRouter.newRouteIsCalculated(true); } } }
public class BinaryMapRouteReaderAdapter { protected static final Log LOG = PlatformUtil.getLog(BinaryMapRouteReaderAdapter.class); private static final int SHIFT_COORDINATES = 4; private static class RouteTypeCondition { String condition = ""; OpeningHoursParser.OpeningHours hours = null; int intValue; float floatValue; } public static class RouteTypeRule { private static final int ACCESS = 1; private static final int ONEWAY = 2; private static final int HIGHWAY_TYPE = 3; private static final int MAXSPEED = 4; private static final int ROUNDABOUT = 5; public static final int TRAFFIC_SIGNALS = 6; public static final int RAILWAY_CROSSING = 7; private static final int LANES = 8; private final String t; private final String v; private int intValue; private float floatValue; private int type; private List<RouteTypeCondition> conditions = null; public RouteTypeRule(String t, String v) { this.t = t.intern(); if ("true".equals(v)) { v = "yes"; } if ("false".equals(v)) { v = "no"; } this.v = v == null ? null : v.intern(); analyze(); } public String getTag() { return t; } public String getValue() { return v; } public boolean roundabout() { return type == ROUNDABOUT; } public int getType() { return type; } public boolean conditional() { return conditions != null; } public int onewayDirection() { if (type == ONEWAY) { return intValue; } return 0; } public float maxSpeed() { if (type == MAXSPEED) { if (conditions != null) { Calendar i = Calendar.getInstance(); i.setTimeInMillis(System.currentTimeMillis()); for (RouteTypeCondition c : conditions) { if (c.hours != null && c.hours.isOpenedForTime(i)) { return c.floatValue; } } } return floatValue; } return -1; } public int lanes() { if (type == LANES) { return intValue; } return -1; } public String highwayRoad() { if (type == HIGHWAY_TYPE) { return v; } return null; } private void analyze() { if (t.equalsIgnoreCase("oneway")) { type = ONEWAY; if ("-1".equals(v) || "reverse".equals(v)) { intValue = -1; } else if ("1".equals(v) || "yes".equals(v)) { intValue = 1; } else { intValue = 0; } } else if (t.equalsIgnoreCase("highway") && "traffic_signals".equals(v)) { type = TRAFFIC_SIGNALS; } else if (t.equalsIgnoreCase("railway") && ("crossing".equals(v) || "level_crossing".equals(v))) { type = RAILWAY_CROSSING; } else if (t.equalsIgnoreCase("roundabout") && v != null) { type = ROUNDABOUT; } else if (t.equalsIgnoreCase("junction") && "roundabout".equalsIgnoreCase(v)) { type = ROUNDABOUT; } else if (t.equalsIgnoreCase("highway") && v != null) { type = HIGHWAY_TYPE; } else if (t.startsWith("access") && v != null) { type = ACCESS; } else if (t.equalsIgnoreCase("maxspeed:conditional") && v != null) { conditions = new ArrayList<RouteTypeCondition>(); String[] cts = v.split(";"); for (String c : cts) { int ch = c.indexOf('@'); if (ch > 0) { RouteTypeCondition cond = new RouteTypeCondition(); cond.floatValue = parseMaxSpeed(c.substring(0, ch)); cond.condition = c.substring(ch + 1).trim(); if (cond.condition.startsWith("(") && cond.condition.endsWith(")")) { cond.condition = cond.condition.substring(1, cond.condition.length() - 1).trim(); } cond.hours = OpeningHoursParser.parseOpenedHours(cond.condition); conditions.add(cond); } } type = MAXSPEED; } else if (t.equalsIgnoreCase("maxspeed") && v != null) { type = MAXSPEED; floatValue = parseMaxSpeed(v); } else if (t.equalsIgnoreCase("lanes") && v != null) { intValue = -1; int i = 0; type = LANES; while (i < v.length() && Character.isDigit(v.charAt(i))) { i++; } if (i > 0) { intValue = Integer.parseInt(v.substring(0, i)); } } } private float parseMaxSpeed(String v) { float floatValue = -1; if (v.equals("none")) { floatValue = RouteDataObject.NONE_MAX_SPEED; } else { int i = 0; while (i < v.length() && Character.isDigit(v.charAt(i))) { i++; } if (i > 0) { floatValue = Integer.parseInt(v.substring(0, i)); floatValue /= 3.6; // km/h -> m/s if (v.contains("mph")) { floatValue *= 1.6; } } } return floatValue; } } public static class RouteRegion extends BinaryIndexPart { public int regionsRead; public int borderBoxPointer = 0; public int baseBorderBoxPointer = 0; public int borderBoxLength = 0; public int baseBorderBoxLength = 0; List<RouteSubregion> subregions = new ArrayList<RouteSubregion>(); List<RouteSubregion> basesubregions = new ArrayList<RouteSubregion>(); List<RouteTypeRule> routeEncodingRules = new ArrayList<BinaryMapRouteReaderAdapter.RouteTypeRule>(); int nameTypeRule = -1; int refTypeRule = -1; int destinationTypeRule = -1; public RouteTypeRule quickGetEncodingRule(int id) { return routeEncodingRules.get(id); } private void initRouteEncodingRule(int id, String tags, String val) { while (routeEncodingRules.size() <= id) { routeEncodingRules.add(null); } routeEncodingRules.set(id, new RouteTypeRule(tags, val)); if (tags.equals("name")) { nameTypeRule = id; } else if (tags.equals("ref")) { refTypeRule = id; } else if (tags.equals("destination")) { destinationTypeRule = id; } } public List<RouteSubregion> getSubregions() { return subregions; } public List<RouteSubregion> getBaseSubregions() { return basesubregions; } public double getLeftLongitude() { double l = 180; for (RouteSubregion s : subregions) { l = Math.min(l, MapUtils.get31LongitudeX(s.left)); } return l; } public double getRightLongitude() { double l = -180; for (RouteSubregion s : subregions) { l = Math.max(l, MapUtils.get31LongitudeX(s.right)); } return l; } public double getBottomLatitude() { double l = 90; for (RouteSubregion s : subregions) { l = Math.min(l, MapUtils.get31LatitudeY(s.bottom)); } return l; } public double getTopLatitude() { double l = -90; for (RouteSubregion s : subregions) { l = Math.max(l, MapUtils.get31LatitudeY(s.top)); } return l; } } // Used in C++ public static class RouteSubregion { private static final int INT_SIZE = 4; public final RouteRegion routeReg; public RouteSubregion(RouteSubregion copy) { this.routeReg = copy.routeReg; this.left = copy.left; this.right = copy.right; this.top = copy.top; this.bottom = copy.bottom; this.filePointer = copy.filePointer; this.length = copy.length; } public RouteSubregion(RouteRegion routeReg) { this.routeReg = routeReg; } public int length; public int filePointer; public int left; public int right; public int top; public int bottom; public int shiftToData; public List<RouteSubregion> subregions = null; public List<RouteDataObject> dataObjects = null; public int getEstimatedSize() { int shallow = 7 * INT_SIZE + 4 * 3; if (subregions != null) { shallow += 8; for (RouteSubregion s : subregions) { shallow += s.getEstimatedSize(); } } return shallow; } public int countSubregions() { int cnt = 1; if (subregions != null) { for (RouteSubregion s : subregions) { cnt += s.countSubregions(); } } return cnt; } } private CodedInputStream codedIS; private final BinaryMapIndexReader map; protected BinaryMapRouteReaderAdapter(BinaryMapIndexReader map) { this.codedIS = map.codedIS; this.map = map; } private void skipUnknownField(int t) throws IOException { map.skipUnknownField(t); } private int readInt() throws IOException { return map.readInt(); } protected void readRouteIndex(RouteRegion region) throws IOException { int routeEncodingRule = 1; while (true) { int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return; case OsmandOdb.OsmAndRoutingIndex.NAME_FIELD_NUMBER: region.name = codedIS.readString(); break; case OsmandOdb.OsmAndRoutingIndex.RULES_FIELD_NUMBER: { int len = codedIS.readInt32(); int oldLimit = codedIS.pushLimit(len); readRouteEncodingRule(region, routeEncodingRule++); codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); codedIS.popLimit(oldLimit); } break; case OsmandOdb.OsmAndRoutingIndex.ROOTBOXES_FIELD_NUMBER: case OsmandOdb.OsmAndRoutingIndex.BASEMAPBOXES_FIELD_NUMBER: { RouteSubregion subregion = new RouteSubregion(region); subregion.length = readInt(); subregion.filePointer = codedIS.getTotalBytesRead(); int oldLimit = codedIS.pushLimit(subregion.length); readRouteTree(subregion, null, 0, true); if (tag == OsmandOdb.OsmAndRoutingIndex.ROOTBOXES_FIELD_NUMBER) { region.subregions.add(subregion); } else { region.basesubregions.add(subregion); } codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); codedIS.popLimit(oldLimit); break; } case OsmandOdb.OsmAndRoutingIndex.BASEBORDERBOX_FIELD_NUMBER: case OsmandOdb.OsmAndRoutingIndex.BORDERBOX_FIELD_NUMBER: { int length = readInt(); int filePointer = codedIS.getTotalBytesRead(); if (tag == OsmandOdb.OsmAndRoutingIndex.BORDERBOX_FIELD_NUMBER) { region.borderBoxLength = length; region.borderBoxPointer = filePointer; } else { region.baseBorderBoxLength = length; region.baseBorderBoxPointer = filePointer; } codedIS.skipRawBytes(length); break; } case OsmandOdb.OsmAndRoutingIndex.BLOCKS_FIELD_NUMBER: { // Finish reading file! codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); break; } default: skipUnknownField(t); break; } } } private RouteDataObject readRouteDataObject(RouteRegion reg, int pleftx, int ptopy) throws IOException { RouteDataObject o = new RouteDataObject(reg); TIntArrayList pointsX = new TIntArrayList(); TIntArrayList pointsY = new TIntArrayList(); TIntArrayList types = new TIntArrayList(); List<TIntArrayList> globalpointTypes = new ArrayList<TIntArrayList>(); while (true) { int ts = codedIS.readTag(); int tags = WireFormat.getTagFieldNumber(ts); switch (tags) { case 0: o.pointsX = pointsX.toArray(); o.pointsY = pointsY.toArray(); o.types = types.toArray(); if (globalpointTypes.size() > 0) { o.pointTypes = new int[globalpointTypes.size()][]; for (int k = 0; k < o.pointTypes.length; k++) { TIntArrayList l = globalpointTypes.get(k); if (l != null) { o.pointTypes[k] = l.toArray(); } } } return o; case RouteData.TYPES_FIELD_NUMBER: int len = codedIS.readRawVarint32(); int oldLimit = codedIS.pushLimit(len); while (codedIS.getBytesUntilLimit() > 0) { types.add(codedIS.readRawVarint32()); } codedIS.popLimit(oldLimit); break; case RouteData.STRINGNAMES_FIELD_NUMBER: o.names = new TIntObjectHashMap<String>(); int sizeL = codedIS.readRawVarint32(); int old = codedIS.pushLimit(sizeL); while (codedIS.getBytesUntilLimit() > 0) { int stag = codedIS.readRawVarint32(); int pId = codedIS.readRawVarint32(); o.names.put(stag, ((char) pId) + ""); } codedIS.popLimit(old); break; case RouteData.POINTS_FIELD_NUMBER: len = codedIS.readRawVarint32(); oldLimit = codedIS.pushLimit(len); int px = pleftx >> SHIFT_COORDINATES; int py = ptopy >> SHIFT_COORDINATES; while (codedIS.getBytesUntilLimit() > 0) { int x = (codedIS.readSInt32()) + px; int y = (codedIS.readSInt32()) + py; pointsX.add(x << SHIFT_COORDINATES); pointsY.add(y << SHIFT_COORDINATES); px = x; py = y; } codedIS.popLimit(oldLimit); break; case RouteData.POINTTYPES_FIELD_NUMBER: len = codedIS.readRawVarint32(); oldLimit = codedIS.pushLimit(len); while (codedIS.getBytesUntilLimit() > 0) { int pointInd = codedIS.readRawVarint32(); TIntArrayList pointTypes = new TIntArrayList(); int lens = codedIS.readRawVarint32(); int oldLimits = codedIS.pushLimit(lens); while (codedIS.getBytesUntilLimit() > 0) { pointTypes.add(codedIS.readRawVarint32()); } codedIS.popLimit(oldLimits); while (pointInd >= globalpointTypes.size()) { globalpointTypes.add(null); } globalpointTypes.set(pointInd, pointTypes); } codedIS.popLimit(oldLimit); break; case RouteData.ROUTEID_FIELD_NUMBER: o.id = codedIS.readInt32(); break; default: skipUnknownField(ts); break; } } } private void readRouteTreeData( RouteSubregion routeTree, TLongArrayList idTables, TLongObjectHashMap<TLongArrayList> restrictions) throws IOException { routeTree.dataObjects = new ArrayList<RouteDataObject>(); idTables.clear(); restrictions.clear(); List<String> stringTable = null; while (true) { int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: TLongObjectIterator<TLongArrayList> it = restrictions.iterator(); while (it.hasNext()) { it.advance(); int from = (int) it.key(); RouteDataObject fromr = routeTree.dataObjects.get(from); fromr.restrictions = new long[it.value().size()]; for (int k = 0; k < fromr.restrictions.length; k++) { int to = (int) (it.value().get(k) >> RouteDataObject.RESTRICTION_SHIFT); long valto = (idTables.get(to) << RouteDataObject.RESTRICTION_SHIFT) | ((long) it.value().get(k) & RouteDataObject.RESTRICTION_MASK); fromr.restrictions[k] = valto; } } for (RouteDataObject o : routeTree.dataObjects) { if (o != null) { if (o.id < idTables.size()) { o.id = idTables.get((int) o.id); } if (o.names != null && stringTable != null) { int[] keys = o.names.keys(); for (int j = 0; j < keys.length; j++) { o.names.put(keys[j], stringTable.get(o.names.get(keys[j]).charAt(0))); } } } } return; case RouteDataBlock.DATAOBJECTS_FIELD_NUMBER: int length = codedIS.readRawVarint32(); int oldLimit = codedIS.pushLimit(length); RouteDataObject obj = readRouteDataObject(routeTree.routeReg, routeTree.left, routeTree.top); while (obj.id >= routeTree.dataObjects.size()) { routeTree.dataObjects.add(null); } routeTree.dataObjects.set((int) obj.id, obj); codedIS.popLimit(oldLimit); break; case RouteDataBlock.IDTABLE_FIELD_NUMBER: long routeId = 0; length = codedIS.readRawVarint32(); oldLimit = codedIS.pushLimit(length); idLoop: while (true) { int ts = codedIS.readTag(); int tags = WireFormat.getTagFieldNumber(ts); switch (tags) { case 0: break idLoop; case IdTable.ROUTEID_FIELD_NUMBER: routeId += codedIS.readSInt64(); idTables.add(routeId); break; default: skipUnknownField(ts); break; } } codedIS.popLimit(oldLimit); break; case RouteDataBlock.RESTRICTIONS_FIELD_NUMBER: length = codedIS.readRawVarint32(); oldLimit = codedIS.pushLimit(length); long from = 0; long to = 0; long type = 0; idLoop: while (true) { int ts = codedIS.readTag(); int tags = WireFormat.getTagFieldNumber(ts); switch (tags) { case 0: break idLoop; case RestrictionData.FROM_FIELD_NUMBER: from = codedIS.readInt32(); break; case RestrictionData.TO_FIELD_NUMBER: to = codedIS.readInt32(); break; case RestrictionData.TYPE_FIELD_NUMBER: type = codedIS.readInt32(); break; default: skipUnknownField(ts); break; } } if (!restrictions.containsKey(from)) { restrictions.put(from, new TLongArrayList()); } restrictions.get(from).add((to << RouteDataObject.RESTRICTION_SHIFT) + type); codedIS.popLimit(oldLimit); break; case RouteDataBlock.STRINGTABLE_FIELD_NUMBER: length = codedIS.readRawVarint32(); oldLimit = codedIS.pushLimit(length); stringTable = map.readStringTable(); // codedIS.skipRawBytes(codedIS.getBytesUntilLimit()); codedIS.popLimit(oldLimit); break; default: skipUnknownField(t); break; } } } private void readRouteEncodingRule(RouteRegion index, int id) throws IOException { String tags = null; String val = null; while (true) { int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: index.initRouteEncodingRule(id, tags, val); return; case RouteEncodingRule.VALUE_FIELD_NUMBER: val = codedIS.readString().intern(); break; case RouteEncodingRule.TAG_FIELD_NUMBER: tags = codedIS.readString().intern(); break; case RouteEncodingRule.ID_FIELD_NUMBER: id = codedIS.readUInt32(); break; default: skipUnknownField(t); break; } } } private RouteSubregion readRouteTree( RouteSubregion thisTree, RouteSubregion parentTree, int depth, boolean readCoordinates) throws IOException { boolean readChildren = depth != 0; if (readChildren) { thisTree.subregions = new ArrayList<BinaryMapRouteReaderAdapter.RouteSubregion>(); } thisTree.routeReg.regionsRead++; while (true) { int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return thisTree; case RouteDataBox.LEFT_FIELD_NUMBER: int i = codedIS.readSInt32(); if (readCoordinates) { thisTree.left = i + (parentTree != null ? parentTree.left : 0); } break; case RouteDataBox.RIGHT_FIELD_NUMBER: i = codedIS.readSInt32(); if (readCoordinates) { thisTree.right = i + (parentTree != null ? parentTree.right : 0); } break; case RouteDataBox.TOP_FIELD_NUMBER: i = codedIS.readSInt32(); if (readCoordinates) { thisTree.top = i + (parentTree != null ? parentTree.top : 0); } break; case RouteDataBox.BOTTOM_FIELD_NUMBER: i = codedIS.readSInt32(); if (readCoordinates) { thisTree.bottom = i + (parentTree != null ? parentTree.bottom : 0); } break; case RouteDataBox.SHIFTTODATA_FIELD_NUMBER: thisTree.shiftToData = readInt(); if (!readChildren) { // usually 0 thisTree.subregions = new ArrayList<BinaryMapRouteReaderAdapter.RouteSubregion>(); readChildren = true; } break; case RouteDataBox.BOXES_FIELD_NUMBER: if (readChildren) { RouteSubregion subregion = new RouteSubregion(thisTree.routeReg); subregion.length = readInt(); subregion.filePointer = codedIS.getTotalBytesRead(); int oldLimit = codedIS.pushLimit(subregion.length); readRouteTree(subregion, thisTree, depth - 1, true); thisTree.subregions.add(subregion); codedIS.popLimit(oldLimit); codedIS.seek(subregion.filePointer + subregion.length); } else { codedIS.seek(thisTree.filePointer + thisTree.length); // skipUnknownField(t); } break; default: skipUnknownField(t); break; } } } public void initRouteTypesIfNeeded(SearchRequest<RouteDataObject> req, List<RouteSubregion> list) throws IOException { for (RouteSubregion rs : list) { if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) { initRouteRegion(rs.routeReg); } } } public void initRouteRegion(RouteRegion routeReg) throws IOException, InvalidProtocolBufferException { if (routeReg.routeEncodingRules.isEmpty()) { codedIS.seek(routeReg.filePointer); int oldLimit = codedIS.pushLimit(routeReg.length); readRouteIndex(routeReg); codedIS.popLimit(oldLimit); } } public List<RouteDataObject> loadRouteRegionData(RouteSubregion rs) throws IOException { TLongArrayList idMap = new TLongArrayList(); TLongObjectHashMap<TLongArrayList> restrictionMap = new TLongObjectHashMap<TLongArrayList>(); if (rs.dataObjects == null) { codedIS.seek(rs.filePointer + rs.shiftToData); int limit = codedIS.readRawVarint32(); int oldLimit = codedIS.pushLimit(limit); readRouteTreeData(rs, idMap, restrictionMap); codedIS.popLimit(oldLimit); } List<RouteDataObject> res = rs.dataObjects; rs.dataObjects = null; return res; } public void loadRouteRegionData( List<RouteSubregion> toLoad, ResultMatcher<RouteDataObject> matcher) throws IOException { Collections.sort( toLoad, new Comparator<RouteSubregion>() { @Override public int compare(RouteSubregion o1, RouteSubregion o2) { int p1 = o1.filePointer + o1.shiftToData; int p2 = o2.filePointer + o2.shiftToData; return p1 == p2 ? 0 : (p1 < p2 ? -1 : 1); } }); TLongArrayList idMap = new TLongArrayList(); TLongObjectHashMap<TLongArrayList> restrictionMap = new TLongObjectHashMap<TLongArrayList>(); for (RouteSubregion rs : toLoad) { if (rs.dataObjects == null) { codedIS.seek(rs.filePointer + rs.shiftToData); int limit = codedIS.readRawVarint32(); int oldLimit = codedIS.pushLimit(limit); readRouteTreeData(rs, idMap, restrictionMap); codedIS.popLimit(oldLimit); } for (RouteDataObject ro : rs.dataObjects) { if (ro != null) { matcher.publish(ro); } } // free objects rs.dataObjects = null; } } public List<RouteSubregion> searchRouteRegionTree( SearchRequest<RouteDataObject> req, List<RouteSubregion> list, List<RouteSubregion> toLoad) throws IOException { for (RouteSubregion rs : list) { if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) { if (rs.subregions == null) { codedIS.seek(rs.filePointer); int old = codedIS.pushLimit(rs.length); readRouteTree( rs, null, req.contains(rs.left, rs.top, rs.right, rs.bottom) ? -1 : 1, false); codedIS.popLimit(old); } searchRouteRegionTree(req, rs.subregions, toLoad); if (rs.shiftToData != 0) { toLoad.add(rs); } } } return toLoad; } public List<RouteDataBorderLinePoint> searchBorderPoints( SearchRequest<RouteDataBorderLinePoint> req, RouteRegion r) throws IOException { if (r.borderBoxPointer != 0) { codedIS.seek(r.borderBoxPointer); int old = codedIS.pushLimit(r.borderBoxLength); TIntArrayList blocksToRead = new TIntArrayList(); readBorderLines(req, blocksToRead); blocksToRead.sort(); for (int j = 0; j < blocksToRead.size(); j++) { codedIS.seek(blocksToRead.get(j)); int len = codedIS.readRawVarint32(); int oldLimit = codedIS.pushLimit(len); readBorderLinePoints(req, r); codedIS.popLimit(oldLimit); } codedIS.popLimit(old); } return req.getSearchResults(); } private void readBorderLinePoints(SearchRequest<RouteDataBorderLinePoint> req, RouteRegion r) throws IOException { int x = 0; int y = 0; long id = 0; while (true) { int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return; case RouteBorderPointsBlock.X_FIELD_NUMBER: { x = codedIS.readInt32(); break; } case RouteBorderPointsBlock.Y_FIELD_NUMBER: { y = codedIS.readInt32(); break; } case RouteBorderPointsBlock.BASEID_FIELD_NUMBER: { id = codedIS.readInt64(); break; } case RouteBorderPointsBlock.POINTS_FIELD_NUMBER: int len = codedIS.readRawVarint32(); int oldLimit = codedIS.pushLimit(len); RouteDataBorderLinePoint p = readBorderLinePoint(new RouteDataBorderLinePoint(r), x, y, id); codedIS.popLimit(oldLimit); x = p.x; y = p.y; id = p.id; req.publish(p); break; default: skipUnknownField(t); break; } } } private RouteDataBorderLinePoint readBorderLinePoint( RouteDataBorderLinePoint p, int x, int y, long id) throws IOException { while (true) { int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return p; case RouteBorderPoint.DX_FIELD_NUMBER: p.x = x + codedIS.readInt32(); break; case RouteBorderPoint.DY_FIELD_NUMBER: p.y = y + codedIS.readInt32(); break; case RouteBorderPoint.ROADID_FIELD_NUMBER: p.id = id + codedIS.readSInt64(); break; case RouteBorderPoint.DIRECTION_FIELD_NUMBER: p.direction = codedIS.readBool(); break; case RouteBorderPoint.TYPES_FIELD_NUMBER: TIntArrayList types = new TIntArrayList(); int len = codedIS.readRawVarint32(); int oldLimit = codedIS.pushLimit(len); while (codedIS.getBytesUntilLimit() > 0) { types.add(codedIS.readRawVarint32()); } codedIS.popLimit(oldLimit); p.types = types.toArray(); break; default: skipUnknownField(t); break; } } } private void readBorderLines( SearchRequest<RouteDataBorderLinePoint> req, TIntArrayList blocksToRead) throws IOException { while (true) { int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return; case RouteBorderBox.BORDERLINES_FIELD_NUMBER: { int fp = codedIS.getTotalBytesRead(); int length = codedIS.readRawVarint32(); int old = codedIS.pushLimit(length); RouteBorderLine ln = readBorderLine(); if (ln.hasTox() && req.intersects(ln.getX(), ln.getY(), ln.getTox(), ln.getY())) { blocksToRead.add(ln.getShiftToPointsBlock() + fp); // FIXME borders approach // } else if(ln.hasToy() && req.intersects(ln.getX(), ln.getY(), ln.getX(), // ln.getToy())) { // blocksToRead.add(ln.getShiftToPointsBlock() + fp); } codedIS.popLimit(old); break; } case RouteBorderBox.BLOCKS_FIELD_NUMBER: return; default: skipUnknownField(t); break; } } } private RouteBorderLine readBorderLine() throws IOException { Builder bld = RouteBorderLine.newBuilder(); while (true) { int t = codedIS.readTag(); int tag = WireFormat.getTagFieldNumber(t); switch (tag) { case 0: return bld.build(); case RouteBorderLine.X_FIELD_NUMBER: bld.setX(codedIS.readInt32()); break; case RouteBorderLine.Y_FIELD_NUMBER: bld.setY(codedIS.readInt32()); break; case RouteBorderLine.TOX_FIELD_NUMBER: bld.setTox(codedIS.readInt32()); break; case RouteBorderLine.TOY_FIELD_NUMBER: bld.setToy(codedIS.readInt32()); break; case RouteBorderLine.SHIFTTOPOINTSBLOCK_FIELD_NUMBER: bld.setShiftToPointsBlock(readInt()); break; default: skipUnknownField(t); break; } } } public List<RouteSubregion> loadInteresectedPoints( SearchRequest<RouteDataObject> req, List<RouteSubregion> list, List<RouteSubregion> toLoad) throws IOException { for (RouteSubregion rs : list) { if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) { if (rs.subregions == null) { codedIS.seek(rs.filePointer); int old = codedIS.pushLimit(rs.length); readRouteTree( rs, null, req.contains(rs.left, rs.top, rs.right, rs.bottom) ? -1 : 1, false); codedIS.popLimit(old); } searchRouteRegionTree(req, rs.subregions, toLoad); if (rs.shiftToData != 0) { toLoad.add(rs); } } } return toLoad; } }
public class MainUtilities { private static Log log = PlatformUtil.getLog(MainUtilities.class); public static void main(String[] args) throws Exception { if (args.length == 0) { printSynopsys(); } else { String utl = args[0]; List<String> subArgs = new ArrayList<String>(Arrays.asList(args).subList(1, args.length)); String[] subArgsArray = subArgs.toArray(new String[args.length - 1]); if (utl.equals("check-ocean-tile")) { OceanTilesCreator.checkOceanTile(subArgsArray); } else if (utl.equals("compare")) { BinaryComparator.main(subArgsArray); } else if (utl.equals("merge-index")) { BinaryMerger.main(subArgsArray); } else if (utl.equals("generate-region-tags")) { GenerateRegionTags.main(subArgsArray); } else if (utl.equals("generate-ocean-tile-osm")) { OceanTilesCreator.createJOSMFile(subArgsArray); } else if (utl.equals("generate-java-style")) { RenderingRulesStoragePrinter.main(subArgsArray); } else if (utl.equals("explain-rendering-style")) { RenderingRulesStorage.main(subArgsArray); } else if (utl.equals("generate-ocean-tile")) { OceanTilesCreator.createTilesFile( subArgsArray[0], subArgsArray.length > 1 ? args[1] : null); } else if (utl.equals("test-routing")) { net.osmand.router.TestRouting.main(subArgsArray); } else if (utl.equals("generate-ocbf")) { CountryOcbfGeneration.main(subArgsArray); } else if (utl.equals("generate-obf")) { IndexCreator ic = new IndexCreator(new File(".")); ic.setIndexMap(true); ic.setIndexPOI(true); ic.setIndexRouting(true); ic.setIndexAddress(true); ic.setLastModifiedDate(new File(subArgsArray[0]).lastModified()); generateObf(subArgsArray, ic); } else if (utl.equals("generate-map")) { IndexCreator ic = new IndexCreator(new File(".")); ic.setIndexMap(true); ic.setLastModifiedDate(new File(subArgsArray[0]).lastModified()); generateObf(subArgsArray, ic); } else if (utl.equals("generate-address")) { IndexCreator ic = new IndexCreator(new File(".")); ic.setIndexAddress(true); ic.setLastModifiedDate(new File(subArgsArray[0]).lastModified()); generateObf(subArgsArray, ic); } else if (utl.equals("extract-roads-only")) { File mainFile = new File(subArgsArray[0]); IndexUploader.extractRoadOnlyFile( mainFile, new File( mainFile.getParentFile(), mainFile .getName() .replace( IndexConstants.BINARY_MAP_INDEX_EXT, IndexConstants.BINARY_ROAD_MAP_INDEX_EXT))); } else if (utl.equals("generate-poi")) { IndexCreator ic = new IndexCreator(new File(".")); ic.setIndexPOI(true); ic.setLastModifiedDate(new File(subArgsArray[0]).lastModified()); generateObf(subArgsArray, ic); } else if (utl.equals("delete-unused-strings")) { ResourceDeleter.main(subArgsArray); } else if (utl.equals("merge-std-files")) { BinaryMerger.mergeStandardFiles(subArgsArray); } else if (utl.equals("generate-roads")) { IndexCreator ic = new IndexCreator(new File(".")); ic.setIndexRouting(true); ic.setLastModifiedDate(new File(subArgsArray[0]).lastModified()); generateObf(subArgsArray, ic); } else { printSynopsys(); } } } private static void generateObf(String[] subArgsArray, IndexCreator ic) throws IOException, SQLException, InterruptedException, XmlPullParserException { String fn = DataExtractionSettings.getSettings().getMapRenderingTypesFile(); String regionName = subArgsArray[0]; MapRenderingTypesEncoder types = new MapRenderingTypesEncoder(fn, regionName); ic.generateIndexes( new File(subArgsArray[0]), new ConsoleProgressImplementation(), null, MapZooms.getDefault(), types, log); } private static void printSynopsys() { System.out.println("This utility provides access to all other console utilities of OsmAnd,"); System.out.println("each utility has own argument list and own synopsys. Here is the list:"); System.out.println( "\t\t generate-obf <path to osm file>: simple way to generate obf file in place. " + "\t\t\t Another supported options generate-map, generate-address, generate-poi, generate-roads (generate obf partially)"); System.out.println( "\t\t check-ocean-tile <lat> <lon> <zoom=11>: checks ocean or land tile is in bz2 list"); System.out.println( "\t\t generate-ocean-tile <coastline osm file> <optional output file>: creates ocean tiles 12 zoom"); System.out.println( "\t\t generate-java-style <pathtostyle> <pathtooutputfolder>: prints rendering style as java interpreted code"); System.out.println( "\t\t explain-rendering-style <pathtostyle>: prints explanation of the style"); System.out.println( "\t\t test-routing <own list of parameters>: helps to run routing test for specific locations"); System.out.println( "\t\t generate-ocbf <path to osmand/repos/ repository>: generates regions.ocbf file, this path should contain folders 'misc', 'tools', 'resources'"); System.out.println( "\t\t delete-unused-strings <path to repos/android/OsmAnd/res>: deletes unused translation in git repository (transforms all strings.xml)"); System.out.println( "\t\t extract-roads-only <path to full map obf file> : extracts .road.obf (road-only map) file from full .obf"); System.out.println( "\t\t generate-region-tags <path to input osm file (osm, bz2, gz)> <path to output osm file> <path to ocbf file>: process osm file and assign tag osmand_region_name to every entity."); System.out.println( "\t\t generate-ocean-tile-osm <optional path to osm file to write> <optional path to oceantiles_12.dat file>: generates ocean tiles osm file to check in JOSM "); System.out.println("\t\t merge-index " + BinaryMerger.helpMessage); System.out.println("\t\t compare " + BinaryComparator.helpMessage); } }
public class RoutingContext { public static final boolean SHOW_GC_SIZE = false; private static final Log log = PlatformUtil.getLog(RoutingContext.class); public static final int OPTION_NO_LOAD = 0; public static final int OPTION_SMART_LOAD = 1; public static final int OPTION_IN_MEMORY_LOAD = 2; public static boolean USE_BORDER_LINES = false; // Final context variables public final RoutingConfiguration config; private final boolean useBaseMap; public final NativeLibrary nativeLib; public final Map<BinaryMapIndexReader, List<RouteSubregion>> map = new LinkedHashMap<BinaryMapIndexReader, List<RouteSubregion>>(); public final Map<RouteRegion, BinaryMapIndexReader> reverseMap = new LinkedHashMap<RouteRegion, BinaryMapIndexReader>(); // 1. Initial variables public long firstRoadId = 0; public int firstRoadDirection = 0; public int startX; public int startY; public int targetX; public int targetY; public RouteCalculationProgress calculationProgress; public List<RouteSegmentResult> previouslyCalculatedRoute; public BaseRouteResult baseRouteResult; // 2. Routing memory cache (big objects) TLongObjectHashMap<List<RoutingSubregionTile>> indexedSubregions = new TLongObjectHashMap<List<RoutingSubregionTile>>(); TLongObjectHashMap<List<RouteDataObject>> tileRoutes = new TLongObjectHashMap<List<RouteDataObject>>(); RouteDataBorderLine[] borderLines = new RouteDataBorderLine[0]; int[] borderLineCoordinates = new int[0]; int leftBorderBoundary; int rightBorderBoundary; // Needs to be a sorted array list . Another option to use hashmap but it will be more memory // expensive List<RoutingSubregionTile> subregionTiles = new ArrayList<RoutingSubregionTile>(); // 3. Warm object caches ArrayList<RouteSegment> segmentsToVisitPrescripted = new ArrayList<BinaryRoutePlanner.RouteSegment>(5); ArrayList<RouteSegment> segmentsToVisitNotForbidden = new ArrayList<BinaryRoutePlanner.RouteSegment>(5); // 5. debug information (package accessor) public TileStatistics global = new TileStatistics(); // updated by route planner in bytes public int memoryOverhead = 0; long timeToLoad = 0; long timeToLoadHeaders = 0; long timeToFindInitialSegments = 0; long timeToCalculate = 0; int distinctLoadedTiles = 0; int maxLoadedTiles = 0; int loadedPrevUnloadedTiles = 0; int unloadedTiles = 0; public float routingTime = 0; public int loadedTiles = 0; public int visitedSegments = 0; public int relaxedSegments = 0; // callback of processing segments RouteSegmentVisitor visitor = null; // old planner public FinalRouteSegment finalRouteSegment; public RoutingContext(RoutingContext cp) { this.config = cp.config; this.map.putAll(cp.map); this.useBaseMap = cp.useBaseMap; this.reverseMap.putAll(cp.reverseMap); this.nativeLib = cp.nativeLib; // copy local data and clear caches for (RoutingSubregionTile tl : subregionTiles) { if (tl.isLoaded()) { subregionTiles.add(tl); for (RouteSegment rs : tl.routes.valueCollection()) { RouteSegment s = rs; while (s != null) { s.parentRoute = null; s.parentSegmentEnd = 0; s.distanceFromStart = 0; s.distanceToEnd = 0; s = s.next; } } } } } public RoutingContext( RoutingConfiguration config, NativeLibrary nativeLibrary, BinaryMapIndexReader[] map) { this(config, nativeLibrary, map, false); } public RoutingContext( RoutingConfiguration config, NativeLibrary nativeLibrary, BinaryMapIndexReader[] map, boolean useBasemap) { this.useBaseMap = useBasemap; for (BinaryMapIndexReader mr : map) { List<RouteRegion> rr = mr.getRoutingIndexes(); List<RouteSubregion> subregions = new ArrayList<BinaryMapRouteReaderAdapter.RouteSubregion>(); for (RouteRegion r : rr) { List<RouteSubregion> subregs = useBaseMap ? r.getBaseSubregions() : r.getSubregions(); for (RouteSubregion rs : subregs) { subregions.add(new RouteSubregion(rs)); } this.reverseMap.put(r, mr); } this.map.put(mr, subregions); } this.config = config; this.nativeLib = nativeLibrary; } public RouteSegmentVisitor getVisitor() { return visitor; } public int getCurrentlyLoadedTiles() { int cnt = 0; for (RoutingSubregionTile t : this.subregionTiles) { if (t.isLoaded()) { cnt++; } } return cnt; } public int getCurrentEstimatedSize() { return global.size; } public void setVisitor(RouteSegmentVisitor visitor) { this.visitor = visitor; } public void setRouter(VehicleRouter router) { config.router = router; } public void setHeuristicCoefficient(float heuristicCoefficient) { config.heuristicCoefficient = heuristicCoefficient; } public VehicleRouter getRouter() { return config.router; } public boolean planRouteIn2Directions() { return config.planRoadDirection == 0; } public int getPlanRoadDirection() { return config.planRoadDirection; } public void setPlanRoadDirection(int planRoadDirection) { config.planRoadDirection = planRoadDirection; } public int roadPriorityComparator( double o1DistanceFromStart, double o1DistanceToEnd, double o2DistanceFromStart, double o2DistanceToEnd) { return BinaryRoutePlanner.roadPriorityComparator( o1DistanceFromStart, o1DistanceToEnd, o2DistanceFromStart, o2DistanceToEnd, config.heuristicCoefficient); } public void registerRouteDataObject(RouteDataObject o) { if (!getRouter().acceptLine(o)) { return; } for (int k = 0; k < o.getPointsLength(); k++) { int x31 = o.getPoint31XTile(k); int y31 = o.getPoint31YTile(k); long tileId = getRoutingTile(x31, y31, 0, OPTION_NO_LOAD); List<RouteDataObject> routes = tileRoutes.get(tileId); if (routes == null) { routes = new ArrayList<RouteDataObject>(); tileRoutes.put(tileId, routes); } if (!routes.contains(o)) { routes.add(o); } } } public void unloadAllData() { unloadAllData(null); } public void unloadAllData(RoutingContext except) { for (RoutingSubregionTile tl : subregionTiles) { if (tl.isLoaded()) { if (except == null || except.searchSubregionTile(tl.subregion) < 0) { tl.unload(); unloadedTiles++; global.size -= tl.tileStatistics.size; } } } subregionTiles.clear(); tileRoutes.clear(); indexedSubregions.clear(); } private int searchSubregionTile(RouteSubregion subregion) { RoutingSubregionTile key = new RoutingSubregionTile(subregion); long now = System.nanoTime(); int ind = Collections.binarySearch( subregionTiles, key, new Comparator<RoutingSubregionTile>() { @Override public int compare(RoutingSubregionTile o1, RoutingSubregionTile o2) { if (o1.subregion.left == o2.subregion.left) { return 0; } return o1.subregion.left < o2.subregion.left ? 1 : -1; } }); if (ind >= 0) { for (int i = ind; i <= subregionTiles.size(); i++) { if (i == subregionTiles.size() || subregionTiles.get(i).subregion.left > subregion.left) { ind = -i - 1; return ind; } if (subregionTiles.get(i).subregion == subregion) { return i; } } } timeToLoadHeaders += (System.nanoTime() - now); return ind; } public void loadBorderPoints() throws IOException { Iterator<Entry<RouteRegion, BinaryMapIndexReader>> it = reverseMap.entrySet().iterator(); int sleft = Math.min(startX, targetX); int sright = Math.max(startX, targetX); int stop = Math.min(startY, targetY); int sbottom = Math.max(startY, targetY); // one tile of 12th zoom around (?) int zoomAround = 10; int distAround = 1 << (31 - zoomAround); leftBorderBoundary = sleft - distAround; rightBorderBoundary = sright + distAround; SearchRequest<RouteDataBorderLinePoint> req = BinaryMapIndexReader.buildSearchRouteBorderRequest(sleft, sright, stop, sbottom); while (it.hasNext()) { Entry<RouteRegion, BinaryMapIndexReader> entry = it.next(); entry.getValue().searchBorderPoints(req, entry.getKey()); } TIntObjectHashMap<RouteDataBorderLine> lines = new TIntObjectHashMap<RoutingContext.RouteDataBorderLine>(); for (RouteDataBorderLinePoint p : req.getSearchResults()) { if (config.router.acceptLine(p) && p.x > leftBorderBoundary && p.x < rightBorderBoundary) { if (!lines.containsKey(p.y)) { RouteDataBorderLine line = new RouteDataBorderLine(p.y); lines.put(p.y, line); RouteDataBorderLinePoint lft = new RouteDataBorderLinePoint(p.region); lft.y = p.y; lft.id = Long.MIN_VALUE; lft.x = leftBorderBoundary; line.borderPoints.add(lft); RouteDataBorderLinePoint rht = new RouteDataBorderLinePoint(p.region); rht.y = p.y; rht.id = Long.MIN_VALUE; rht.x = rightBorderBoundary; line.borderPoints.add(rht); } lines.get(p.y).borderPoints.add(p); } } borderLines = lines.values(new RouteDataBorderLine[lines.size()]); Arrays.sort(borderLines); borderLineCoordinates = new int[borderLines.length]; for (int i = 0; i < borderLineCoordinates.length; i++) { borderLineCoordinates[i] = borderLines[i].borderLine; // FIXME borders approach // not less then 14th zoom if (i > 0 && borderLineCoordinates[i - 1] >> 17 == borderLineCoordinates[i] >> 17) { throw new IllegalStateException(); } System.out.println( "Line " + (borderLineCoordinates[i] >> 17) + " points " + borderLines[i].borderPoints.size() /* + " " +borderLines[i].borderPoints*/); } updateDistanceForBorderPoints(startX, startY, true); updateDistanceForBorderPoints(targetX, targetY, false); } protected void updateDistanceForBorderPoints(int sX, int sy, boolean distanceToStart) { boolean plus = borderLines.length > 0 && sy < borderLines[0].borderLine; if (borderLines.length > 0 && !plus && sy < borderLines[borderLines.length - 1].borderLine) { throw new IllegalStateException(); } // calculate min distance to start for (int i = 0; i < borderLines.length; i++) { int ind = plus ? i : borderLines.length - i - 1; for (RouteDataBorderLinePoint ps : borderLines[ind].borderPoints) { float res = (float) Math.sqrt(MapUtils.squareDist31TileMetric(sX, sy, ps.x, ps.y)); if (i > 0) { int prevInd = plus ? i - 1 : borderLines.length - i; double minDist = 0; for (RouteDataBorderLinePoint prevs : borderLines[prevInd].borderPoints) { double d = Math.sqrt(MapUtils.squareDist31TileMetric(prevs.x, prevs.y, ps.x, ps.y)) + (distanceToStart ? prevs.distanceToStartPoint : prevs.distanceToEndPoint); if (minDist == 0 || d < minDist) { minDist = d; } } if (minDist > 0) { // System.out.println("Border line " + i + " exp="+res + " min="+ minDist); res = (float) minDist; } } if (distanceToStart) { ps.distanceToStartPoint = res; } else { ps.distanceToEndPoint = res; } } } } // returns from 0 to borderLineCoordinates.length inclusive public int searchBorderLineIndex(int y) { int k = Arrays.binarySearch(borderLineCoordinates, y); if (k < 0) { k = -(k + 1); } return k; } public RouteSegment loadRouteSegment(int x31, int y31, int memoryLimit) { long tileId = getRoutingTile(x31, y31, memoryLimit, OPTION_SMART_LOAD); TLongObjectHashMap<RouteDataObject> excludeDuplications = new TLongObjectHashMap<RouteDataObject>(); RouteSegment original = null; if (tileRoutes.containsKey(tileId)) { List<RouteDataObject> routes = tileRoutes.get(tileId); if (routes != null) { for (RouteDataObject ro : routes) { for (int i = 0; i < ro.pointsX.length; i++) { if (ro.getPoint31XTile(i) == x31 && ro.getPoint31YTile(i) == y31) { excludeDuplications.put(calcRouteId(ro, i), ro); RouteSegment segment = new RouteSegment(ro, i); segment.next = original; original = segment; } } } } } List<RoutingSubregionTile> subregions = indexedSubregions.get(tileId); if (subregions != null) { for (RoutingSubregionTile rs : subregions) { original = rs.loadRouteSegment(x31, y31, this, excludeDuplications, original); } } return original; } private void loadSubregionTile(final RoutingSubregionTile ts, boolean loadObjectsInMemory) { boolean wasUnloaded = ts.isUnloaded(); int ucount = ts.getUnloadCont(); if (nativeLib == null) { long now = System.nanoTime(); try { BinaryMapIndexReader reader = reverseMap.get(ts.subregion.routeReg); ts.setLoadedNonNative(); List<RouteDataObject> res = reader.loadRouteIndexData(ts.subregion); // System.out.println(ts.subregion.shiftToData + " " + res); for (RouteDataObject ro : res) { if (ro != null && config.router.acceptLine(ro)) { ts.add(ro); } } } catch (IOException e) { throw new RuntimeException("Loading data exception", e); } timeToLoad += (System.nanoTime() - now); } else { long now = System.nanoTime(); NativeRouteSearchResult ns = nativeLib.loadRouteRegion(ts.subregion, loadObjectsInMemory); // System.out.println(ts.subregion.shiftToData + " " + Arrays.toString(ns.objects)); ts.setLoadedNative(ns, this); timeToLoad += (System.nanoTime() - now); } loadedTiles++; if (wasUnloaded) { if (ucount == 1) { loadedPrevUnloadedTiles++; } } else { if (global != null) { global.allRoutes += ts.tileStatistics.allRoutes; global.coordinates += ts.tileStatistics.coordinates; } distinctLoadedTiles++; } global.size += ts.tileStatistics.size; } private List<RoutingSubregionTile> loadTileHeaders(final int x31, final int y31) { final int zoomToLoad = 31 - config.ZOOM_TO_LOAD_TILES; int tileX = x31 >> zoomToLoad; int tileY = y31 >> zoomToLoad; SearchRequest<RouteDataObject> request = BinaryMapIndexReader.buildSearchRouteRequest( tileX << zoomToLoad, (tileX + 1) << zoomToLoad, tileY << zoomToLoad, (tileY + 1) << zoomToLoad, null); List<RoutingSubregionTile> collection = null; for (Entry<BinaryMapIndexReader, List<RouteSubregion>> r : map.entrySet()) { // NOTE: load headers same as we do in non-native (it is not native optimized) try { if (r.getValue().size() > 0) { long now = System.nanoTime(); // int rg = r.getValue().get(0).routeReg.regionsRead; List<RouteSubregion> subregs = r.getKey().searchRouteIndexTree(request, r.getValue()); for (RouteSubregion sr : subregs) { int ind = searchSubregionTile(sr); RoutingSubregionTile found; if (ind < 0) { found = new RoutingSubregionTile(sr); subregionTiles.add(-(ind + 1), found); } else { found = subregionTiles.get(ind); } if (collection == null) { collection = new ArrayList<RoutingContext.RoutingSubregionTile>(4); } collection.add(found); } timeToLoadHeaders += (System.nanoTime() - now); } } catch (IOException e) { throw new RuntimeException("Loading data exception", e); } } return collection; } public void loadTileData(int x31, int y31, int zoomAround, final List<RouteDataObject> toFillIn) { int t = config.ZOOM_TO_LOAD_TILES - zoomAround; int coordinatesShift = (1 << (31 - config.ZOOM_TO_LOAD_TILES)); if (t <= 0) { t = 1; coordinatesShift = (1 << (31 - zoomAround)); } else { t = 1 << t; } TLongHashSet ts = new TLongHashSet(); long now = System.nanoTime(); for (int i = -t; i <= t; i++) { for (int j = -t; j <= t; j++) { ts.add( getRoutingTile( x31 + i * coordinatesShift, y31 + j * coordinatesShift, 0, OPTION_IN_MEMORY_LOAD)); } } TLongIterator it = ts.iterator(); while (it.hasNext()) { getAllObjects(it.next(), toFillIn); } timeToFindInitialSegments += (System.nanoTime() - now); } @SuppressWarnings("unused") private long getRoutingTile(int x31, int y31, int memoryLimit, int loadOptions) { // long now = System.nanoTime(); long xloc = x31 >> (31 - config.ZOOM_TO_LOAD_TILES); long yloc = y31 >> (31 - config.ZOOM_TO_LOAD_TILES); long tileId = (xloc << config.ZOOM_TO_LOAD_TILES) + yloc; if (loadOptions != OPTION_NO_LOAD) { if (memoryLimit == 0) { memoryLimit = config.memoryLimitation; } if (getCurrentEstimatedSize() > 0.9 * memoryLimit) { int sz1 = getCurrentEstimatedSize(); long h1 = 0; if (SHOW_GC_SIZE && sz1 > 0.7 * memoryLimit) { runGCUsedMemory(); h1 = runGCUsedMemory(); } int clt = getCurrentlyLoadedTiles(); long us1 = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()); unloadUnusedTiles(memoryLimit); if (h1 != 0 && getCurrentlyLoadedTiles() != clt) { int sz2 = getCurrentEstimatedSize(); runGCUsedMemory(); long h2 = runGCUsedMemory(); float mb = (1 << 20); log.warn( "Unload tiles : estimated " + (sz1 - sz2) / mb + " ?= " + (h1 - h2) / mb + " actual"); log.warn( "Used after " + h2 / mb + " of " + Runtime.getRuntime().totalMemory() / mb + " max " + maxMemory() / mb); } else { float mb = (1 << 20); int sz2 = getCurrentEstimatedSize(); log.warn( "Unload tiles : occupied before " + sz1 / mb + " Mb - now " + sz2 / mb + "MB " + memoryLimit / mb + " limit MB " + config.memoryLimitation / mb); long us2 = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()); log.warn( "Used memory before " + us1 / mb + "after " + us1 / mb + " of max " + maxMemory() / mb); } } if (!indexedSubregions.containsKey(tileId)) { List<RoutingSubregionTile> collection = loadTileHeaders(x31, y31); indexedSubregions.put(tileId, collection); } List<RoutingSubregionTile> subregions = indexedSubregions.get(tileId); if (subregions != null) { for (RoutingSubregionTile ts : subregions) { if (!ts.isLoaded()) { loadSubregionTile(ts, loadOptions == OPTION_IN_MEMORY_LOAD); } } } } // timeToLoad += (System.nanoTime() - now); return tileId; } private long maxMemory() { // AVIAN FIXME // return Runtime.getRuntime().maxMemory(); return 0; } public boolean checkIfMemoryLimitCritical(int memoryLimit) { return getCurrentEstimatedSize() > 0.9 * memoryLimit; } public void unloadUnusedTiles(int memoryLimit) { float desirableSize = memoryLimit * 0.7f; List<RoutingSubregionTile> list = new ArrayList<RoutingSubregionTile>(subregionTiles.size() / 2); int loaded = 0; for (RoutingSubregionTile t : subregionTiles) { if (t.isLoaded()) { list.add(t); loaded++; } } maxLoadedTiles = Math.max(maxLoadedTiles, getCurrentlyLoadedTiles()); Collections.sort( list, new Comparator<RoutingSubregionTile>() { private int pow(int base, int pw) { int r = 1; for (int i = 0; i < pw; i++) { r *= base; } return r; } @Override public int compare(RoutingSubregionTile o1, RoutingSubregionTile o2) { int v1 = (o1.access + 1) * pow(10, o1.getUnloadCont() - 1); int v2 = (o2.access + 1) * pow(10, o2.getUnloadCont() - 1); return v1 < v2 ? -1 : (v1 == v2 ? 0 : 1); } }); int i = 0; while (getCurrentEstimatedSize() >= desirableSize && (list.size() - i) > loaded / 5 && i < list.size()) { RoutingSubregionTile unload = list.get(i); i++; // System.out.println("Unload " + unload); unload.unload(); unloadedTiles++; global.size -= unload.tileStatistics.size; // tile could be cleaned from routing tiles and deleted from whole list } for (RoutingSubregionTile t : subregionTiles) { t.access /= 3; } } private void getAllObjects(long tileId, final List<RouteDataObject> toFillIn) { TLongObjectHashMap<RouteDataObject> excludeDuplications = new TLongObjectHashMap<RouteDataObject>(); if (tileRoutes.containsKey(tileId)) { List<RouteDataObject> routes = tileRoutes.get(tileId); if (routes != null) { for (RouteDataObject ro : routes) { if (!excludeDuplications.contains(ro.id)) { excludeDuplications.put(ro.id, ro); toFillIn.add(ro); } } } } List<RoutingSubregionTile> subregions = indexedSubregions.get(tileId); if (subregions != null) { for (RoutingSubregionTile rs : subregions) { rs.loadAllObjects(toFillIn, this, excludeDuplications); } } } protected static long runGCUsedMemory() { Runtime runtime = Runtime.getRuntime(); long usedMem1 = runtime.totalMemory() - runtime.freeMemory(); long usedMem2 = Long.MAX_VALUE; int cnt = 4; while (cnt-- >= 0) { for (int i = 0; (usedMem1 < usedMem2) && (i < 1000); ++i) { // AVIAN FIXME runtime.runFinalization(); runtime.gc(); Thread.yield(); usedMem2 = usedMem1; usedMem1 = runtime.totalMemory() - runtime.freeMemory(); } } return usedMem1; } private static long calcRouteId(RouteDataObject o, int ind) { return (o.getId() << 10) + ind; } public static class RoutingSubregionTile { public final RouteSubregion subregion; // make it without get/set for fast access public int access; public TileStatistics tileStatistics = new TileStatistics(); private NativeRouteSearchResult searchResult = null; private int isLoaded = 0; private TLongObjectMap<RouteSegment> routes = null; public RoutingSubregionTile(RouteSubregion subregion) { this.subregion = subregion; } private void loadAllObjects( final List<RouteDataObject> toFillIn, RoutingContext ctx, TLongObjectHashMap<RouteDataObject> excludeDuplications) { if (routes != null) { Iterator<RouteSegment> it = routes.valueCollection().iterator(); while (it.hasNext()) { RouteSegment rs = it.next(); while (rs != null) { RouteDataObject ro = rs.road; if (!excludeDuplications.contains(ro.id)) { excludeDuplications.put(ro.id, ro); toFillIn.add(ro); } rs = rs.next; } } } else if (searchResult != null) { RouteDataObject[] objects = searchResult.objects; if (objects != null) { for (RouteDataObject ro : objects) { if (ro != null && !excludeDuplications.contains(ro.id)) { excludeDuplications.put(ro.id, ro); toFillIn.add(ro); } } } } } private RouteSegment loadRouteSegment( int x31, int y31, RoutingContext ctx, TLongObjectHashMap<RouteDataObject> excludeDuplications, RouteSegment original) { if (searchResult == null && routes == null) { return original; } access++; if (searchResult == null) { long l = (((long) x31) << 31) + (long) y31; RouteSegment segment = routes.get(l); while (segment != null) { RouteDataObject ro = segment.road; RouteDataObject toCmp = excludeDuplications.get(calcRouteId(ro, segment.getSegmentStart())); if (toCmp == null || toCmp.getPointsLength() < ro.getPointsLength()) { excludeDuplications.put(calcRouteId(ro, segment.getSegmentStart()), ro); RouteSegment s = new RouteSegment(ro, segment.getSegmentStart()); s.next = original; original = s; } segment = segment.next; } return original; } // Native use case long nanoTime = System.nanoTime(); RouteDataObject[] res = ctx.nativeLib.getDataObjects(searchResult, x31, y31); ctx.timeToLoad += (System.nanoTime() - nanoTime); if (res != null) { for (RouteDataObject ro : res) { boolean accept = ro != null; if (ctx != null) { accept = ctx.getRouter().acceptLine(ro); } if (accept) { for (int i = 0; i < ro.pointsX.length; i++) { if (ro.getPoint31XTile(i) == x31 && ro.getPoint31YTile(i) == y31) { RouteDataObject toCmp = excludeDuplications.get(calcRouteId(ro, i)); if (toCmp == null || toCmp.getPointsLength() < ro.getPointsLength()) { RouteSegment segment = new RouteSegment(ro, i); segment.next = original; original = segment; excludeDuplications.put(calcRouteId(ro, i), ro); } } } } } } return original; } public boolean isLoaded() { return isLoaded > 0; } public int getUnloadCont() { return Math.abs(isLoaded); } public boolean isUnloaded() { return isLoaded < 0; } public void unload() { if (isLoaded == 0) { this.isLoaded = -1; } else { isLoaded = -Math.abs(isLoaded); } if (searchResult != null) { searchResult.deleteNativeResult(); } searchResult = null; routes = null; } public void setLoadedNonNative() { isLoaded = Math.abs(isLoaded) + 1; routes = new TLongObjectHashMap<BinaryRoutePlanner.RouteSegment>(); tileStatistics = new TileStatistics(); } public void add(RouteDataObject ro) { tileStatistics.addObject(ro); for (int i = 0; i < ro.pointsX.length; i++) { int x31 = ro.getPoint31XTile(i); int y31 = ro.getPoint31YTile(i); long l = (((long) x31) << 31) + (long) y31; RouteSegment segment = new RouteSegment(ro, i); if (!routes.containsKey(l)) { routes.put(l, segment); } else { RouteSegment orig = routes.get(l); while (orig.next != null) { orig = orig.next; } orig.next = segment; } } } public void setLoadedNative(NativeRouteSearchResult r, RoutingContext ctx) { isLoaded = Math.abs(isLoaded) + 1; tileStatistics = new TileStatistics(); if (r.objects != null) { searchResult = null; routes = new TLongObjectHashMap<BinaryRoutePlanner.RouteSegment>(); for (RouteDataObject ro : r.objects) { if (ro != null && ctx.config.router.acceptLine(ro)) { add(ro); } } } else { searchResult = r; tileStatistics.size += 100; } } } static int getEstimatedSize(RouteDataObject o) { // calculate size int sz = 0; sz += 8 + 4; // overhead if (o.names != null) { sz += 12; TIntObjectIterator<String> it = o.names.iterator(); while (it.hasNext()) { it.advance(); String vl = it.value(); sz += 12 + vl.length(); } sz += 12 + o.names.size() * 25; } sz += 8; // id // coordinates sz += (8 + 4 + 4 * o.getPointsLength()) * 4; sz += o.types == null ? 4 : (8 + 4 + 4 * o.types.length); sz += o.restrictions == null ? 4 : (8 + 4 + 8 * o.restrictions.length); sz += 4; if (o.pointTypes != null) { sz += 8 + 4 * o.pointTypes.length; for (int i = 0; i < o.pointTypes.length; i++) { sz += 4; if (o.pointTypes[i] != null) { sz += 8 + 8 * o.pointTypes[i].length; } } } // Standard overhead? return (int) (sz * 3.5); } protected static class TileStatistics { public int size = 0; public int allRoutes = 0; public int coordinates = 0; @Override public String toString() { return "All routes " + allRoutes + " size " + (size / 1024f) + " KB coordinates " + coordinates + " ratio coord " + (((float) size) / coordinates) + " ratio routes " + (((float) size) / allRoutes); } public void addObject(RouteDataObject o) { allRoutes++; coordinates += o.getPointsLength() * 2; size += getEstimatedSize(o); } } protected static class RouteDataBorderLine implements Comparable<RouteDataBorderLine> { final List<RouteDataBorderLinePoint> borderPoints = new ArrayList<RouteDataBorderLinePoint>(); final int borderLine; public RouteDataBorderLine(int borderLine) { this.borderLine = borderLine; } @Override public int compareTo(RouteDataBorderLine o) { if (o.borderLine == borderLine) { return 0; } return borderLine < o.borderLine ? -1 : 1; } } }
public class SQLiteTileSource implements ITileSource { public static final String EXT = IndexConstants.SQLITE_EXT; private static final Log LOG = PlatformUtil.getLog(SQLiteTileSource.class); private ITileSource base; private String urlTemplate = null; private String name; private SQLiteConnection db = null; private final File file; private int minZoom = 1; private int maxZoom = 17; private boolean inversiveZoom = true; // BigPlanet private boolean timeSupported = false; private int expirationTimeMillis = -1; // never private boolean isEllipsoid = false; private String rule = null; private String referer = null; static final int tileSize = 256; private OsmandApplication ctx; private boolean onlyReadonlyAvailable = false; public SQLiteTileSource(OsmandApplication ctx, File f, List<TileSourceTemplate> toFindUrl) { this.ctx = ctx; this.file = f; if (f != null) { int i = f.getName().lastIndexOf('.'); name = f.getName().substring(0, i); i = name.lastIndexOf('.'); if (i > 0) { String sourceName = name.substring(i + 1); for (TileSourceTemplate is : toFindUrl) { if (is.getName().equalsIgnoreCase(sourceName)) { base = is; urlTemplate = is.getUrlTemplate(); break; } } } } } @Override public int getBitDensity() { return base != null ? base.getBitDensity() : 16; } @Override public int getMaximumZoomSupported() { getDatabase(); return base != null ? base.getMaximumZoomSupported() : maxZoom; } @Override public int getMinimumZoomSupported() { getDatabase(); return base != null ? base.getMinimumZoomSupported() : minZoom; } @Override public String getName() { return name; } @Override public String getTileFormat() { return base != null ? base.getTileFormat() : ".png"; // $NON-NLS-1$ } @Override public int getTileSize() { return base != null ? base.getTileSize() : tileSize; } Interpreter bshInterpreter = null; @Override public String getUrlToLoad(int x, int y, int zoom) { if (zoom > maxZoom) return null; SQLiteConnection db = getDatabase(); if (db == null || db.isReadOnly() || urlTemplate == null) { return null; } if (TileSourceManager.RULE_BEANSHELL.equalsIgnoreCase(rule)) { try { if (bshInterpreter == null) { bshInterpreter = new Interpreter(); bshInterpreter.eval(urlTemplate); } return (String) bshInterpreter.eval("getTileUrl(" + zoom + "," + x + "," + y + ");"); } catch (bsh.EvalError e) { LOG.debug("getUrlToLoad Error" + e.getMessage()); AccessibleToast.makeText(ctx, e.getMessage(), Toast.LENGTH_LONG).show(); LOG.error(e.getMessage(), e); return null; } } else { return MessageFormat.format( urlTemplate, zoom + "", x + "", y + ""); // $NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((base == null) ? 0 : base.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SQLiteTileSource other = (SQLiteTileSource) obj; if (base == null) { if (other.base != null) return false; } else if (!base.equals(other.base)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } protected SQLiteConnection getDatabase() { if ((db == null || db.isClosed()) && file.exists()) { LOG.debug("Open " + file.getAbsolutePath()); try { onlyReadonlyAvailable = false; db = ctx.getSQLiteAPI().openByAbsolutePath(file.getAbsolutePath(), false); } catch (RuntimeException e) { onlyReadonlyAvailable = true; db = ctx.getSQLiteAPI().openByAbsolutePath(file.getAbsolutePath(), true); } try { SQLiteCursor cursor = db.rawQuery("SELECT * FROM info", null); if (cursor.moveToFirst()) { String[] columnNames = cursor.getColumnNames(); List<String> list = Arrays.asList(columnNames); int url = list.indexOf("url"); if (url != -1) { String template = cursor.getString(url); if (!Algorithms.isEmpty(template)) { // urlTemplate = template; urlTemplate = TileSourceTemplate.normalizeUrl(template); } } int ruleId = list.indexOf("rule"); if (ruleId != -1) { rule = cursor.getString(ruleId); } int refererId = list.indexOf("referer"); if (refererId != -1) { referer = cursor.getString(refererId); } int tnumbering = list.indexOf("tilenumbering"); if (tnumbering != -1) { inversiveZoom = "BigPlanet".equalsIgnoreCase(cursor.getString(tnumbering)); } else { inversiveZoom = true; addInfoColumn("tilenumbering", "BigPlanet"); } int timecolumn = list.indexOf("timecolumn"); if (timecolumn != -1) { timeSupported = "yes".equalsIgnoreCase(cursor.getString(timecolumn)); } else { timeSupported = hasTimeColumn(); addInfoColumn("timecolumn", timeSupported ? "yes" : "no"); } int expireminutes = list.indexOf("expireminutes"); this.expirationTimeMillis = -1; if (expireminutes != -1) { int minutes = (int) cursor.getInt(expireminutes); if (minutes > 0) { this.expirationTimeMillis = minutes * 60 * 1000; } } else { addInfoColumn("expireminutes", "0"); } int ellipsoid = list.indexOf("ellipsoid"); if (ellipsoid != -1) { int set = (int) cursor.getInt(ellipsoid); if (set == 1) { this.isEllipsoid = true; } } // boolean inversiveInfoZoom = tnumbering != -1 && // "BigPlanet".equals(cursor.getString(tnumbering)); boolean inversiveInfoZoom = inversiveZoom; int mnz = list.indexOf("minzoom"); if (mnz != -1) { minZoom = (int) cursor.getInt(mnz); } int mxz = list.indexOf("maxzoom"); if (mxz != -1) { maxZoom = (int) cursor.getInt(mxz); } if (inversiveInfoZoom) { mnz = minZoom; minZoom = 17 - maxZoom; maxZoom = 17 - mnz; } } cursor.close(); } catch (RuntimeException e) { e.printStackTrace(); } } return db; } private void addInfoColumn(String columnName, String value) { if (!onlyReadonlyAvailable) { db.execSQL("alter table info add column " + columnName + " TEXT"); db.execSQL("update info set " + columnName + " = '" + value + "'"); } } private boolean hasTimeColumn() { SQLiteCursor cursor; cursor = db.rawQuery("SELECT * FROM tiles", null); cursor.moveToFirst(); List<String> cols = Arrays.asList(cursor.getColumnNames()); boolean timeSupported = cols.contains("time"); cursor.close(); return timeSupported; } public boolean exists(int x, int y, int zoom) { SQLiteConnection db = getDatabase(); if (db == null) { return false; } long time = System.currentTimeMillis(); try { int z = getFileZoom(zoom); SQLiteCursor cursor = db.rawQuery( "SELECT 1 FROM tiles WHERE x = ? AND y = ? AND z = ?", new String[] { x + "", y + "", z + "" }); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ try { boolean e = cursor.moveToFirst(); cursor.close(); return e; } catch (SQLiteDiskIOException e) { return false; } } finally { if (LOG.isDebugEnabled()) { LOG.debug( "Checking tile existance x = " + x + " y = " + y + " z = " + zoom + " for " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } } } public boolean isLocked() { SQLiteConnection db = getDatabase(); if (db == null) { return false; } return db.isDbLockedByOtherThreads(); } public byte[] getBytes(int x, int y, int zoom, String dirWithTiles, long[] timeHolder) throws IOException { SQLiteConnection db = getDatabase(); if (db == null) { return null; } long ts = System.currentTimeMillis(); try { if (zoom <= maxZoom) { // return the normal tile if exists String[] params = new String[] {x + "", y + "", getFileZoom(zoom) + ""}; boolean queryTime = timeHolder != null && timeHolder.length > 0 && timeSupported; SQLiteCursor cursor = db.rawQuery( "SELECT image " + (queryTime ? ", time" : "") + " FROM tiles WHERE x = ? AND y = ? AND z = ?", params); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ byte[] blob = null; if (cursor.moveToFirst()) { blob = cursor.getBlob(0); if (queryTime) { timeHolder[0] = cursor.getLong(1); } } cursor.close(); return blob; } return null; } finally { if (LOG.isDebugEnabled()) { LOG.debug( "Load tile " + x + "/" + y + "/" + zoom + " for " + (System.currentTimeMillis() - ts) + " ms "); } } } @Override public byte[] getBytes(int x, int y, int zoom, String dirWithTiles) throws IOException { return getBytes(x, y, zoom, dirWithTiles, null); } public Bitmap getImage(int x, int y, int zoom, long[] timeHolder) { SQLiteConnection db = getDatabase(); if (db == null) { return null; } String[] params = new String[] {x + "", y + "", getFileZoom(zoom) + ""}; byte[] blob; try { blob = getBytes(x, y, zoom, null, timeHolder); } catch (IOException e) { return null; } if (blob != null) { Bitmap bmp = null; bmp = BitmapFactory.decodeByteArray(blob, 0, blob.length); if (bmp == null) { // broken image delete it db.execSQL("DELETE FROM tiles WHERE x = ? AND y = ? AND z = ?", params); } return bmp; } return null; } public ITileSource getBase() { return base; } public QuadRect getRectBoundary(int coordinatesZoom, int minZ) { SQLiteConnection db = getDatabase(); if (db == null || coordinatesZoom > 25) { return null; } SQLiteCursor q; if (inversiveZoom) { int minZoom = (17 - minZ) + 1; // 17 - z = zoom, x << (25 - zoom) = 25th x tile = 8 + z, q = db.rawQuery( "SELECT max(x << (8+z)), min(x << (8+z)), max(y << (8+z)), min(y << (8+z))" + " from tiles where z < " + minZoom, new String[0]); } else { q = db.rawQuery( "SELECT max(x << (25-z)), min(x << (25-z)), max(y << (25-z)), min(y << (25-z))" + " from tiles where z > " + minZ, new String[0]); } q.moveToFirst(); int right = (int) (q.getInt(0) >> (25 - coordinatesZoom)); int left = (int) (q.getInt(1) >> (25 - coordinatesZoom)); int top = (int) (q.getInt(3) >> (25 - coordinatesZoom)); int bottom = (int) (q.getInt(2) >> (25 - coordinatesZoom)); return new QuadRect(left, top, right, bottom); } public void deleteImage(int x, int y, int zoom) { SQLiteConnection db = getDatabase(); if (db == null || db.isReadOnly()) { return; } db.execSQL( "DELETE FROM tiles WHERE x = ? AND y = ? AND z = ?", new String[] { x + "", y + "", getFileZoom(zoom) + "" }); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ } private static final int BUF_SIZE = 1024; public void insertImage(int x, int y, int zoom, File fileToSave) throws IOException { ByteBuffer buf = ByteBuffer.allocate((int) fileToSave.length()); FileInputStream is = new FileInputStream(fileToSave); int i = 0; byte[] b = new byte[BUF_SIZE]; while ((i = is.read(b, 0, BUF_SIZE)) > -1) { buf.put(b, 0, i); } insertImage(x, y, zoom, buf.array()); is.close(); } /** * Makes method synchronized to give a little more time for get methods and let all writing * attempts to wait outside of this method */ public /*synchronized*/ void insertImage(int x, int y, int zoom, byte[] dataToSave) throws IOException { SQLiteConnection db = getDatabase(); if (db == null || db.isReadOnly() || onlyReadonlyAvailable) { return; } /*There is no sense to downoad and do not save. If needed, check should perform before downlad if (exists(x, y, zoom)) { return; }*/ String query = timeSupported ? "INSERT OR REPLACE INTO tiles(x,y,z,s,image,time) VALUES(?, ?, ?, ?, ?, ?)" : "INSERT OR REPLACE INTO tiles(x,y,z,s,image) VALUES(?, ?, ?, ?, ?)"; net.osmand.plus.api.SQLiteAPI.SQLiteStatement statement = db.compileStatement(query); // $NON-NLS-1$ statement.bindLong(1, x); statement.bindLong(2, y); statement.bindLong(3, getFileZoom(zoom)); statement.bindLong(4, 0); statement.bindBlob(5, dataToSave); if (timeSupported) { statement.bindLong(6, System.currentTimeMillis()); } statement.execute(); statement.close(); } private int getFileZoom(int zoom) { return inversiveZoom ? 17 - zoom : zoom; } public void closeDB() { LOG.debug("closeDB"); bshInterpreter = null; if (timeSupported) clearOld(); if (db != null) { db.close(); db = null; } } public void clearOld() { SQLiteConnection db = getDatabase(); if (db == null || db.isReadOnly()) { return; } LOG.debug( "DELETE FROM tiles WHERE time<" + (System.currentTimeMillis() - getExpirationTimeMillis())); db.execSQL( "DELETE FROM tiles WHERE time<" + (System.currentTimeMillis() - getExpirationTimeMillis())); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ db.execSQL("VACUUM"); // $NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ } @Override public boolean couldBeDownloadedFromInternet() { if (getDatabase() == null || getDatabase().isReadOnly() || onlyReadonlyAvailable) { return false; } return urlTemplate != null; } @Override public boolean isEllipticYTile() { return this.isEllipsoid; // return false; } public int getExpirationTimeMinutes() { if (expirationTimeMillis < 0) { return -1; } return expirationTimeMillis / (60 * 1000); } public int getExpirationTimeMillis() { return expirationTimeMillis; } public String getReferer() { return referer; } }
public class OsmandApplication extends Application { public static final String EXCEPTION_PATH = "exception.log"; // $NON-NLS-1$ private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(OsmandApplication.class); final AppInitializer appInitializer = new AppInitializer(this); OsmandSettings osmandSettings = null; OsmAndAppCustomization appCustomization; private final SQLiteAPI sqliteAPI = new SQLiteAPIImpl(this); private final OsmAndTaskManager taskManager = new OsmAndTaskManager(this); private final IconsCache iconsCache = new IconsCache(this); Handler uiHandler; NavigationService navigationService; // start variables ResourceManager resourceManager; OsmAndLocationProvider locationProvider; RendererRegistry rendererRegistry; DayNightHelper daynightHelper; PoiFiltersHelper poiFilters; MapPoiTypes poiTypes; RoutingHelper routingHelper; FavouritesDbHelper favorites; CommandPlayer player; GpxSelectionHelper selectedGpxHelper; SavingTrackHelper savingTrackHelper; NotificationHelper notificationHelper; LiveMonitoringHelper liveMonitoringHelper; TargetPointsHelper targetPointsHelper; WaypointHelper waypointHelper; AvoidSpecificRoads avoidSpecificRoads; BRouterServiceConnection bRouterServiceConnection; OsmandRegions regions; RoutingConfiguration.Builder defaultRoutingConfig; private Locale preferredLocale = null; private Locale defaultLocale; private File externalStorageDirectory; // Typeface @Override public void onCreate() { long timeToStart = System.currentTimeMillis(); if (Version.getAppName(this).equals("OsmAnd~")) { if (android.os.Build.VERSION.SDK_INT >= 9) { try { Class.forName("net.osmand.plus.base.EnableStrictMode").newInstance(); } catch (Exception e) { e.printStackTrace(); } } } super.onCreate(); createInUiThread(); uiHandler = new Handler(); appCustomization = new OsmAndAppCustomization(); appCustomization.setup(this); osmandSettings = appCustomization.getOsmandSettings(); externalStorageDirectory = osmandSettings.getExternalStorageDirectory(); checkPreferredLocale(); appInitializer.onCreateApplication(); // if(!osmandSettings.FOLLOW_THE_ROUTE.get()) { // targetPointsHelper.clearPointToNavigate(false); // } startApplication(); System.out.println( "Time to start application " + (System.currentTimeMillis() - timeToStart) + " ms. Should be less < 800 ms"); timeToStart = System.currentTimeMillis(); OsmandPlugin.initPlugins(this); System.out.println( "Time to init plugins " + (System.currentTimeMillis() - timeToStart) + " ms. Should be less < 800 ms"); osmandSettings.NUMBER_OF_APPLICATION_STARTS.set( osmandSettings.NUMBER_OF_APPLICATION_STARTS.get() + 1); } public AppInitializer getAppInitializer() { return appInitializer; } public MapPoiTypes getPoiTypes() { return poiTypes; } private void createInUiThread() { new Toast(this); // activate in UI thread to avoid further exceptions new AsyncTask<View, Void, Void>() { @Override protected Void doInBackground(View... params) { return null; } protected void onPostExecute(Void result) {} }.execute(); } public IconsCache getIconsCache() { return iconsCache; } @Override public void onTerminate() { super.onTerminate(); if (routingHelper != null) { routingHelper.getVoiceRouter().onApplicationTerminate(); } if (DashRateUsFragment.shouldShow(osmandSettings)) { osmandSettings.RATE_US_STATE.set(DashRateUsFragment.RateUsState.IGNORED); } getNotificationHelper().removeServiceNotification(); } public RendererRegistry getRendererRegistry() { return rendererRegistry; } public OsmAndTaskManager getTaskManager() { return taskManager; } public AvoidSpecificRoads getAvoidSpecificRoads() { return avoidSpecificRoads; } public OsmAndLocationProvider getLocationProvider() { return locationProvider; } public OsmAndAppCustomization getAppCustomization() { return appCustomization; } public void setAppCustomization(OsmAndAppCustomization appCustomization) { this.appCustomization = appCustomization; this.appCustomization.setup(this); } /** * Application settings * * @return Reference to instance of OsmandSettings */ public OsmandSettings getSettings() { if (osmandSettings == null) { LOG.error("Trying to access settings before they were created"); } return osmandSettings; } public SavingTrackHelper getSavingTrackHelper() { return savingTrackHelper; } public NotificationHelper getNotificationHelper() { return notificationHelper; } public LiveMonitoringHelper getLiveMonitoringHelper() { return liveMonitoringHelper; } public WaypointHelper getWaypointHelper() { return waypointHelper; } public PoiFiltersHelper getPoiFilters() { return poiFilters; } public GpxSelectionHelper getSelectedGpxHelper() { return selectedGpxHelper; } public FavouritesDbHelper getFavorites() { return favorites; } public ResourceManager getResourceManager() { return resourceManager; } public DayNightHelper getDaynightHelper() { return daynightHelper; } @Override public void onLowMemory() { super.onLowMemory(); resourceManager.onLowMemory(); } @Override public void onConfigurationChanged(Configuration newConfig) { if (preferredLocale != null && !newConfig.locale.getLanguage().equals(preferredLocale.getLanguage())) { super.onConfigurationChanged(newConfig); // ugly fix ! On devices after 4.0 screen is blinking when you rotate device! if (Build.VERSION.SDK_INT < 14) { newConfig.locale = preferredLocale; } getBaseContext() .getResources() .updateConfiguration(newConfig, getBaseContext().getResources().getDisplayMetrics()); Locale.setDefault(preferredLocale); } else { super.onConfigurationChanged(newConfig); } } public void checkPreferredLocale() { Configuration config = getBaseContext().getResources().getConfiguration(); String lang = osmandSettings.PREFERRED_LOCALE.get(); if (defaultLocale == null) { defaultLocale = Locale.getDefault(); } if (!"".equals(lang) && !config.locale.getLanguage().equals(lang)) { preferredLocale = new Locale(lang); Locale.setDefault(preferredLocale); config.locale = preferredLocale; getBaseContext() .getResources() .updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); } else if ("".equals(lang) && defaultLocale != null && Locale.getDefault() != defaultLocale) { Locale.setDefault(defaultLocale); config.locale = defaultLocale; preferredLocale = null; getBaseContext() .getResources() .updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); } } public static final int PROGRESS_DIALOG = 5; public void checkApplicationIsBeingInitialized( Activity activity, AppInitializeListener listener) { // start application if it was previously closed startApplication(); if (listener != null) { appInitializer.addListener(listener); } } public void unsubscribeInitListener(AppInitializeListener listener) { if (listener != null) { appInitializer.removeListener(listener); } } public boolean isApplicationInitializing() { return appInitializer.isAppInitializing(); } public RoutingHelper getRoutingHelper() { return routingHelper; } public CommandPlayer getPlayer() { return player; } public void initVoiceCommandPlayer(final Activity uiContext) { showDialogInitializingCommandPlayer(uiContext, true, null, false); } public void showDialogInitializingCommandPlayer( final Activity uiContext, boolean warningNoneProvider) { showDialogInitializingCommandPlayer(uiContext, warningNoneProvider, null, true); } public void showDialogInitializingCommandPlayer( final Activity uiContext, boolean warningNoneProvider, Runnable run, boolean showDialog) { String voiceProvider = osmandSettings.VOICE_PROVIDER.get(); if (voiceProvider == null || OsmandSettings.VOICE_PROVIDER_NOT_USE.equals(voiceProvider)) { if (warningNoneProvider && voiceProvider == null) { Builder builder = new AccessibleAlertBuilder(uiContext); LinearLayout ll = new LinearLayout(uiContext); ll.setOrientation(LinearLayout.VERTICAL); final TextView tv = new TextView(uiContext); tv.setPadding(7, 3, 7, 0); tv.setText(R.string.voice_is_not_available_msg); tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 19); ll.addView(tv); final CheckBox cb = new CheckBox(uiContext); cb.setText(R.string.shared_string_remember_my_choice); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); lp.setMargins(7, 10, 7, 0); cb.setLayoutParams(lp); ll.addView(cb); builder.setCancelable(true); builder.setNegativeButton( R.string.shared_string_cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (cb.isChecked()) { osmandSettings.VOICE_PROVIDER.set(OsmandSettings.VOICE_PROVIDER_NOT_USE); } } }); builder.setPositiveButton( R.string.shared_string_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(uiContext, SettingsActivity.class); intent.putExtra( SettingsActivity.INTENT_KEY_SETTINGS_SCREEN, SettingsActivity.SCREEN_GENERAL_SETTINGS); uiContext.startActivity(intent); } }); builder.setTitle(R.string.voice_is_not_available_title); builder.setView(ll); // builder.setMessage(R.string.voice_is_not_available_msg); builder.show(); } } else { if (player == null || !Algorithms.objectEquals(voiceProvider, player.getCurrentVoice())) { appInitializer.initVoiceDataInDifferentThread(uiContext, voiceProvider, run, showDialog); } } } public NavigationService getNavigationService() { return navigationService; } public void setNavigationService(NavigationService navigationService) { this.navigationService = navigationService; } private void fullExit() { // http://stackoverflow.com/questions/2092951/how-to-close-android-application System.runFinalizersOnExit(true); System.exit(0); } public synchronized void closeApplication(final Activity activity) { if (getNavigationService() != null) { Builder bld = new AlertDialog.Builder(activity); bld.setMessage(R.string.background_service_is_enabled_question); bld.setPositiveButton( R.string.shared_string_yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { closeApplicationAnywayImpl(activity, true); } }); bld.setNegativeButton( R.string.shared_string_no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { closeApplicationAnywayImpl(activity, false); } }); bld.show(); } else { closeApplicationAnywayImpl(activity, true); } } private void closeApplicationAnyway(final Activity activity, boolean disableService) { activity.finish(); Intent newIntent = new Intent(activity, ExitActivity.class); newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); newIntent.putExtra(ExitActivity.DISABLE_SERVICE, disableService); startActivity(newIntent); } public void closeApplicationAnywayImpl(final Activity activity, boolean disableService) { if (appInitializer.isAppInitializing()) { resourceManager.close(); } activity.finish(); if (getNavigationService() == null) { fullExit(); } else if (disableService) { final Intent serviceIntent = new Intent(this, NavigationService.class); stopService(serviceIntent); new Thread( new Runnable() { public void run() { // wait until the service has fully stopped while (getNavigationService() != null) { try { Thread.sleep(100); } catch (InterruptedException e) { } } fullExit(); } }) .start(); } } public void startApplication() { Thread.setDefaultUncaughtExceptionHandler(new DefaultExceptionHandler()); appInitializer.startApplication(); } private class DefaultExceptionHandler implements UncaughtExceptionHandler { private UncaughtExceptionHandler defaultHandler; private PendingIntent intent; public DefaultExceptionHandler() { defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); intent = PendingIntent.getActivity( OsmandApplication.this.getBaseContext(), 0, new Intent( OsmandApplication.this.getBaseContext(), getAppCustomization().getMapActivity()), 0); } @Override public void uncaughtException(final Thread thread, final Throwable ex) { File file = getAppPath(EXCEPTION_PATH); try { ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream printStream = new PrintStream(out); ex.printStackTrace(printStream); StringBuilder msg = new StringBuilder(); msg.append("Version " + Version.getFullVersion(OsmandApplication.this) + "\n") . //$NON-NLS-1$ append(DateFormat.format("dd.MM.yyyy h:mm:ss", System.currentTimeMillis())); try { PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0); if (info != null) { msg.append("\nApk Version : ") .append(info.versionName) .append(" ") .append(info.versionCode); // $NON-NLS-1$ //$NON-NLS-2$ } } catch (Throwable e) { } msg.append("\n") . //$NON-NLS-1$//$NON-NLS-2$ append("Exception occured in thread " + thread.toString() + " : \n") . //$NON-NLS-1$ //$NON-NLS-2$ append(new String(out.toByteArray())); if (file.getParentFile().canWrite()) { BufferedWriter writer = new BufferedWriter(new FileWriter(file, true)); writer.write(msg.toString()); writer.close(); } if (routingHelper.isFollowingMode()) { AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 2000, intent); System.exit(2); } defaultHandler.uncaughtException(thread, ex); } catch (Exception e) { // swallow all exceptions android.util.Log.e( PlatformUtil.TAG, "Exception while handle other exception", e); // $NON-NLS-1$ } } } public TargetPointsHelper getTargetPointsHelper() { return targetPointsHelper; } public void showShortToastMessage(final int msgId, final Object... args) { uiHandler.post( new Runnable() { @Override public void run() { AccessibleToast.makeText( OsmandApplication.this, getString(msgId, args), Toast.LENGTH_SHORT) .show(); } }); } public void showShortToastMessage(final String msg) { uiHandler.post( new Runnable() { @Override public void run() { AccessibleToast.makeText(OsmandApplication.this, msg, Toast.LENGTH_SHORT).show(); } }); } public void showToastMessage(final int msgId, final Object... args) { uiHandler.post( new Runnable() { @Override public void run() { AccessibleToast.makeText( OsmandApplication.this, getString(msgId, args), Toast.LENGTH_LONG) .show(); } }); } public void showToastMessage(final String msg) { uiHandler.post( new Runnable() { @Override public void run() { AccessibleToast.makeText(OsmandApplication.this, msg, Toast.LENGTH_LONG).show(); } }); } public SQLiteAPI getSQLiteAPI() { return sqliteAPI; } public void runInUIThread(Runnable run) { uiHandler.post(run); } public void runInUIThread(Runnable run, long delay) { uiHandler.postDelayed(run, delay); } public void runMessageInUIThreadAndCancelPrevious( final int messageId, final Runnable run, long delay) { Message msg = Message.obtain( uiHandler, new Runnable() { @Override public void run() { if (!uiHandler.hasMessages(messageId)) { run.run(); } } }); msg.what = messageId; uiHandler.removeMessages(messageId); uiHandler.sendMessageDelayed(msg, delay); } public File getAppPath(String path) { if (path == null) { path = ""; } return new File(externalStorageDirectory, path); } public void setExternalStorageDirectory(int type, String directory) { osmandSettings.setExternalStorageDirectory(type, directory); externalStorageDirectory = osmandSettings.getExternalStorageDirectory(); getResourceManager().resetStoreDirectory(); } public void applyTheme(Context c) { int t = R.style.OsmandDarkTheme; if (osmandSettings.OSMAND_THEME.get() == OsmandSettings.OSMAND_DARK_THEME) { t = R.style.OsmandDarkTheme; } else if (osmandSettings.OSMAND_THEME.get() == OsmandSettings.OSMAND_LIGHT_THEME) { t = R.style.OsmandLightTheme; } setLanguage(c); c.setTheme(t); } public IBRouterService getBRouterService() { if (bRouterServiceConnection == null) { return null; } return bRouterServiceConnection.getBrouterService(); } public void setLanguage(Context context) { if (preferredLocale != null) { Configuration config = context.getResources().getConfiguration(); String lang = preferredLocale.getLanguage(); if (!"".equals(lang) && !config.locale.getLanguage().equals(lang)) { preferredLocale = new Locale(lang); Locale.setDefault(preferredLocale); config.locale = preferredLocale; context .getResources() .updateConfiguration(config, context.getResources().getDisplayMetrics()); } else if ("".equals(lang) && defaultLocale != null && Locale.getDefault() != defaultLocale) { Locale.setDefault(defaultLocale); config.locale = defaultLocale; getBaseContext() .getResources() .updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); } } } public String getLanguage() { String lang = ""; if (preferredLocale != null) { lang = preferredLocale.getLanguage(); } else { lang = Locale.getDefault().getLanguage(); } if (lang != null && lang.length() > 2) { lang = lang.substring(0, 2).toLowerCase(); } return lang; } public RoutingConfiguration.Builder getDefaultRoutingConfig() { if (defaultRoutingConfig == null) { defaultRoutingConfig = appInitializer.getLazyDefaultRoutingConfig(); } return defaultRoutingConfig; } public OsmandRegions getRegions() { return regions; } public boolean accessibilityExtensions() { return (Build.VERSION.SDK_INT < 14) ? getSettings().ACCESSIBILITY_EXTENSIONS.get() : false; } public boolean accessibilityEnabled() { final AccessibilityMode mode = getSettings().ACCESSIBILITY_MODE.get(); if (OsmandPlugin.getEnabledPlugin(AccessibilityPlugin.class) == null) { return false; } if (mode == AccessibilityMode.ON) { return true; } else if (mode == AccessibilityMode.OFF) { return false; } return ((AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE)).isEnabled(); } public String getVersionName() { try { PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0); return info.versionName; } catch (NameNotFoundException e) { return ""; } } public int getVersionCode() { try { PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0); return info.versionCode; } catch (NameNotFoundException e) { return 0; } } public void startNavigationService(int intent) { final Intent serviceIntent = new Intent(this, NavigationService.class); serviceIntent.putExtra(NavigationService.USAGE_INTENT, intent); if (getNavigationService() == null) { if (intent != NavigationService.USED_BY_GPX) { // for only-USED_BY_GPX case use pre-configured SERVICE_OFF_INTERVAL // other cases always use "continuous": getSettings().SERVICE_OFF_INTERVAL.set(0); } startService(serviceIntent); } else { // additional cases always use "continuous" // TODO: fallback to custom USED_BY_GPX interval in case all other sleep mode purposes have // been stopped getSettings().SERVICE_OFF_INTERVAL.set(0); getNavigationService().addUsageIntent(intent); getNotificationHelper().showNotification(); } } }
public class OsMoDroidPlugin extends OsmandPlugin implements MonitoringInfoControlServices { IRemoteOsMoDroidListener.Stub inter = new IRemoteOsMoDroidListener.Stub() { @Override public void channelUpdated() throws RemoteException { if (activity != null) { activity.refreshMap(); // test } } @Override public void channelsListUpdated() throws RemoteException { if (activity != null && connected) { for (OsMoDroidLayer myOsMoDroidLayer : osmoDroidLayerList) { activity.getMapView().removeLayer(myOsMoDroidLayer); } osmoDroidLayerList.clear(); requestLayersFromOsMoDroid(activity); for (OsMoDroidLayer myOsMoDroidLayer : osmoDroidLayerList) { activity.getMapView().addLayer(myOsMoDroidLayer, 4.5f); } } } public void reRouteTo(LatLon loc) { final OsmandApplication app = activity.getMyApplication(); final TargetPointsHelper targets = app.getTargetPointsHelper(); // If we are in following mode then just update target point targets.navigateToPoint(loc, true, -1); if (!app.getRoutingHelper().isFollowingMode()) { // If we are not in following mode then request new route to calculate // Use default application mode activity.runOnUiThread( new Runnable() { @Override public void run() { activity.followRoute( app.getSettings().getApplicationMode(), targets.getPointToNavigate(), targets.getIntermediatePoints(), app.getLastKnownLocation(), null); } }); } } @Override public void routeTo(float Lat, float Lon) throws RemoteException { reRouteTo(new LatLon(Lat, Lon)); } }; @Override public void updateLayers(OsmandMapTileView mapView, MapActivity activity) { registerLayers(activity); super.updateLayers(mapView, activity); MonitoringInfoControl lock = activity.getMapLayers().getMapInfoLayer().getMonitoringInfoControl(); if (lock != null && !lock.getMonitorActions().contains(this)) { lock.addMonitorActions(this); } } MapActivity activity; public static final String ID = "osmand.osmodroid"; private static final Log log = PlatformUtil.getLog(OsMoDroidPlugin.class); private OsmandApplication app; IRemoteOsMoDroidService mIRemoteService; private ServiceConnection mConnection; private int OSMODROID_SUPPORTED_VERSION_MIN = 5; private OsMoDroidLayer osmoDroidLayer; protected boolean connected = false; ArrayList<OsMoDroidLayer> osmoDroidLayerList = new ArrayList<OsMoDroidLayer>(); public ArrayList<OsMoDroidPoint> getOsMoDroidPointArrayList(int id) { ArrayList<OsMoDroidPoint> result = new ArrayList<OsMoDroidPoint>(); try { for (int i = 0; i < mIRemoteService.getNumberOfObjects(id); i++) { result.add( new OsMoDroidPoint( mIRemoteService.getObjectLat(id, mIRemoteService.getObjectId(id, i)), mIRemoteService.getObjectLon(id, mIRemoteService.getObjectId(id, i)), mIRemoteService.getObjectName(id, mIRemoteService.getObjectId(id, i)), mIRemoteService.getObjectDescription(id, mIRemoteService.getObjectId(id, i)), mIRemoteService.getObjectId(id, i), id, mIRemoteService.getObjectSpeed(id, mIRemoteService.getObjectId(id, i)), mIRemoteService.getObjectColor(id, mIRemoteService.getObjectId(id, i)))); } } catch (RemoteException e) { log.error(e.getMessage(), e); } return result; } @Override public String getId() { return ID; } public OsMoDroidPlugin(OsmandApplication app) { this.app = app; } @Override public String getDescription() { return app.getString(R.string.osmodroid_plugin_description); } @Override public String getName() { return app.getString(R.string.osmodroid_plugin_name); } // test @Override public boolean init(final OsmandApplication app) { mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mIRemoteService = IRemoteOsMoDroidService.Stub.asInterface(service); try { System.out.println(mIRemoteService.getVersion()); if (mIRemoteService.getVersion() < OSMODROID_SUPPORTED_VERSION_MIN) { app.showToastMessage(R.string.osmodroid_plugin_old_ver_not_supported); shutdown(app); } else { mIRemoteService.registerListener(inter); connected = true; } } catch (RemoteException e) { log.error(e.getMessage(), e); } } @Override public void onServiceDisconnected(ComponentName name) { connected = false; mIRemoteService = null; } }; Intent serviceIntent = (new Intent("OsMoDroid.remote")); app.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE); return true; } void requestLayersFromOsMoDroid(MapActivity activity) { try { for (int i = 0; i < mIRemoteService.getNumberOfLayers(); i++) { osmoDroidLayerList.add( new OsMoDroidLayer( activity, mIRemoteService.getLayerId(i), this, mIRemoteService.getLayerName(mIRemoteService.getLayerId(i)), mIRemoteService.getLayerDescription(mIRemoteService.getLayerId(i)))); } } catch (RemoteException e) { log.error(e.getMessage(), e); } } @Override public void registerLayers(MapActivity activity) { this.activity = activity; if (connected) { for (OsMoDroidLayer myOsMoDroidLayer : osmoDroidLayerList) { activity.getMapView().removeLayer(myOsMoDroidLayer); } osmoDroidLayerList.clear(); requestLayersFromOsMoDroid(activity); for (OsMoDroidLayer myOsMoDroidLayer : osmoDroidLayerList) { activity.getMapView().addLayer(myOsMoDroidLayer, 4.5f); } } } @Override public void registerLayerContextMenuActions( OsmandMapTileView mapView, ContextMenuAdapter adapter, MapActivity mapActivity) { for (OsMoDroidLayer myOsMoDroidLayer : osmoDroidLayerList) { adapter.item(myOsMoDroidLayer.layerName).reg(); } super.registerLayerContextMenuActions(mapView, adapter, mapActivity); } @Override public void disable(OsmandApplication app) { shutdown(app); } private void shutdown(OsmandApplication app) { if (mIRemoteService != null) { if (connected) { try { mIRemoteService.unregisterListener(inter); } catch (RemoteException e) { log.error(e.getMessage(), e); } } app.unbindService(mConnection); mIRemoteService = null; } } @Override public void addMonitorActions( final ContextMenuAdapter qa, final MonitoringInfoControl li, final OsmandMapTileView view) { boolean o = true; try { o = mIRemoteService.isActive(); } catch (Exception e) { log.error(e.getMessage(), e); } final boolean off = !o; qa.item(off ? R.string.osmodroid_mode_off : R.string.osmodroid_mode_on) .icon(off ? R.drawable.monitoring_rec_inactive : R.drawable.monitoring_rec_big) .listen( new OnContextMenuClick() { @Override public void onContextMenuClick( int itemId, int pos, boolean isChecked, DialogInterface dialog) { try { if (off) { mIRemoteService.Activate(); } else { mIRemoteService.Deactivate(); } } catch (Exception e) { log.error(e.getMessage(), e); } } }) .reg(); } }
/** Created by yurkiss on 04.03.16. */ @RunWith(Parameterized.class) public class RouteResultPreparationTest { private static RoutePlannerFrontEnd fe; private static RoutingContext ctx; private String testName; private LatLon startPoint; private LatLon endPoint; private Map<Long, String> expectedResults; private Log log = PlatformUtil.getLog(RouteResultPreparationTest.class); public RouteResultPreparationTest( String testName, LatLon startPoint, LatLon endPoint, Map<Long, String> expectedResults) { this.testName = testName; this.startPoint = startPoint; this.endPoint = endPoint; this.expectedResults = expectedResults; } @BeforeClass public static void setUp() throws Exception { String fileName = "../../resources/Turn_lanes_test.obf"; File fl = new File(fileName); RandomAccessFile raf = new RandomAccessFile(fl, "r"); fe = new RoutePlannerFrontEnd(false); RoutingConfiguration.Builder builder = RoutingConfiguration.getDefault(); Map<String, String> params = new LinkedHashMap<String, String>(); params.put("car", "true"); params.put("short_way", "true"); RoutingConfiguration config = builder.build("car", RoutingConfiguration.DEFAULT_MEMORY_LIMIT * 3, params); BinaryMapIndexReader[] binaryMapIndexReaders = {new BinaryMapIndexReader(raf, fl)}; ctx = fe.buildRoutingContext( config, null, binaryMapIndexReaders, RoutePlannerFrontEnd.RouteCalculationMode.NORMAL); ctx.leftSideNavigation = false; RouteResultPreparation.PRINT_TO_CONSOLE_ROUTE_INFORMATION_TO_TEST = true; } @Parameterized.Parameters(name = "{index}: {0}") public static Collection<Object[]> data() throws IOException { String fileName = "/test_turn_lanes.json"; Reader reader = new InputStreamReader(RouteResultPreparationTest.class.getResourceAsStream(fileName)); Gson gson = new GsonBuilder().setPrettyPrinting().create(); TestEntry[] testEntries = gson.fromJson(reader, TestEntry[].class); ArrayList<Object[]> twoDArray = new ArrayList<Object[]>(); for (int i = 0; i < testEntries.length; ++i) { if (!testEntries[i].isIgnore()) { Object[] arr = new Object[] { testEntries[i].getTestName(), testEntries[i].getStartPoint(), testEntries[i].getEndPoint(), testEntries[i].getExpectedResults() }; twoDArray.add(arr); } } reader.close(); return twoDArray; } @Test public void testLanes() throws Exception { List<RouteSegmentResult> routeSegments = fe.searchRoute(ctx, startPoint, endPoint, null); Set<Long> reachedSegments = new TreeSet<Long>(); Assert.assertNotNull(routeSegments); int prevSegment = -1; for (int i = 0; i <= routeSegments.size(); i++) { if (i == routeSegments.size() || routeSegments.get(i).getTurnType() != null) { if (prevSegment >= 0) { String lanes = getLanesString(routeSegments.get(prevSegment)); String turn = routeSegments.get(prevSegment).getTurnType().toXmlString(); String turnLanes = turn + ":" + lanes; String name = routeSegments.get(prevSegment).getDescription(); long segmentId = routeSegments.get(prevSegment).getObject().getId(); String expectedResult = expectedResults.get(segmentId); if (expectedResult != null) { if (!Algorithms.objectEquals(expectedResult, turnLanes) && !Algorithms.objectEquals(expectedResult, lanes) && !Algorithms.objectEquals(expectedResult, turn)) { Assert.assertEquals("Segment " + segmentId, expectedResult, turnLanes); } } System.out.println("segmentId: " + segmentId + " description: " + name); } prevSegment = i; } if (i < routeSegments.size()) { reachedSegments.add(routeSegments.get(i).getObject().getId()); } } Set<Long> expectedSegments = expectedResults.keySet(); for (Long expSegId : expectedSegments) { Assert.assertTrue( "Expected segment " + expSegId + " weren't reached in route segments " + reachedSegments.toString(), reachedSegments.contains(expSegId)); } } private String getLanesString(RouteSegmentResult segment) { final int[] lns = segment.getTurnType().getLanes(); if (lns != null) { String s = ""; for (int h = 0; h < lns.length; h++) { if (h > 0) { s += "|"; } if (lns[h] % 2 == 1) { s += "+"; } int pt = TurnType.getPrimaryTurn(lns[h]); if (pt == 0) { pt = 1; } s += TurnType.valueOf(pt, false).toXmlString(); int st = TurnType.getSecondaryTurn(lns[h]); if (st != 0) { s += "," + TurnType.valueOf(st, false).toXmlString(); } int tt = TurnType.getTertiaryTurn(lns[h]); if (tt != 0) { s += "," + TurnType.valueOf(tt, false).toXmlString(); } } s += ""; return s; } return null; } }
public class EditPoiDialogFragment extends DialogFragment { public static final String TAG = "EditPoiDialogFragment"; private static final Log LOG = PlatformUtil.getLog(EditPoiDialogFragment.class); private static final String KEY_AMENITY_NODE = "key_amenity_node"; private static final String KEY_AMENITY = "key_amenity"; private static final String TAGS_LIST = "tags_list"; private EditPoiData editPoiData; private ViewPager viewPager; private AutoCompleteTextView poiTypeEditText; private Node node; private Map<String, PoiType> allTranslatedSubTypes; private OpenstreetmapUtil mOpenstreetmapUtil; private TextInputLayout poiTypeTextInputLayout; @Override public void onAttach(Activity activity) { super.onAttach(activity); OsmandSettings settings = getMyApplication().getSettings(); OsmEditingPlugin plugin = OsmandPlugin.getPlugin(OsmEditingPlugin.class); if (settings.OFFLINE_EDITION.get() || !settings.isInternetConnectionAvailable(true)) { mOpenstreetmapUtil = new OpenstreetmapLocalUtil(plugin, activity); } else if (!settings.isInternetConnectionAvailable(true)) { mOpenstreetmapUtil = new OpenstreetmapLocalUtil(plugin, activity); } else { mOpenstreetmapUtil = new OpenstreetmapRemoteUtil(activity); } node = (Node) getArguments().getSerializable(KEY_AMENITY_NODE); allTranslatedSubTypes = getMyApplication().getPoiTypes().getAllTranslatedNames(); Amenity amenity = (Amenity) getArguments().getSerializable(KEY_AMENITY); editPoiData = new EditPoiData(amenity, node, allTranslatedSubTypes); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); boolean isLightTheme = ((OsmandApplication) getActivity().getApplication()).getSettings().OSMAND_THEME.get() == OsmandSettings.OSMAND_LIGHT_THEME; int themeId = isLightTheme ? R.style.OsmandLightTheme : R.style.OsmandDarkTheme; setStyle(STYLE_NO_FRAME, themeId); getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_edit_poi, container, false); final OsmandSettings settings = getMyApplication().getSettings(); boolean isLightTheme = settings.OSMAND_THEME.get() == OsmandSettings.OSMAND_LIGHT_THEME; if (savedInstanceState != null) { Map<String, String> mp = (Map<String, String>) savedInstanceState.getSerializable(TAGS_LIST); editPoiData.updateTags(mp); } Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar); toolbar.setTitle(R.string.poi_create_title); toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha); toolbar.setNavigationOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { dismissCheckForChanges(); } }); viewPager = (ViewPager) view.findViewById(R.id.viewpager); String basicTitle = getResources().getString(R.string.tab_title_basic); String extendedTitle = getResources().getString(R.string.tab_title_advanced); final MyAdapter pagerAdapter = new MyAdapter(getChildFragmentManager(), basicTitle, extendedTitle); viewPager.setAdapter(pagerAdapter); viewPager.addOnPageChangeListener( new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int i, float v, int i1) {} @Override public void onPageSelected(int i) { ((OnFragmentActivatedListener) pagerAdapter.getItem(i)).onFragmentActivated(); } @Override public void onPageScrollStateChanged(int i) {} }); final TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tab_layout); tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); // Hack due to bug in design support library v22.2.1 // https://code.google.com/p/android/issues/detail?id=180462 // TODO remove in new version if (ViewCompat.isLaidOut(tabLayout)) { tabLayout.setupWithViewPager(viewPager); } else { tabLayout.addOnLayoutChangeListener( new View.OnLayoutChangeListener() { @Override public void onLayoutChange( View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { tabLayout.setupWithViewPager(viewPager); tabLayout.removeOnLayoutChangeListener(this); } }); } ImageButton onlineDocumentationButton = (ImageButton) view.findViewById(R.id.onlineDocumentationButton); onlineDocumentationButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { getActivity() .startActivity( new Intent( Intent.ACTION_VIEW, Uri.parse("https://wiki.openstreetmap.org/wiki/Map_Features"))); } }); final int colorId = isLightTheme ? R.color.inactive_item_orange : R.color.dash_search_icon_dark; final int color = getResources().getColor(colorId); onlineDocumentationButton.setImageDrawable( getMyApplication().getIconsCache().getPaintedContentIcon(R.drawable.ic_action_help, color)); final ImageButton poiTypeButton = (ImageButton) view.findViewById(R.id.poiTypeButton); poiTypeButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { DialogFragment fragment = PoiTypeDialogFragment.createInstance(editPoiData.amenity); fragment.show(getChildFragmentManager(), "PoiTypeDialogFragment"); } }); EditText poiNameEditText = (EditText) view.findViewById(R.id.poiNameEditText); poiNameEditText.addTextChangedListener( new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void afterTextChanged(Editable s) { if (!getEditPoiData().isInEdit()) { if (!TextUtils.isEmpty(s)) { getEditPoiData().putTag(OSMSettings.OSMTagKey.NAME.getValue(), s.toString()); } else { getEditPoiData().removeTag(OSMSettings.OSMTagKey.NAME.getValue()); } } } }); poiNameEditText.setText(node.getTag(OSMSettings.OSMTagKey.NAME)); poiTypeTextInputLayout = (TextInputLayout) view.findViewById(R.id.poiTypeTextInputLayout); poiTypeEditText = (AutoCompleteTextView) view.findViewById(R.id.poiTypeEditText); poiTypeEditText.addTextChangedListener( new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void afterTextChanged(Editable s) { if (!getEditPoiData().isInEdit()) { getEditPoiData().putTag(EditPoiData.POI_TYPE_TAG, s.toString()); } } }); poiNameEditText.setOnEditorActionListener(mOnEditorActionListener); poiTypeEditText.setOnEditorActionListener(mOnEditorActionListener); poiTypeEditText.setText(editPoiData.amenity.getSubType()); Button saveButton = (Button) view.findViewById(R.id.saveButton); saveButton.setText(R.string.shared_string_save); saveButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { save(); } }); Button cancelButton = (Button) view.findViewById(R.id.cancelButton); cancelButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); fragmentManager.beginTransaction().remove(EditPoiDialogFragment.this).commit(); fragmentManager.popBackStack(); } }); setAdapterForPoiTypeEditText(); setCancelable(false); return view; } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Dialog dialog = super.onCreateDialog(savedInstanceState); dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); return dialog; } private void save() { if (TextUtils.isEmpty(poiTypeEditText.getText())) { poiTypeEditText.setError(getResources().getString(R.string.please_specify_poi_type)); return; } OsmPoint.Action action = node.getId() == -1 ? OsmPoint.Action.CREATE : OsmPoint.Action.MODIFY; for (Map.Entry<String, String> tag : editPoiData.getTagValues().entrySet()) { if (tag.getKey().equals(EditPoiData.POI_TYPE_TAG)) { final PoiType poiType = allTranslatedSubTypes.get(tag.getValue().trim().toLowerCase()); if (poiType != null) { node.putTag(poiType.getOsmTag(), poiType.getOsmValue()); if (poiType.getOsmTag2() != null) { node.putTag(poiType.getOsmTag2(), poiType.getOsmValue2()); } } else { node.putTag(editPoiData.amenity.getType().getDefaultTag(), tag.getValue()); } // } else if (tag.tag.equals(OSMSettings.OSMTagKey.DESCRIPTION.getValue())) { // description = tag.value; } else { if (tag.getKey().length() > 0) { node.putTag(tag.getKey(), tag.getValue()); } else { node.removeTag(tag.getKey()); } } } commitNode( action, node, mOpenstreetmapUtil.getEntityInfo(), "", false, // closeChange.isSelected(), new Runnable() { @Override public void run() { OsmEditingPlugin plugin = OsmandPlugin.getPlugin(OsmEditingPlugin.class); if (plugin != null) { List<OpenstreetmapPoint> points = plugin.getDBPOI().getOpenstreetmapPoints(); OsmPoint point = points.get(points.size() - 1); if (getActivity() instanceof MapActivity) { MapActivity mapActivity = (MapActivity) getActivity(); mapActivity .getContextMenu() .showOrUpdate( new LatLon(point.getLatitude(), point.getLongitude()), plugin.getOsmEditsLayer(mapActivity).getObjectName(point), point); } } if (getActivity() instanceof MapActivity) { ((MapActivity) getActivity()).getMapView().refreshMap(true); } dismiss(); } }, getActivity(), mOpenstreetmapUtil); } @Override public void onResume() { super.onResume(); getDialog() .setOnKeyListener( new DialogInterface.OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == android.view.KeyEvent.KEYCODE_BACK) { if (event.getAction() == KeyEvent.ACTION_DOWN) { return true; } else { dismissCheckForChanges(); return true; } } return false; } }); } private void dismissCheckForChanges() { if (editPoiData.hasChangesBeenMade()) { new AreYouSureDialogFragment().show(getChildFragmentManager(), "AreYouSureDialogFragment"); } else { dismiss(); } } @Override public void onSaveInstanceState(Bundle outState) { outState.putSerializable(TAGS_LIST, (Serializable) editPoiData.getTagValues()); super.onSaveInstanceState(outState); } public static EditPoiDialogFragment createAddPoiInstance( double latitude, double longitude, OsmandApplication application) { Node node = new Node(latitude, longitude, -1); Amenity amenity; amenity = new Amenity(); amenity.setType(application.getPoiTypes().getOtherPoiCategory()); amenity.setSubType(""); amenity.setAdditionalInfo(OSMSettings.OSMTagKey.OPENING_HOURS.getValue(), ""); return createInstance(node, amenity); } public static EditPoiDialogFragment createInstance(Node node, Amenity amenity) { EditPoiDialogFragment editPoiDialogFragment = new EditPoiDialogFragment(); Bundle args = new Bundle(); args.putSerializable(KEY_AMENITY_NODE, node); args.putSerializable(KEY_AMENITY, amenity); editPoiDialogFragment.setArguments(args); return editPoiDialogFragment; } public EditPoiData getEditPoiData() { return editPoiData; } public void setSubCategory(String subCategory) { poiTypeEditText.setText(subCategory); } public static void commitNode( final OsmPoint.Action action, final Node n, final EntityInfo info, final String comment, final boolean closeChangeSet, final Runnable successAction, final Activity activity, final OpenstreetmapUtil openstreetmapUtil) { if (info == null && OsmPoint.Action.CREATE != action) { AccessibleToast.makeText( activity, activity.getResources().getString(R.string.poi_error_info_not_loaded), Toast.LENGTH_LONG) .show(); return; } new AsyncTask<Void, Void, Node>() { ProgressDialog progress; @Override protected void onPreExecute() { progress = ProgressDialog.show( activity, activity.getString(R.string.uploading), activity.getString(R.string.uploading_data)); super.onPreExecute(); } @Override protected Node doInBackground(Void... params) { return openstreetmapUtil.commitNodeImpl(action, n, info, comment, closeChangeSet); } @Override protected void onPostExecute(Node result) { progress.dismiss(); if (result != null) { successAction.run(); } } }.execute(); } public void updateType(Amenity amenity) { poiTypeEditText.setText(amenity.getSubType()); poiTypeTextInputLayout.setHint(amenity.getType().getTranslation()); setAdapterForPoiTypeEditText(); poiTypeEditText.setOnTouchListener( new View.OnTouchListener() { @Override public boolean onTouch(final View v, MotionEvent event) { final EditText editText = (EditText) v; final int DRAWABLE_RIGHT = 2; if (event.getAction() == MotionEvent.ACTION_UP) { if (event.getX() >= (editText.getRight() - editText.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width() - editText.getPaddingRight())) { if (editPoiData.amenity.getType() != null) { DialogFragment dialogFragment = PoiSubTypeDialogFragment.createInstance(editPoiData.amenity); dialogFragment.show(getChildFragmentManager(), "PoiSubTypeDialogFragment"); } return true; } } return false; } }); } private void setAdapterForPoiTypeEditText() { final Map<String, PoiType> subCategories = new LinkedHashMap<>(); for (Map.Entry<String, PoiType> s : allTranslatedSubTypes.entrySet()) { if (!subCategories.containsKey(s.getKey())) { subCategories.put(Algorithms.capitalizeFirstLetterAndLowercase(s.getKey()), s.getValue()); } } final ArrayAdapter<Object> adapter; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { adapter = new ArrayAdapter<>( getActivity(), R.layout.list_textview, subCategories.keySet().toArray()); } else { TypedValue typedValue = new TypedValue(); Resources.Theme theme = getActivity().getTheme(); theme.resolveAttribute(android.R.attr.textColorSecondary, typedValue, true); final int textColor = typedValue.data; adapter = new ArrayAdapter<Object>( getActivity(), R.layout.list_textview, subCategories.keySet().toArray()) { @Override public View getView(int position, View convertView, ViewGroup parent) { final View view = super.getView(position, convertView, parent); ((TextView) view.findViewById(R.id.textView)).setTextColor(textColor); return view; } }; } poiTypeEditText.setAdapter(adapter); poiTypeEditText.setOnItemSelectedListener( new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { Object item = parent.getAdapter().getItem(position); LOG.debug("item=" + item); //noinspection SuspiciousMethodCalls if (subCategories.containsKey(item)) { //noinspection SuspiciousMethodCalls String keyName = subCategories.get(item).getKeyName(); poiTypeEditText.setText(keyName); } } @Override public void onNothingSelected(AdapterView<?> parent) {} }); } private OsmandApplication getMyApplication() { return (OsmandApplication) getActivity().getApplication(); } public static void showEditInstance(final Amenity amenity, final AppCompatActivity activity) { final OsmandSettings settings = ((OsmandApplication) activity.getApplication()).getSettings(); final OpenstreetmapUtil openstreetmapUtilToLoad; if (settings.OFFLINE_EDITION.get() || !settings.isInternetConnectionAvailable(true)) { OsmEditingPlugin plugin = OsmandPlugin.getPlugin(OsmEditingPlugin.class); openstreetmapUtilToLoad = new OpenstreetmapLocalUtil(plugin, activity); } else if (!settings.isInternetConnectionAvailable(true)) { openstreetmapUtilToLoad = new OpenstreetmapRemoteUtil(activity); } else { openstreetmapUtilToLoad = new OpenstreetmapRemoteUtil(activity); } new AsyncTask<Void, Void, Node>() { @Override protected Node doInBackground(Void... params) { return openstreetmapUtilToLoad.loadNode(amenity); } protected void onPostExecute(Node n) { if (n != null) { EditPoiDialogFragment fragment = EditPoiDialogFragment.createInstance(n, amenity); fragment.show(activity.getSupportFragmentManager(), TAG); } else { AccessibleToast.makeText( activity, activity.getString(R.string.poi_error_poi_not_found), Toast.LENGTH_SHORT) .show(); } } }.execute(); } public static class MyAdapter extends FragmentPagerAdapter { private final Fragment[] fragments = new Fragment[] {new BasicEditPoiFragment(), new AdvancedEditPoiFragment()}; private final String[] titles; public MyAdapter(FragmentManager fm, String basicTitle, String extendedTitle) { super(fm); titles = new String[] {basicTitle, extendedTitle}; } @Override public int getCount() { return 2; } @Override public Fragment getItem(int position) { return fragments[position]; } @Override public CharSequence getPageTitle(int position) { return titles[position]; } } // TODO: 8/28/15 Move to helper public static class ShowDeleteDialogAsyncTask extends AsyncTask<Amenity, Void, Node> { private final OpenstreetmapUtil openstreetmapUtil; private final AppCompatActivity activity; public ShowDeleteDialogAsyncTask(AppCompatActivity activity) { this.activity = activity; OsmandSettings settings = ((OsmandApplication) activity.getApplication()).getSettings(); OsmEditingPlugin plugin = OsmandPlugin.getPlugin(OsmEditingPlugin.class); if (settings.OFFLINE_EDITION.get() || !settings.isInternetConnectionAvailable(true)) { openstreetmapUtil = new OpenstreetmapLocalUtil(plugin, activity); } else if (!settings.isInternetConnectionAvailable(true)) { openstreetmapUtil = new OpenstreetmapLocalUtil(plugin, activity); } else { openstreetmapUtil = new OpenstreetmapRemoteUtil(activity); } } protected Node doInBackground(Amenity[] params) { return openstreetmapUtil.loadNode(params[0]); } protected void onPostExecute(Node n) { if (n == null) { AccessibleToast.makeText( activity, activity.getResources().getString(R.string.poi_error_poi_not_found), Toast.LENGTH_LONG) .show(); return; } DeletePoiDialogFragment.createInstance(n) .show(activity.getSupportFragmentManager(), "DeletePoiDialogFragment"); } } public static class AreYouSureDialogFragment extends DialogFragment { @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder .setTitle(getResources().getString(R.string.are_you_sure)) .setMessage(getResources().getString(R.string.unsaved_changes_will_be_lost)) .setPositiveButton( R.string.shared_string_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ((DialogFragment) getParentFragment()).dismiss(); } }) .setNegativeButton(R.string.shared_string_cancel, null); return builder.create(); } } private TextView.OnEditorActionListener mOnEditorActionListener = new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { boolean handled = false; if (actionId == EditorInfo.IME_ACTION_SEND) { save(); handled = true; } return handled; } }; public interface OnFragmentActivatedListener { void onFragmentActivated(); } }
public class CountryOcbfGeneration { private int OSM_ID = -1000; private static final Log log = PlatformUtil.getLog(CountryOcbfGeneration.class); public static void main(String[] args) throws XmlPullParserException, IOException, SAXException, SQLException, InterruptedException { String repo = "/Users/victorshcherb/osmand/repos/"; if (args != null && args.length > 0) { repo = args[0]; } new CountryOcbfGeneration().generate(repo); } public Map<String, File> getPolygons(String repo) { String[] polygonFolders = new String[] { repo + "misc/osm-planet/polygons", // repo +"misc/osm-planet/gislab-polygons", repo + "misc/osm-planet/geo-polygons", repo + "misc/osm-planet/srtm-polygons" }; Map<String, File> polygonFiles = new LinkedHashMap<String, File>(); for (String folder : polygonFolders) { scanPolygons(new File(folder), polygonFiles); } return polygonFiles; } public Map<String, Set<TranslateEntity>> getTranslates(String repo) throws XmlPullParserException, IOException { String[] translations = new String[] { repo + "misc/osm-planet/osm-data/states_places.osm", repo + "misc/osm-planet/osm-data/states_regions.osm", repo + "misc/osm-planet/osm-data/countries_places.osm", repo + "misc/osm-planet/osm-data/countries_admin_level_2.osm" }; Map<String, Set<TranslateEntity>> translates = new TreeMap<String, Set<TranslateEntity>>(); for (String t : translations) { scanTranslates(new File(t), translates); } return translates; } public CountryRegion parseRegionStructure(String repo) throws XmlPullParserException, IOException { String regionsXml = repo + "/resources/countries-info/regions.xml"; XmlPullParser parser = PlatformUtil.newXMLPullParser(); parser.setInput(new FileReader(regionsXml)); int tok; CountryRegion global = new CountryRegion(); List<CountryRegion> stack = new ArrayList<CountryOcbfGeneration.CountryRegion>(); stack.add(global); CountryRegion current = global; while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { if (tok == XmlPullParser.START_TAG) { String name = parser.getName(); if (name.equals("region")) { Map<String, String> attrs = new LinkedHashMap<String, String>(); for (int i = 0; i < parser.getAttributeCount(); i++) { attrs.put(parser.getAttributeName(i), parser.getAttributeValue(i)); } CountryRegion cr = createRegion(current, attrs); stack.add(cr); current = cr; } } else if (tok == XmlPullParser.END_TAG) { String name = parser.getName(); if (name.equals("region")) { stack.remove(stack.size() - 1); current = stack.get(stack.size() - 1); } } } return global; } public static class TranslateEntity { private Map<String, String> tm = new TreeMap<String, String>(); private String name; public TranslateEntity(String name) { this.name = name; } public boolean isEmpty() { return tm.isEmpty(); } } public static class CountryRegion { CountryRegion parent = null; List<CountryRegion> children = new ArrayList<CountryRegion>(); static final String[] tagsPropagate = new String[] { "lang", "left_hand_navigation", "metric", "road_signs", "maxspeed", "maxspeed_urban", "maxspeed_rural" }; Map<String, String> additionalTags = new LinkedHashMap<String, String>(); String name; String downloadSuffix; String innerDownloadSuffix; String downloadPrefix; String innerDownloadPrefix; public String boundary; public String translate; public String polyExtract; public boolean map; public boolean wiki; public boolean roads; public boolean hillshade; public boolean srtm; public long timestampToUpdate; public CountryRegion getParent() { return parent; } public Iterator<CountryRegion> iterator() { final LinkedList<CountryRegion> stack = new LinkedList<CountryRegion>(children); return new Iterator<CountryRegion>() { @Override public boolean hasNext() { return !stack.isEmpty(); } @Override public CountryRegion next() { CountryRegion reg = stack.pollFirst(); stack.addAll(0, reg.children); return reg; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } public String getPolyExtract() { if (!Algorithms.isEmpty(polyExtract) || parent == null) { return polyExtract; } return parent.getPolyExtract(); } public String getAdditionalTag(String tg) { if (!Algorithms.isEmpty(additionalTags.get(tg)) || parent == null) { return additionalTags.get(tg); } return parent.getAdditionalTag(tg); } public String getFullName() { if (parent == null) { return name; } else { return parent.getFullName() + "_" + name; } } public String getDownloadName() { String s = name; String p = getDownloadPrefix(); if (p != null && p.length() > 0) { s = p + "_" + s; } String suf = getDownloadSuffix(); if (suf != null && suf.length() > 0) { s = s + "_" + suf; } return s; } public String getInnerDownloadPrefix() { if (innerDownloadPrefix != null) { return innerDownloadPrefix; } return getDownloadPrefix(); } public String getDownloadPrefix() { if (downloadPrefix == null && parent != null) { return parent.getInnerDownloadPrefix(); } return downloadPrefix == null ? "" : downloadPrefix; } public String getInnerDownloadSuffix() { if (innerDownloadSuffix != null) { return innerDownloadSuffix; } return getDownloadSuffix(); } public String getDownloadSuffix() { if (downloadSuffix == null && parent != null) { return parent.getInnerDownloadSuffix(); } return downloadSuffix == null ? "" : downloadSuffix; } public void setInnerDownloadSuffix(String string) { if (string != null) { if ("$name".equals(string)) { innerDownloadSuffix = name; } else { innerDownloadSuffix = string; } } } public void setDownloadPrefix(String string) { if (string != null) { if ("$name".equals(string)) { downloadPrefix = name; } else { downloadPrefix = string; } } } public void setDownloadSuffix(String string) { if (string != null) { if ("$name".equals(string)) { downloadSuffix = name; } else { downloadSuffix = string; } } } public void setInnerDownloadPrefix(String string) { if (string != null) { if ("$name".equals(string)) { innerDownloadPrefix = name; } else { innerDownloadPrefix = string; } } } } private void scanTranslates(File file, Map<String, Set<TranslateEntity>> translates) throws XmlPullParserException, IOException { XmlPullParser parser = PlatformUtil.newXMLPullParser(); parser.setInput(new FileReader(file)); int tok; TranslateEntity te = null; while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { if (tok == XmlPullParser.START_TAG) { String name = parser.getName(); if (name.equals("way") || name.equals("node") || name.equals("relation")) { te = new TranslateEntity(name); } else if (name.equals("tag") && te != null) { Map<String, String> attrs = new LinkedHashMap<String, String>(); for (int i = 0; i < parser.getAttributeCount(); i++) { attrs.put(parser.getAttributeName(i), parser.getAttributeValue(i)); } te.tm.put(attrs.get("k"), attrs.get("v")); } } else if (tok == XmlPullParser.END_TAG) { String name = parser.getName(); if (name.equals("way") || name.equals("node") || name.equals("relation")) { if (!te.isEmpty()) { Iterator<Entry<String, String>> it = te.tm.entrySet().iterator(); addTranslate(translates, te, "entity=" + te.name); while (it.hasNext()) { Entry<String, String> e = it.next(); addTranslate( translates, te, e.getKey().toLowerCase() + "=" + e.getValue().toLowerCase()); } } te = null; } } } } private void addTranslate( Map<String, Set<TranslateEntity>> translates, TranslateEntity te, String k) { if (!translates.containsKey(k)) { translates.put(k, new HashSet<CountryOcbfGeneration.TranslateEntity>()); } translates.get(k).add(te); } private void generate(String repo) throws XmlPullParserException, IOException, SAXException, SQLException, InterruptedException { String targetObf = repo + "regions.ocbf"; String targetOsmXml = repo + "regions.osm.xml"; Map<String, Set<TranslateEntity>> translates = getTranslates(repo); Map<String, File> polygonFiles = getPolygons(repo); CountryRegion global = parseRegionStructure(repo); createFile(global, translates, polygonFiles, targetObf, targetOsmXml); } private void createFile( CountryRegion global, Map<String, Set<TranslateEntity>> translates, Map<String, File> polygonFiles, String targetObf, String targetOsmXml) throws IOException, SQLException, InterruptedException, XmlPullParserException { File osm = new File(targetOsmXml); XmlSerializer serializer = new org.kxml2.io.KXmlSerializer(); FileOutputStream fous = new FileOutputStream(osm); serializer.setOutput(fous, "UTF-8"); serializer.startDocument("UTF-8", true); serializer.startTag(null, "osm"); serializer.attribute(null, "version", "0.6"); serializer.attribute(null, "generator", "OsmAnd"); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); for (CountryRegion r : global.children) { r.parent = null; processRegion(r, translates, polygonFiles, targetObf, targetOsmXml, "", serializer); } serializer.endDocument(); serializer.flush(); fous.close(); IndexCreator creator = new IndexCreator(new File(targetObf).getParentFile()); // $NON-NLS-1$ creator.setMapFileName(new File(targetObf).getName()); creator.setIndexMap(true); creator.setIndexAddress(false); creator.setIndexPOI(false); creator.setIndexTransport(false); creator.setIndexRouting(false); MapZooms zooms = MapZooms.parseZooms("5-6"); creator.generateIndexes( osm, new ConsoleProgressImplementation(1), null, zooms, new MapRenderingTypesEncoder("regions"), log); } private static void addTag(XmlSerializer serializer, String key, String value) throws IOException { serializer.startTag(null, "tag"); serializer.attribute(null, "k", key); serializer.attribute(null, "v", value); serializer.endTag(null, "tag"); } private void processRegion( CountryRegion r, Map<String, Set<TranslateEntity>> translates, Map<String, File> polygonFiles, String targetObf, String targetOsmXml, String indent, XmlSerializer serializer) throws IOException { String line = "key=" + r.name; File boundary = null; if (r.boundary != null) { boundary = polygonFiles.get(r.boundary); if (boundary == null) { System.out.println("!!! Missing boundary " + r.boundary); } else { line += " boundary=" + boundary.getName(); } } List<List<String>> boundaryPoints = Collections.emptyList(); if (boundary != null) { boundaryPoints = readBoundaryPoints(boundary, serializer); } if (boundaryPoints.size() > 0) { // find the biggest with points List<String> ls = boundaryPoints.get(0); for (int i = 0; i < boundaryPoints.size(); i++) { if (ls.size() < boundaryPoints.get(i).size()) { ls = boundaryPoints.get(i); } } for (int i = 0; i < boundaryPoints.size(); i++) { if (boundaryPoints.get(i) == ls) { continue; } writeWay(serializer, boundaryPoints.get(i)); addTag(serializer, "osmand_region", "boundary"); addTag(serializer, "key_name", r.name); addTag(serializer, "download_name", r.getDownloadName()); addTag(serializer, "region_full_name", r.getFullName()); serializer.endTag(null, "way"); } writeWay(serializer, ls); } else { serializer.startTag(null, "node"); serializer.attribute(null, "id", OSM_ID-- + ""); serializer.attribute(null, "visible", "true"); serializer.attribute(null, "lat", "0"); serializer.attribute(null, "lon", "0"); } addTag(serializer, "osmand_region", "yes"); addTag(serializer, "key_name", r.name); addTag(serializer, "region_full_name", r.getFullName()); if (r.parent != null) { addTag(serializer, "region_parent_name", r.parent.getFullName()); } for (String tg : CountryRegion.tagsPropagate) { if (!Algorithms.isEmpty(r.getAdditionalTag(tg))) { addTag(serializer, "region_" + tg, r.getAdditionalTag(tg)); } } if (r.map || r.roads || r.wiki || r.srtm || r.hillshade) { line += " download=" + r.getDownloadName(); addTag(serializer, "download_name", r.getDownloadName()); addTag(serializer, "region_prefix", r.getDownloadPrefix()); addTag( serializer, "region_suffix", r.getDownloadSuffix()); // add exception for Russia for BW? if (r.map) { line += " map=yes"; addTag(serializer, "region_map", "yes"); } if (r.wiki) { line += " wiki=yes"; addTag(serializer, "region_wiki", "yes"); } if (r.roads) { line += " roads=yes"; addTag(serializer, "region_roads", "yes"); } if (r.srtm) { line += " srtm=yes"; addTag(serializer, "region_srtm", "yes"); } if (r.hillshade) { line += " hillshade=yes"; addTag(serializer, "region_hillshade", "yes"); } } if (r.translate == null) { line += " translate-no=" + Algorithms.capitalizeFirstLetterAndLowercase(r.name); } else if (r.translate.startsWith("=")) { line += " translate-assign=" + r.translate.substring(1); } else { String[] tags = r.translate.split(";"); Set<TranslateEntity> set = null; for (String t : tags) { if (!t.contains("=")) { if (translates.containsKey("name=" + t)) { t = "name=" + t; } else if (translates.containsKey("name:en=" + t)) { t = "name:en=" + t; } } if (set == null) { set = translates.get(t); if (set == null) { break; } } else { Set<TranslateEntity> st2 = translates.get(t); if (st2 != null) { set = new HashSet<TranslateEntity>(set); set.retainAll(st2); } else { set = null; break; } } } if (set == null || set.size() == 0) { System.out.println("!!! Couldn't find translation name " + r.translate); } else if (set.size() > 1) { System.out.println("!!! More than 1 translation " + r.translate); } else { TranslateEntity nt = set.iterator().next(); line += " translate-" + nt.tm.size() + "=" + nt.tm.get("name"); Iterator<Entry<String, String>> it = nt.tm.entrySet().iterator(); while (it.hasNext()) { Entry<String, String> e = it.next(); addTag(serializer, e.getKey(), e.getValue()); } } } // COMMENT TO SEE ONLY WARNINGS System.out.println(indent + line); if (boundaryPoints.size() > 0) { serializer.endTag(null, "way"); } else { serializer.endTag(null, "node"); } for (CountryRegion c : r.children) { processRegion( c, translates, polygonFiles, targetObf, targetOsmXml, indent + " ", serializer); } } private void writeWay(XmlSerializer serializer, List<String> ls) throws IOException { serializer.startTag(null, "way"); serializer.attribute(null, "id", OSM_ID-- + ""); serializer.attribute(null, "visible", "true"); for (String bnd : ls) { serializer.startTag(null, "nd"); serializer.attribute(null, "ref", bnd); serializer.endTag(null, "nd"); } } private List<List<String>> readBoundaryPoints(File boundary, XmlSerializer serializer) throws IOException { List<List<String>> res = new ArrayList<List<String>>(); List<String> l = new ArrayList<String>(); BufferedReader br = new BufferedReader(new FileReader(boundary)); br.readLine(); // name boolean newContour = true; String s; while ((s = br.readLine()) != null) { if (newContour) { // skip newContour = false; if (l.size() > 0) { res.add(l); } l = new ArrayList<String>(); } else if (s.trim().length() == 0) { } else if (s.trim().equals("END")) { newContour = true; } else { s = s.trim(); int i = s.indexOf(' '); if (i == -1) { i = s.indexOf('\t'); } if (i == -1) { System.err.println("? " + s); } String lat = s.substring(i, s.length()).trim(); String lon = s.substring(0, i).trim(); serializer.startTag(null, "node"); try { serializer.attribute(null, "lat", Double.parseDouble(lat) + ""); serializer.attribute(null, "lon", Double.parseDouble(lon) + ""); } catch (NumberFormatException e) { System.err.println(lat + " " + lon); e.printStackTrace(); } long id = OSM_ID--; l.add(id + ""); serializer.attribute(null, "id", id + ""); serializer.endTag(null, "node"); } } if (l.size() > 0) { res.add(l); } br.close(); return res; } private CountryRegion createRegion(CountryRegion parent, Map<String, String> attrs) { CountryRegion reg = new CountryRegion(); reg.parent = parent; if (parent != null) { parent.children.add(reg); } String type = attrs.get("type"); reg.name = attrs.get("name"); reg.setDownloadSuffix(attrs.get("download_suffix")); reg.setDownloadPrefix(attrs.get("download_prefix")); reg.setInnerDownloadSuffix(attrs.get("inner_download_suffix")); reg.setInnerDownloadPrefix(attrs.get("inner_download_prefix")); for (String tg : CountryRegion.tagsPropagate) { reg.additionalTags.put(tg, attrs.get(tg)); } if (attrs.containsKey("hillshade")) { reg.hillshade = parseBoolean(attrs.get("hillshade")); } else { reg.hillshade = type == null || type.equals("hillshade"); } if (attrs.containsKey("srtm")) { reg.srtm = parseBoolean(attrs.get("srtm")); } else { reg.srtm = type == null || type.equals("srtm"); } if (attrs.containsKey("map")) { reg.map = parseBoolean(attrs.get("map")); } else { reg.map = type == null || type.equals("map"); } if (attrs.containsKey("roads")) { reg.roads = parseBoolean(attrs.get("roads")); } else { reg.roads = reg.map; } if (attrs.containsKey("wiki")) { reg.wiki = parseBoolean(attrs.get("wiki")); } else { reg.wiki = reg.map; } if (attrs.containsKey("poly_extract")) { reg.polyExtract = attrs.get("poly_extract"); } if (attrs.containsKey("translate")) { reg.translate = attrs.get("translate").toLowerCase(); if (reg.translate.equals("no")) { reg.translate = null; } } else { reg.translate = "entity=node;" + reg.name.toLowerCase(); } if (attrs.containsKey("boundary")) { reg.boundary = attrs.get("boundary"); if (reg.boundary.equals("no")) { reg.boundary = null; } } else { reg.boundary = reg.name; } return reg; } private boolean parseBoolean(String string) { return Boolean.parseBoolean(string) || "yes".equalsIgnoreCase(string); } private void scanPolygons(File file, Map<String, File> polygonFiles) { if (file.isDirectory()) { for (File c : file.listFiles()) { if (c.isDirectory()) { scanPolygons(c, polygonFiles); } else if (c.getName().endsWith(".poly")) { String name = c.getName().substring(0, c.getName().length() - 5); if (!polygonFiles.containsKey(name)) { polygonFiles.put(name, c); } else { File rm = polygonFiles.remove(name); System.out.println( "Polygon duplicate -> " + rm.getParentFile().getName() + "/" + name + " and " + c.getParentFile().getName() + "/" + name); } polygonFiles.put(c.getParentFile().getName() + "/" + name, c); } } } } }
public class GeocodingUtilities { private static final Log log = PlatformUtil.getLog(GeocodingUtilities.class); // Location to test parameters http://www.openstreetmap.org/#map=18/53.896473/27.540071 (hno 44) // BUG http://www.openstreetmap.org/#map=19/50.9356/13.35348 (hno 26) street is public static final float THRESHOLD_MULTIPLIER_SKIP_STREETS_AFTER = 5; public static final float STOP_SEARCHING_STREET_WITH_MULTIPLIER_RADIUS = 250; public static final float STOP_SEARCHING_STREET_WITHOUT_MULTIPLIER_RADIUS = 400; public static final int DISTANCE_STREET_NAME_PROXIMITY_BY_NAME = 15000; public static final float DISTANCE_STREET_FROM_CLOSEST_WITH_SAME_NAME = 1000; public static final float THRESHOLD_MULTIPLIER_SKIP_BUILDINGS_AFTER = 1.5f; public static final float DISTANCE_BUILDING_PROXIMITY = 100; public static final String[] SUFFIXES = new String[] { "av.", "avenue", "просп.", "пер.", "пр.", "заул.", "проспект", "переул.", "бул.", "бульвар", "тракт" }; public static final String[] DEFAULT_SUFFIXES = new String[] {"str.", "street", "улица", "ул.", "вулица", "вул.", "вулиця"}; private static Set<String> SET_DEF_SUFFIXES = null; private static Set<String> SET_SUFFIXES = null; public static Set<String> getDefSuffixesSet() { if (SET_DEF_SUFFIXES == null) { SET_DEF_SUFFIXES = new TreeSet<String>(); SET_DEF_SUFFIXES.addAll(Arrays.asList(DEFAULT_SUFFIXES)); } return SET_DEF_SUFFIXES; } public static Set<String> getSuffixesSet() { if (SET_SUFFIXES == null) { SET_SUFFIXES = new TreeSet<String>(); SET_SUFFIXES.addAll(Arrays.asList(SUFFIXES)); } return SET_SUFFIXES; } public static final Comparator<GeocodingResult> DISTANCE_COMPARATOR = new Comparator<GeocodingResult>() { @Override public int compare(GeocodingResult o1, GeocodingResult o2) { LatLon l1 = o1.getLocation(); LatLon l2 = o2.getLocation(); if (l1 == null || l2 == null) { return l2 == l1 ? 0 : (l1 == null ? -1 : 1); } return Double.compare( MapUtils.getDistance(l1, o1.searchPoint), MapUtils.getDistance(l2, o2.searchPoint)); } }; public static class GeocodingResult { public GeocodingResult() {} public GeocodingResult(GeocodingResult r) { this.searchPoint = r.searchPoint; this.regionFP = r.regionFP; this.regionLen = r.regionLen; this.connectionPoint = r.connectionPoint; this.streetName = r.streetName; this.point = r.point; this.building = r.building; this.city = r.city; this.street = r.street; } // input public LatLon searchPoint; // 1st step public LatLon connectionPoint; public int regionFP; public int regionLen; public RouteSegmentPoint point; public String streetName; // justification public Building building; public String buildingInterpolation; public Street street; public City city; private double dist = -1; public LatLon getLocation() { return connectionPoint; } public double getDistance() { if (dist == -1 && connectionPoint != null && searchPoint != null) { dist = MapUtils.getDistance(connectionPoint, searchPoint); } return dist; } public double getDistanceP() { if (point != null && searchPoint != null) { // Need distance between searchPoint and nearest RouteSegmentPoint here, to approximate // distance from neareest named road return Math.sqrt(point.distSquare); } else { return -1; } } @Override public String toString() { StringBuilder bld = new StringBuilder(); if (building != null) { bld.append(building.getName()); } if (street != null) { bld.append(" str. ").append(street.getName()).append(" city ").append(city.getName()); } else if (streetName != null) { bld.append(" str. ").append(streetName); } else if (city != null) { bld.append(" city ").append(city.getName()); } if (connectionPoint != null && searchPoint != null) { bld.append(" dist=").append((int) getDistance()); } return bld.toString(); } } public List<GeocodingResult> reverseGeocodingSearch(RoutingContext ctx, double lat, double lon) throws IOException { RoutePlannerFrontEnd rp = new RoutePlannerFrontEnd(false); List<GeocodingResult> lst = new ArrayList<GeocodingUtilities.GeocodingResult>(); List<RouteSegmentPoint> listR = new ArrayList<BinaryRoutePlanner.RouteSegmentPoint>(); rp.findRouteSegment(lat, lon, ctx, listR); double distSquare = 0; TLongHashSet set = new TLongHashSet(); Set<String> streetNames = new HashSet<String>(); for (RouteSegmentPoint p : listR) { RouteDataObject road = p.getRoad(); if (!set.add(road.getId())) { continue; } // System.out.println(road.toString() + " " + Math.sqrt(p.distSquare)); boolean emptyName = Algorithms.isEmpty(road.getName()) && Algorithms.isEmpty(road.getRef("", false, true)); if (!emptyName) { if (distSquare == 0 || distSquare > p.distSquare) { distSquare = p.distSquare; } GeocodingResult sr = new GeocodingResult(); sr.searchPoint = new LatLon(lat, lon); sr.streetName = Algorithms.isEmpty(road.getName()) ? road.getRef("", false, true) : road.getName(); sr.point = p; sr.connectionPoint = new LatLon(MapUtils.get31LatitudeY(p.preciseY), MapUtils.get31LongitudeX(p.preciseX)); sr.regionFP = road.region.getFilePointer(); sr.regionLen = road.region.getLength(); if (streetNames.add(sr.streetName)) { lst.add(sr); } } if (p.distSquare > STOP_SEARCHING_STREET_WITH_MULTIPLIER_RADIUS * STOP_SEARCHING_STREET_WITH_MULTIPLIER_RADIUS && distSquare != 0 && p.distSquare > THRESHOLD_MULTIPLIER_SKIP_STREETS_AFTER * distSquare) { break; } if (p.distSquare > STOP_SEARCHING_STREET_WITHOUT_MULTIPLIER_RADIUS * STOP_SEARCHING_STREET_WITHOUT_MULTIPLIER_RADIUS) { break; } } Collections.sort(lst, GeocodingUtilities.DISTANCE_COMPARATOR); return lst; } public List<String> prepareStreetName(String s) { List<String> ls = new ArrayList<String>(); int beginning = 0; for (int i = 1; i < s.length(); i++) { if (s.charAt(i) == ' ') { addWord(ls, s.substring(beginning, i)); beginning = i; } else if (s.charAt(i) == '(') { addWord(ls, s.substring(beginning, i)); while (i < s.length()) { char c = s.charAt(i); i++; beginning = i; if (c == ')') break; } } } if (beginning < s.length()) { String lastWord = s.substring(beginning, s.length()); addWord(ls, lastWord); } Collections.sort(ls, Collator.getInstance()); return ls; } private void addWord(List<String> ls, String word) { String w = word.trim().toLowerCase(); if (!Algorithms.isEmpty(w) && !getDefSuffixesSet().contains(w)) { ls.add(w); } } public List<GeocodingResult> justifyReverseGeocodingSearch( final GeocodingResult road, BinaryMapIndexReader reader, double knownMinBuildingDistance, final ResultMatcher<GeocodingResult> result) throws IOException { // test address index search final List<GeocodingResult> streetsList = new ArrayList<GeocodingResult>(); final List<String> streetNamePacked = prepareStreetName(road.streetName); if (streetNamePacked.size() > 0) { log.info("Search street by name " + road.streetName + " " + streetNamePacked); String mainWord = ""; for (int i = 0; i < streetNamePacked.size(); i++) { String s = streetNamePacked.get(i); if (!getSuffixesSet().contains(s) && s.length() > mainWord.length()) { mainWord = s; } } if (Algorithms.isEmpty(mainWord)) { mainWord = streetNamePacked.get(0); } SearchRequest<MapObject> req = BinaryMapIndexReader.buildAddressByNameRequest( new ResultMatcher<MapObject>() { @Override public boolean publish(MapObject object) { if (object instanceof Street && prepareStreetName(object.getName()).equals(streetNamePacked)) { double d = MapUtils.getDistance( object.getLocation(), road.searchPoint.getLatitude(), road.searchPoint.getLongitude()); // double check to suport old format if (d < DISTANCE_STREET_NAME_PROXIMITY_BY_NAME) { GeocodingResult rs = new GeocodingResult(road); rs.street = (Street) object; // set connection point to sort rs.connectionPoint = rs.street.getLocation(); rs.city = rs.street.getCity(); streetsList.add(rs); return true; } return false; } return false; } @Override public boolean isCancelled() { return result != null && result.isCancelled(); } }, mainWord, StringMatcherMode.CHECK_EQUALS_FROM_SPACE); req.setBBoxRadius( road.getLocation().getLatitude(), road.getLocation().getLongitude(), DISTANCE_STREET_NAME_PROXIMITY_BY_NAME); reader.searchAddressDataByName(req); } final List<GeocodingResult> res = new ArrayList<GeocodingResult>(); if (streetsList.size() == 0) { res.add(road); } else { Collections.sort(streetsList, DISTANCE_COMPARATOR); double streetDistance = 0; for (GeocodingResult street : streetsList) { if (streetDistance == 0) { streetDistance = street.getDistance(); } else if (street.getDistance() > streetDistance + DISTANCE_STREET_FROM_CLOSEST_WITH_SAME_NAME) { continue; } street.connectionPoint = road.connectionPoint; final List<GeocodingResult> streetBuildings = loadStreetBuildings(road, reader, street); Collections.sort(streetBuildings, DISTANCE_COMPARATOR); if (streetBuildings.size() > 0) { Iterator<GeocodingResult> it = streetBuildings.iterator(); if (knownMinBuildingDistance == 0) { GeocodingResult firstBld = it.next(); knownMinBuildingDistance = firstBld.getDistance(); res.add(firstBld); } while (it.hasNext()) { GeocodingResult nextBld = it.next(); if (nextBld.getDistance() > knownMinBuildingDistance * THRESHOLD_MULTIPLIER_SKIP_BUILDINGS_AFTER) { break; } res.add(nextBld); } } res.add(street); } } Collections.sort(res, DISTANCE_COMPARATOR); return res; } private List<GeocodingResult> loadStreetBuildings( final GeocodingResult road, BinaryMapIndexReader reader, GeocodingResult street) throws IOException { final List<GeocodingResult> streetBuildings = new ArrayList<GeocodingResult>(); reader.preloadBuildings(street.street, null); log.info( "Preload buildings " + street.street.getName() + " " + street.city.getName() + " " + street.street.getId()); for (Building b : street.street.getBuildings()) { if (b.getLatLon2() != null) { double slat = b.getLocation().getLatitude(); double slon = b.getLocation().getLongitude(); double tolat = b.getLatLon2().getLatitude(); double tolon = b.getLatLon2().getLongitude(); double coeff = MapUtils.getProjectionCoeff( road.searchPoint.getLatitude(), road.searchPoint.getLongitude(), slat, slon, tolat, tolon); double plat = slat + (tolat - slat) * coeff; double plon = slon + (tolon - slon) * coeff; if (MapUtils.getDistance(road.searchPoint, plat, plon) < DISTANCE_BUILDING_PROXIMITY) { GeocodingResult bld = new GeocodingResult(street); bld.building = b; bld.connectionPoint = b.getLocation(); streetBuildings.add(bld); if (!Algorithms.isEmpty(b.getName2())) { int fi = Algorithms.extractFirstIntegerNumber(b.getName()); int si = Algorithms.extractFirstIntegerNumber(b.getName2()); if (si != 0 && fi != 0) { int num = (int) (fi + (si - fi) * coeff); BuildingInterpolation type = b.getInterpolationType(); if (type == BuildingInterpolation.EVEN || type == BuildingInterpolation.ODD) { if (num % 2 == (type == BuildingInterpolation.EVEN ? 1 : 0)) { num--; } } else if (b.getInterpolationInterval() > 0) { int intv = b.getInterpolationInterval(); if ((num - fi) % intv != 0) { num = ((num - fi) / intv) * intv + fi; } } bld.buildingInterpolation = num + ""; } } } } else if (MapUtils.getDistance(b.getLocation(), road.searchPoint) < DISTANCE_BUILDING_PROXIMITY) { GeocodingResult bld = new GeocodingResult(street); bld.building = b; bld.connectionPoint = b.getLocation(); streetBuildings.add(bld); } } return streetBuildings; } }
public class DashboardOnMap implements ObservableScrollViewCallbacks { private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(DashboardOnMap.class); private static final String TAG = "DashboardOnMap"; public static boolean staticVisible = false; public static DashboardType staticVisibleType = DashboardType.DASHBOARD; public static final String SHOULD_SHOW = "should_show"; private final DashFragmentData[] fragmentsData = new DashFragmentData[] { new DashFragmentData( DashRateUsFragment.TAG, DashRateUsFragment.class, DashRateUsFragment.SHOULD_SHOW_FUNCTION, 0, null), new DashFragmentData( DashDashboardOrDrawerFragment.TAG, DashDashboardOrDrawerFragment.class, DashDashboardOrDrawerFragment.SHOULD_SHOW_FUNCTION, 5, null), new DashFragmentData( DashErrorFragment.TAG, DashErrorFragment.class, DashErrorFragment.SHOULD_SHOW_FUNCTION, 30, null), new DashFragmentData( DashNavigationFragment.TAG, DashNavigationFragment.class, DashNavigationFragment.SHOULD_SHOW_FUNCTION, 40, null), new DashFragmentData( DashWaypointsFragment.TAG, DashWaypointsFragment.class, DashWaypointsFragment.SHOULD_SHOW_FUNCTION, 60, null), new DashFragmentData( DashSearchFragment.TAG, DashSearchFragment.class, DashSearchFragment.SHOULD_SHOW_FUNCTION, 70, null), DashRecentsFragment.FRAGMENT_DATA, DashFavoritesFragment.FRAGMENT_DATA, new DashFragmentData( DashPluginsFragment.TAG, DashPluginsFragment.class, DashPluginsFragment.SHOULD_SHOW_FUNCTION, 140, null) }; private MapActivity mapActivity; private ImageView actionButton; private FrameLayout dashboardView; private ArrayAdapter<?> listAdapter; private OnItemClickListener listAdapterOnClickListener; private boolean visible = false; private DashboardType visibleType; private DashboardType previousVisibleType; private boolean landscape; private List<WeakReference<DashBaseFragment>> fragList = new LinkedList<>(); private net.osmand.Location myLocation; private LatLon mapViewLocation; private float heading; private boolean mapLinkedToLocation; private float mapRotation; private boolean inLocationUpdate = false; private ListView listView; private View listBackgroundView; private Toolbar toolbar; private View paddingView; private int mFlexibleSpaceImageHeight; private int mFlexibleBlurSpaceHeight; private boolean portrait; int baseColor; private WaypointDialogHelper waypointDialogHelper; private final int[] running = new int[] {-1}; private List<LocationPointWrapper> deletedPoints = new ArrayList<>(); private Drawable gradientToolbar; public DashFragmentData[] getFragmentsData() { return fragmentsData; } public enum DashboardType { WAYPOINTS, WAYPOINTS_FLAT, WAYPOINTS_EDIT, CONFIGURE_SCREEN, CONFIGURE_MAP, LIST_MENU, DASHBOARD } public DashboardOnMap(MapActivity ma) { this.mapActivity = ma; } public void createDashboardView() { baseColor = mapActivity.getResources().getColor(R.color.osmand_orange) & 0x00ffffff; waypointDialogHelper = new WaypointDialogHelper(mapActivity); landscape = !AndroidUiHelper.isOrientationPortrait(mapActivity); dashboardView = (FrameLayout) mapActivity.findViewById(R.id.dashboard); View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { hideDashboard(); } }; toolbar = ((Toolbar) dashboardView.findViewById(R.id.toolbar)); ObservableScrollView scrollView = ((ObservableScrollView) dashboardView.findViewById(R.id.main_scroll)); listView = (ListView) dashboardView.findViewById(R.id.dash_list_view); gradientToolbar = mapActivity.getResources().getDrawable(R.drawable.gradient_toolbar).mutate(); if (AndroidUiHelper.isOrientationPortrait(mapActivity)) { this.portrait = true; scrollView.setScrollViewCallbacks(this); ((ObservableListView) listView).setScrollViewCallbacks(this); mFlexibleSpaceImageHeight = mapActivity.getResources().getDimensionPixelSize(R.dimen.dashboard_map_top_padding); mFlexibleBlurSpaceHeight = mapActivity.getResources().getDimensionPixelSize(R.dimen.dashboard_map_toolbar); // Set padding view for ListView. This is the flexible space. paddingView = new View(mapActivity); AbsListView.LayoutParams lp = new AbsListView.LayoutParams( AbsListView.LayoutParams.MATCH_PARENT, mFlexibleSpaceImageHeight); paddingView.setLayoutParams(lp); // This is required to disable header's list selector effect paddingView.setClickable(true); paddingView.setOnClickListener(listener); listView.addHeaderView(paddingView); listBackgroundView = mapActivity.findViewById(R.id.dash_list_background); } dashboardView.findViewById(R.id.animateContent).setOnClickListener(listener); dashboardView.findViewById(R.id.map_part_dashboard).setOnClickListener(listener); initActionButton(); dashboardView.addView(actionButton); } private void updateListBackgroundHeight() { if (listBackgroundView == null || listBackgroundView.getHeight() > 0) { return; } final View contentView = mapActivity.getWindow().getDecorView().findViewById(android.R.id.content); if (contentView.getHeight() > 0) { listBackgroundView.getLayoutParams().height = contentView.getHeight(); } else { contentView.post( new Runnable() { @Override public void run() { // mListBackgroundView's should fill its parent vertically // but the height of the content view is 0 on 'onCreate'. // So we should get it with post(). listBackgroundView.getLayoutParams().height = contentView.getHeight(); } }); } } private void updateToolbarActions() { TextView tv = (TextView) dashboardView.findViewById(R.id.toolbar_text); tv.setText(""); boolean waypointsVisible = visibleType == DashboardType.WAYPOINTS || visibleType == DashboardType.WAYPOINTS_FLAT; boolean waypointsEdit = visibleType == DashboardType.WAYPOINTS_EDIT; if (waypointsVisible || waypointsEdit) { tv.setText(R.string.waypoints); } else if (visibleType == DashboardType.CONFIGURE_MAP) { tv.setText(R.string.configure_map); } else if (visibleType == DashboardType.CONFIGURE_SCREEN) { tv.setText(R.string.layer_map_appearance); } ImageView edit = (ImageView) dashboardView.findViewById(R.id.toolbar_edit); edit.setVisibility(View.GONE); ImageView sort = (ImageView) dashboardView.findViewById(R.id.toolbar_sort); sort.setVisibility(View.GONE); ImageView ok = (ImageView) dashboardView.findViewById(R.id.toolbar_ok); ok.setVisibility(View.GONE); ImageView flat = (ImageView) dashboardView.findViewById(R.id.toolbar_flat); flat.setVisibility(View.GONE); ImageView settingsButton = (ImageView) dashboardView.findViewById(R.id.toolbar_settings); settingsButton.setVisibility(View.GONE); IconsCache iconsCache = mapActivity.getMyApplication().getIconsCache(); ImageView lst = (ImageView) dashboardView.findViewById(R.id.toolbar_list); lst.setVisibility(View.GONE); ImageView back = (ImageView) dashboardView.findViewById(R.id.toolbar_back); back.setImageDrawable( getMyApplication().getIconsCache().getIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha)); back.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { backPressed(); } }); if (waypointsVisible && getMyApplication().getWaypointHelper().getAllPoints().size() > 0) { if (mapActivity.getMyApplication().getTargetPointsHelper().getIntermediatePoints().size() > 0) { sort.setVisibility(View.VISIBLE); sort.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { hideDashboard(); IntermediatePointsDialog.openIntermediatePointsDialog( mapActivity, getMyApplication(), true); } }); } edit.setVisibility(View.VISIBLE); edit.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { setDashboardVisibility(true, DashboardType.WAYPOINTS_EDIT); } }); if (getMyApplication().getWaypointHelper().isRouteCalculated()) { flat.setVisibility(View.VISIBLE); final boolean flatNow = visibleType == DashboardType.WAYPOINTS_FLAT; flat.setImageDrawable( iconsCache.getIcon( flatNow ? R.drawable.ic_tree_list_dark : R.drawable.ic_flat_list_dark)); flat.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { setDashboardVisibility( true, flatNow ? DashboardType.WAYPOINTS : DashboardType.WAYPOINTS_FLAT, previousVisibleType, false); } }); } } if (waypointsEdit) { ok.setVisibility(View.VISIBLE); ok.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { mapActivity .getMyApplication() .getWaypointHelper() .removeVisibleLocationPoint(deletedPoints); hideDashboard(); } }); } if (visibleType == DashboardType.DASHBOARD || visibleType == DashboardType.LIST_MENU) { settingsButton.setVisibility(View.VISIBLE); settingsButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { new DashboardSettingsDialogFragment() .show(mapActivity.getSupportFragmentManager(), "dashboard_settings"); } }); lst.setVisibility(View.VISIBLE); lst.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { hideDashboard(false); mapActivity.openDrawer(); } }); } } private void initActionButton() { actionButton = new ImageView(mapActivity); int btnSize = (int) mapActivity.getResources().getDimension(R.dimen.map_button_size); int topPad = (int) mapActivity.getResources().getDimension(R.dimen.dashboard_map_top_padding); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(btnSize, btnSize); int marginRight = btnSize / 4; params.setMargins( 0, landscape ? 0 : topPad - 2 * btnSize, marginRight, landscape ? marginRight : 0); params.gravity = landscape ? Gravity.BOTTOM | Gravity.RIGHT : Gravity.TOP | Gravity.RIGHT; actionButton.setLayoutParams(params); actionButton.setScaleType(ScaleType.CENTER); actionButton.setImageDrawable( mapActivity.getResources().getDrawable(R.drawable.map_my_location)); actionButton.setBackgroundDrawable( mapActivity.getResources().getDrawable(R.drawable.btn_circle_blue)); hideActionButton(); actionButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { if (getMyApplication().accessibilityEnabled()) { mapActivity.getMapActions().whereAmIDialog(); } else { mapActivity.getMapViewTrackingUtilities().backToLocationImpl(); } hideDashboard(); } }); } private void hideActionButton() { actionButton.setVisibility(View.GONE); } public net.osmand.Location getMyLocation() { return myLocation; } public LatLon getMapViewLocation() { return mapViewLocation; } public float getHeading() { return heading; } public float getMapRotation() { return mapRotation; } public boolean isMapLinkedToLocation() { return mapLinkedToLocation; } protected OsmandApplication getMyApplication() { return mapActivity.getMyApplication(); } public ArrayAdapter<?> getListAdapter() { return listAdapter; } public OnItemClickListener getListAdapterOnClickListener() { return listAdapterOnClickListener; } public void hideDashboard() { setDashboardVisibility(false, visibleType); } public void hideDashboard(boolean animation) { setDashboardVisibility(false, visibleType, animation); } public void setDashboardVisibility(boolean visible, DashboardType type) { setDashboardVisibility(visible, type, this.visible ? visibleType : null, true); } public void setDashboardVisibility(boolean visible, DashboardType type, boolean animation) { setDashboardVisibility(visible, type, this.visible ? visibleType : null, animation); } public void refreshDashboardFragments() { addOrUpdateDashboardFragments(); } public void setDashboardVisibility( boolean visible, DashboardType type, DashboardType prevItem, boolean animation) { if (visible == this.visible && type == visibleType) { return; } this.previousVisibleType = prevItem; this.visible = visible; boolean refresh = this.visibleType == type; this.visibleType = type; DashboardOnMap.staticVisible = visible; DashboardOnMap.staticVisibleType = type; mapActivity.enableDrawer(); if (visible) { mapViewLocation = mapActivity.getMapLocation(); mapRotation = mapActivity.getMapRotate(); mapLinkedToLocation = mapActivity.getMapViewTrackingUtilities().isMapLinkedToLocation(); myLocation = mapActivity.getMyApplication().getLocationProvider().getLastKnownLocation(); mapActivity.getMapViewTrackingUtilities().setDashboard(this); mapActivity.disableDrawer(); dashboardView.setVisibility(View.VISIBLE); if (isActionButtonVisible()) { actionButton.setVisibility(View.VISIBLE); } else { hideActionButton(); } updateDownloadBtn(); View listViewLayout = dashboardView.findViewById(R.id.dash_list_view_layout); ScrollView scrollView = (ScrollView) dashboardView.findViewById(R.id.main_scroll); if (visibleType == DashboardType.DASHBOARD) { addOrUpdateDashboardFragments(); scrollView.setVisibility(View.VISIBLE); scrollView.scrollTo(0, 0); listViewLayout.setVisibility(View.GONE); onScrollChanged(scrollView.getScrollY(), false, false); } else { scrollView.setVisibility(View.GONE); listViewLayout.setVisibility(View.VISIBLE); if (listView instanceof ObservableListView) { onScrollChanged(listView.getScrollY(), false, false); } if (refresh) { refreshContent(false); } else { updateListAdapter(); updateListBackgroundHeight(); } } mapActivity .findViewById(R.id.toolbar_back) .setVisibility(isBackButtonVisible() ? View.VISIBLE : View.GONE); mapActivity.findViewById(R.id.MapHudButtonsOverlay).setVisibility(View.INVISIBLE); updateToolbarActions(); // fabButton.showFloatingActionButton(); open(dashboardView.findViewById(R.id.animateContent), animation); updateLocation(true, true, false); // addOrUpdateDashboardFragments(); } else { mapActivity.getMapViewTrackingUtilities().setDashboard(null); hide(dashboardView.findViewById(R.id.animateContent), animation); mapActivity.findViewById(R.id.MapHudButtonsOverlay).setVisibility(View.VISIBLE); hideActionButton(); for (WeakReference<DashBaseFragment> df : fragList) { if (df.get() != null) { df.get().onCloseDash(); } } } } private void updateListAdapter() { ContextMenuAdapter cm = null; if (DashboardType.WAYPOINTS == visibleType || DashboardType.WAYPOINTS_FLAT == visibleType) { ArrayAdapter<Object> listAdapter = waypointDialogHelper.getWaypointsDrawerAdapter( false, deletedPoints, mapActivity, running, DashboardType.WAYPOINTS_FLAT == visibleType); OnItemClickListener listener = waypointDialogHelper.getDrawerItemClickListener(mapActivity, running, listAdapter); updateListAdapter(listAdapter, listener); } else if (DashboardType.WAYPOINTS_EDIT == visibleType) { deletedPoints.clear(); ArrayAdapter<Object> listAdapter = waypointDialogHelper.getWaypointsDrawerAdapter( true, deletedPoints, mapActivity, running, DashboardType.WAYPOINTS_FLAT == visibleType); OnItemClickListener listener = waypointDialogHelper.getDrawerItemClickListener(mapActivity, running, listAdapter); updateListAdapter(listAdapter, listener); } else { if (DashboardType.CONFIGURE_SCREEN == visibleType) { cm = mapActivity .getMapLayers() .getMapWidgetRegistry() .getViewConfigureMenuAdapter(mapActivity); } else if (DashboardType.CONFIGURE_MAP == visibleType) { cm = new ConfigureMapMenu().createListAdapter(mapActivity); } else if (DashboardType.LIST_MENU == visibleType) { cm = mapActivity.getMapActions().createMainOptionsMenu(); } if (cm != null) { updateListAdapter(cm); } } } public void updateListAdapter(ContextMenuAdapter cm) { final ArrayAdapter<?> listAdapter = cm.createListAdapter(mapActivity, getMyApplication().getSettings().isLightContent()); OnItemClickListener listener = getOptionsMenuOnClickListener(cm, listAdapter); updateListAdapter(listAdapter, listener); } public void refreshContent(boolean force) { if (visibleType == DashboardType.WAYPOINTS || visibleType == DashboardType.WAYPOINTS_EDIT || force) { updateListAdapter(); } else if (visibleType == DashboardType.CONFIGURE_MAP) { int index = listView.getFirstVisiblePosition(); View v = listView.getChildAt(0); int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop()); updateListAdapter(); listView.setSelectionFromTop(index, top); } else { listAdapter.notifyDataSetChanged(); } } private OnItemClickListener getOptionsMenuOnClickListener( final ContextMenuAdapter cm, final ArrayAdapter<?> listAdapter) { return new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int which, long id) { OnContextMenuClick click = cm.getClickAdapter(which); if (click instanceof OnRowItemClick) { boolean cl = ((OnRowItemClick) click) .onRowItemClick(listAdapter, view, cm.getElementId(which), which); if (cl) { hideDashboard(); } } else if (click != null) { CompoundButton btn = (CompoundButton) view.findViewById(R.id.check_item); if (btn != null && btn.getVisibility() == View.VISIBLE) { btn.setChecked(!btn.isChecked()); } else { if (click.onContextMenuClick(listAdapter, cm.getElementId(which), which, false)) { hideDashboard(); } } } else { hideDashboard(); } } }; } private void updateDownloadBtn() { Button btn = (Button) dashboardView.findViewById(R.id.map_download_button); String filter = null; String txt = ""; OsmandMapTileView mv = mapActivity.getMapView(); if (mv != null && !mapActivity.getMyApplication().isApplicationInitializing()) { if (mv.getZoom() < 11 && !mapActivity.getMyApplication().getResourceManager().containsBasemap()) { filter = "basemap"; txt = mapActivity.getString(R.string.shared_string_download) + " " + mapActivity.getString(R.string.base_world_map); } else { DownloadedRegionsLayer dl = mv.getLayerByClass(DownloadedRegionsLayer.class); if (dl != null) { StringBuilder btnName = new StringBuilder(); filter = dl.getFilter(btnName); txt = btnName.toString(); } } } btn.setText(txt); btn.setVisibility(filter == null ? View.GONE : View.VISIBLE); final String f = filter; btn.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { hideDashboard(false); final Intent intent = new Intent( mapActivity, mapActivity .getMyApplication() .getAppCustomization() .getDownloadIndexActivity()); if (f != null && !f.equals("basemap")) { intent.putExtra(DownloadActivity.FILTER_KEY, f); } intent.putExtra(DownloadActivity.TAB_TO_OPEN, DownloadActivity.DOWNLOAD_TAB); mapActivity.startActivity(intent); } }); scheduleDownloadButtonCheck(); } private void scheduleDownloadButtonCheck() { mapActivity .getMyApplication() .runInUIThread( new Runnable() { @Override public void run() { if (isVisible()) { updateDownloadBtn(); } } }, 4000); } public void navigationAction() { RoutingHelper routingHelper = mapActivity.getRoutingHelper(); if (!routingHelper.isFollowingMode() && !routingHelper.isRoutePlanningMode()) { mapActivity.getMapActions().enterRoutePlanningMode(null, null); } else { mapActivity.getRoutingHelper().setRoutePlanningMode(true); mapActivity.getMapViewTrackingUtilities().switchToRoutePlanningMode(); mapActivity.refreshMap(); } hideDashboard(true); } // To animate view slide out from right to left private void open(View view, boolean animation) { if (animation) { TranslateAnimation animate = new TranslateAnimation( -mapActivity.findViewById(R.id.MapHudButtonsOverlay).getWidth(), 0, 0, 0); animate.setDuration(500); animate.setFillAfter(true); view.startAnimation(animate); view.setVisibility(View.VISIBLE); } else { view.setVisibility(View.VISIBLE); } } private void hide(View view, boolean animation) { if (!animation) { dashboardView.setVisibility(View.GONE); } else { TranslateAnimation animate = new TranslateAnimation( 0, -mapActivity.findViewById(R.id.MapHudButtonsOverlay).getWidth(), 0, 0); animate.setDuration(500); animate.setFillAfter(true); animate.setAnimationListener( new AnimationListener() { @Override public void onAnimationStart(Animation animation) {} @Override public void onAnimationRepeat(Animation animation) {} @Override public void onAnimationEnd(Animation animation) { dashboardView.setVisibility(View.GONE); } }); view.startAnimation(animate); } view.setVisibility(View.GONE); } private void addOrUpdateDashboardFragments() { OsmandSettings settings = getMyApplication().getSettings(); TransactionBuilder builder = new TransactionBuilder(mapActivity.getSupportFragmentManager(), settings, mapActivity); builder .addFragmentsData(fragmentsData) .addFragmentsData(OsmandPlugin.getPluginsCardsList()) .getFragmentTransaction() .commit(); } public boolean isVisible() { return visible; } public void onDetach(DashBaseFragment dashBaseFragment) { Iterator<WeakReference<DashBaseFragment>> it = fragList.iterator(); while (it.hasNext()) { WeakReference<DashBaseFragment> wr = it.next(); if (wr.get() == dashBaseFragment) { it.remove(); } } } public void updateLocation( final boolean centerChanged, final boolean locationChanged, final boolean compassChanged) { if (inLocationUpdate) { return; } inLocationUpdate = true; mapActivity.runOnUiThread( new Runnable() { @Override public void run() { inLocationUpdate = false; for (WeakReference<DashBaseFragment> df : fragList) { if (df.get() instanceof DashLocationFragment) { ((DashLocationFragment) df.get()) .updateLocation(centerChanged, locationChanged, compassChanged); } } } }); } public void updateMyLocation(net.osmand.Location location) { myLocation = location; updateLocation(false, true, false); } public void updateCompassValue(double heading) { this.heading = (float) heading; updateLocation(false, false, true); } public void onAttach(DashBaseFragment dashBaseFragment) { fragList.add(new WeakReference<>(dashBaseFragment)); } public void requestLayout() { dashboardView.requestLayout(); } public void onMenuPressed() { if (!isVisible()) { setDashboardVisibility(true, DashboardType.DASHBOARD); } else { hideDashboard(); } } public boolean onBackPressed() { if (isVisible()) { backPressed(); return true; } return false; } private void backPressed() { if (previousVisibleType != visibleType && previousVisibleType != null) { visibleType = null; setDashboardVisibility(true, previousVisibleType); } else { hideDashboard(); } } @Override public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { // Translate list background if (portrait) { if (listBackgroundView != null) { setTranslationY(listBackgroundView, Math.max(0, -scrollY + mFlexibleSpaceImageHeight)); } } if (portrait) { setTranslationY( toolbar, Math.min(0, -scrollY + mFlexibleSpaceImageHeight - mFlexibleBlurSpaceHeight)); } updateColorOfToolbar(scrollY); updateTopButton(scrollY); } private boolean isActionButtonVisible() { return visibleType == DashboardType.DASHBOARD || visibleType == DashboardType.LIST_MENU || visibleType == DashboardType.CONFIGURE_SCREEN; } private boolean isBackButtonVisible() { return !(visibleType == DashboardType.DASHBOARD || visibleType == DashboardType.LIST_MENU); } private void updateTopButton(int scrollY) { if (actionButton != null && portrait && isActionButtonVisible()) { double scale = mapActivity.getResources().getDisplayMetrics().density; int originalPosition = mFlexibleSpaceImageHeight - (int) (80 * scale); int minTop = mFlexibleBlurSpaceHeight + (int) (5 * scale); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) actionButton.getLayoutParams(); if (minTop > originalPosition - scrollY) { hideActionButton(); } else { actionButton.setVisibility(View.VISIBLE); lp.topMargin = originalPosition - scrollY; ((FrameLayout) actionButton.getParent()).updateViewLayout(actionButton, lp); } } } private void updateColorOfToolbar(int scrollY) { if (portrait) { float sh = mFlexibleSpaceImageHeight - mFlexibleBlurSpaceHeight; float t = sh == 0 ? 1 : (1 - Math.max(0, -scrollY + sh) / sh); t = Math.max(0, t); int alpha = (int) (t * 255); // in order to have proper fast scroll down int malpha = t == 1 ? 0 : alpha; setAlpha(paddingView, malpha, baseColor); setAlpha(dashboardView.findViewById(R.id.map_part_dashboard), malpha, baseColor); gradientToolbar.setAlpha((int) ((1 - t) * 255)); setAlpha(dashboardView, (int) (t * 128), 0); if (t < 1) { dashboardView.findViewById(R.id.toolbar).setBackgroundDrawable(gradientToolbar); } else { dashboardView.findViewById(R.id.toolbar).setBackgroundColor(0xff000000 | baseColor); } } } private void updateListAdapter(ArrayAdapter<?> listAdapter, OnItemClickListener listener) { this.listAdapter = listAdapter; this.listAdapterOnClickListener = listener; if (this.listView != null) { listView.setAdapter(listAdapter); if (!portrait) { listView.setOnItemClickListener(this.listAdapterOnClickListener); } else if (this.listAdapterOnClickListener != null) { listView.setOnItemClickListener( new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { listAdapterOnClickListener.onItemClick(parent, view, position - 1, id); } }); } else { listView.setOnItemClickListener(null); } } } @SuppressLint("NewApi") private void setTranslationY(View v, int y) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { v.setTranslationY(y); } else { TranslateAnimation anim = new TranslateAnimation(0, 0, y, y); anim.setFillAfter(true); anim.setDuration(0); v.startAnimation(anim); } } @SuppressLint("NewApi") private void setAlpha(View v, int alpha, int clr) { // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // v.setAlpha(alpha/255.f); // } else { int colr = (alpha << 24) | clr; v.setBackgroundColor(colr); // } } @Override public void onDownMotionEvent() {} @Override public void onUpOrCancelMotionEvent(ScrollState scrollState) { // ActionBar ab = getSupportActionBar(); // if (scrollState == ScrollState.UP) { // if (ab.isShowing()) { // ab.hide(); // } // } else if (scrollState == ScrollState.DOWN) { // if (!ab.isShowing()) { // ab.show(); // } // } } public <T extends DashBaseFragment> T getFragmentByClass(Class<T> class1) { for (WeakReference<DashBaseFragment> f : fragList) { DashBaseFragment b = f.get(); if (b != null && !b.isDetached() && class1.isInstance(b)) { //noinspection unchecked return (T) b; } } return null; } public void blacklistFragmentByTag(String tag) { hideFragmentByTag(tag); getMyApplication() .getSettings() .registerBooleanPreference(SHOULD_SHOW + tag, true) .makeGlobal() .set(false); } public void hideFragmentByTag(String tag) { FragmentManager manager = mapActivity.getSupportFragmentManager(); FragmentTransaction transaction = manager.beginTransaction(); Fragment frag = manager.findFragmentByTag(tag); transaction.hide(frag).commit(); } public void unblacklistFragmentClass(String tag) { unhideFragmentByTag(tag); getMyApplication() .getSettings() .registerBooleanPreference(SHOULD_SHOW + tag, true) .makeGlobal() .set(true); } public void unhideFragmentByTag(String tag) { FragmentManager manager = mapActivity.getSupportFragmentManager(); FragmentTransaction transaction = manager.beginTransaction(); Fragment frag = manager.findFragmentByTag(tag); transaction.show(frag).commit(); } View getParentView() { return dashboardView; } public static <T> List<T> handleNumberOfRows( List<T> list, OsmandSettings settings, String rowNumberTag) { int numberOfRows = settings.registerIntPreference(rowNumberTag, 3).makeGlobal().get(); if (list.size() > numberOfRows) { while (list.size() != numberOfRows) { list.remove(numberOfRows); } } return list; } public static class DefaultShouldShow extends DashFragmentData.ShouldShowFunction { public boolean shouldShow(OsmandSettings settings, MapActivity activity, String tag) { return settings.registerBooleanPreference(SHOULD_SHOW + tag, true).makeGlobal().get(); } } }
public class OpeningHoursDaysDialogFragment extends DialogFragment { private static final Log LOG = PlatformUtil.getLog(OpeningHoursDaysDialogFragment.class); public static final String POSITION_TO_ADD = "position_to_add"; public static final String ITEM = "item"; @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final OpeningHoursParser.BasicOpeningHourRule item = (OpeningHoursParser.BasicOpeningHourRule) getArguments().getSerializable(ITEM); final int positionToAdd = getArguments().getInt(POSITION_TO_ADD); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); final boolean createNew = positionToAdd == -1; Calendar inst = Calendar.getInstance(); final int first = inst.getFirstDayOfWeek(); final boolean[] dayToShow = new boolean[7]; String[] daysToShow = new String[7]; for (int i = 0; i < 7; i++) { int d = (first + i - 1) % 7 + 1; inst.set(Calendar.DAY_OF_WEEK, d); CharSequence dayName = DateFormat.format("EEEE", inst); String result = "" + Character.toUpperCase(dayName.charAt(0)) + dayName.subSequence(1, dayName.length()); daysToShow[i] = result; // $NON-NLS-1$ final int pos = (d + 5) % 7; dayToShow[i] = item.getDays()[pos]; } builder.setTitle(getResources().getString(R.string.working_days)); builder.setMultiChoiceItems( daysToShow, dayToShow, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { dayToShow[which] = isChecked; } }); builder.setPositiveButton( createNew ? R.string.next_proceed : R.string.shared_string_save, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { boolean[] days = item.getDays(); for (int i = 0; i < 7; i++) { days[(first + 5 + i) % 7] = dayToShow[i]; } if (createNew) { OpeningHoursHoursDialogFragment.createInstance(item, positionToAdd, true) .show(getFragmentManager(), "TimePickerDialogFragment"); } else { ((BasicDataFragment) getParentFragment()) .setBasicOpeningHoursRule(item, positionToAdd); } } }); builder.setNegativeButton(getActivity().getString(R.string.shared_string_cancel), null); return builder.create(); } public static OpeningHoursDaysDialogFragment createInstance( @NonNull final OpeningHoursParser.BasicOpeningHourRule item, final int positionToAdd) { LOG.debug("createInstance(" + "item=" + item + ", positionToAdd=" + positionToAdd + ")"); OpeningHoursDaysDialogFragment daysDialogFragment = new OpeningHoursDaysDialogFragment(); Bundle bundle = new Bundle(); bundle.putSerializable(ITEM, item); bundle.putInt(POSITION_TO_ADD, positionToAdd); daysDialogFragment.setArguments(bundle); return daysDialogFragment; } }
public class DownloadTilesDialog { private static final Log log = PlatformUtil.getLog(DownloadTilesDialog.class); private final Context ctx; private final OsmandApplication app; private final OsmandMapTileView mapView; public DownloadTilesDialog(Context ctx, OsmandApplication app, OsmandMapTileView mapView) { this.ctx = ctx; this.app = app; this.mapView = mapView; } public void openDialog() { BaseMapLayer mainLayer = mapView.getMainLayer(); if (!(mainLayer instanceof MapTileLayer) || !((MapTileLayer) mainLayer).isVisible()) { AccessibleToast.makeText(ctx, R.string.maps_could_not_be_downloaded, Toast.LENGTH_SHORT) .show(); } final ITileSource mapSource = ((MapTileLayer) mainLayer).getMap(); if (mapSource == null || !mapSource.couldBeDownloadedFromInternet()) { AccessibleToast.makeText(ctx, R.string.maps_could_not_be_downloaded, Toast.LENGTH_SHORT) .show(); return; } final RotatedTileBox rb = mapView.getCurrentRotatedTileBox(); final int max = mapSource.getMaximumZoomSupported(); // get narrow zoom final int zoom = rb.getZoom(); // calculate pixel rectangle AlertDialog.Builder builder = new AlertDialog.Builder(ctx); LayoutInflater inflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.download_tiles, null); ((TextView) view.findViewById(R.id.MinZoom)).setText(zoom + ""); // $NON-NLS-1$ ((TextView) view.findViewById(R.id.MaxZoom)).setText(max + ""); // $NON-NLS-1$ final SeekBar seekBar = (SeekBar) view.findViewById(R.id.ZoomToDownload); seekBar.setMax(max - zoom); seekBar.setProgress((max - zoom) / 2); final TextView downloadText = ((TextView) view.findViewById(R.id.DownloadDescription)); final String template = ctx.getString(R.string.tiles_to_download_estimated_size); updateLabel(zoom, rb.getLatLonBounds(), downloadText, template, seekBar.getProgress()); seekBar.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { updateLabel(zoom, rb.getLatLonBounds(), downloadText, template, progress); } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} }); builder.setPositiveButton( R.string.shared_string_download, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); run(zoom, seekBar.getProgress(), rb.getLatLonBounds(), mapSource); } }); builder.setNegativeButton(R.string.shared_string_cancel, null); builder.setView(view); builder.show(); } private volatile boolean cancel = false; private IMapDownloaderCallback callback; public void run( final int zoom, final int progress, final QuadRect latlonRect, final ITileSource map) { cancel = false; int numberTiles = 0; for (int z = zoom; z <= progress + zoom; z++) { int x1 = (int) MapUtils.getTileNumberX(z, latlonRect.left); int x2 = (int) MapUtils.getTileNumberX(z, latlonRect.right); int y1 = (int) MapUtils.getTileNumberY(z, latlonRect.top); int y2 = (int) MapUtils.getTileNumberY(z, latlonRect.bottom); numberTiles += (x2 - x1 + 1) * (y2 - y1 + 1); } final ProgressDialog progressDlg = new ProgressDialog(ctx); progressDlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDlg.setMessage( ctx.getString(R.string.shared_string_downloading) + ctx.getString(R.string.shared_string_ellipsis)); progressDlg.setCancelable(true); progressDlg.setMax(numberTiles); progressDlg.setOnCancelListener( new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { cancel = true; } }); final MapTileDownloader instance = MapTileDownloader.getInstance(Version.getFullVersion(app)); final List<IMapDownloaderCallback> previousCallbacks = instance.getDownloaderCallbacks(); instance.clearCallbacks(); callback = new IMapDownloaderCallback() { @Override public void tileDownloaded(DownloadRequest request) { if (request != null) { progressDlg.setProgress(progressDlg.getProgress() + 1); } } }; instance.addDownloaderCallback(callback); Runnable r = new Runnable() { @Override public void run() { int requests = 0; int limitRequests = 50; try { ResourceManager rm = app.getResourceManager(); for (int z = zoom; z <= zoom + progress && !cancel; z++) { int x1 = (int) MapUtils.getTileNumberX(z, latlonRect.left); int x2 = (int) MapUtils.getTileNumberX(z, latlonRect.right); int y1 = (int) MapUtils.getTileNumberY(z, latlonRect.top); int y2 = (int) MapUtils.getTileNumberY(z, latlonRect.bottom); for (int x = x1; x <= x2 && !cancel; x++) { for (int y = y1; y <= y2 && !cancel; y++) { String tileId = rm.calculateTileId(map, x, y, z); if (rm.tileExistOnFileSystem(tileId, map, x, y, z)) { progressDlg.setProgress(progressDlg.getProgress() + 1); } else { rm.getTileImageForMapSync(tileId, map, x, y, z, true); requests++; } if (!cancel) { if (requests >= limitRequests) { requests = 0; while (instance.isSomethingBeingDownloaded()) { try { Thread.sleep(500); } catch (InterruptedException e) { throw new IllegalArgumentException(e); } } } } } } } if (cancel) { instance.refuseAllPreviousRequests(); } else { while (instance.isSomethingBeingDownloaded()) { try { Thread.sleep(500); } catch (InterruptedException e) { throw new IllegalArgumentException(e); } } } mapView.refreshMap(); callback = null; } catch (Exception e) { log.error("Exception while downloading tiles ", e); // $NON-NLS-1$ instance.refuseAllPreviousRequests(); } finally { instance.clearCallbacks(); for (IMapDownloaderCallback cbck : previousCallbacks) { instance.addDownloaderCallback(cbck); } app.getResourceManager().reloadTilesFromFS(); } progressDlg.dismiss(); } }; new Thread(r, "Downloading tiles").start(); // $NON-NLS-1$ progressDlg.show(); } private void updateLabel( final int zoom, final QuadRect latlonRect, final TextView downloadText, final String template, int progress) { int numberTiles = 0; for (int z = zoom; z <= progress + zoom; z++) { int x1 = (int) MapUtils.getTileNumberX(z, latlonRect.left); int x2 = (int) MapUtils.getTileNumberX(z, latlonRect.right); int y1 = (int) MapUtils.getTileNumberY(z, latlonRect.top); int y2 = (int) MapUtils.getTileNumberY(z, latlonRect.bottom); numberTiles += (x2 - x1 + 1) * (y2 - y1 + 1); } downloadText.setText( MessageFormat.format( template, (progress + zoom) + "", // $NON-NLS-1$ numberTiles, (double) numberTiles * 12 / 1000)); } }
@SuppressLint("NewApi") public abstract class SearchByNameAbstractActivity<T> extends OsmandListActivity { private EditText searchText; private AsyncTask<Object, ?, ?> initializeTask; protected static final int MESSAGE_CLEAR_LIST = OsmAndConstants.UI_HANDLER_SEARCH + 2; protected static final int MESSAGE_ADD_ENTITY = OsmAndConstants.UI_HANDLER_SEARCH + 3; protected static final String SELECT_ADDRESS = "SEQUENTIAL_SEARCH"; protected ProgressBar progress; protected LatLon locationToSearch; protected OsmandSettings settings; protected List<T> initialListToFilter = new ArrayList<T>(); protected Handler uiHandler; protected Collator collator; protected NamesFilter namesFilter; private String currentFilter = ""; private boolean initFilter = false; private String endingText = ""; private T endingObject; private StyleSpan previousSpan; private static final Log log = PlatformUtil.getLog(SearchByNameAbstractActivity.class); private static final int NAVIGATE_TO = 3; private static final int ADD_WAYPOINT = 4; private static final int SHOW_ON_MAP = 5; private static final int ADD_TO_FAVORITE = 6; private void separateMethod() { getWindow().setUiOptions(ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); settings = ((OsmandApplication) getApplication()).getSettings(); setContentView(R.layout.search_by_name); initializeTask = getInitializeTask(); uiHandler = new UIUpdateHandler(); namesFilter = new NamesFilter(); addFooterViews(); final NamesAdapter namesAdapter = new NamesAdapter(new ArrayList<T>(), createComparator()); // $NON-NLS-1$ setListAdapter(namesAdapter); collator = OsmAndCollator.primaryCollator(); progress = (ProgressBar) findViewById(R.id.ProgressBar); searchText = (EditText) findViewById(R.id.SearchText); searchText.addTextChangedListener( new TextWatcher() { @Override public void afterTextChanged(Editable s) { String newFilter = s.toString(); String newEndingText = endingText; if (newEndingText.length() > 0) { while (!newFilter.endsWith(newEndingText) && newEndingText.length() > 0) { newEndingText = newEndingText.substring(1); } newFilter = newFilter.substring(0, newFilter.length() - newEndingText.length()); } updateTextBox(newFilter, newEndingText, endingObject, false); querySearch(newFilter); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} }); // Not perfect // filter.setOnClickListener(new OnClickListener() { // } // }); searchText.setImeOptions(EditorInfo.IME_ACTION_DONE); searchText.requestFocus(); searchText.setOnEditorActionListener( new OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { if (endingObject != null) { itemSelectedBase(endingObject, v); } return true; } return false; } }); progress.setVisibility(View.INVISIBLE); findViewById(R.id.ResetButton) .setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { reset(); } }); selectAddress = getIntent() != null && getIntent().hasExtra(SELECT_ADDRESS); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); if (initializeTask != null) { initializeTask.execute(); } } protected void reset() { searchText.setText(""); } public String getLangPreferredName(MapObject mo) { return mo.getName(settings.MAP_PREFERRED_LOCALE.get()); } protected void addFooterViews() {} public void setLabelText(int res) { getSupportActionBar().setSubtitle(getString(res)); } protected int getZoomToDisplay(T item) { return 15; } protected LatLon getLocation(T item) { if (item instanceof MapObject) { return ((MapObject) item).getLocation(); } return null; } public AsyncTask<Object, ?, ?> getInitializeTask() { return null; } public Editable getFilter() { return searchText.getText(); } public boolean initializeTaskIsFinished() { return initializeTask == null || initializeTask.getStatus() == Status.FINISHED; } private int MAX_VISIBLE_NAME = 18; private boolean selectAddress; public String getCurrentFilter() { return currentFilter; } public void research() { initFilter = false; querySearch(currentFilter); } protected View getFooterView() { return null; } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("ENDING_TEXT", endingText); outState.putParcelable("PREVIOUS_SPAN", this.previousSpan); } @Override protected void onRestoreInstanceState(Bundle prevState) { endingText = prevState.getString("ENDING_TEXT"); if (endingText == null) { endingText = ""; } previousSpan = prevState.getParcelable("PREVIOUS_SPAN"); super.onRestoreInstanceState(prevState); } protected boolean isSelectAddres() { return selectAddress; } private void querySearch(final String filter) { if (!currentFilter.equals(filter) || !initFilter) { currentFilter = filter; initFilter = true; progress.setVisibility(View.VISIBLE); namesFilter.cancelPreviousFilter(filter); namesFilter.filter(filter); } } private void updateTextBox( String currentFilter, String locEndingText, T obj, boolean updateText) { String prevEndtext = endingText; endingText = locEndingText; endingObject = obj; if (updateText) { searchText .getText() .replace( currentFilter.length(), currentFilter.length() + prevEndtext.length(), locEndingText); } if (previousSpan != null) { searchText.getText().removeSpan(previousSpan); previousSpan = null; } if (locEndingText.length() > 0) { previousSpan = new StyleSpan(Typeface.BOLD_ITALIC); searchText .getText() .setSpan( previousSpan, currentFilter.length(), currentFilter.length() + locEndingText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); if (searchText.getSelectionEnd() > currentFilter.length()) { searchText.setSelection(currentFilter.length()); } } } protected void addObjectToInitialList(T initial) { initialListToFilter.add(initial); if (!namesFilter.active) { if (filterObject(initial, currentFilter)) { Message msg = uiHandler.obtainMessage(MESSAGE_ADD_ENTITY, initial); msg.sendToTarget(); } } } protected void finishInitializing(List<T> list) { Comparator<? super T> cmp = createComparator(); getListAdapter().sort(cmp); if (list != null) { Collections.sort(list, cmp); initialListToFilter = list; } research(); } protected abstract Comparator<? super T> createComparator(); public String getDistanceText(T obj) { return null; } public abstract String getText(T obj); public String getAdditionalFilterText(T obj) { return null; } public String getShortText(T obj) { return getText(obj); } public void itemSelectedBase(final T obj, View v) { itemSelected(obj); } public abstract void itemSelected(T obj); public boolean filterObject(T obj, String filter) { if (filter == null || filter.length() == 0) { return true; } boolean matches = CollatorStringMatcher.cmatches( collator, getText(obj), filter, StringMatcherMode.CHECK_STARTS_FROM_SPACE); if (!matches && getAdditionalFilterText(obj) != null) { matches = CollatorStringMatcher.cmatches( collator, getAdditionalFilterText(obj), filter, StringMatcherMode.CHECK_STARTS_FROM_SPACE); } return matches; } @Override protected void onResume() { super.onResume(); selectAddress = getIntent() != null && getIntent().getBooleanExtra(SELECT_ADDRESS, false); setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { T repo = getListAdapter().getItem(position); itemSelectedBase(repo, view); } }); Intent intent = getIntent(); if (intent != null) { if (intent.hasExtra(SearchActivity.SEARCH_LAT) && intent.hasExtra(SearchActivity.SEARCH_LON)) { double lat = intent.getDoubleExtra(SearchActivity.SEARCH_LAT, 0); double lon = intent.getDoubleExtra(SearchActivity.SEARCH_LON, 0); locationToSearch = new LatLon(lat, lon); } } if (locationToSearch == null) { locationToSearch = settings.getLastKnownMapLocation(); } } @Override public NamesAdapter getListAdapter() { return (NamesAdapter) super.getListAdapter(); } @Override protected void onPause() { super.onPause(); namesFilter.cancelPreviousFilter(currentFilter); } protected void filterLoop(String query, Collection<T> list) { for (T obj : list) { if (namesFilter.isCancelled) { break; } if (filterObject(obj, query)) { Message msg = uiHandler.obtainMessage(MESSAGE_ADD_ENTITY, obj); msg.sendToTarget(); } } } class UIUpdateHandler extends Handler { private Map<String, Integer> endingMap = new HashMap<String, Integer>(); private int minimalIndex = Integer.MAX_VALUE; private String minimalText = null; @SuppressWarnings("unchecked") @Override public void handleMessage(Message msg) { String currentFilter = SearchByNameAbstractActivity.this.currentFilter; if (msg.what == MESSAGE_CLEAR_LIST) { minimalIndex = Integer.MAX_VALUE; minimalText = null; getListAdapter().clear(); if (currentFilter.length() == 0) { endingMap.clear(); } updateTextBox(currentFilter, "", null, true); } else if (msg.what == MESSAGE_ADD_ENTITY) { getListAdapter().add((T) msg.obj); if (currentFilter.length() > 0) { String shortText = getShortText((T) msg.obj); int entries = !endingMap.containsKey(shortText) ? 0 : endingMap.get(shortText); if (entries < minimalIndex) { if (minimalText != null) { endingMap.put(minimalText, endingMap.get(minimalText) - 1); } minimalIndex = entries; minimalText = shortText; endingMap.put(shortText, entries + 1); String locEndingText; if (shortText.toLowerCase().startsWith(currentFilter.toLowerCase())) { locEndingText = shortText.substring(currentFilter.length()); } else { locEndingText = " - " + shortText; } if (locEndingText.length() > MAX_VISIBLE_NAME) { locEndingText = locEndingText.substring(0, MAX_VISIBLE_NAME) + ".."; } updateTextBox(currentFilter, locEndingText, (T) msg.obj, true); } } } } } class NamesFilter extends Filter { protected boolean isCancelled = false; private String newFilter; private boolean active = false; private long startTime; protected void cancelPreviousFilter(String newFilter) { this.newFilter = newFilter; isCancelled = true; } @Override protected FilterResults performFiltering(CharSequence constraint) { isCancelled = false; String query = constraint.toString(); if (query.equals(newFilter)) { active = true; startTime = System.currentTimeMillis(); uiHandler.sendEmptyMessage(MESSAGE_CLEAR_LIST); // make link copy Collection<T> list = initialListToFilter; filterLoop(query, list); active = false; } if (!isCancelled) { return new FilterResults(); } return null; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { if (results != null && initializeTaskIsFinished()) { log.debug( "Search " + constraint + " finished in " + (System.currentTimeMillis() - startTime)); progress.setVisibility(View.INVISIBLE); } } } protected class NamesAdapter extends ArrayAdapter<T> { NamesAdapter(List<T> list, Comparator<? super T> cmp) { super(SearchByNameAbstractActivity.this, R.layout.searchbyname_list, list); Collections.sort(list, cmp); } @Override public View getView(int position, View convertView, ViewGroup parent) { View row; if (convertView != null) { row = convertView; } else { LayoutInflater inflater = getLayoutInflater(); row = inflater.inflate(R.layout.searchbyname_list, parent, false); } TextView label = (TextView) row.findViewById(R.id.NameLabel); String distanceText = getDistanceText(getItem(position)); String text = getText(getItem(position)); if (distanceText == null) { label.setText(text); } else { label.setText(distanceText + " " + text, BufferType.SPANNABLE); ((Spannable) label.getText()) .setSpan( new ForegroundColorSpan(getResources().getColor(R.color.color_distance)), 0, distanceText.length(), 0); } return row; } } protected void quitActivity(Class<? extends Activity> next) { finish(); if (next != null) { Intent intent = new Intent(this, next); if (getIntent() != null) { Intent cintent = getIntent(); if (cintent.hasExtra(SearchActivity.SEARCH_LAT) && cintent.hasExtra(SearchActivity.SEARCH_LON)) { intent.putExtra( SearchActivity.SEARCH_LAT, cintent.getDoubleExtra(SearchActivity.SEARCH_LAT, 0)); intent.putExtra( SearchActivity.SEARCH_LON, cintent.getDoubleExtra(SearchActivity.SEARCH_LON, 0)); } } intent.putExtra(SELECT_ADDRESS, selectAddress); startActivity(intent); } } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == 1) { finish(); return true; } else { select(item.getItemId()); } return super.onOptionsItemSelected(item); } @Override public boolean onCreateOptionsMenu(Menu menu) { if (!selectAddress && getAddressInformation() != null) { createMenuItem( menu, SHOW_ON_MAP, R.string.shared_string_show_on_map, R.drawable.ic_action_marker_dark, MenuItem.SHOW_AS_ACTION_ALWAYS); } else { createMenuItem( menu, 1, R.string.shared_string_ok, R.drawable.ic_action_done, MenuItem.SHOW_AS_ACTION_ALWAYS); } return super.onCreateOptionsMenu(menu); } protected AddressInformation getAddressInformation() { return null; } protected void select(int mode) { LatLon searchPoint = settings.getLastSearchedPoint(); AddressInformation ai = getAddressInformation(); if (ai != null) { if (mode == ADD_TO_FAVORITE) { Bundle b = new Bundle(); Dialog dlg = FavoriteDialogs.createAddFavouriteDialog(getActivity(), b); dlg.show(); FavoriteDialogs.prepareAddFavouriteDialog( getActivity(), dlg, b, searchPoint.getLatitude(), searchPoint.getLongitude(), new PointDescription(PointDescription.POINT_TYPE_ADDRESS, ai.objectName)); } else if (mode == NAVIGATE_TO) { DirectionsDialogs.directionsToDialogAndLaunchMap( getActivity(), searchPoint.getLatitude(), searchPoint.getLongitude(), ai.getHistoryName()); } else if (mode == ADD_WAYPOINT) { DirectionsDialogs.addWaypointDialogAndLaunchMap( getActivity(), searchPoint.getLatitude(), searchPoint.getLongitude(), ai.getHistoryName()); } else if (mode == SHOW_ON_MAP) { showOnMap(searchPoint, ai); } } } public void showOnMap(LatLon searchPoint, AddressInformation ai) { settings.setMapLocationToShow( searchPoint.getLatitude(), searchPoint.getLongitude(), ai.zoom, ai.getHistoryName()); MapActivity.launchMapActivityMoveToTop(getActivity()); } private Activity getActivity() { return this; } }