diff --git a/OsmAnd/src/net/osmand/plus/SettingsHelper.java b/OsmAnd/src/net/osmand/plus/SettingsHelper.java index 8a1668d364..eb5d5bf434 100644 --- a/OsmAnd/src/net/osmand/plus/SettingsHelper.java +++ b/OsmAnd/src/net/osmand/plus/SettingsHelper.java @@ -14,6 +14,7 @@ import com.google.gson.reflect.TypeToken; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; +import net.osmand.data.LatLon; import net.osmand.map.ITileSource; import net.osmand.map.TileSourceManager; import net.osmand.osm.MapPoiTypes; @@ -21,6 +22,8 @@ import net.osmand.osm.PoiCategory; import net.osmand.plus.ApplicationMode.ApplicationModeBean; import net.osmand.plus.ApplicationMode.ApplicationModeBuilder; import net.osmand.plus.OsmandSettings.OsmandPreference; +import net.osmand.plus.helpers.AvoidSpecificRoads; +import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.quickaction.QuickAction; import net.osmand.plus.quickaction.QuickActionRegistry; @@ -147,6 +150,7 @@ public class SettingsHelper { QUICK_ACTION, POI_UI_FILTERS, MAP_SOURCES, + AVOID_ROADS } public abstract static class SettingsItem { @@ -1435,6 +1439,191 @@ public class SettingsHelper { } } + public static class AvoidRoadsSettingsItem extends CollectionSettingsItem { + + private OsmandApplication app; + private OsmandSettings settings; + private AvoidSpecificRoads specificRoads; + + public AvoidRoadsSettingsItem(@NonNull OsmandApplication app, @NonNull List items) { + super(SettingsItemType.AVOID_ROADS, items); + this.app = app; + settings = app.getSettings(); + specificRoads = app.getAvoidSpecificRoads(); + existingItems = new ArrayList<>(specificRoads.getImpassableRoads().values()); + } + + AvoidRoadsSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(SettingsItemType.AVOID_ROADS, json); + this.app = app; + settings = app.getSettings(); + specificRoads = app.getAvoidSpecificRoads(); + existingItems = new ArrayList<>(specificRoads.getImpassableRoads().values()); + } + + @NonNull + @Override + public String getName() { + return "avoid_roads"; + } + + @NonNull + @Override + public String getPublicName(@NonNull Context ctx) { + return "avoid_roads"; + } + + @NonNull + @Override + public String getFileName() { + return getName() + ".json"; + } + + @Override + public void apply() { + for (AvoidRoadInfo duplicate : duplicateItems) { + if (shouldReplace) { + LatLon latLon = new LatLon(duplicate.latitude, duplicate.longitude); + if (settings.removeImpassableRoad(latLon)) { + settings.addImpassableRoad(duplicate); + } + } else { + settings.addImpassableRoad(duplicate); + } + } + for (AvoidRoadInfo avoidRoad : items) { + settings.addImpassableRoad(avoidRoad); + } + specificRoads.loadImpassableRoads(); + specificRoads.initRouteObjects(true); + } + + @NonNull + @Override + public List excludeDuplicateItems() { + duplicateItems = new ArrayList<>(); + for (AvoidRoadInfo item : items) { + if (isDuplicate(item)) { + duplicateItems.add(item); + } + } + items.removeAll(duplicateItems); + return duplicateItems; + } + + @Override + public boolean isDuplicate(AvoidRoadInfo item) { + for (AvoidRoadInfo existingItem : existingItems) { + if (item.latitude == existingItem.latitude && item.longitude == existingItem.longitude) { + return true; + } + } + return false; + } + + @Override + public boolean shouldReadOnCollecting() { + return true; + } + + @NonNull + @Override + public AvoidRoadInfo renameItem(AvoidRoadInfo item) { + int number = 0; + while (true) { + number++; + AvoidRoadInfo renamedItem = item; + renamedItem.name = renamedItem.name + "_" + number; + if (!isDuplicate(renamedItem)) { + return renamedItem; + } + } + } + + @NonNull + @Override + SettingsItemReader getReader() { + return new SettingsItemReader(this) { + @Override + public void readFromStream(@NonNull InputStream inputStream) throws IOException, IllegalArgumentException { + StringBuilder buf = new StringBuilder(); + try { + BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + String str; + while ((str = in.readLine()) != null) { + buf.append(str); + } + } catch (IOException e) { + throw new IOException("Cannot read json body", e); + } + String jsonStr = buf.toString(); + if (Algorithms.isEmpty(jsonStr)) { + throw new IllegalArgumentException("Cannot find json body"); + } + final JSONObject json; + try { + items = new ArrayList<>(); + json = new JSONObject(jsonStr); + JSONArray jsonArray = json.getJSONArray("items"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject object = jsonArray.getJSONObject(i); + double latitude = object.optDouble("latitude"); + double longitude = object.optDouble("longitude"); + String name = object.optString("name"); + String appModeKey = object.optString("appModeKey"); + AvoidRoadInfo roadInfo = new AvoidRoadInfo(); + roadInfo.id = 0; + roadInfo.latitude = latitude; + roadInfo.longitude = longitude; + roadInfo.name = name; + roadInfo.appModeKey = appModeKey; + items.add(roadInfo); + } + } catch (JSONException e) { + throw new IllegalArgumentException("Json parse error", e); + } + } + }; + } + + @NonNull + @Override + SettingsItemWriter getWriter() { + return new SettingsItemWriter(this) { + @Override + public boolean writeToStream(@NonNull OutputStream outputStream) throws IOException { + JSONObject json = new JSONObject(); + JSONArray jsonArray = new JSONArray(); + if (!items.isEmpty()) { + try { + for (AvoidRoadInfo avoidRoad : items) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("latitude", avoidRoad.latitude); + jsonObject.put("longitude", avoidRoad.longitude); + jsonObject.put("name", avoidRoad.name); + jsonObject.put("appModeKey", avoidRoad.appModeKey); + jsonArray.put(jsonObject); + } + json.put("items", jsonArray); + } catch (JSONException e) { + LOG.error("Failed write to json", e); + } + } + if (json.length() > 0) { + try { + String s = json.toString(2); + outputStream.write(s.getBytes("UTF-8")); + } catch (JSONException e) { + LOG.error("Failed to write json to stream", e); + } + return true; + } + return false; + } + }; + } + } + private static class SettingsItemsFactory { private OsmandApplication app; @@ -1500,6 +1689,9 @@ public class SettingsHelper { case MAP_SOURCES: item = new MapSourcesSettingsItem(app, json); break; + case AVOID_ROADS: + item = new AvoidRoadsSettingsItem(app, json); + break; } return item; } diff --git a/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java b/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java index d724e0f9ce..eaf9f9ed43 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java +++ b/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java @@ -54,6 +54,10 @@ public class AvoidSpecificRoads { public AvoidSpecificRoads(final OsmandApplication app) { this.app = app; + loadImpassableRoads(); + } + + public void loadImpassableRoads(){ for (AvoidRoadInfo avoidRoadInfo : app.getSettings().getImpassableRoadPoints()) { impassableRoads.put(new LatLon(avoidRoadInfo.latitude, avoidRoadInfo.longitude), avoidRoadInfo); } diff --git a/OsmAnd/src/net/osmand/plus/profiles/AdditionalDataWrapper.java b/OsmAnd/src/net/osmand/plus/profiles/AdditionalDataWrapper.java index 9eb2ceed13..c0300b6e9b 100644 --- a/OsmAnd/src/net/osmand/plus/profiles/AdditionalDataWrapper.java +++ b/OsmAnd/src/net/osmand/plus/profiles/AdditionalDataWrapper.java @@ -31,6 +31,7 @@ public class AdditionalDataWrapper { POI_TYPES, MAP_SOURCES, CUSTOM_RENDER_STYLE, - CUSTOM_ROUTING + CUSTOM_ROUTING, + AVOID_ROADS } } diff --git a/OsmAnd/src/net/osmand/plus/settings/DuplicatesSettingsAdapter.java b/OsmAnd/src/net/osmand/plus/settings/DuplicatesSettingsAdapter.java index 8619202977..44f4beb613 100644 --- a/OsmAnd/src/net/osmand/plus/settings/DuplicatesSettingsAdapter.java +++ b/OsmAnd/src/net/osmand/plus/settings/DuplicatesSettingsAdapter.java @@ -15,6 +15,7 @@ import net.osmand.plus.ApplicationMode.ApplicationModeBean; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; +import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.profiles.ProfileIconColors; import net.osmand.plus.quickaction.QuickAction; @@ -118,6 +119,11 @@ public class DuplicatesSettingsAdapter extends RecyclerView.Adapter impassableRoads = app.getAvoidSpecificRoads().getImpassableRoads(); + if (!impassableRoads.isEmpty()) { + dataList.add(new AdditionalDataWrapper( + AdditionalDataWrapper.Type.AVOID_ROADS, + new ArrayList<>(impassableRoads.values()) + )); + } return dataList; } @@ -262,6 +270,7 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet { List quickActions = new ArrayList<>(); List poiUIFilters = new ArrayList<>(); List tileSourceTemplates = new ArrayList<>(); + List avoidRoads = new ArrayList<>(); for (Object object : adapter.getDataToOperate()) { if (object instanceof QuickAction) { quickActions.add((QuickAction) object); @@ -272,6 +281,8 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet { tileSourceTemplates.add((ITileSource) object); } else if (object instanceof File) { settingsItems.add(new SettingsHelper.FileSettingsItem(app, (File) object)); + } else if (object instanceof AvoidRoadInfo) { + avoidRoads.add((AvoidRoadInfo) object); } } if (!quickActions.isEmpty()) { @@ -283,6 +294,9 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet { if (!tileSourceTemplates.isEmpty()) { settingsItems.add(new SettingsHelper.MapSourcesSettingsItem(app, tileSourceTemplates)); } + if (!avoidRoads.isEmpty()) { + settingsItems.add(new SettingsHelper.AvoidRoadsSettingsItem(app, avoidRoads)); + } return settingsItems; } diff --git a/OsmAnd/src/net/osmand/plus/settings/ImportDuplicatesFragment.java b/OsmAnd/src/net/osmand/plus/settings/ImportDuplicatesFragment.java index 52dd061de0..e1c9f9a503 100644 --- a/OsmAnd/src/net/osmand/plus/settings/ImportDuplicatesFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/ImportDuplicatesFragment.java @@ -19,6 +19,7 @@ import net.osmand.plus.SettingsHelper; import net.osmand.plus.SettingsHelper.SettingsItem; import net.osmand.plus.UiUtilities; import net.osmand.plus.base.BaseOsmAndDialogFragment; +import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.quickaction.QuickAction; import net.osmand.view.ComplexButton; @@ -97,6 +98,7 @@ public class ImportDuplicatesFragment extends BaseOsmAndDialogFragment implement List tileSources = new ArrayList<>(); List renderFilesList = new ArrayList<>(); List routingFilesList = new ArrayList<>(); + List avoidRoads = new ArrayList<>(); for (Object object : duplicatesList) { if (object instanceof ApplicationMode.ApplicationModeBean) { @@ -114,6 +116,8 @@ public class ImportDuplicatesFragment extends BaseOsmAndDialogFragment implement } else if (file.getAbsolutePath().contains("files/routing")) { routingFilesList.add(file); } + } else if (object instanceof AvoidRoadInfo) { + avoidRoads.add((AvoidRoadInfo) object); } } if (!profiles.isEmpty()) { @@ -140,6 +144,10 @@ public class ImportDuplicatesFragment extends BaseOsmAndDialogFragment implement duplicates.add(getString(R.string.shared_string_custom_rendering_style)); duplicates.addAll(renderFilesList); } + if (!avoidRoads.isEmpty()) { + duplicates.add(getString(R.string.avoid_road)); + duplicates.addAll(avoidRoads); + } return duplicates; } diff --git a/OsmAnd/src/net/osmand/plus/settings/ImportSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/ImportSettingsFragment.java index 0f092e6044..6b671de15a 100644 --- a/OsmAnd/src/net/osmand/plus/settings/ImportSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/ImportSettingsFragment.java @@ -20,6 +20,7 @@ import net.osmand.plus.SettingsHelper; import net.osmand.plus.SettingsHelper.SettingsItem; import net.osmand.plus.UiUtilities; import net.osmand.plus.base.BaseOsmAndDialogFragment; +import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.profiles.AdditionalDataWrapper; import net.osmand.plus.quickaction.QuickAction; @@ -124,7 +125,6 @@ public class ImportSettingsFragment extends BaseOsmAndDialogFragment } } - @SuppressWarnings("unchecked") private List getDuplicatesData(List items) { List duplicateItems = new ArrayList<>(); for (SettingsItem item : items) { @@ -151,6 +151,11 @@ public class ImportSettingsFragment extends BaseOsmAndDialogFragment if (item.exists()) { duplicateItems.add(((SettingsHelper.FileSettingsItem) item).getFile()); } + } else if (item instanceof SettingsHelper.AvoidRoadsSettingsItem) { + List avoidRoads = ((SettingsHelper.AvoidRoadsSettingsItem) item).excludeDuplicateItems(); + if (!avoidRoads.isEmpty()) { + duplicateItems.addAll(avoidRoads); + } } } return duplicateItems; @@ -165,6 +170,7 @@ public class ImportSettingsFragment extends BaseOsmAndDialogFragment List quickActions = new ArrayList<>(); List poiUIFilters = new ArrayList<>(); List tileSourceTemplates = new ArrayList<>(); + List avoidRoads = new ArrayList<>(); for (Object object : dataToOperate) { if (object instanceof ApplicationMode.ApplicationModeBean) { settingsItems.add(new SettingsHelper.ProfileSettingsItem(app, (ApplicationMode.ApplicationModeBean) object)); @@ -178,6 +184,8 @@ public class ImportSettingsFragment extends BaseOsmAndDialogFragment tileSourceTemplates.add((ITileSource) object); } else if (object instanceof File) { settingsItems.add(new SettingsHelper.FileSettingsItem(getMyApplication(), (File) object)); + } else if (object instanceof AvoidRoadInfo) { + avoidRoads.add((AvoidRoadInfo) object); } } if (!quickActions.isEmpty()) { @@ -189,10 +197,12 @@ public class ImportSettingsFragment extends BaseOsmAndDialogFragment if (!tileSourceTemplates.isEmpty()) { settingsItems.add(new SettingsHelper.MapSourcesSettingsItem(getMyApplication(), tileSourceTemplates)); } + if (!avoidRoads.isEmpty()) { + settingsItems.add(new SettingsHelper.AvoidRoadsSettingsItem(getMyApplication(), avoidRoads)); + } return settingsItems; } - @SuppressWarnings("unchecked") private List getSettingsToOperate() { List settingsToOperate = new ArrayList<>(); List profiles = new ArrayList<>(); @@ -201,6 +211,7 @@ public class ImportSettingsFragment extends BaseOsmAndDialogFragment List tileSourceTemplates = new ArrayList<>(); List routingFilesList = new ArrayList<>(); List renderFilesList = new ArrayList<>(); + List avoidRoads = new ArrayList<>(); for (SettingsHelper.SettingsItem item : settingsItems) { if (item.getType().equals(SettingsHelper.SettingsItemType.PROFILE)) { @@ -217,6 +228,8 @@ public class ImportSettingsFragment extends BaseOsmAndDialogFragment } else if (item.getName().startsWith("/routing/")) { routingFilesList.add(((SettingsHelper.FileSettingsItem) item).getFile()); } + } else if (item.getType().equals(SettingsHelper.SettingsItemType.AVOID_ROADS)) { + avoidRoads.addAll(((SettingsHelper.AvoidRoadsSettingsItem) item).getItems()); } } @@ -253,6 +266,12 @@ public class ImportSettingsFragment extends BaseOsmAndDialogFragment routingFilesList )); } + if (!avoidRoads.isEmpty()) { + settingsToOperate.add(new AdditionalDataWrapper( + AdditionalDataWrapper.Type.AVOID_ROADS, + avoidRoads + )); + } return settingsToOperate; }