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;
 }
Example #3
0
  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;
  }
Example #4
0
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;
  }
}
Example #5
0
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;
  }
}
Example #7
0
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();
          }
        });
  }
}
Example #9
0
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;
  }
}
Example #10
0
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;
  }
}
Example #12
0
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);
    }
  }
}
Example #13
0
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;
    }
  }
}
Example #14
0
/**
 * 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;
  }
}
Example #15
0
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();
  }
}
Example #16
0
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;
  }
}
Example #18
0
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);
  }
}
Example #19
0
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;
    }
  }
}
Example #20
0
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;
  }
}
Example #21
0
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();
    }
  }
}
Example #22
0
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);
        }
      }
    }
  }
}
Example #26
0
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;
  }
}
Example #27
0
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;
  }
}
Example #29
0
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;
  }
}